Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ Moderately rounded controls and fields sit within larger rounded evidence and de
- **Actions:** cobalt primary buttons, white text, restrained background hover; secondary actions use white or transparent backgrounds and blue text. Keyboard focus uses a 3px cobalt outline with 4px offset. Disabled buttons retain their disabled behaviour and reduced opacity.
- **Navigation:** native header links preserve real destinations. Active review links use cobalt text and a bottom rule with aria-current. Landing navigation switches to Reviews, Decisions and Follow-up within the operating journey.
- **Evidence selectors:** selected state uses pale blue, cobalt text and a labelled selected/pressed state. Findings retain title, explanation and source context.
- **Evidence panels:** tonal backgrounds contain dates, facts, quoted narrative, source files and a separately labelled review question. File names can wrap.
- **Evidence panels:** tonal backgrounds lead with the practical finding, dates, quoted narrative and a separately labelled review question. The finish example states the schedule/update contradiction in its heading. The review finding uses “The finish moved N days. The update needs clarification.” while retaining the exact narrative as evidence. Review provenance sits in “Source files and check time”; exact source identity and fields remain in “Inspect source files and fields”, alongside activity or relationship evidence disclosures. File names can wrap.
- **Inputs and decisions:** white fields on tonal panels, visible labels and thin borders. Owner and rationale remain required; conditional decisions retain condition, owner and due date. Persistent errors include recovery guidance.
- **Preview and walkthrough:** the clearly labelled synthetic preview reads the real comparison engine. Factual questions are fixed independently of narrative matching. Replay explicitly draws the finish relationship over 700ms; evidence changes use bounded 180ms transitions. No looping motion. Reduced motion removes animation and replay while retaining facts and relationships. Walkthrough records remain disposable and isolated from ordinary decisions.

**The Human Decision Rule.** Interface hierarchy leads from fact to source to question before presenting the action that records a person's decision.
**The Human Decision Rule.** Interface hierarchy leads with the finding and next review question, keeps source evidence easy to inspect, and leaves the decision to a person.

## Do's and Don'ts

