v7.2 — machine contour, process control, revision conveyor (waves 1–4) - #3
Merged
Conversation
… queue, hash graph, gate overtaking, A.25+A.26, rule card taxonomy, 6 starters, progress.py, direction-drafter pack, self-test 93/0
…tokens.json, compile-tokens.py reads from $meta
…profiles + nightly
…ages) D23 scanned the project virtualenv and reported six findings from pip's own vendored SPDX data. A blocking check that cries wolf on every run stops being read, which is worse than the risk it was guarding against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ming a verdict The first live CI run failed, and it was right to. The workflow ran `dops verify` against this repository's own root — asking whether the toolkit passes the floor it applies to websites. There is no skeleton, no tokens and no contract here, so D16 failed and half the floor was unavailable (pyyaml was not installed either). CI now checks what it should: - `selftest` job — the toolkit's own acceptance (dops selftest + package self-test), with pyyaml installed; - `end-to-end` job — materialises a real project from the landing-event starter, serves it, records the D22 baseline and runs the standard floor against THAT. Rehearsed locally: 24 pass, 1 skip, 1 unavailable (D1, no build system for static HTML), verdict ready_with_caveats in 8.8 s. Two defects surfaced by the rehearsal: 1. Every starter's contract.yaml shipped `verdict: ready_with_caveats`, so a materialised project claimed a verdict from its first second. The starter's own factory verdict belongs in starter.yaml (`floor_verdict`); the project's contract starts empty. This is exactly the retro-fitted compliance D19 exists to catch — and D19 did catch it. 2. Open doctrine question, noted in the workflow: a UX model INHERITED from a Verified Starter currently trips the quick-mode ceiling (AC-23). Producing one in quick mode is over-work; receiving one pre-verified is the entire point of starter_first. Left for the design side to rule on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…es it A Verified Starter promised pre-verified structure but shipped no model to prove it, so every starter_first project lived out its life with D18 and D.38 reporting `unavailable`. All six starters now carry ux/experience-model.yaml, and inject.py copies it into artifacts/ux (--ux-out) — shipping it without carrying it would have changed nothing. app-dashboard also carries the state_matrix D.38 reads: three async modules, seven states each. A dashboard with only its ideal state is precisely the defect A.12 exists to catch. Also fixed, mine from an earlier commit: site-portfolio and site-content declared `evidence-first` and `browse-first`, which are not in the closed pattern taxonomy the validator enforces. Mapped to answer-first (the positioning line answers the visitor's question, cases support it) and object-first (articles are objects the reader scans) rather than widening the taxonomy unilaterally. Self-test 94/0/0; starters/recheck.sh green; all six models validate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ation
Every edit cost the same. "Make the button darker" and "add a booking flow"
both ran the full cycle — read context, write contract, route through S0,
apply, re-verify, report — and the big model met both. Two moves from Kimi's
revision-conveyor design fix that.
П-1, pins always-on: gate-annotate.js worked only on gate artifacts. It now
embeds in any artifact for the life of the project, and a pin carries
selector + viewport + kind. A comment is born in the artifact and used to be
expressed in chat ("that blue button on the third screen"); half of every
revision went into working out which element was meant. The v6 field names
are still written, so annotations-log.py keeps reading its own artifacts —
verified in the self-test.
П-2, the sorting station: `dops pins classify` routes each pin before the
model sees anything — A token/copy (script, seconds), B block swap, C
structure (narrow K1 + targeted gate), D taste or ambiguous. The classifier
is a word list, not a model: it has to be cheaper than the work it routes.
Structure rules are checked before token rules, so "add a page about prices"
is not mistaken for a colour edit.
Measured on a realistic 15-pin revision set: **67% never reach the big
model** — the design estimated ~80%, and the measured number is what the
documentation now states. Ambiguous wording routes to D and waits: a wrong
cheap lane costs more than one honest question. Duplicate pins are marked,
never dropped — the owner wrote it twice for a reason.
Self-test: 96 passed, 0 failed, 0 skipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Revision conveyor, move П-3. A pin is now checked between sorting and execution — negotiation at the cheapest possible moment, before a second of work is spent. Four questions in a fixed order: does the selector still resolve, is the value inside the declared ranges, does it contradict a decision already made, has the owner said this before. The ranges are read, never re-typed: the type scale and 8pt ladder come from the skin's tokens.json, the contrast pairs from $meta.contrastPairs (move 8), and the WCAG maths is imported from check-contrast.py. A second copy of any of them in the checker would be the drift [A.10] forbids. Two rules make a refusal usable. A refusal always carries alternatives, and annotations-log.py fails a `rejected` verdict without a reason and a way out — a wall is not a negotiation. A duplicate is questioned, never silently merged. Measured on the same realistic 15-pin set used for П-2: 15 of 15 decided by script alone, zero model calls; 4 became instant negotiations instead of work that would have been built and thrown away. The small-model boundary exists ($DOPS_SMALL_MODEL, one JSON in, one JSON out) and is exercised by the self-test with a stub; with no model in the environment those pins go to the owner marked checker_unavailable rather than guessed at [A.6]. AC-23 closed per Kimi's ruling: the quick-mode ceiling limits what a run PRODUCES, not what a Verified Starter carries in. inject.py stamps artifacts.ux.origin=inherited, validate-pipeline.py exempts it, and absent or `produced` still violates the ceiling — silence buys no exemption. The CI rehearsal moves back to quick mode, which is the real starter_first case. Found on the way: $meta.contrastPairs declares seven pairs and D3 gates four, and the gap hides a real one — textTertiary/canvasRaised measures 3.39:1 in both flagship skins. Whether that pair is normal text or UI chrome is a design call, so `check-contrast.py --tokens` reports it instead of inventing a threshold to make it disappear. Version named v7.2 per Pavel's decision. Contract schema stays 7.0 — no migration. Self-test 96 → 101, 0 failed, 0 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…uild The skins declared seven contrast pairs in $meta.contrastPairs and check-contrast.py gated four of them, hardcoded. Two copies of one geometry, and they had drifted for two versions — hiding textTertiary/canvasRaised at 3.39:1 in both flagship skins. Thresholds decided by the owner: the two text tiers are normal text (4.5:1); the tertiary tier is UI chrome, so it answers to 3:1. It measures 3.39 light / 3.17 dark and clears that floor — it would have failed the text floor, which is precisely why the gap mattered. The report added earlier is no longer only a report: the self-test fails when a skin declares a pair the gate does not measure, so the next new pair has to be given a floor deliberately. Verified against injected drift — a bogus focusRing/canvas declaration is caught. D3 now runs 14 checks per skin (7 pairs x 2 themes), all green. Self-test 101 -> 102, 0 failed, 0 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A skipped job still publishes a check run under its own name. With main about to require that check, a skip could have satisfied the requirement instead of a real run — a gate a skip can pass is not a gate. Costs one extra minute per push and makes 'CI green' mean the browser floor actually executed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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 this changes
The pipeline was slow, and the usual explanation — "too many rules" — was only half right. Reading rules is not a one-time cost: a resident instruction is paid on every turn that follows it, and cost and wall-clock of an agent run are linear in the number of turns. So the work went after turns first, resident context second, and only then the rules themselves.
Everything deterministic now lives behind one entry point,
tools/dops, and never enters the model's context.Measured
RULES.card.md, generated)cost.actualMain additions
tools/—dops verify(whole floor, one JSON; the agent readsfailures[], not the registry),doctor,card,handoff,shots,harvest,hash,announce,checkpoint,control,guard,stage/report/cost.rules-taxonomy.json) — a rule belongs in the prompt only if it changes what the model writes. Rules a checker enforces are deliberately off the card.[E.3]implemented — it had been prose since v5: nothing stored input hashes, so every change request rebuilt everything.dops hash plannow names exactly what must be recomputed and what may be reused.[A.25]gate-overtaking +[A.26]"nothing silently" — shipped as one package. Autonomous needs the owner's explicit grant; every machine gate decision is announced at the moment with a rollback command.dops guard) — verified rules are caught before the edit instead of being carried in the prompt.Defects found and fixed on the way
run-ui-checks.shwrites its runner into a temp directory whererequire('playwright')cannot resolve. Masked because playwright was not installed — the checks degraded honestly and the bug could not surface. Every historicalready_with_caveatsverdict rested on fs-level checks alone.check-divergence.pycrashed on a stringaxesinstead of reporting — a check must produce a status, never a traceback.check-packs.pyinvoked a barepython3, producing phantom resolver errors.check-reduced-motion.pyrejected the strongerprefers-reduced-motion: no-preferencepattern, pushing authors toward the weaker one.dops doctor --fixcould not install PyYAML on an externally-managed Python (PEP 668); it now creates a project venv instead of forcing the system one.Self-test: 94 passed, 0 failed, 0 skipped. CI runs the quick profile on push and the standard profile with Playwright on this PR.
🤖 Generated with Claude Code