Build Log
Alloy — Product/Workpiece/Arc Vertical Slice
July 20, 2026
Corrected the Product/Workpiece/Arc identity model through two review rounds, then built and proved a real end-to-end entry path (Workpieces → Product → Workpiece → Orient → Charge) — stopping honestly before Ignite, which needs its own dedicated rebuild.
Timeline
What shipped
products/alloy/lib/db/schema.ts — new products table (kind/status enums), workpieces.productId + completionCondition + completionConditionDeferredReason, workpieceStatusEnum extended (draft/active/paused/completed/archived) with legacy 'banked' value PRESERVED not dropped
products/alloy/drizzle/migrations/0002_gifted_absorbing_man.sql — additive-only migration (CREATE TYPE, ALTER TYPE ADD VALUE, ADD COLUMN — zero DROPs), applied via drizzle-kit push, verified against real pre-existing rows (4 workpieces, 1 arc) before writing
forgekit-os/product-registry.json + forgekit-os/scripts/verify-product-registry.mjs — explicit 18-entry product/experiment/os manifest, NOT parsed from PROJECT_INDEX.md (verified that file omits Alloy itself, ForgeKit OS, and 4 real experiment folders), with validation + drift-report functions
products/alloy/lib/products-sync.ts + scripts/sync-products.mjs — idempotent DB sync from the manifest, wired as an explicit `npm run sync:products` command (NOT coupled to page render — corrected after review)
products/alloy/app/workpieces/page.tsx — rebuilt as a Product-grouped landing page (real DB Workpieces + legacy session history via lib/workpiece-classification.ts + an Unassigned Workpieces holding view for pre-model rows)
products/alloy/app/workpieces/[productId]/page.tsx — new Product workspace screen
products/alloy/app/workpieces/[productId]/[workpieceId]/page.tsx — new Workpiece detail screen (Start/Continue Session, completion condition, Arc history, delete)
products/alloy/app/orient/page.tsx — real per-Arc Orient mode (?arc=) added alongside the existing global dashboard mode; assembles Product/Workpiece identity, previous Arc's Bank summary, carried-forward return conditions
products/alloy/app/charge/page.tsx + ChargeContractPanel.tsx — Charge rewritten to session-scoped fields with inherited (read-only) Workpiece context, a completion-condition gate before finalizing, and Orient<->Charge back/forward navigation
products/alloy/lib/actions.ts — createWorkpiece (Product required, lightweight), startArc (session-entry dedup, NOT a concurrency model — resumes any existing open Arc rather than double-opening), setWorkpieceCompletionCondition, deleteWorkpiece (real cascading delete via existing FK onDelete:'cascade' chain), bankArc fixed to stop writing workpieces.status='banked'
products/alloy/components/DeleteWorkpieceControl.tsx — type-the-title-to-confirm destructive-action UI
6 design docs in products/alloy/docs/ — workpiece-model, workpiece-schema-delta, workpiece-migration-plan, workpiece-screen-flow, product-registry-manifest, vscode-to-alloy-mapping — plus a 7th, visual-hardening-plan.md, scoping the next session's prototype-visual-matching pass
Home page (earlier in this session): SystemStatePanel/SystemOpenItemsPanel rewired to real Forge Energy/Pressure/Mechanisms/Network/spin/reuse data, new /health-engine page, Recent Workpieces/Forge Arcs/Active Workpiece restyled to a consistent icon-badge visual language
3 stale workpiece_id session JSONs corrected (alloy-orient-screen-build, arcade-cartridge-grammar, duplication-sweep-followthrough) + _SCHEMA.md's workpiece_id section rewritten to require deriving the value from real touched paths
“The next Ignite session should begin from a trusted doorway, not from a foyer whose light switches have only been typechecked.”