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

@@ -397,6 +397,20 @@ function constraintNames(){
function nextSeq(){ return savedPackages.length+1; }
// ── SOP LOADING ──────────────────────────────────────────────────────────────
// S7 / T9.4: the one sample entry point. loadSampleSOP()/loadExample() stay as
// internals (probes and this function call them); the USER reaches sample data
// through exactly this, which says what it will and will not do before doing it.
async function loadSampleAll(){
if(!(await wpConfirmDialog({
title:'Load sample data',
message:'Replaces the SOP shown on this page with the fictional Micron sample and fills '
+ 'the form with the example work package.\n\nThis page only: nothing is written to '
+ 'the project unless you then save.',
okLabel:'Load the sample'}))) return;
loadSampleSOP();
loadExample();
}
function loadSampleSOP(){ SOP=JSON.parse(JSON.stringify(SAMPLE_SOP)); applySOP(); newPackage(); toast('Sample SOP loaded — ' + (SOP.project&&SOP.project.name||'project')); track('sample_loaded'); }
function importSOP(ev){
const f=ev.target.files&&ev.target.files[0]; if(!f) return; const r=new FileReader();
@@ -531,7 +545,7 @@ function renderCtxBar(){
if(!SOP){
bar.innerHTML = activeProjectId
? `<div class="ctx-empty">No SOP found for this project yet — complete the <strong>SOP Configuration</strong> first, then return here.</div>`
: `<div class="ctx-empty">No SOP loaded — <button class="link-btn" onclick="loadSampleSOP()">load the sample</button> or import one from the Configuration tool.</div>`;
: `<div class="ctx-empty">No SOP loaded — import one from the Configuration tool, or use <strong>Load sample data</strong> in the toolbar above.</div>`;
return;
}
const p=SOP.project||{}, g=SOP.governance||{};