Skip to content

feat: add a family conformance gate that executes documented exit contracts - #82

Merged
chaoz23 merged 1 commit into
mainfrom
family/conformance-gate
Aug 19, 2026
Merged

chaoz23 merged 1 commit into
mainfrom
family/conformance-gate

Conversation

@chaoz23

@chaoz23 chaoz23 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Closes the gap found by the 2026-08-17 family-wide conformance audit. Implements the gate proposed in #81.

The problem

Every member's SKILL.md passed the clause-7 acceptance test — "a fresh-context agent, given only this file, can produce a well-formed invocation" — when the front doors landed in #71. All four still misstate their own exit-code contract.

That test checked invocability. Nothing checked truth. --schema, tool.json, and the MCP surface are all generated or tested; SKILL.md is hand-maintained prose that nothing executes.

What this adds

scripts/family_conformance.py — stdlib only, no new dependencies. It runs a member's CLI with read-only probes and diffs observed behaviour against its SKILL.md:

rule catches
UNDOCUMENTED_EXIT_CODE the CLI returns a code SKILL.md never documents
HONEST_LANE_OVERLOAD a code reserved for cannot-adjudicate is also returned by plain usage errors
STREAM_MISMATCH SKILL.md names one stream for failures, the tool uses the other
MISSING_PIPE clause 7 codifies --pipe by name; the CLI rejects it
SCHEMA_NOT_FLAG clause 7 codifies --schema as a flag, not a subcommand

It obeys the contract it enforces — exit 0 conformant / 1 findings / 2 cannot-adjudicate, plus --pipe, --schema, and an action field on the honest lane. Pointed at a repo with no SKILL.md it exits 2 rather than guessing.

python3 scripts/family_conformance.py ../charactercheck ../dmcheck ../table-kit

Current results

repo verdict findings
srdcheck PASS —
charactercheck FAIL honest-lane overload
dmcheck FAIL missing --pipe, honest-lane overload, stream mismatch
table-kit FAIL missing --pipe, --schema not a flag, honest-lane overload

Reproduces 4 of the 8 independently-confirmed audit findings plus part of a fifth, with zero per-repo configuration. The highest-impact one is dmcheck's STREAM_MISMATCH: its SKILL.md promises failures print to stderr, and every envelope goes to stdout (stderr is 0 bytes) — so an agent following the docs sees no payload at all. Filed as chaoz23/dmcheck#13.

Scope and safety

All probes are read-only: --help, --schema, a nonexistent flag, and one verb pointed at a path that does not exist. Nothing is written, installed, or mutated.

Python 3.10-safe: tomllib is 3.11+, so [project.scripts] parsing falls back to a regex, verified to produce identical output to tomllib on all four members' manifests.

Known blind spots, stated up front

One caveat worth recording

The first version of this gate passed dmcheck clean. The stream claim wraps across two source lines — "Failures print JSON on" / "stderr, never a traceback." — and a line-based matcher saw neither half. A checker that reports PASS because it structurally cannot perceive the defect is the exact failure class this gate exists to catch.

Fixed by matching at paragraph level, and it is why the gate was validated against known-bad repos rather than trusted on a green result. A gate's first test is whether it can fail.

Adoption

It currently fails 3 of 4 repos, so land it non-blocking first, fix the members, then flip to blocking. HONEST_LANE_OVERLOAD is deliberately medium, not high — it fires on 3 of 4 members, and a rule that flags most of the family at high severity trains readers to ignore it.

Refs #81. Related contract decision: #80.

🤖 Generated with Claude Code

…tracts

