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

@@ -391,95 +391,14 @@ function initializeWPTypes(){
}
// ── LOAD SAMPLE DATA ──────────────────────────────────────────────────────────
// This page's sample is the sample SOP. It used to reach into the frame and call
// the creator's loadExample() when the WP tab was open; the creator is its own
// page since T7.1 and carries its own sample controls in its toolbar (D1), so
// there is nothing to reach into and nothing to duplicate here.
//
// S7 still counts more sample affordances than it should. T9.4 reduces them; this
// task only stops one of them pretending to belong to a document it is not in.
function loadSampleData(){
if(currentTool && currentTool !== 'sop'){
wizardToast('The work package sample is on the Work Package Creation page, in its toolbar.',
{role: 'status'});
return;
}
// Populate Step 1
document.getElementById('proj_name').value = 'MICRON_PH1_CUP_HPM_FMCS INSTALL';
document.getElementById('proj_number').value = '26-67-008';
document.getElementById('proj_client').value = 'Micron Technology, Inc.';
document.getElementById('proj_division').value = 'Semiconductor';
document.getElementById('proj_site').value = 'Boise, ID — Fab 7';
// Removed at T9.4 (S7). This was the affordance one click from live project
// data: it filled the wizard state that completeSOP() pushes to the PROJECT,
// with no confirm and no undo. The suite's one sample control lives on the
// creator's toolbar, where it fills that page only. The location and material
// list steps keep their "Load sample values" buttons - those fill a PASTE BOX
// that acts only through an explicit, dry-runnable Import, which is a
// different thing from loading sample data into a live configuration.
// Populate Step 2. The leadership slots are account pickers now, so the sample's
// fictional names can't be "selected" — setting .value on a <select> with no
// matching option silently does nothing. Store them as names without an account,
// which is exactly how the picker shows a person who isn't a suite user yet.
state.team.pm = 'Mariano Sanchez';
state.team.apm = 'Assistant PM';
state.team.cm = 'K. Boyd';
state.team.qm = 'D. Nguyen';
state.teamIds = {pm:'', apm:'', cm:'', qm:''};
state.qaGroupIds = [];
renderTeamPickers();
// Step 3 — standard required roles
if(state.signoffRoles[0]) state.signoffRoles[0].role = 'Superintendent';
if(state.signoffRoles[1]) state.signoffRoles[1].role = 'Foreman';
const stEl = document.getElementById('role_super_title'); if(stEl) stEl.value = 'Superintendent';
const ftEl = document.getElementById('role_foreman_title'); if(ftEl) ftEl.value = 'Foreman';
state.signoffRoles[0].name = 'John Smith'; state.signoffRoles[0].userId = '';
state.signoffRoles[1].name = 'Mike Jones'; state.signoffRoles[1].userId = '';
renderSignoffRolePickers();
// Populate Step 5
document.getElementById('gov_woformat').value = 'WP##-[Sector]-[TYPE]';
document.getElementById('gov_wosize').value = 'Standard — 35 days (≈4080 hrs)';
document.getElementById('gov_disciplines').value = 'Mechanical, Electrical, Tech';
document.getElementById('gov_discmode').value = 'choice';
document.getElementById('gov_size_hours_max').value = '80';
// Populate Step 6
document.getElementById('qual_qcreq').value = 'Yes — Detailed inspection items';
document.getElementById('qual_photo').value = 'Key checkpoints only';
document.getElementById('qual_hold').value = 'HOLD: Prime QAQC to inspect rough-in before cover/cover-up.\nWITNESS: Client QC to observe megger test before energization.';
// Step 7 already has defaults
// CR-016 / T5.7. The sample is the Micron EUV configuration, and Micron does not
// use Assets: its content duplicates the database Clinton's team maintains, and
// that integration is deferred. Off by toggle, so the section and its model stay
// in the application for the integration to land in — and any other project can
// turn it back on from step 12 without a code change.
//
// A partial map on purpose. WPSections.normalize fills the rest in as ON, so a
// section added after today is not silently off for this SOP.
if(typeof WPSections !== 'undefined'){
state.sections = WPSections.normalize({assets: false, kitting: false}); // CR-009: Micron EUV is not kitting today
state.fields = WPSections.normalizeFields({costCode: false, acumaticaTask: false}); // CR-002, mirrored on the export (T9.1)
}
// The Micron FMCS sample includes BIM/VDC — enable it so the sequence shows the
// full BIM → construction flow (BIM steps first) and the Creator offers IWP/EWP.
state.bimEnabled = true;
const beEl = document.getElementById('bim_enabled'); if(beEl) beEl.checked = true;
enableBIM();
// Collect all data
collectStepData();
track('sample_loaded');
wizardToast('Sample data loaded. Every step now holds example values — edit or replace any of them.');
// Switch to step 1. Every step now holds sample content, so the rail marks them
// all as visited — otherwise a fully populated wizard shows ten unstarted steps.
for(let i = 1; i <= LAST_STEP; i++) _visitedSteps.add(i);
currentStep = 1;
updateStepUI();
updateProjectDisplay();
}
// ── RESTORE A COMPLETED SOP (for "Review" + WP tab across reloads) ─────────────
function restoreSavedSOP(){
let savedState = null, savedSop = null, complete = false;
try {

View File

@@ -40,7 +40,6 @@
</div>
</div>
<div class="header-right">
<button id="load-sample-btn" class="header-button" onclick="loadSampleData()" title="Load sample data for the current tool (SOP or Work Package)">Load sample</button>
<button class="header-button" onclick="toggleComments()" title="Leave feedback for the current step">Feedback</button>
<button class="header-button" onclick="openHelp()" title="How the suite works + key concepts">Help</button>
<!-- A4/S9: the orphaned "1 / 10" counter lived here. It is retired — the

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||{};

View File

@@ -84,12 +84,15 @@
<button class="btn btn-ghost" onclick="openSopModal()">View SOP</button>
<button class="btn btn-ghost" onclick="document.getElementById('sop-import').click()">Import SOP</button>
<input type="file" id="sop-import" accept="application/json" style="display:none" onchange="importSOP(event)">
<button class="btn btn-ghost" onclick="loadSampleSOP()">Sample SOP</button>
<button class="btn btn-ghost" onclick="loadExample()">Load example</button>
<span class="wp-toolbar-sep" aria-hidden="true"></span>
<!-- T7.10 moves this to the admin console and deletes one of the two
implementations. It stays visible here until then rather than being
deleted by the task that dissolved the frame. -->
<!-- S7 / T9.4: THE sample-data affordance - exactly one in the whole suite,
under one name. Pushed to the far end, away from the live actions (New /
Duplicate sit at the other side of two separators), it confirms before
acting and names exactly what it does. It fills THIS PAGE only: nothing
is written to the project unless the user then saves, which the probe
verifies against a real project. The wizard's copy and the split
Sample SOP / Load example pair are gone. -->
<span class="wp-toolbar-sep" aria-hidden="true" style="margin-left:auto"></span>
<button class="btn btn-ghost" onclick="loadSampleAll()">Load sample data</button>
</div>
<div class="dev-banner" id="dev-banner" style="display:none">⚙ DEV MODE — usage tracking paused. This session's actions are not being recorded.</div>