Issues/Exec/Parts R2: execution document, nonconformance flow, risk & register calibration#56
Merged
Merged
Conversation
Master's structure, devel's register: the local optimum the redesign was hunting. Data renders in tables — detail rows for facts, headed data-tables for collections. A section's structure follows population: empty stays the one-line rule (ok.empty_line, reimplemented to emit the ledger row it always described), populated promotes to the panel grammar at ledger pitch (panel-ledger: header bar carrying the count and its flags in the value slot, + ADD at the edge, body a headed data-table at the parts-list 5px pitch). - Identity readout: the wrapping mono line becomes a part-identity detail table — spelled-out labels (CATEGORY, TRACKING, REORDER…), full-ISO tooltips on CREATED/UPDATED/ACTIVATED, absence as a muted dash in a row that always renders. DESCRIPTION joins as a row. - Columns regrouped: left is identity + DESIGN (REQUIREMENTS, TESTS); right is STOCK & USE (BOM — moved per amendment, STOCK, PO LINES, SUPPLIERS, WHERE USED, PROCEDURE USE, CONSUMED last — the one unbounded section). Zone captions are two nouns over the columns; the grid is a bounded identity rail plus a flexible collection track the wide tables actually spend. BOM regains its NOTES column, dropped silently by Amendment 1. - Stock carries its rung: the on-hand figure renders at 1.125rem/600 in the STOCK bar, with a derived LOW flag (coalesce(total, 0) < reorder, matching the parts-list derivation) on both the populated bar and the empty row. + ADD opens an inline receive micro-form (qty/location/lot posting to the inventory API); FULL RECORD remains the long form. - Contrast floor: --text-muted #666 (3.44:1, below AA at ledger sizes) becomes #7a7a7a (4.61:1); interactive elements never render muted — the [+] was dimmest exactly when it was the only action. One pitch for all ledger rows. - Labels spelled out (REQUIREMENTS, WHERE USED, PROCEDURE USE, RECEIVED); PRINT LABEL returns (the compression rode Amendment 1's register pass with no rationale of its own). The tier control degrades to a select above five configured tiers — tiers are project config, not a fixed trio. The tag macro drops its never-rendered activated_at parameter. - Style guide: the state-dependent section is documented, empty_line is the empty-state primitive, id-cell examples marked legacy (rule 7). CLAUDE.md: empty-state rule rewritten to name the promotion, plus a new layer rule — structure (panels, header bars, headed tables, grids) may not be deleted by a register pass; register (pitch, accent budget, voice, contrast ladder) is where density lives. Produced by a 22-agent design review (4 readers, 5 critics, adversarial verification of every recommendation against the amendment lineage) and verified by a 21-agent audit (conformance, voice, regression, consistency); all confirmed findings fixed. 578 tests pass; runtime smoke on seeded data confirmed all three population states, the receive flow end-to-end, and LOW derivation parity with the parts list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two gates, not one: raised → UNDISPOSITIONED → (disposition signed) → DISPOSITIONED → (corrective action done) → CLOSED. The blocking predicate is `undispositioned`, never `open`; holds are derived from issues at the commitment moments (COMPLETE/START/SKIP/sign-off/close-out), never stored on the step. Schema (a7c41d92e803): containment (step|op|wo|advisory) + containment_step_id, raised_step_id/raised_by_id, disposition signature (dispositioned_by_id, dispositioned_at, disposition_rationale), is_condition→actual, MIL-STD-1520 disposition set, IssueStatus collapses to open|closed, stored ON_HOLD step rows revert to in_progress. - core/holds.py: containment semantics §2 — step blocks its COMPLETE (and its OP's), bound future step blocks START, op blocks OP COMPLETE, wo blocks WO close-out, advisory blocks nothing - execution API: COMPLETE/SKIP/sign-off/START gated, 400 names the issue; auto-completion gated; capture endpoint gains should-be/is, containment, assign; ON_HOLD flipping and auto-resume machinery deleted - issues API: POST /disposition (signature releases holds live, SSE), POST /containment (narrowing audited with a note), close rules (disposition first; NC close requires corrective action), /holding - execution UI: ANOMALY capture modal (photo-first, no alert(), toast names the ticket), held controls absent + blocker lines, hold chips, live release via issue_dispositioned SSE - issue page §5: HOLDING readout, disposition panel with readiness + SIGN (confirm names what it releases), SELF-DISPOSITIONED badge, attachments upload, spawn-redline deep link for rework/repair - issues list §6: IT · TITLE · TYPE · DISP-STATE · HOLDING · PRIORITY · AGE; undispositioned-with-containment sorts first - MCP §7: raise_issue capture fields, sign_disposition, set_containment, get_holds(execution_id) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three corrections from review on the running build: - The part page frames like every other OPAL view: the identity head (tag, identity table) now lives in a panel window titled PART with the actions (PRINT LABEL / EDIT / ACTIVATE / DELETE) in its header bar — the app's panel-header-actions grammar, as on the supplier page. No more content floating on the page background. - Sections render their structure always: panel header bar (label, count, add affordance) plus the table's column heads, with one muted dash row as the body when empty — never a tall box, never narration. The one-line empty row is retired from the part page (ok.empty_line remains the primitive for index rows outside section panels). The empty-state rule in CLAUDE.md and the styleguide page are rewritten to match; the LOW flag now derives in one place since the STOCK header (figure · REC · LOW) renders in both states. - Pages restored from the back/forward cache re-sync with the server (pageshow + persisted → reload, in the base layout): creating a part lands on the part page, and backing out to the list previously showed the pre-mutation DOM until a manual refresh — same staleness after Ctrl+Z undo. 578 tests pass; live smoke confirmed 9 section panels with dash rows on an all-empty part, populated tables intact, and the guard served. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the executions OPERATIONS + META tabs with one scrolling
document: live per-step presence (step_claim), a docked MY STEP bar,
issue-bound hold points gating START and COMPLETE (undispositioned
predicate, Issues R2 §9 addendum), authored step images (versioned at
publish), execution captures with attribution, strict_sequence OPs,
and MCP parity tools for agents observing live tests.
- step_claim: claim=START, one user per step, one active step per
user (supersede), release/complete/skip end claims; zombie claims
return steps to pending
- issue_step_block: derived holds — active while the issue is
undispositioned, lift live at disposition (no status choreography);
fixes the decorative-hold bug (COMPLETE now refuses held scope,
server-side, and the control renders absent with a blocker line)
- core/execution_flow.py: single home for commitment-moment rules
(gating, completion, state builder) shared by API routes and MCP;
also fixes parent-OP resume after disposition (sibling NC count
included the issue being dispositioned under autoflush=False)
- GET /api/procedure-instances/{id}/state: the controller's document
as JSON; polled by the page every 5s and identical to the MCP
get_execution_state payload (MCP runs out-of-process, so the poll
is the primary live channel; SSE accelerates it)
- web: document landing tab (META/OPERATIONS aliases redirect), OP
cards with Abby's-rule expansion, one-line step rows with role
chips/indicators/presence, header roster + progress + meta readout
popover, rail (holds first, evidence grouped by step, reference
docs), docked bar with data capture, anomaly capture modal
(should-be/is, containment, bound steps, photo-first), lightbox,
tablet drawer via CSS only
- attachments: uploaded_by/note columns, capture + step_image kinds
- authoring: step reference images (snapshot by attachment id into
both publish implementations), strict_sequence flag
- seed: StepExecution rows now carry parent_step_order
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both 26297a8e0c76 and devel's a7b9c1d3e5f7 revised ccf93c36170a, leaving two alembic heads after the merge. Chain is now linear: ccf93c36170a -> a7b9c1d3e5f7 -> 26297a8e0c76. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review on the running build: an empty section's column heads were the visual noise that master's quiet empty bodies avoided. Empty sections now keep the panel header bar and go quiet below it — one centered muted dash row, no thead. Master's narration stays banned; retracting empty sections entirely was considered and rejected as progressive disclosure. The empty-state rule and the styleguide page updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.execdoc-lightbox sets display:flex, and author display rules override the UA stylesheet's [hidden] default — so the full-viewport rgba(0,0,0,.88) overlay rendered on page load and swallowed every click. Add the standard global guard so the hidden attribute always wins; this also fixes the JOIN/LEAVE buttons rendering before presence is known (.btn sets display). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- InstanceStatus: pending -> CUT (untouched since cut from the master procedure), in_progress -> IN_WORK. Real rename in the enum and data (migration b3d1f0a2c4e6), not a display mapping — one vocabulary for one fact across UI, API, and MCP. Step statuses unchanged. - Executions list: WORK ORDER is the leading, linked column; the database-id column is gone (voice rule: ids never render in lists). - Batch cut: InstanceCreate.quantity (1-100) cuts N identical work orders in one request, each with its own generated WO number; an explicit work_order_number requires quantity 1. The create endpoint loops the full per-instance build (steps, BUILD as-built allocation, audit log); draft-part validation hoisted ahead of the loop. - Cut form: quantity field, CUT verb, redirect to the list for batches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stylesheet/script links carried no version, so browsers kept serving stale CSS/JS on normal reloads — shop-floor pages stay open for days, which made the lightbox fix invisible to an already-open browser. static_url() appends the file's mtime; URLs change exactly when files do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g, glyph purge
Disposition state is scoped to containment-bearing issues: advisory issues
carry plain open|closed, close freely, and cannot sign a disposition (API +
MCP). The STATE filter grows to four values (open = advisory-open) across
API, MCP, and the web list, with ?state= deep links.
Issues list recolumned to IT·TITLE·TYPE·STATE·PRI·AGE: HOLDS column deleted,
plain unboxed types, UNDISP badge only for bearing+undispositioned rows,
content links render primary (row-link-plain global), one-line rows. The
execution issues tab shares the same register via the new ok.issue_state
macro. Dashboard gains a red "undispositioned holds: N" line.
Detail page: header badge only when bearing, glyphless state words, holds
line reads scope+verb ("OP 4 COMPLETE") from level-aware scope_label,
disposition panel renders only where a disposition exists or is owed, CLOSE
for advisory-open, SIGN flow matches the baseline-panel pattern with an
inline signature confirm. Attachments collapse to one file input + CAMERA
accumulator (issue page and ANOMALY modal).
Glyph purge across templates/JS, enforced by a pictograph-scan unit test;
register rules added to CLAUDE.md. Issue metrics gain advisory_open so the
disposition buckets partition again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… linking The issue page opens read-only — every field renders as a fact; a header EDIT control switches to in-place editors (per-field save on blur) and DONE returns to view. The disposition box loses the readiness checklist and the SIGN wording: one DISPOSITION button, grayed until type and rationale are filled (the tooltip names what is missing), with the inline confirm keeping the release consequence. Linking now goes both directions: in edit mode the LINKS panel attaches a work order (EXECUTION select) and a containment boundary step (BOUNDARY select over that WO's steps), so containment is settable from the issue side, not only from execution. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nto claude/competent-poitras-7724dd
… state, layout order The disposition draft (type, rationale, button) is an edit like any other — view mode renders the draft as facts. Disabled buttons now gray out (.btn:disabled had no styling, so the gate was functional but invisible). DESCRIPTION moves below the SHOULD BE / IS pair. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- step_claim table/model/endpoints/MCP tools are gone (branch never
shipped, so the infrastructure is removed, not deprecated). step_focus
replaces it: one row per (instance, user), upserted as the cursor
moves, no audit events — plus first_focused_at soft telemetry on
step_execution (never rendered as state).
- Events are exactly COMPLETE / SKIP / ISSUE. The structural gates
(bound holds, parent holds, strict_sequence, OP dependencies, open
redlines) moved from START to COMPLETE — a hold cannot physically
prevent starting work, so it no longer pretends to. SKIP keeps only
the held-scope gate.
- cut -> in_work flips on the first COMPLETE/SKIP (no separate start
moment); NC-resume returns steps to PENDING since nothing produces
IN_PROGRESS anymore.
- POST /{id}/focus + focus_step MCP tool broadcast cursor position
(cursor_moved SSE event replaces step_started); /leave drops the
cursor; state payload carries cursors[] per step (several users may
focus one step) and a focus-based roster.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…purge - Step rows carry state + indicators only — a 21-step document renders zero buttons outside the docked bar. All controls moved to the bar, which renders the FOCUSED step: [notes] [ATTACH] [ISSUE] [COMPLETE] (COMPLETE primary/rightmost, ISSUE renamed from ANOMALY), SKIP / CONSUME PARTS / + REDLINE in the overflow, SIGN OFF replaces COMPLETE when the step awaits it. _mystep.html is gone; /dockbar?step=N serves the bar for any step. - Presence: arrow keys and j/k move the cursor row-to-row, click focuses; moves POST /focus (debounced) and record no event. Other users' cursors render as bordered initials chips in the right gutter, outside the OP card border; your own chip renders inverted. Header shows "N ONLINE" with a tap/hover roster popover (the only roster when rails hide); opening the document joins, pagehide leaves. - Layout: the document centers at 860px with page-level scroll (inner scrollbars gone); the rails float in the gutters — LEFT: online roster + OP minimap (click jumps), RIGHT: issues/holds, attachments, reference docs. Rails hide below 1280px; the drawer keeps the right rail reachable. - Glyph purge (Amendment 1.1): state is words in state colors + typography, never symbols. Complete rows show dim initials+time (no mark), holds read "HOLD · IT-xxxxx" in warning color, "HELD BY" on OP heads, indicators read "2 ATT · 1 NOTE" and vanish at zero, progress drops the check (green carries it). Repo-wide template sweep for U+2600-27BF / U+1F000+ and the named set passes with zero hits — including pre-existing glyphs on the dashboard, parts ledger, requirements queue, and risk pages. - OP cards: ~2x inter-card margin; OP descriptions and step titles gain one type-scale step and one contrast tier. - Presence heartbeat: the 5s state poll and focus moves touch User.last_seen_at (throttled) — roster/cursor staleness was previously permanent because nothing in the web path updated it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s R2 Issue keeps raised_step/containment_step and takes the risk module's risk_links (the one-to-one risk relationship is gone). Alembic gets a merge revision joining the issues-R2 and risk-scenarios heads. The risk templates pass the new pictograph scan (REVIEWED stamp and blocker lines drop their glyphs, test updated accordingly). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-00b96d Issues R2: nonconformance, containment, and disposition
Claim-era tests map to their focus equivalents: focus places/moves a cursor without touching step status, two users share a step without conflict, /leave clears the cursor, COMPLETE acts directly on pending steps by any user, bound holds and strict_sequence and OP dependencies gate COMPLETE (not start), NC-resume pops steps to PENDING, and the first COMPLETE or SKIP flips the WO out of CUT. Document-web tests now carry the Amendment 1 exit criteria: zero buttons in the exec-doc column, dockbar serves ?step=N and follows the cursor, indicators and holds render as words. Also fixes a real bug the new dockbar tests caught: the partial routes (/dockbar, /step-row, /rail) rendered with inst_status undefined — every dockbar refetch dropped the ISSUE/COMPLETE/SKIP controls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ibration The risk page opens read-only — scenario fields, scores, owner, narrative, and links render as facts; EDIT switches to the in-place editors (lint overlays, selects, link/spawn controls, disposition transitions) and DONE returns to view, mirroring the issue page. The ACCEPTANCE READINESS checklist box is gone: one ACCEPT button, grayed until every hard check passes, with the tooltip naming what is missing (readiness stays the single source for the gate). The inline accept-confirm keeps the signature consequence. Once signed, the facts live in the rail (ACCEPTED · RATIONALE) — the panel's duplicate signature block is deleted. Register rows drop the per-row disposition badge: REALIZED keeps warning weight, CLOSED renders muted, everything else calm plain text. Linked-issue rows reuse the ok.issue_state register and plain mono roles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Emptiness is information only where content is expected. Declared intent decides where it's expected; data always renders. - Part gains procurement: make | buy | both (String(10), indexed, server_default make). Creation default: BUY when an external PN is provided, else MAKE — applied identically at the API create, JSON/CSV import, MCP helper, and seed; explicit values override everywhere. Migration b3c5d7e9f1a2 backfills by the same heuristic (empty-string external_pn counts as absent). Onshape sync deliberately stays at default make: its external_pn is a CAD part number, not a vendor PN (documented at the creation site; the backfill asymmetry for pre-existing synced parts is noted in the migration docstring). - Section relevance on the part page: BOM renders for make|both; SUPPLIERS and PO LINES for buy|both; STOCK, WHERE USED, PROCEDURE USE, CONSUMED, REQUIREMENTS, TESTS always. An irrelevant empty section is absent — not collapsed, absent. DATA WINS: any section with rows renders regardless of the declaration. - Empty-state fix: an empty relevant section is ONE line — "LABEL — none · + ADD" (ok.empty_line, with an optional flag slot so STOCK can carry · LOW) — never a header bar over an empty box. The boxed dash placeholders and the ledger-row CSS family are deleted. - The form gains a PROCUREMENT select in the meta grid (editable in draft and active edits); untouched it is omitted so the server heuristic stays the one home, with a live preview that follows the EXTERNAL REF input in the create flow (external_pn is trimmed on submit so the preview and the server agree). PROCUREMENT joins the identity table; PartResponse, MCP get_part/create_part echoes, and the parts CSV export expose the declaration; the MCP helper validates it through the enum and the bulk_create_parts item schema declares it (parity with create_part). CSV import parses and validates a procurement column. - CLAUDE.md empty-state rule rewritten around relevance; the styleguide page documents the one-line empty and the relevance doctrine. The manual's part-creation field table gains the row. Exit criteria verified live on seeded data: a buy part renders no BOM section and "SUPPLIERS — none" as one line with + ADD; a make part renders no SUPPLIERS or PO LINES; a BOM edge forced onto a buy part renders the section as a populated panel (data wins); every empty section on any part page is exactly one line. Backfill split the seed 28 buy / 27 make. Migration upgrade/backfill/downgrade tested against a scratch DB at the prior head. Audited by a 22-agent workflow (spec conformance, regression, voice, consistency) with adversarial verification; all confirmed findings fixed. 681 tests pass. Branch merged with features/issues-exec-parts-r2 (its base) before implementation so the heuristic builds on the config-derived tier semantics from PR #46. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR #49 (issues R2) adopted ok.empty_line per the empty-state rule while Amendment 6 changed the macro's grammar and signature in parallel. The macro stays the one home: issue callers drop the retired action_text positional (the affordance is always + ADD), the holds test asserts the new span markup, and the issues-R2 glyph purge (no pictographs; words carry state) is applied to the new part-page partials (EDIT for the pencil, YES for the preferred check). The procurement migration re-parents onto the merged head 52973e130a6f, keeping a single head. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both rails get the bordered-panel treatment; the docked bar sits above the status footer (offset measured from the footer's real height, set as --opal-footer-h on init/resize); the bottom scroll reserve drops 7rem -> 5.5rem, which was painting a phantom band under the document. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…itras-7724dd Fix part page
Keyboard parity with row click: Enter toggles the focused step's body (instructions, kit, data readout) without leaving the keyboard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gned - Expanding a step (click/Enter) reveals its full control surface: editable data fields, notes, ATTACH / ISSUE / SKIP / CONSUME PARTS / COMPLETE (SIGN OFF when awaiting). Collapsed rows stay state-only — the zero-buttons criterion now reads "no controls outside step bodies" and the web test parses the column to enforce it. - The docked bar is hidden by default; the header BAR button toggles it (persisted). completeStep reads capture fields from whichever surface invoked it (step body or bar). - The bar aligns to the document column instead of the viewport center and rides a step above the status footer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- skip refuses SIGNED_OFF steps (was silently overwriting the sign-off
record) and both skip and sign-off now require an active instance —
aborted/completed WOs no longer accept step events.
- PATCH /{id} status is no longer freeform: status derives from events;
the only settable transition is active -> aborted (COMPLETED -> CUT
reverts and re-emittable completions are gone).
- skip emits instance_started when it is the WO's first event, matching
complete.
- Concurrent COMPLETE of one step within a poll window: the status flip
is now a guarded UPDATE (precondition re-asserted at the write), so
the loser gets "Step already completed" instead of double-writing
completed_by and re-firing instance completion.
- focus_step uses an atomic upsert — two tabs racing the same user's
first focus no longer 500 on the unique constraint.
- A contingency step ON_HOLD blocks instance auto-completion (was
completing around an open NC on a touched contingency).
- touch_user_presence documents its commit constraint; stale "cannot
START" wording in NC/bind docstrings now says COMPLETE.
Findings from the core-flow and api-routes review dimensions
(wf_d20174ec-dd0); remaining dimensions re-run post-reconciliation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h Issues R2 The seam swap, as coordinated — this branch landed second, so it adopts R2's issue ontology wholesale: - core/holds.py is the single home of containment-derived blocking; execution_flow consumes get_hold_state (held_scope_blockers wraps blockers_for_complete + boundary holds; the old IssueStatus-based predicates are gone with the enum members they filtered on). - issue_step_block never ships: R2's single containment boundary (containment_step_id, "resolve by") replaces the many-to-many binding. bind_issue_hold (MCP) now moves the boundary; the ISSUE modal offers the full STEP / OP / WO / ADVISORY ladder plus one RESOLVE BY select; the NC endpoint takes containment_step_number and writes R2 fields (raised_step_id, actual, raised_by_id). - Stored ON_HOLD choreography dissolves: NC capture no longer mutates step status, maybe_resume_step_after_nc_update is deleted — holds derive and lift live at the disposition signature, per both specs. - Containment scopes per R2's table: step containment holds the raised step's and its OP's COMPLETE (boundary holds the bound step's); op containment holds only the OP's COMPLETE — sub-steps keep completing; wo containment holds instance close-out (check_instance_completion gains the wo_blocked gate). - Migration 26297a8e0c76 re-parents onto R2's merge-migration 52973e130a6f and drops the issue_step_block table; chain is linear and round-trips. - Execution tests reconciled: R2 semantics on focus-model mechanics (no /start; derived holds; _sign releases live; sibling work continues under step containment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The integration branch gained PR #48 (part page, Amendment 6) while the R2 reconciliation was landing. One template casualty: _ledger_is.html was deleted upstream (its glyph purge rode along into the new ledger structure); the empty_line macro's new signature flows through the rail's attachments line. Migration heads (exec focus b3d1f0a2c4e6 + part procurement b3c5d7e9f1a2) joined by merge migration 894e35219894. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Never instantiated; listened for the step_started event that no longer exists. The execution document's live layer is execdoc.js. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ence-f396a1 Lists, detail rows, and titles stop rendering database ids
This was referenced Jul 6, 2026
rationale, category, verification_method, tbr_owner_id, and tbr_due clear on explicit null via model_fields_set (the containment-boundary pattern); omitted fields stay untouched and non-nullable fields keep the None-skip. A wrong TBR owner no longer requires toggling tbr off and on. Closes #30 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…orphous-engineering/OPAL into claude/heuristic-meitner-aa96d4
The frozen server died at startup on fido2's missing public_suffix_list.dat — collect_data_files brings the WebAuthn package data into the bundle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 'Waiting on sub-steps' blocker labeled children by parent.step_number + '.' + child global order, naming steps that do not exist (5.6, 5.7 for children displayed 5.1, 5.2). Use step_display — the same derivation every other blocker in sequence_blockers uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
skip_blockers kept only held-scope + redline, so an OP header could be SKIPPED over pending children — a terminal parent stranding live children, inconsistent with COMPLETE's refusal. Fold the kind='children' blocker into the SKIP gate alongside redline; strict_sequence and OP-dependency ordering stay deliberately omitted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
'workable and not has_children' hid the COMPLETE control for OP rows entirely — the pre-amendment absent-control pattern, leaving the children-gate reason without a UI home. Render the standard gated control instead: complete_control with the op's complete_gate, which carries the children blocker, so the bar shows an inert COMPLETE with 'Waiting on sub-steps 1.1, 1.2'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The client updater (execdoc.js updateOpProgress) included both 'raised' and 'bound' holds; the server-rendered data-op-holds included only raised/containment holds, so a bound 'resolve by' hold flickered in via the poll and out via any re-render. A bound child hold gates that child's COMPLETE and holds the OP's completion (check_instance_completion._row_held folds blockers_for_start), so the server derivation now folds bound_blocks_by_step into op_holds_by_order — both surfaces agree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Thrust Structure & Feedline Integration step kit demanded tca_assy and tank_assy — both in _NO_STOCK and produced by earlier ops of the SAME procedure. The model has no per-step outputs (ProcedureOutput is procedure-level; step kits check flat against inventory), so 'consume the subassembly you just built' is not representable. The ops BUILD these assemblies; the kit already carries their raw materials — the two lines are removed. New test pins the invariant: per-part step-kit demand never exceeds the procedure gather list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kit_relevant gated both BOM and KITTING on kit_items|consumptions| step_kits, but KITTING is also home to PRODUCTIONS and the FINALIZE PRODUCTION control — an output-only procedure hid the tab and stranded WIP productions. Split the declaration: kit_relevant (parts IN) keeps the BOM tab; material_relevant (parts in OR out — folds productions and output_items) gates KITTING. One home per predicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From the receive rehearsal: - The receive table led with the raw line id; it now leads with the linked internal PN (voice rules), name beside it. - Locations: a datalist of known stock locations backs every location input, and FILL ALL carries the first entered location down to empty ones — receiving into an existing location needs no retyping. - A line with a quantity but no location was SILENTLY dropped from the receive payload; it now blocks with a message naming the part. Blank quantity still legitimately leaves a line outstanding (partial receive). - Purchases list: the row is the PO's click target (row onclick, supplier renders plain — accent = identifier); PO page title uses the reference, not the database id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PO-0001 line quantities came from BOM totals while shelf quantities came from kit-demand x3, so ~37 stock records cited a source line that received a fraction of what the record holds (e.g. 354 on hand vs 4 received). _stock_plan() is now the single derivation: the kit order bought exactly what the shelf holds. Pack realism is kept where the catalog declares it — bulk quantities round up to whole packs and pack prices become per-unit costs; PO-0002's hand-authored spare quantities stay in packs and expand to stock units on the line. Test pins the invariant: stock sourced from a PO line (on hand + consumed) never exceeds what the line received. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
source_po.number IS Purchase.reference ('PO-0001'); prefixing 'PO-'
rendered 'PO-PO-0001' in the SOURCE fact and the history CONTEXT.
Render the identifier verbatim with the purchases/detail.html fallback
register.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Serialized parts were seeded as a single unit record regardless of demand: the vehicle kit needs 2x each of the fuel bulkhead, interface ring, and oxidizer bulkhead, and the valve assembly is demanded 3x across the vehicle and GSE kits (2 + 1) with only 2 units around. _stock_plan now sizes serialized stock at total kit demand and _seed_inventory writes one unit-quantity record per unit (serialized = one OPAL number per physical unit, mirroring the inventory API), with WO-4's freshly built valve counting as the third sabv unit. Tests pin both invariants: every published kit line (procedure kit and consuming step kit) is coverable from on-hand stock, serialized demand is covered across procedures summed, and serialized records only ever hold 0 or 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
linkBoundaryStep() bailed on empty value, so the '—' option was a silent no-op and the server's explicit-null clear (F8 capability, model_fields_set) had no web path. Selecting '—' now sends containment_step_id: null and reloads to show the cleared state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The WO-1/WO-4 consumption loops skipped tracking=='serialized', so vehicle SN 001 was 'built' while its serialized components (bulkheads, interface rings, valve assemblies, TCA parts) sat untouched in stock — genealogy incomplete, and it masked the serialized shortages S2 fixed. The two loops collapse into _consume_kit(): bulk lines draw down the shelf record as before; serialized lines consume distinct unit records created for the build (qty 1 -> 0 with a consumption row each), leaving the shelf units for the open demand. Test walks every completed WO and asserts serialized kit lines are fully consumed as unit records; the S2 availability test pins that remaining stock still covers open demand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_switch_database/init_database create the demo file before seed_database commits, so an interrupted first seed left an empty-but-existing demo file — and every later ENTER DEMO silently opened a blank demo. enter_demo now decides freshness inside the demo session: no users and no parts means unseeded, so it seeds; a seeded demo (users exist) never re-seeds. This self-heals the interrupted case. demo_file_exists() stays for the settings UI, where file presence (RESUME vs ENTER) is the right signal. Integration test simulates the trap: touch the demo file, enter_demo, assert the demo is seeded; the existing resume test pins idempotence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…copy F2: get_current_step_data returned the first non-terminal step by step_number — always the level-0 OP header, which the children gate 400s on COMPLETE and which SKIP would commit wholesale. Select the next actionable LEAF (level>0 or childless op), mirroring the web dockbar's bar_step pick; Focus/Complete/Skip/Add Note/Log NC all share this one selection home. F6: kit availability read required_quantity/quantity and available_quantity/available, but KitAvailabilityItem sends quantity_required/quantity_available — rows rendered 0/'?' always. Fix the field names in an extracted, testable _kit_row_label and surface the locations' OPAL numbers. F12 (TUI half): the finalize confirmation said 'execution #<db id>'; lead with the work order number, else procedure name + cut date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e case The guard was db.query(Part).first() alone — a configured real instance (users, workcenters, project config, no parts yet) sailed through, and seed_database then OVERWROTE the live project_config and inserted the publicly documented build/kestrel-demo admin. On a LAN-exposed instance that is a security problem. The CLI now refuses when the database has ANY of parts, users, workcenters, suppliers, or a saved project config, names exactly what it found on stderr, and exits 1; opal seed --force remains for the deliberate case. Integration tests cover refusal (with and without parts), the untouched-database guarantee, --force, and the normal fresh-database path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
'182 parts, 4 published procedures' had drifted (the seed now carries 6 procedures) — counted facts about database contents have exactly one home, the database. The demo card now says what the demo IS (part catalog, BOM, procedures, live work orders, issues, risks, POs) with no numbers to drift. No test asserted the old copy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 'already holds COMPLETE of step X' early return left the orphaned- issue adoption (procedure_instance_id) merely flushed; call_tool's cleanup closes (rolls back) the session, silently dropping the very adoption the cross-WO seam exists for. Commit before returning, mirroring the normal path (_set_containment commits). Test asserts the handler leaves no open transaction and the adoption is visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
From the PO-receive rehearsal: generic hardware (the 1/4-20 threaded stud, nuts, washers, raw stock) carried tier 1 — FLIGHT, which enforces lot tracking and auto-serials at receive. Tier 1 now follows the guidebook's own emphasis: pressure-bearing (bulkheads, tank casing and bolts' machined counterparts, valves, TCA parts, propellant-path fittings/hoses/seals) and recovery-critical (chutes, cords, altimeters, recovery load-path hardware, shear pins). 45 generic COTS parts — fasteners, raw stock, commodity electrical, consumables — move to tier 3, renumbered SPX-D-0014..0058 so the part number's tier code stays truthful. Tier descriptions updated to declare the philosophy. The lot-gate receive demo keeps its tier-1 lot-tracked purchased parts (the -238 O-rings of the erratum NC, QD seals, graphite gaskets), and the backordered PO-0002 O-ring line still exercises the gate on receive. Test pins the assignments, PN/tier-code coherence, and the surviving lot-tracked tier-1 stock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /api/procedure-instances serialized every step's notes and each note's author via lazy loads — the TUI refresh degraded O(steps+notes). selectinload the chain (and joinedload the procedure), and batch the page's version lookup into one query. Payload kept intact: the response model is shared with the detail endpoint and the TUI consumes it, so trimming bodies is the wrong altitude here — eager loading alone makes the query count flat (asserted by test). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Search labeled executions 'Execution #<db id>' with the work order demoted to sublabel. Lead with the work order number (procedure name as sublabel); WO-less cuts fall back to procedure name + cut date — a database id never renders. Also: ruff format pass over the touched files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es-seed Demo seed sweep fixes: coherent PO/stock story, serialized kitting, lifecycle & CLI guards, tier calibration
…es-core R2 sweep fixes: children gate, hold derivations, TUI targeting, N+1, register
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integration PR bringing the R2 line into devel — the 1.4.0 feature set.
Core features
StepNoterows on any step at any time — migration backfills the old blob.Demo: Mojave Sphinx (#64, #67)
Kestrel replaced by a demo built from Half Cat Rocketry's HCR-5100 guidebook (GPL, attributed): 182 parts / 197 BOM lines / 7 vendors reconciled to the book, 6 procedures (Vehicle Assembly 11 ops + component builds for ball valves & fin brackets with outputs, GSE, Launch Operations with 20 real signoff hold-points + 2 contingency ops, maintenance), 15 requirements, 17 risks, live demo state incl. a WO held by a real guidebook erratum NC.
Register & traceability passes (live-rehearsal driven)
OPAL-# · location · qty · lot.Security & correctness
tojsononly in single-quoted event attributes, enforced by a template lint test (Stored XSS / broken EDIT handler: BOM table inline onclick escapes only single quotes #47/[low priority] confirm_btn macro interpolates message into inline JS without tojson #55 + the ATTACH/ISSUE regression).issue_dispositioned, TUI endpoints, parent-OP COMPLETE refused with open children.State
c7a2e9d41f88(notes migration atop894e35219894).v1.4.0-beta.1on devel, version1.4.0b1; satellites Startup guard: refuse to serve when DB revision is unknown to the branch #57/Startup FTS integrity check + full-chain migration smoke test (issue #43) #58 merge to devel separately; Part number variants: optional {variant} token, derived families #45 pending Abby.🤖 Generated with Claude Code