Expand Down
2 changes: 1 addition & 1 deletion PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ No verified customer logos, testimonials, time savings, independent accuracy cla
2. Keep observations, narrative claims, unresolved questions and human decisions distinct.
3. Demonstrate real behaviour with clearly labelled synthetic inputs.
4. State practical limits at the point where they affect interpretation.
5. Make product value understandable to a visitor without hiding the evidence from a practitioner.
5. Make product value understandable to a visitor without hiding the evidence from a practitioner. Lead with the practical finding and next review question; keep source files, check time and exact fields available through labelled disclosures.
2 changes: 1 addition & 1 deletion Processing/tests/browser_change_assurance.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def run_recovery(browser):
return results;
}""", [(ROOT / "docs/demo/northstar-previous.xer").read_text(), (ROOT / "docs/demo/northstar-current.xer").read_text()])
assert all(rejected.values()), rejected
page.get_by_role("button", name="Use your own XER files").click()
page.get_by_role("button", name="Use your own schedules").click()
page.locator("#assurancePrevious").set_input_files(ROOT / "docs/demo/northstar-previous.xer")
page.locator("#assuranceCurrent").set_input_files(ROOT / "docs/demo/riverside-current.xer")
page.get_by_role("button", name="Check decision readiness").click()
Expand Down
2 changes: 1 addition & 1 deletion Processing/tests/browser_product_landing.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def landing(browser):
page.on("request", lambda request: requests.append((request.method, request.url)))
loaded(page)
expect(page.locator("#productTitle")).to_have_text("The schedule changed.Did the update explain it?")
expect(page.locator("#exampleTitle")).to_have_text("The finish moves by 73 days.")
expect(page.locator("#exampleTitle")).to_have_text("The finish moves by 73 days. The update says unchanged.")
expect(page.locator("#exampleComparison")).to_contain_text("15 Dec 2027")
expect(page.locator("#exampleComparison")).to_contain_text("26 Feb 2028")
assert page.evaluate("localStorage.getItem('projectlens:change-decisions:v1')") is None
Expand Down
10 changes: 5 additions & 5 deletions docs/change-assurance.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@
<div id="productLanding">
<section class="product-hero" id="top" aria-labelledby="productTitle">
<h1 id="productTitle">The schedule changed.<br>Did the update explain it?</h1>
<p class="product-introduction">Compare two Primavera P6 schedules with the project update. See the evidence behind the movement, then record a human decision.</p>
<p class="product-introduction">Compare two project schedules from Primavera P6 planning software with the project update. See the evidence behind the movement, then record a human decision.</p>
<div class="product-actions">
<button class="assurance-primary" id="loadAssuranceDemo" type="button">Try the Northstar example</button>
<button class="product-text-button" id="showOwnPack" type="button">Use your own XER files</button>
<button class="product-text-button" id="showOwnPack" type="button">Use your own schedules</button>
</div>
<p class="product-privacy">Runs in your browser. Your schedule files stay with you.</p>
</section>
Expand All @@ -70,7 +70,7 @@ <h1 id="productTitle">The schedule changed.<br>Did the update explain it?</h1>
<button type="button" role="tab" id="example-relationships" aria-selected="false" aria-controls="examplePanel" data-example="relationships" tabindex="-1" disabled>Relationship changes</button>
</div></div>
<div class="example-panel" id="examplePanel" role="tabpanel" aria-labelledby="example-finish" tabindex="0">
<h2 id="exampleTitle">The finish moves by 73 days.</h2>
<h2 id="exampleTitle">The finish moves by 73 days. The update says unchanged.</h2>
<div class="example-comparison" id="exampleComparison"><div><span>Previous finish</span><strong>15 Dec 2027</strong></div><div class="example-connection"><span>+73 days</span><div class="example-line" aria-hidden="true"></div></div><div><span>Current finish</span><strong>26 Feb 2028</strong></div></div>
<div class="example-reading"><div><span>The update says</span><blockquote id="exampleClaim">“The key completion date is unchanged.”</blockquote></div><div><span>The schedules show</span><p id="exampleObservation">Scheduled finish moves by 73 days.</p></div></div>
<div class="example-question"><div><span>Next review question</span><p id="exampleQuestion">What explains the revised finish?</p></div><button type="button" id="exampleInspect" class="product-text-button" aria-expanded="false" aria-controls="exampleSource" disabled>Inspect source evidence</button></div>
Expand Down Expand Up @@ -114,7 +114,7 @@ <h3>Follow one complete review.</h3>
<button type="button" id="backToProduct" class="product-text-button">Back to product</button>
<div class="compact-heading">
<div><h2 id="intakeTitle">Add your two XER files</h2></div>
<p>Export both schedules from Primavera P6 (File &rarr; Export &rarr; Primavera XER): the previous submission and the current one. Your browser reads the files and uploads nothing. Add the narrative if you want ProjectLens to check what the update claims.</p>
<p>XER is the schedule export format from Primavera P6 planning software. Export both schedules (File &rarr; Export &rarr; Primavera XER): the previous submission and the current one. Your browser reads the files and uploads nothing. Add the narrative if you want ProjectLens to check what the update claims.</p>
<p class="sample-pair">No XER files to hand? Download a synthetic pair from either example project and choose the files below: <a href="./demo/northstar-previous.xer" download>Northstar previous</a> &middot; <a href="./demo/northstar-current.xer" download>Northstar current</a> · <a href="./demo/riverside-previous.xer" download>Riverside previous</a> &middot; <a href="./demo/riverside-current.xer" download>Riverside current</a>. Optional narrative: <a href="./demo/riverside-narrative.txt" download>Riverside narrative</a>.</p>
</div>
<div class="pack-grid">
Expand Down Expand Up @@ -144,7 +144,7 @@ <h3>Follow one complete review.</h3>

<section class="readiness-workspace" id="readinessWorkspace" hidden>
<div class="review-context">
<div><span>Active review</span><strong id="reviewName">Northstar Grid Connection</strong><small id="reviewEvidenceVersion"></small></div>
<div><span>Active review</span><strong id="reviewName">Northstar Grid Connection</strong><details class="review-source-details"><summary>Source files and check time</summary><small id="reviewEvidenceVersion"></small></details></div>
<div class="review-tools"><button type="button" id="saveDraft">Save draft on this device</button><button type="button" id="startAnotherReview">Start another review</button></div>
</div>

Expand Down
12 changes: 6 additions & 6 deletions docs/change-assurance.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ function analyseReview(previous, current, narrative) {
if (statedStable && finishMovement >= 7) {
blockers.push({
type: "Contradiction",
title: `The finish moved ${finishMovement} days but the narrative says the schedule is stable`,
title: `The finish moved ${finishMovement} days. The update needs clarification.`,
detail: `${formatDate(previous.project.scheduledFinish)} → ${formatDate(current.project.scheduledFinish)}.`,
});
} else if (finishMovement !== null && Math.abs(finishMovement) >= 7) {
Expand Down Expand Up @@ -342,10 +342,10 @@ function renderReview(review, restoredContext = null) {

const facts = [
["Scheduled finish", review.finishMovement === null ? "Not comparable" : `${review.finishMovement > 0 ? "+" : ""}${review.finishMovement} days`, `${formatDate(review.previous.project.scheduledFinish)} → ${formatDate(review.current.project.scheduledFinish)}`],
["Material activities", String(review.changes.changes.length), "Activities with material date, float, constraint or presence changes"],
["Constraint changes", String(review.changes.constraintChanges), "Compared by constraint type and date"],
["Float erosion", String(review.changes.floatErosion), "Changed activities with lower total float"],
["Relationship changes", String(review.changes.relationChanges), "Added or removed predecessor signatures"],
["Material activities", String(review.changes.changes.length), "Activities with changed dates, spare time, fixed dates, or added or removed activities"],
["Constraint changes", String(review.changes.constraintChanges), "Changes to imposed dates or rules (constraints)"],
["Float erosion", String(review.changes.floatErosion), "Activities with less spare time before delaying the finish (total float)"],
["Relationship changes", String(review.changes.relationChanges), "Changed activity links: predecessor, successor, link type or lag"],
["Evidence supplied", "2 XER files", $("#assuranceNarrative").value.trim() ? "Schedule narrative included" : "No narrative supplied"],
];
$("#evidenceSummary").innerHTML = facts.map(([label, value, detail]) => `
Expand Down Expand Up @@ -987,7 +987,7 @@ function renderSelectedFinding(index) {
<p>${escapeHtml(finding?.detail || "No blockers found by these fixed checks.")}</p>
<div class="review-question"><h4>Next review question</h4><p>${escapeHtml(finding ? questionFor(finding) : "What evidence supports the proposed update?")}</p></div>
<dl class="source-dates"><div><dt>Previous finish</dt><dd>${escapeHtml(formatDate(review.previous.project.scheduledFinish))}</dd></div><div><dt>Current finish</dt><dd>${escapeHtml(formatDate(review.current.project.scheduledFinish))}</dd></div></dl>
<p class="source-files">Sources: ${escapeHtml(evidenceVersion(review))} · PROJECT scheduled finish; TASK activity dates, constraints and total float.</p>
<details><summary>Inspect source files and fields</summary><p class="source-files">Sources: ${escapeHtml(evidenceVersion(review))} · PROJECT scheduled finish; TASK activity dates, constraints and total float.</p></details>
<details ${constraintFinding || floatFinding || relationshipFinding ? "open" : ""}><summary>${relationshipFinding ? `Relationship field evidence (${review.changes.relationEvidence.length})` : `Activity field evidence (${relevant.length})`}</summary><ul>${relationshipFinding ? relationshipRows : activityRows}</ul></details>
<h4>Narrative under review</h4><blockquote>${escapeHtml(($("#assuranceNarrative").value.split(/\n\s*\n/).find(paragraph => /unchanged|on track|stable|no impact/i.test(paragraph)) || $("#assuranceNarrative").value || "No narrative supplied.").trim())}</blockquote><details><summary>Read full narrative</summary><p>${escapeHtml($("#assuranceNarrative").value || "No narrative supplied.")}</p></details>
${assuranceState.originalNarrative !== null && assuranceState.originalNarrative !== $("#assuranceNarrative").value ? `<details class="original-narrative"><summary>Read original narrative from the first check</summary><p>${escapeHtml(assuranceState.originalNarrative || "No narrative was supplied at the first check.")}</p></details>` : ""}
Expand Down
5 changes: 5 additions & 0 deletions docs/product-landing.css
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,8 @@ body.product-design { margin: 0; background: var(--pl-white); color: var(--pl-in
.product-design .tour-guide { top: 0; }
}
@media (prefers-reduced-motion: reduce) { .product-design *, .product-design *::before,.product-design *::after { animation: none !important; scroll-behavior: auto !important; transition-duration: .01ms !important; } }

/* Keep review provenance available without preceding the practical outcome. */
.review-source-details { margin-top: 8px; max-width: 72ch; }
.review-source-details summary { cursor: pointer; font-size: 14px; }
.review-source-details small { display: block; overflow-wrap: anywhere; margin-top: 8px; }
2 changes: 1 addition & 1 deletion docs/product-landing.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
tabs.forEach(tab => { const active = tab.dataset.example === name; tab.setAttribute('aria-selected', String(active)); tab.tabIndex = active ? 0 : -1; });
byId('examplePanel').setAttribute('aria-labelledby', `example-${name}`);
const options = {
finish: { title: `The finish moves by ${review.finishMovement} days.`, claim: (narrative.match(/the key completion date[^.!?]*[.!?]/i)?.[0] || sentence(/key completion date/)).replace(/^the/, 'The'), observation: `Scheduled finish moves by ${review.finishMovement} days.`, question: 'What explains the revised finish?' },
finish: { title: `The finish moves by ${review.finishMovement} days. The update says unchanged.`, claim: (narrative.match(/the key completion date[^.!?]*[.!?]/i)?.[0] || sentence(/key completion date/)).replace(/^the/, 'The'), observation: `Scheduled finish moves by ${review.finishMovement} days.`, question: 'What explains the revised finish?' },
constraints: { title: `${review.changes.constraintChanges} activities have changed constraints.`, claim: sentence(/programme continues to manage/), observation: 'Constraint types or dates changed between the two exports.', question: 'Which instruction or approval explains each constraint change?' },
relationships: { title: `${review.changes.relationChanges} relationship signatures changed.`, claim: sentence(/No material change to the delivery sequence/), observation: 'Predecessor links, relationship types or lag values differ.', question: 'What changed in the delivery sequence, and why?' },
};
Expand Down
Loading