Every check-family member's SKILL.md passed the clause-7 acceptance test
("a fresh-context agent, given only this file, can produce a well-formed
invocation"). All four still misstate their own exit-code contract. That
test checked invocability; nothing checked truth.

scripts/family_conformance.py runs a member's CLI with read-only probes and
diffs observed behaviour against what its SKILL.md claims:

  UNDOCUMENTED_EXIT_CODE  the CLI returns a code SKILL.md never documents
  HONEST_LANE_OVERLOAD    SKILL.md reserves a code for cannot-adjudicate with
                          do-not-retry guidance, but a usage error returns it too
  STREAM_MISMATCH         SKILL.md names one stream for failures, the tool uses
                          the other
  MISSING_PIPE            clause 7 codifies --pipe by name; the CLI rejects it
  SCHEMA_NOT_FLAG         clause 7 codifies --schema as a flag, not a subcommand

It obeys the contract it enforces: exit 0 conformant / 1 findings / 2 cannot
adjudicate, plus --pipe, --schema, and an action field on the honest lane.
Pointed at a repo with no SKILL.md it exits 2 rather than guessing.

Current results: srdcheck PASS; charactercheck, dmcheck, table-kit FAIL.
Stdlib only, and 3.10-safe (tomllib is 3.11+, so [project.scripts] falls back
to a regex parse verified to match tomllib on all four members).

Refs #81

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chaoz23
chaoz23 merged commit b58b2cd into main Aug 19, 2026
14 checks passed
@chaoz23
chaoz23 deleted the family/conformance-gate branch August 19, 2026 03:52
chaoz23 added a commit that referenced this pull request Aug 21, 2026
Closes the remaining half of #81. The gate landed in #82 but nothing ran it,
so it could only catch defects for whoever remembered to invoke it by hand.

Blocking rather than advisory. An advisory job that is permanently red is
noise people learn to scroll past, and the gate currently reports real
findings in three of four members. So known findings are waived in
family-conformance-baseline.json and the job fails only on a NEW divergence.

Two rules make the waiver list a ratchet instead of a place defects go to die:

- every waiver must name the issue tracking it. A waiver without a ticket is
  just a hidden defect with extra steps.
- a waiver that no longer fires FAILS the gate. Fixing a defect and leaving
  its waiver behind would silently pre-accept the next regression, so fixes
  have to shrink the file.

Verified in all three directions, because a gate that cannot fail is
worthless:

  known findings waived            -> exit 0
  a waived finding un-waived       -> exit 1, reported as not in the baseline
  a waiver that no longer fires    -> exit 1, reported as STALE BASELINE

srdcheck itself passes with no waivers. The baseline's sibling entries are
consulted only when those repos are audited, so this job reports nothing for
them; the sibling CI jobs come next and will read the same canonical file
rather than copying it, per FAMILY.md's pin-by-link rule.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
chaoz23 added a commit that referenced this pull request Aug 25, 2026
* feat: FAMILY.md v2.2 harmonises exit 3; gate can see codes above 3

Two halves of one problem, surfaced by chaoz23/charactercheck#18.

CONTRACT. v2.1 said "srdcheck's exit 3 is the family precedent", which was too
weak to settle what a member should do when 3 is already spent. v2.2 states
the pattern outright: 0/1/2 are the universal verdict contract, >= 3 is the
no-verdict taxonomy, and 3 is usage error in EVERY member. A malformed call is
the one non-verdict outcome every tool has, so harmonising it means an agent
that mis-invokes any member gets the same answer. Further outcomes take 4+.

charactercheck therefore moves could-not-retrieve from 3 to 4 rather than
taking a different usage code. Decided deliberately over the cheaper option of
letting it keep 3 and putting usage on 4: that would have left the family with
two spellings for the concept an agent hits most often.

GATE. parse_skill capped recorded codes at 0..3, so a documented exit 4 was
silently dropped -- and the CLI returning 4 then surfaced as a bogus
UNDOCUMENTED_EXIT_CODE against a tool that had documented itself correctly.
The gate would have rejected the very refactor the contract now requires.

That is the third time this parser has been the defect rather than the tools
it checks (line-vs-paragraph in #82, stream-word matching in #84). The shape
repeats: the checker silently cannot perceive the thing it is checking.

Verified both directions: a synthetic 0/1/2/3/4 SKILL.md now parses all five
with honest-lane=2 and usage=4, and all four real SKILL.md files parse
unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: drop charactercheck's HONEST_LANE_OVERLOAD waiver

charactercheck is harmonising onto exit 3 for usage errors
(chaoz23/charactercheck#18). The waiver comes out before the fix lands, as the
ratchet requires; the SHA pin keeps charactercheck's CI green in the meantime.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant