Skip to content

issue_81: Define the canonical Convergence Check - #84

Merged
dieterbaier merged 2 commits into
mainfrom
issue_81
Aug 31, 2026
Merged

issue_81: Define the canonical Convergence Check#84
dieterbaier merged 2 commits into
mainfrom
issue_81

Conversation

@dieterbaier

Copy link
Copy Markdown
Member

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 list convergence-check
  • Every relative cross-reference in the new skill resolves to an existing file — checked mechanically

No 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 .feature for 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.

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

Copy link
Copy Markdown
Member Author

Review: change needed before merge

The 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:

  • the result table says a check is blocked when evidence is unavailable or a human decision has not been made;
  • the following explanation defines a blocker as an impossibility and says human authority cannot turn it into a waiver.

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:

  • define blocked broadly as “the gate cannot currently reach a result because required evidence or a required human decision is unavailable”, and distinguish temporary/pending from structurally unavailable blockers; or
  • reserve blocked for unavailable evidence/impossibility and introduce a clearly named pending-human-decision disposition/result.

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
@dieterbaier

Copy link
Copy Markdown
Member Author

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:

Kind Meaning What resolves it
Pending obtainable, does not exist yet name who must decide or which check must run, then run the gate again
Unavailable cannot be produced, or unanswerable at this layer a change of approach, or an explicit record of the limitation — waiting does not help

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:

  • Findings gained a fourth disposition. A blocked finding previously had none, which broke "no finding is left without one". "Blocked" alone is now explicitly not a disposition either — without its kind and named next step it is an open question wearing a status.
  • "Not applicable" is now separated from "unavailable". Not applicable means the question has no bearing on this change; unavailable means it bears and cannot be answered here. The first needs no follow-up, the second does — and they were easy to conflate.

./build.sh test 35 JS / 157 Ruby, 0 failures; check-adapters current.

On the merge order in your review of #83: agreed, and this PR is ready first.

@dieterbaier
dieterbaier merged commit 3cf2889 into main Aug 31, 2026
1 check passed
@dieterbaier
dieterbaier deleted the issue_81 branch August 31, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slice #77.2: Define the canonical Convergence Check

1 participant