issue_81: Define the canonical Convergence Check - #84
Conversation
Every artifact of a change is reviewed on its own — the issue at refinement, the scenario at specification, the ADR at decision, the diff at review — and nothing looks across them at the end. Drift between two individually correct artifacts is what this gate finds. A dedicated skill rather than a reference under skills/references/: that directory holds writing guides, and this is a workflow with a result that an agent has to be able to select by name. Three rules carry most of its weight: - Every question must be able to fail for this change. One that cannot is reported as not applicable, never as passed — the same defect as a scenario written green against unfixed code. - The three tiers stay separate, and an assisted finding is never reported as deterministic proof. - A waiver is a decision about proportionality; a blocker is an impossibility. Recording one as the other buries the problem in an accepted deviation. It composes rules it does not own and reads the skill that owns each one, because a second copy of a rule inside a gate is the drift the gate exists to detect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01USXp58FoRppFK6FUht8K7u
Review: change needed before mergeThe seven-question structure, tier separation, non-repair rule, and explicit dispositions form a strong canonical gate. The validation workflow is green. The definition of Blocked is internally contradictory, though:
A pending human decision is not an impossibility: it is available in principle and becomes resolved once the person decides. This matters operationally because agents need an unambiguous boundary between “pause for a required decision”, “evidence cannot be obtained”, and “a deviation was accepted”. Please choose and consistently define the model. For example:
Then align the result table, the waiver/blocker explanation, and the findings dispositions with that choice. |
Review on #84: the result table said blocked covers a human decision that has not been made, while the paragraph below defined a blocker as an impossibility that human authority cannot convert into a waiver. A pending decision is not an impossibility — it resolves the moment someone decides — so the two halves contradicted each other. Blocked now means the gate cannot reach a result yet, and every blocker names its kind. Pending resolves by naming who must decide or which check must run; unavailable does not resolve by waiting, and reporting it as pending sends someone to wait for something that will not arrive. That also fixes the waiver rule rather than restating it. A waiver is a legitimate outcome of a pending blocker: the person judges the deviation disproportionate and records why. It is never an outcome of an unavailable one, because what is missing there is not disproportionate — it cannot be produced. Findings gain a fourth disposition, since a blocked finding previously had none. And "not applicable" is now separated from "unavailable": the first has no bearing on the change, the second bears on it and cannot be answered here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01USXp58FoRppFK6FUht8K7u
|
Reworked in 37df96d. The contradiction was real: a pending decision is available in principle, so calling it an impossibility was wrong on its own terms. Took the first of your two options — keep four result states, make every blocker name its kind — because #77 asks for a small, explicit result model, and a fifth top-level state would have bought the same clarity at the cost of that. Blocked now means the gate cannot reach a result yet, and each blocker is one of:
The distinction earns its place operationally, which is what you asked for: reporting an unavailable blocker as pending sends someone to wait for something that will not arrive. The waiver rule got better rather than merely consistent. A waiver is now explicitly a legitimate outcome of a pending blocker — the person judges the deviation disproportionate, records why, and the gate returns converged with recorded waivers. That path was missing before, and it is the normal one. It is never an outcome of an unavailable blocker, because what is missing there is not disproportionate: it cannot be produced at all. Two further alignments the change forced:
On the merge order in your review of #83: agreed, and this PR is ready first. |
Summary
Adds
skills/convergence-check/SKILL.md: the final consistency gate across request, behaviour specification, architecture knowledge, implementation, tests and delivery metadata.Every artifact of a change is reviewed on its own — the issue at refinement, the scenario at specification, the ADR at decision, the diff at review — and nothing looks across them at the end. Drift between two individually correct artifacts is what this gate finds.
Closes #81
The four decisions #81 asked for
Form — a dedicated skill.
skills/references/holds writing guides; this is a workflow that produces a result and that an agent must be able to select by name. One canonical definition, thin references from the participating skills (#82).The seven questions — intent and scope, behaviour, architecture impact, decisions, traceability, implementation and verification, documentation and delivery state. Each names the skill that owns its rules rather than restating them.
Result model — converged · converged with recorded waivers · not converged · blocked.
Automation boundary — three tiers (deterministic / assisted / human), stated with every finding, and an assisted finding is never reported as deterministic proof.
Three rules that do most of the work
Every question must be able to fail. Before answering, know what evidence would make it fail for this change. A question that cannot fail here is reported not applicable, never as passed. This is the defect #74 found in the BDD default, applied one level up: a guard with no failure path is worse than an absent guard, because it reads like coverage.
A waiver and a blocker are different things. A waiver is a human decision about proportionality — "too small to specify". A blocker is an impossibility — the evidence cannot be produced, or the question is unanswerable at this layer. No amount of human authority converts one into the other, and recording an impossibility as an accepted deviation buries it.
The gate never repairs. It must not rewrite an accepted requirement, soften a recorded decision, or retarget a relation to make artifacts agree. Making the evidence fit the conclusion is precisely what it is meant to catch.
Verification
./build.sh test— 35 JS tests, 157 Ruby assertions, 0 failures./build.sh check-adapters— current; adapters regenerated, all six listconvergence-checkNo
features/scenario, deliberately. This slice adds no executable behaviour: the deterministic checks it names (build.sh validate,check-adapters, the test suites) already exist and are already specified. Adding a.featurefor a prose contract would produce exactly the scenario-that-cannot-fail this skill forbids.Two things left open on purpose
The Capabilities section still ends before convergence. #83 (slice #80) is not merged, so that section does not exist on this branch. Completing the sentence belongs to #82, once both have landed — noted there.
No
adapters/openai/convergence-check/openai.yaml. Only 6 of 19 skills carry OpenAI UI metadata; it is selective, and adding one here would be a decision this slice was not asked to make.Merge order
#83 first, then this. Both touch
README.md, in different sections.