T9.4 - S7: one sample-data affordance, confirmed, and fenced off the project

Four affordances under three names became ONE: "Load sample data", on the
creator's toolbar, at the far end of two separators from the live actions
(New / Duplicate), pushed right with its own gap. It confirms through the
T7.9 dialog, naming exactly what it does - and what it does not: "This page
only: nothing is written to the project unless you then save." The probe
verifies the fence the way the done-when demands - against a REAL project,
reading the server's SOP and work-package list before and after and asserting
byte-identical.

Gone: the wizard's header "Load sample" (the dangerous one: it filled the
state completeSOP() pushes to the LIVE project, one click, no confirm, no
undo - reconciled with D1 exactly as the task records: the creator's control
is the survivor, the wizard copy goes), the creator's split Sample SOP /
Load example pair (now internals behind the one entry point), and the
empty-state context bar's third button (its text now points at the toolbar
control). The location/material "Load sample values" buttons stay: they fill
a PASTE BOX that acts only through an explicit, dry-runnable import - a
different thing, stated in the code.

Probes re-pointed with reasons in place: frame_check's D1 toolbar list names
the consolidated control; validation_check's sample-driven toast checks
became the-affordance-is-gone checks (and its stale showAnalytics drive,
orphaned by T7.10, became a the-duplicate-stays-gone check).

Verification (each probe run alone): NEW tests/sample_check.py 10/10.
Regressions: validation_check 77/77, frame_check 38/38, kitting_check 26/26,
export_check 20/20, sections_check 95/95.

Items: S7 (D1 reconciliation honored)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 13:05:56 -07:00
parent c2a1cc7c26
commit b44afa7672
8 changed files with 221 additions and 147 deletions

View File

@@ -231,48 +231,19 @@ def run(page, base, tok):
".filter(e => (e.textContent||'').trim()).map(e => e.id))"))
chk("no error is shown on a step nobody has tried to leave", not marks, marks)
print("\n5. the wizard opens no native dialog at all")
print("\n5. the wizard opens no native dialog, and its sample affordance is gone")
open_wizard(page, base, tok)
# Drive every path that used to raise one.
page.eval("loadSampleData()")
settle(1.6)
chk("loading the sample announces instead of interrupting",
page.eval("!document.getElementById('wp-toast').hidden"))
chk("...politely, because it is a confirmation not an error",
page.eval("document.getElementById('wp-toast').getAttribute('role')") == "status")
chk("...and says what happened",
"Sample data loaded" in (page.eval(
"(document.getElementById('wp-toast')||{}).textContent||''")),
ascii_(page.eval("(document.getElementById('wp-toast')||{}).textContent||''")))
chk("...and can be dismissed from the keyboard", page.eval("""(() => {
const b = document.querySelector('#wp-toast .wp-toast-close');
if (!b) return false;
b.focus();
const focused = document.activeElement === b;
b.click();
return focused && document.getElementById('wp-toast').hidden;
})()"""))
# T9.4 (S7) removed this page's Load sample - it filled the state that
# completeSOP() pushes to the LIVE project, one click, no confirm. The
# suite's one sample control is on the creator. What this section still
# owns: the wizard must not regrow the affordance, and must stay dialog-free.
chk("the wizard's Load sample control is gone (S7/T9.4)",
page.eval("!document.getElementById('load-sample-btn')")
and page.eval("typeof loadSampleData === 'undefined'"))
chk("...and no native dialog fired anywhere so far",
not json.loads(page.eval("JSON.stringify(window.__dialogs||[])")),
page.eval("JSON.stringify(window.__dialogs||[])"))
# This used to be "loading the sample on the wrong tab interrupts", because the
# wizard's Load sample was context-aware: on the WP tab it reached into the
# iframe and called the creator's own loadExample(), and complained when the
# frame was not there. B7/T7.1 made the creator a page, so there is no wrong
# tab to be on and nothing to reach into - this page's sample is the SOP
# sample, always. What is left to check is that the one remaining edge, the
# SOP gate, still says something rather than nothing.
page.eval("showCreatorGate(); loadSampleData()")
settle(1.2)
chk("from the gate, loading the sample announces rather than failing silently",
page.eval("!document.getElementById('wp-toast').hidden"))
chk("...politely, because being on the gate is not an error",
page.eval("document.getElementById('wp-toast').getAttribute('role')") == "status",
ascii_(page.eval("(document.getElementById('wp-toast')||{}).textContent||''")))
chk("...and says where the work package sample actually is",
"Work Package Creation" in (page.eval(
"(document.getElementById('wp-toast')||{}).textContent||''")),
ascii_(page.eval("(document.getElementById('wp-toast')||{}).textContent||''")))
page.eval("switchTool('sop')")
settle(0.6)
# Through the control a person uses — the modal's text input — not through the
# library helper, which has no duplicate message to give.
@@ -304,16 +275,12 @@ def run(page, base, tok):
"no feedback to export" in (page.eval(
"(document.getElementById('wp-toast')||{}).textContent||''")).lower(),
ascii_(page.eval("(document.getElementById('wp-toast')||{}).textContent||''")))
page.eval("showAnalytics()")
settle(0.5)
chk("the usage summary is shown rather than crammed into a confirmation",
"Step 1:" in (page.eval("(document.getElementById('wp-toast')||{}).textContent||''")),
ascii_(page.eval("(document.getElementById('wp-toast')||{}).textContent||''"))[:100])
chk("...with the download offered as an action beside it",
page.eval("""(() => {
const b = document.querySelector('#wp-toast .wp-toast-action');
return !!b && /download/i.test(b.textContent);
})()"""))
# showAnalytics() left this page at T7.10 (D5): the usage report lives on
# the admin console now, one implementation for the whole suite. What this
# section keeps: the duplicate must not regrow here.
chk("the wizard's usage-summary duplicate is gone (D5/T7.10)",
page.eval("typeof showAnalytics === 'undefined'")
and page.eval("typeof analyticsLoad === 'undefined'"))
fired = json.loads(page.eval("JSON.stringify(window.__dialogs||[])"))
chk("nothing anywhere in that opened alert / confirm / prompt", not fired, fired[:4])