[US-438] feat: opt-in business-impact.trivial-diff override - #470
Conversation
Code Review — PR #470 ([US-438] feat: opt-in business-impact.trivial-diff override)Reviewer: independent review subagent · Date: 2026-08-30 · Story: #438 · Verdict: CHANGES-REQUESTED Scope VerifiedAC1–AC9 against the diff + code, in a detached worktree pinned to
BR7/D18 verified independently: Suite run (main checkout's vitest against the worktree): Findings1.
|
This comment has been minimized.
This comment has been minimized.
Final remediation — cycle converged (7 fix rounds, 1 escalation, re-review clean)
¹ Three round-7-escalation findings (test-comment rationale, ADL enumeration gap, Accepted / non-actionable (surfaced in the post-round-7 independent re-review)
VerdictReview clean — zero actionable findings on the latest independent re-review. Not merging (human gate). |
Bound re-review to immutable deltas and require a complete finite-state inventory before review or remediation.
Require real boundary evidence for externally-defined state and repair claims.\n\nRefs: #416
c1ac1ac to
349daba
Compare
Questions are, by the review template's own definition, questions for the human: items the reviewer marks "No change requested". The floor defaulted to none, so they entered the set the fixer must resolve, and each answer enlarged the diff and created fresh surface for the next review. Measured on PR #477: the PR reached a zero-actionable APPROVED twice; both times the following round implemented the Questions and the re-review found new Minor findings inside the code that round had just added (three, then two). Work now completes when Critical, Major and Minor are closed. An explicit severityFloor still wins. The default is applied softly - a template without Minor, or without a usable ranking, falls back to no floor instead of throwing, because a default must not break a run that never asked for it. - ADL 2026-09-03-the-review-severity-floor-defaults-to-minor.md - three tests: Questions-only converges and is carried, a Minor still blocks, a vocabulary without Minor falls back Refs: #219 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… yaml-parsed allow-list (#477) The check-only pre-push gate was the sole enforcement point for formatting, and `--no-verify` skips it. CI now runs `format:check` in a dedicated workflow, and the workflow's own shape is guarded by a composition check. The guard parses `.github/workflows/format.yml` with yaml@2.8.2 and expresses every rule as an allow-list over the parsed document, per ADL 2026-09-01; the hand-rolled line reader it replaces is retired. Nothing is ever rewritten in CI — write-mode formatters are banned by the same guard. Also carries a path-scoped `.gitleaks.toml` allowlist for a synthetic 40-hex SHA fixture in scripts/smoke-tests, which gitleaks matched as a generic-api-key at historical commit 5e99d53. Closes #413 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Business impact resolved from the subdomain class alone, so a docs-only or comment-only change inherited the touched subdomain's yellow/red floor — tier read where a change lands, never what it does. - quality-model.md: §3.1 Business impact row points at the override; new §6 subsection defines `business-impact.trivial-diff` (BR2 mechanical definition of trivial, the not-trivial exclusions, all-or-nothing, raises-never-lowers, refinement/unreadable-diff fail-safes, `green` as the only value); 4 walkthrough rows + 3 hand-traced worked examples - risk-matrix-example.md: the key shown in the example ## Overrides - KB default unchanged: absent the declaration every project classifies exactly as before (D21); classify gains no criterion of its own (D18) - this repo declares the key in adoption/tech/risk-matrix.md, + ADL - conformance: 25 assertions over both trees, the example asset, the adoption declaration, and a grep pinning classify threshold-free - mirrors synced byte-equal; .pair/llms.txt indexes the new ADL Refs: #438
…ards that guard - classify matrix template: Business impact Source now offers `[subdomain class | Overrides: business-impact.trivial-diff]`, so a greened core-subdomain diff names the override instead of asserting the subdomain class greened it (AC2's second half, both trees; trips no D18 term) - quality-model §6: value = first token after the colon, inline rationale allowed — this repo's own declaration was otherwise malformed ⇒ inert - worked examples: A labelled non-normative (no rule changed) + why a rule edit still reads yellow; B's Change/diff risk → yellow (two modules); story-internal "(BR3)" → the in-document rule name - risk-matrix-example: `key`: `value` form, matching its sibling - conformance: §6 assertions bounded to §6's own subsection (the old slice spanned §3.1→EOF and passed with the exclusion clause deleted); + AC4 refinement fail-safe, first-token resolution of the live declaration, classify's Source row, example-asset syntax, no BR-numbering in shipped KB Refs: #438
- §6: branch (a) narrowed to non-executable .md/.mdx; new bullet carves skill/workflow markdown + embedded asset scripts out of it, branch (b) decides — an instruction-altering hunk is not trivial - §3.1: yellow/red cells close the row on themselves (override precedence) - ADL: "no skill change" → "no skill criterion" (D18) + the two SKILL.md paths in Adoption Impact; safety-net claim narrowed to guideline prose - risk-matrix.md: declaration's restated definition synced - test: walkthrough rows anchored per row + resolution cell (the loose regexes passed with the row deleted) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
- risk-matrix-example.md restates branch (a) as non-executable .md/.mdx + the skill/workflow exclusion: the asset IS the adoption starting point, so an unconditional restatement re-opens the hole §6 closed, in the copy. - worked example C names its premise (a runbook inside a security-sensitive path) so its Security red is derivable from §3.1's path heuristic, not from the prose mentioning credentials. - both `## Overrides` enumerations (quality-model §6:134, the website catalog row) name the third family — dimension-resolution overrides. Swept: quality-model.mdx + the asset preamble carry the same list. - ADL Adoption Impact lists the two website pages and the asset's carve-out. Guards: 7 assertions, each mutation-verified. Gate EXIT 0.
Templates: user-story + code-review `Source` cell for Business impact now offers `Overrides: business-impact.trivial-diff` (escaped `\|` — these tables render), matching classify's template. quality-model §6: branch (b) tests the statement, categories are examples, prose-only in executable markdown named — the case the carve-out promises. Restated in risk-matrix.md, the example asset, ADL point 2. §6 index bullet + both website enumerations + Phase 3.6's preamble: the bootstrap offer is scoped to threshold/reviewer-SLA; trivial-diff is hand-authored. Cross-file test pins §6's claim to Step 3.6.2's real text. ADL :42 counts a third family (kind of override), agreeing with :50 and §6. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
…pped Branch (a)'s carve-out enumerated skill/workflow/agent markdown only, so `dataset/AGENTS.md` + the root `AGENTS.md`/`CLAUDE.md` it generates — the always-loaded rule set carrying "Tests required" and the test-first bug workflow — stayed inside branch (a): a PR deleting a Quick Rule and re-running the mirror is 3 `.md` files, green everywhere,`risk:green`, and `automation.md` declares that tier unattended-eligible. Enumerated now in §6, in the asset adopters copy, in this repo's declaration and in the ADL. Also: worked example A drops the mirror from its scope (its green needed `change-risk.dataset-mirror-pairs`, an override the fixture never declares); §6's value rule states backticks are markdown, not part of the token, and one resolver executes it against both shipped declarations; ADL point 2 stops cross-referencing itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
…-out skipped Branch (a)'s carve-out enumerated executable markdown only, so a project's own `tech/` policy files — whose declared VALUES an agent parses and acts on — short-circuited to trivial. A one-file PR moving `tech/automation.md`'s `## Eligibility` to the tier its `## Auto-Advance` then names was all-`.md`, matched no glob, resolved risk:green — the tier pair-loop merges unattended. Both classes now named in §6, the copied example asset, this repo's declaration, the ADL and the website catalog cell; the exclusion list counts a markdown-serialized value change like json/yaml/toml/env. Guarded by executing the shipped enumeration against real paths. Refs #438 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
…hree names - carve-out scopes adoption/policy markdown as `tech/**` in all five shipped statements (§6 ×2 trees, the copied asset ×2 trees, this repo's declaration, the ADL); the three names stay as examples. A three-name list left `tech/coverage-baseline.md` in branch (a): a one-file PR moving `baseline.shared=84` to `40` was all-`.md` ⇒ trivial ⇒ Business impact green ⇒ `risk:green`, the unattended tier, on the diff that disables the coverage regression gate. - the Phase 3.6 offer is scoped in the three copies the round-4/6 fix missed: `how-to/02` (the manual, skill-less bootstrap path), `skills-guide.md`, `assets/bootstrap-checklist.md` — both trees, pinned by one test. - D18 guard pins `trivial change`/`objectively trivial` too — the two phrasings the model itself uses, and the natural way to smuggle a criterion into the applier past the four mechanical tokens. - §3.1 row lookup asserts `toBeDefined()` before splitting, so removing the row fails with the guard's diagnostic instead of a TypeError. - ADL states the asymmetry it relied on twice: a guideline stays in branch (a) because many independent consumers resolve from it (§3.1's shared-code trigger fires on its own); a carved-out class has a single consumer. Refs: #438
Regenerated with pair update (offline, local dataset) to resolve the .pair/llms.txt rebase conflict. Refs: #438 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FfhvsS5rippi6aUbbGrf6F
349daba to
102b3aa
Compare
PR Information
PR Title: [US-438] feat: opt-in
business-impact.trivial-diffoverrideStory/Epic: #438 · Epic #208 — Unified classification & assessments → tags
Type: Feature (KB schema addition + adoption declaration)
Priority: P2 (Could-Have)
Assignee: rucka
Labels:
user story,risk:yellowSummary
What Changed
The quality model gains one opt-in
## Overrideskey —business-impact.trivial-diff: green. Declared, the Business impact dimension resolvesgreenfor an objectively trivial change whatever subdomain the touched files belong to. Undeclared (the KB default, and the state of every project that does not ask for it) nothing changes at all."Trivial" is mechanical and checkable from
git diff, never a judgment about how important the prose is. A change is trivial when either:.md/.mdx, guarded mirrors included — excluding the markdown an agent acts on; orMarkdown an agent acts on is out of branch (a) on purpose — two classes, because branch (a) is the first arm of an OR and short-circuits, and because an enumeration is what makes the test mechanical (the general clause alone is a judgement call):
**/SKILL.md,.claude/skills|workflows|agents/**), always-loaded agent-instruction files (a rootAGENTS.md/CLAUDE.md, or the project's equivalent standing rule set), the sources any of them are generated from, an asset script embedded in markdown.tech/**adoption directory (tech/risk-matrix.md,tech/automation.md,tech/way-of-working.md,tech/coverage-baseline.md, and whatever a project declares there next). Scoped as a directory, not a list of names — an enumeration that lags one file is the hole itself. The same content §6's exclusion list already calls non-trivial when it is serialized asjson/yaml/toml/env— markdown is the format pair happens to ship its own config in, not an exemption.Neither class satisfies the all-
.mdbranch: branch (b) decides, so a hunk altering an instruction or a declared value is not trivial, while a typo fix in the same file's rationale prose still is.Three failure cases the carve-out closes, all real on this repo, all ending at
risk:green— which.pair/adoption/tech/automation.mdalso declares as unattended-run Eligibility, the tierpair-loopmerges unattended on the 🟢 gate set:pair-process-review/SKILL.md+ its dataset source, flipping the merge rule from "explicit approval required at risk:red" to "none";- **Bug fix workflow** - NEVER modify code to fix a bug before creating a test…frompackages/knowledge-hub/dataset/AGENTS.mdand re-running the mirror — the diff is that file plus the rootAGENTS.mdandCLAUDE.mdgenerated from it;.pair/adoption/tech/automation.md, moving## Eligibilityfromrisk:greentorisk:yellowand filling## Auto-Advancefrom(none)to that same tier — the diff that switches unattended auto-merge on, landing in the tier that auto-merges it;.pair/adoption/tech/coverage-baseline.md, movingbaseline.shared=84tobaseline.shared=40— the values CI'sCoverage guardrailstep reads withcoverage-gate.sh(plain^key=match). The diff that disables the coverage regression gate, in the tier that processes it unattended. This is why the class is a directory: a three-name enumeration covered the first case and not this one.All are all-
.md. In all three, the Change/diff-risk yellow that was supposed to catch them is a judgement — and this repo's ownchange-risk.dataset-mirror-pairscollapses source+mirrors to one module, so it resolves green too.Why these two classes and not every
.mda machine reads (stated in §6 and the ADL, not left inferable): a normative KB guideline deliberately stays inside branch (a), because it is a shared rule surface many independent consumers resolve from — §3.1's own shared code trigger fires on it and Change/diff risk reads yellow with no carve-out needed. Each carved-out class has a single consumer (the agent that executes the file, or the skill that reads its declared values), so no other dimension fires on its behalf and the exclusion has to be mechanical.Also not trivial however cosmetic it looks: a rename, a string-literal change, a dependency/version change, a config/data value change (in
json/yaml/toml/envand in policy markdown), a test-expectation change, a regenerated build artifact.Guard rails: all-or-nothing per item (one non-trivial file or hunk disables it for the whole PR — tier is per item, not per file); raises green, never lowers anything (
max()still decides the tier, confirm-or-raise at review, D17);greenis the only accepted value, read as the first token after the colon — with backtick formatting treated as markdown rather than part of the token — so neither the inline rationale every key carries nor the asset's`key`: `value`spelling makes a live declaration malformed; an unverifiable diff (binary/truncated) and an empty diff both fail safe to the subdomain rule; at refinement it applies only to an unambiguously trivial declared scope./pair-capability-classifygains no criterion — it already resolves## Overridesqualitatively through Argument > Adoption > KB default (D18). What it gains is one cell: its matrix template's Business-impactSourcenow reads[subdomain class | Overrides: business-impact.trivial-diff], and the two shipped templates the matrix is written into —user-story-template.mdandcode-review-template.md— carry the same alternation, so a greenedcore-subdomain diff records what greened it instead of asserting the subdomain did.This repo declares the key.
Why This Change
Third entry in the risk-matrix calibration series. The first two removed the coarse-default floor on Service/domain criticality and on overall tier resolution; neither could reach the one dimension that still floors trivial work — Business impact, which §3.1 resolves from the subdomain class alone (
generic→green,supporting→yellow,core→red).That rule reads where a change lands, never what it does. On a docs-as-product repository almost every path maps to a Supporting or Core subdomain, so a typo fix in a guideline inherits that subdomain's yellow/red floor — telling us nothing about the change. The earlier ADL rejected "change the KB default"; what it was missing is that the KB may grow a schema the adoption delta then declares. The gap was never that a project-specific delta is wrong — it was that no key existed to express this one.
Story Context
User Story: As a maintainer/adopter using pair's classification model, I want an opt-in override that lets Business impact resolve
greenfor objectively trivial changes regardless of subdomain, so that a diff's tier reflects what the change does, not only where it lives — without changing the KB default for any project that has not opted in.Acceptance Criteria — all nine covered:
keeps §3.1's KB default — subdomain class alone — unchanged(both trees)core⇒ green, sourced to the overrideclassify+ both body templates offerOverrides: business-impact.trivial-diffas theSourceclassifyand on both templates × both trees; the carve-out matcher must not cover ordinary documentationrisk:red)greenis the only accepted value.pair/adoption/tech/risk-matrix.md## OverridesgreenChanges Made
Implementation Details
quality-model.md§6 gains the subsectionbusiness-impact.trivial-diff: the two-branch definition, the carve-out stated on branch (a) (it short-circuits, so an exclusion stated after it is one an agent reads past), the not-trivial exclusion list, all-or-nothing, raises-only, the refinement rule, the unverifiable/empty-diff fail-safes, the first-token value rule, and "this is a rule for the classifying agent, not a config key parsed by code" (D18).genericsubdomain — or any subdomain, when the change is trivial and the project opted in"; the yellow and red cells name the override that outranks them, so the row closes on itself for an agent resolving it cell by cell.classify's SKILL.md: the story's own DoD requiresclassifyto stay grep-verifiably threshold-free. There is no parser to unit-test, so these are what the rule is validated against.classify's matrix template plus the same cell inuser-story-template.mdandcode-review-template.md. Every sibling row offers an alternation of sources; a single-valued[subdomain class]left the agent no cell to fill but the false one.risk-matrix-example.md(the file adopters copy) shows the key and restates branch (a) with both carve-out classes; both website enumerations name the dimension-resolution family and the catalog's summary of branch (a) no longer reads as plain "docs-only". Every shipped statement of the Phase 3.6 offer scopes it to the families the interview actually asks about and names the key as hand-authored: §6's index bullet, both website pages,/pair-process-bootstrapPhase 3.6's preamble, and the three copies that restate the same fact —how-to/02-how-to-complete-bootstrap-checklist.md(the manual, skill-less bootstrap path),skills-guide.md's ownership table andassets/bootstrap-checklist.md. One test pins all of them, in both trees..pair/adoption/tech/risk-matrix.md, third key, with the rule-for-the-classifying-agent rationale the two siblings are written in, and the carve-out restated over the files this repo actually ships (its skills, itsAGENTS.mdchain, its owntech/policy files — this file included).Files Changed
.pair/adoption/decision-log/2026-08-30-business-impact-reads-what-a-trivial-change-does-not-where-it-lives.md(ADL)..pair/knowledge/**mirror for each):guidelines/quality-assurance/quality-model.md,assets/risk-matrix-example.md,guidelines/collaboration/templates/user-story-template.md,guidelines/collaboration/templates/code-review-template.md..claude/skills/**mirror for each):capability/classify/SKILL.md(one cell),process/bootstrap/SKILL.md(one clause in Phase 3.6's preamble).how-to/02-how-to-complete-bootstrap-checklist.md,skills-guide.md,assets/bootstrap-checklist.md..pair/adoption/tech/risk-matrix.md,.pair/llms.txt.reference/quality-model.mdx,reference/quality-gates-configuration.mdx.packages/knowledge-hub/src/conformance/quality-model.test.ts.Database Changes / API Changes
None — documentation, adoption content and one conformance test file.
Testing
Test Coverage
quality-model.test.ts— 92 assertions, +41 in this PR, every KB-content one run over both shipped trees (dataset source and.pair/knowledgemirror).mirror-guard(dataset ↔.pair/knowledge↔.claude/skillslockstep, 531 cases) andllms-index-conformance(the committed.pair/llms.txtagainst its generator) both cover this diff and both pass.Test Results
Testing Strategy
There is no function to call, so every assertion parses the shipped artifact and reads the cell it is about — the §6 subsection sliced heading-to-heading (not first-occurrence, which spans §3.1→EOF and would pass with the exclusion clause deleted), the §3.1 row split cell by cell, each walkthrough row anchored on its own literal first cell, the worked-example rows parsed per dimension.
Two rules are executed rather than paraphrased, each by one helper applied to every shipped statement of the rule, so the guarded artifacts cannot be pinned into mutually inconsistent forms:
resolveOverrideValue— §6's value rule (first token after the colon, backticks stripped) run against both real declarations: this repo's plain-formrisk-matrix.mdline and the asset's backticked one. Both must resolvegreen.carveOutCovers— the branch-(a) carve-out's enumeration: the backticked path tokens are read out of each shipped statement, compiled as globs and matched against real repository paths. Positives it must cover:.claude/skills/pair-loop/SKILL.md,packages/knowledge-hub/dataset/AGENTS.md,.pair/adoption/tech/{automation,risk-matrix,way-of-working,coverage-baseline}.mdand.pair/adoption/tech/a-policy-file-added-later.md— a path this repo does not ship, so only a directory glob satisfies the guard and re-adding a name list fails it. Negatives it must not cover: an ordinary guideline and a website.mdxpage — a carve-out that swallowed plain documentation would kill AC2, the behaviour the key exists for. Applied to §6 (both trees), the copied example asset, this repo's declaration and the ADL.One assertion is genuinely cross-file: §6's "hand-authored today" claim is checked against
bootstrap/SKILL.mdStep 3.6.2's real text, so a later story adding the third question fails the test instead of silently making §6 stale in the other direction.Every assertion is mutation-verified — the guarded sentence, row or cell was reverted in every tree, the suite observed to go red, and the mutation reverted. The decisive one for the carve-out: replacing the
tech/**glob with the previous three-name enumeration while leaving the surrounding prose intact fails in every statement that carries it (the ... enumeration does not reach .pair/adoption/tech/a-policy-file-added-later.md), and renaming the bolded label keeps the suite green — the guard is about coverage, not vocabulary, which is exactly the drift shape (a class discussed in prose, absent from the mechanical list) it exists to catch. Several guards were rewritten during the cycle because that experiment showed they guarded nothing.D18 is enforced negatively: a grep asserts
classify's SKILL.md contains none of the triviality vocabulary — the pinned list is exactlycomment-only,whitespace-only,formatter-output,.mdx,trivial change,objectively trivial. The last two are the phrasings the model itself uses, i.e. the natural way for a later story to smuggle a criterion into the applier past the four mechanical tokens. It names the override key and owns no criterion.Documentation
quality-model.md§3.1 + §6 (the schema's owner),risk-matrix-example.md(the copy-me asset), both website reference pages.2026-08-30-business-impact-reads-what-a-trivial-change-does-not-where-it-lives— opt-in KB schema key (never a KB default change), the mechanical definition, both carve-out classes, all-or-nothing, raises-only,green-only, no skill criterion; rejected alternatives recorded (KB default, local-only declaration, per-file granularity, a subjective definition, extendingtier-resolution.default-artifact-downgrade).Reviewer Guide
Review Focus Areas
git diffand not from an opinion about the prose.carveOutCoversis the mechanical check.generic/supporting/coremapping is asserted verbatim.classifystay a model-applier? One cell naming the key, no threshold — the D18 grep is the mechanical check.Testing the Changes
Key Test Scenarios
generic/supporting/coreassertions.pair-process-review/SKILL.md+ its dataset source is all-.mdbut not trivial — branch (a) excludes executable markdown, branch (b) reads the instruction hunk, Business impact resolvescore⇒ red.SKILL.mdis trivial — branch (b)'s statement test is the gate, and prose-only is named as qualifying..pair/adoption/tech/coverage-baseline.md'sbaseline.shared=84to40is not trivial —tech/**puts it on branch (b), where a changed declared value is a changed declarative statement, and CI'sCoverage guardrailreads exactly that value.dataset/AGENTS.md+ the rootAGENTS.md/CLAUDE.mdit generates are agent-instruction files, so a hunk removing a Quick Rule is not trivial..pair/adoption/tech/automation.md's## Eligibilityto the tier its## Auto-Advancethen names is not trivial — the adoption/policy class puts it on branch (b), where a changed declared value is a changed declarative statement. The identical value change is already non-trivial injson/yaml/toml/env.business-impact.trivial-diff: blue⇒ warn, treat as absent, fall back to the subdomain class — never a HALT.`green`with backticks is not malformed: the backticks are markdown.Notes for the reviewer
cpfor any KB file naming a skill..pair/knowledge/**carries prefixed skill names (/pair-capability-assess-security); the dataset source carries bare ones (/assess-security) and the registry transform adds the prefix.pnpm --filter @pair/knowledge-hub transfer:datasetis a folder move/rename tool, not a sync command. Mirrors here were edited line-by-line andmirror-guardconfirms the lockstep — do not go looking for a sync command that does not exist.quality-model.md§6, not inclassify's SKILL.md, which is a deliberate deviation from the task's literal wording: the DoD requiresclassifyto stay grep-verifiably threshold-free, and the rule's fixtures belong beside the rule.risk:yellow, not green — by its own new rule: it editsquality-model.md,risk-matrix.mdand skill files, all of which the carve-out now puts on branch (b).2026-08-20-a-changeset-is-added-when-a-release-is-scheduled, no release is scheduled.Dependencies & Related Work
Related: decision-log
2026-08-14-risk-matrix-overrides-close-the-zero-green-gapand2026-08-14-tier-resolution-downgrades-artifact-yellows-not-observed-ones— the first two entries of this calibration series.Follow-up work (a call for the human at the merge gate, deliberately not taken here):
business-impact.trivial-diffhas no guided authoring path./pair-process-bootstrapPhase 3.6 asks two override questions (threshold, reviewer/SLA), never this one, and its offer is one-shot — an existing## Overridesis reportedalready authoredand never re-proposed. Every shipped statement of that offer now says so explicitly — the bootstrap SKILL, §6, both website pages, and the three copies an adopter reaches without skills (how-to/02,skills-guide.md,assets/bootstrap-checklist.md), all pinned by one test. Adding a third question to the interview is a change to bootstrap's behaviour, outside this story's scope.