feat: substrate-v2 P5–P8 — loop closure, UI quality gate, forge dash, measured cost report#30
Merged
CodeWithJuber merged 9 commits intoJul 7, 2026
Conversation
…ed metrics Per-stage cost factors computed as pure arithmetic over .forge/metrics.jsonl (src/cost_report.js): gate halt rate, tier-weighted cache hit rate, route saving priced against the always-premium baseline, and context assembly. Composition C = C0 * prod(1 - f_i) runs over ONLY the measured stages — an eventless stage reports measured:false / value:null and lands in the caveats, never a default. The render cites the paper's 62% routing figure as context (not a local measurement) and prints ~90% only as a labeled target. substrateCheck now meters the assumption gate on the explicit path (halt/pass, try/catch best-effort; ambient hooks stay write-free, same contract as reuseQuery vs reusePeek). recordGate/recordRoute give future stage wiring one obvious call each. reports/cost-eval.md scaffolds the paired-run harness report with a truthful no-data-yet state and the commands that populate it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
…t radius (P7) One self-contained page (src/dash.html: inline CSS/JS, no CDN, no framework, no build step) served by a node:http stdlib server, localhost-only by default, zero runtime deps. src/dash.js separates DATA from SERVING: dashData(root) builds the whole /api/data payload (ledger stats + claims capped at 200 with val/evidence/author, contested claims — val in [0.4,0.6] with >=1 contradiction — author trust, metrics summary + recent-events tail, atlas presence) and degrades corrupt/missing stores to empty sections instead of throwing; serve(root) wires GET /, /api/data, and /api/impact?target=X (404 otherwise, GET-only — the ratify/retract POSTs are a later phase). Panels per docs/plans/substrate-v2/08-dashboard-ux.md: Ledger (val bars, kind filter, contested section, per-author trust), Cost/Cache (stage counters and measured saved-token estimates from .forge/metrics.jsonl), Impact (atlas blast-radius explorer). Every claim row carries its provenance affordance — click-to-copy `forge ledger blame <id8>` — so no number in the UI is unexplained. Taste discipline: one accent (the brand ember, not framework blue), a 4px spacing scale, two radius levels, system sans + one mono. CLI: `forge dash [--port N]` prints the URL and stays alive serving. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
…rift, checkpoint cadence Four loop-closure faculties from docs/plans/substrate-v2/06-faculties-and-mechanisms.md: - forge diagnose (src/diagnose.js): failure signatures (sha256 of the normalized error + file + symbol, volatile parts stripped) counted in a 50-entry ring (.forge/trace/failures.jsonl, corrupt-tolerant); the 3rd identical hit mints a content-addressed `diagnosis` claim into the team ledger and directs the agent to stop retrying and escalate one model tier with the diagnosis as the prompt's head (spec §5). - forge imagine (src/imagine.js): the static half of the consequence simulator (Eq. 4) — entities → blast radius → predicted breaks with confidence, plus the minimal dry-run suite via weighted greedy set cover (weight = file size; ln-n approximation) and riskScore = Σ confidence. The sandboxed worktree runner is the documented P5 follow-up (spec §2). - anchor.cusum(): one-sided CUSUM control chart (k=0.35, h=1.0) — sustained small drift alarms, a single within-tolerance spike drains back to zero (spec §5, M4). - verify.checkpointCadence(): the optimal-stopping threshold rule n* = ⌈checkCost / (pErr·tokensPerStep·costPerToken)⌉ clamped to [1, 50] — every input measured or priced (spec §6, M6). Tests: 319 (31 new), typecheck clean, biome clean on touched files. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
… project conformance (P6) Taste becomes measurable (guard-over-prose applied to design). New src/uifingerprint.js extracts a deterministic design fingerprint from CSS/JSX/Tailwind classes — pure static parsing, zero LLM calls, zero screenshots: HSL palette + 12-bin hue histogram, spacing with an approximate-GCD base unit (residual minimization over 2/4/8) and on-scale fraction, font families, radius and shadow levels. Two distances gate generated UI: slopDistance to a shipped, rationale-documented generic-template signature set (default-Tailwind blue/indigo, stock Bootstrap, the AI-landing gradient) must stay high; conformance to the project's own fingerprint — stored as a shared "fingerprint" ledger claim via mintProjectFingerprint — must stay low. uiGate emits actionable per-feature violations, never a bare score. Scale-conformance checks (spacing-on-base within ε, radius/shadow level caps, palette bound) join uicheck's ASSERTABLE_CHECKS; the executable versions live in uifingerprint.scaleChecks under the same ids (drift-tested). forge uicheck gains `fingerprint <file...> [--mint] [--json]` and `design <file...> [--json]` (exit 1 on fail); the existing contrast surface is unchanged and also reachable as `uicheck contrast <fg> <bg>`. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
The P6 gate judged the P7 dashboard 'too close to the tailwind-default template' (slop distance 0.091 < 0.25): cool blue-gray neutrals and a single soft glow. Warmed the dark neutrals to the brand ember's hue and flattened the glow — slop distance now 0.43, gate PASSES. The dashboard is the reference customer of the taste discipline, and the gate catching it first is the discipline working. Known P6 limitation surfaced by this: the CSS extractor doesn't resolve var() indirection, so spacing tokens defined once as custom properties count as a single value — noted for the taste-profile follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
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.
What & why
The final four phases of the substrate-v2 plan (#24), built in parallel by four isolated worktree agents and integrated here. With this, every phase of the whitepaper-completion roadmap (P0–P8) is implemented.
P5 — loop closure (
src/diagnose.js,src/imagine.js, +anchor/verifyextensions)forge diagnose "<error>"— failure signatures (line numbers/addresses/timestamps/abs-paths normalized out) counted in a 50-entry ring; the 3rd identical hit is thrash: a content-addresseddiagnosisclaim is minted into the team ledger and the agent is told to stop retrying and escalate one tier with the diagnosis as the prompt's head. Two sessions (or teammates) hitting the same loop converge on one claim.forge imagine "<task>"— the static half of the paper's consequence simulator (Eq. 4): blast radius → predicted breaks with confidence + the minimal dry-run suite via weighted greedy set cover +riskScore. (Sandboxed worktree runner = documented follow-up.)anchor.cusum()(M4): one-sided CUSUM control chart — sustained small drift alarms, a single exploratory spike drains.verify.checkpointCadence()(M6): the optimal-stopping check-cadence rule, clamped [1, 50].P6 — generated-UI quality gate (
src/uifingerprint.js,uicheckv2)forge uicheck fingerprint|design, four new assertable checks, legacy contrast usage kept working.P7 —
forge dash(src/dash.js,src/dash.html)dashData) split from serving and tested; the page is fully self-contained (asserted by test). Every number carries its provenance affordance (forge ledger blame <id>).P8 — measured cost report (
src/cost_report.js,forge cost --stages).forge/metrics.jsonlonly — a stage with no events reports "no data", never an invented number; composition over measured stages is framed as a lower bound; the paper's 62% is cited as context, and the only "90" in the output is labeled target (unmet until measured). Gate metering wired intosubstrateCheck(explicit path only).reports/cost-eval.mdscaffold with a truthful empty state.The integration test that matters: P6's gate initially failed P7's dashboard — cool blue-gray neutrals sat 0.091 from the tailwind-default signature. The palette was warmed to the brand ember and the glow flattened; slop distance is now 0.43 and the gate passes. The system caught template-drift in its own output before any human did. (Known P6 limitation surfaced by this: the extractor doesn't resolve
var()indirection — noted for the taste-profile follow-up.)Checklist
npm testpasses (363/363 — 68 new across the four phases; each phase's suite was green in its own worktree before integration and the union is green after)npm run checkpasses on changed files (remaining diagnostics pre-date these phases)feat:per phase, merge commits for integration)CHANGELOG.mdupdated under## [Unreleased](one bullet per phase)05–08ofdocs/plans/substrate-v2/Risk & rollback
--stagesearly-return (default path byte-identical).forge/trace/and metrics files are inert dataExtra checks (tick if applicable)
npm run typecheckpasses (0 errors)🤖 Generated with Claude Code
https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
Generated by Claude Code