Skip to content

fix: one Status column, not two names for the same thing - #87

Merged
kreneskyp merged 1 commit into
epic/264-assurance-integrationfrom
fix/collapse-status-column-name
Sep 12, 2026
Merged

fix: one Status column, not two names for the same thing#87
kreneskyp merged 1 commit into
epic/264-assurance-integrationfrom
fix/collapse-status-column-name

Conversation

@kreneskyp

Copy link
Copy Markdown
Contributor

The defect

The TestMatrix contract declared two column names with byte-identical vocabularies:

:256   Coverage Status: '^(✅|❌|🚧|⛔)(\s+.*)?$'
:354   Status:          '^(✅|❌|🚧|⛔)(\s+.*)?$'

and used them inconsistently across sibling tables of the same kind:

Table Column
Functional Requirement Coverage Coverage Status
Non-Functional Requirement Coverage Status

Two coverage tables, same purpose, different names. No distinction was being preserved.

Why it mattered

traceability.status.column is a single global selector reading Status. On the four Coverage Status tables quire reported status-column-matches-nothing and skipped status classification entirely. status_lies then came back empty because nothing ran — not because the rows were honest. Every consumer reading that emptiness as a pass was certifying an unmeasured property.

Engineering Assurance's release gate was doing exactly that.

Broken from the start

Aug 4  721baa0  contract mandates Coverage Status (4 tables) + Status (3 tables)
Aug 5  9145ebb  adds ONE global selector: status: { column: Status }

The selector landed the day after the contract that already declared two names. Its own comment says it exists "so a contract and its coverage computation cannot drift". It read one name from a two-named contract and has silently skipped four tables ever since.

Why the root fix, not the override

There is a per-declaration status_column override (quire-rs#410) with four PRs open against it — spec-artifacts-process #85/#77, spec-artifacts-iso #37/#33. It makes the duplicate naming permanent and supported, and it requires an engine release nobody has shipped: published quire 0.32.0's changelog claims to carry it but the tag pins an engine that predates it.

This change needs none of that. The suite passes on released quire 0.32.0, whose engine has no status_column support:

quire 0.32.0 (cli 55a1c4e2, engine 0.46.0@a874fb64)
92 passed, 1 xfailed

Those four PRs should be closed rather than landed.

Scope

  • manifest.yaml — 5 occurrences across 4 table contracts and one column pattern
  • spec/, tests/fixtures/, tests/test_manifest.py — 36 files, 51 lines
  • reports/2026-08-04-tests-md-sweep.json keeps the old name — it is a record of a past sweep, not a live contract

Consumers renaming their own matrix headers is the intended breaking change. 116 repos carry | Coverage Status | in spec/tests.md; modules are sha-pinned per machine, so nothing breaks until a consumer picks this up.

make test 92 passed, make lint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J4qKWaqT3xCXMbbtDqdDRY

The TestMatrix contract declared `Coverage Status` on four tables and
`Status` on three, with byte-identical vocabularies:

    Coverage Status: '^(✅|❌|🚧|⛔)(\s+.*)?$'
    Status:          '^(✅|❌|🚧|⛔)(\s+.*)?$'

and used them inconsistently across sibling tables of the same kind —
Functional Requirement Coverage took `Coverage Status`, Non-Functional
Requirement Coverage took `Status`. No distinction was being preserved. They
were two names for one thing.

The consequence was not cosmetic. `traceability.status.column` is a single
global selector reading `Status`, so on the four `Coverage Status` tables
quire reported `status-column-matches-nothing` and skipped status
classification entirely. `status_lies` then came back empty because nothing
ran, not because the rows were honest, and every consumer treating that
emptiness as a pass was certifying an unmeasured property.

This has been broken since the beginning. `Coverage Status` entered the shape
contract on 2026-08-04 (721baa0); the single global selector was added the
next day (9145ebb), whose own comment says it exists "so a contract and its
coverage computation cannot drift". It read one name from a contract that
already declared two.

Collapsing to one name is the root repair. The alternative under review
elsewhere — a per-declaration `status_column` override (quire-rs#410) — makes
the duplicate naming permanent and supported, and requires an engine release
nobody has shipped. This needs neither: the suite passes on the released
quire 0.32.0, whose engine has no `status_column` support.

Consumers renaming their own matrix headers is the intended breaking change;
modules are sha-pinned per machine, so nothing breaks until a consumer picks
this up. `reports/2026-08-04-tests-md-sweep.json` keeps the old name — it is a
record of a past sweep, not a live contract.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J4qKWaqT3xCXMbbtDqdDRY
@kreneskyp
kreneskyp requested a review from a team as a code owner September 12, 2026 16:24
@kreneskyp
kreneskyp merged commit 375fc2a into epic/264-assurance-integration Sep 12, 2026
kreneskyp added a commit to agent-ix/spec-artifacts-iso that referenced this pull request Sep 12, 2026
The TestMatrix contract collapsed `Coverage Status` and `Status` into a
single `Status` column (agent-ix/spec-artifacts-process#87, 375fc2a). The two
names carried byte-identical vocabularies and were applied inconsistently
across sibling coverage tables, so the single global
`traceability.status.column` selector matched only some of them and status
classification was silently skipped on the rest — after which `status_lies`
was empty because nothing ran, not because the rows were honest.

This module's own matrix still used the old header, so its Functional
Requirement Coverage table was one of the tables never classified.

One line. Verified the pre-existing failure set is unchanged: 23 failed /
232 passed with and without this change, byte-identical lists.


Claude-Session: https://claude.ai/code/session_01J4qKWaqT3xCXMbbtDqdDRY

Co-authored-by: Agent IX <agent@agent-ix.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
kreneskyp added a commit that referenced this pull request Sep 12, 2026
Ports 375fc2a from the epic/264-assurance-integration cohort to main.

The TestMatrix contract declared `Coverage Status` on four tables and
`Status` on three, with byte-identical vocabularies, and used them
inconsistently across sibling tables of the same kind — Functional
Requirement Coverage took one, Non-Functional Requirement Coverage the
other. No distinction was being preserved.

The consequence was not cosmetic. `traceability.status.column` is a single
global selector reading `Status`, so on the four `Coverage Status` tables
quire reported `status-column-matches-nothing` and skipped status
classification entirely. `status_lies` then came back empty because nothing
ran, not because the rows were honest, and every consumer treating that
emptiness as a pass was certifying an unmeasured property.

Broken since the beginning: `Coverage Status` entered the shape contract on
2026-08-04 (721baa0); the single global selector was added the next day
(9145ebb), whose own comment says it exists "so a contract and its coverage
computation cannot drift".

main carries surfaces the cohort does not, so the port reaches further than
the original commit: `mappings.yaml`, the `TestMatrix`/`TestMatrixIndex`
skeletons, `FunctionalCoverageRow.json`, the TypeSpec source, the negative
fixtures, and the frozen `baseline-0.1.0` fixture. The two golden records
pin a digest of their skeleton, so both digests are recomputed.

Renaming a declared column is not additive, so this is a deliberate
exception to NFR-001-AC-1's 0.1.0 baseline guarantee: the 0.1.0 baseline
fixture is renamed with the contract rather than the guard being weakened,
so it keeps comparing like with like and still catches every other drift.

Verified: the failure set is byte-identical to main's — 8 failed, 246
passed, 42 errors before and after, all pre-existing.

Closes #88


Claude-Session: https://claude.ai/code/session_01J4qKWaqT3xCXMbbtDqdDRY

Co-authored-by: Claude Opus 5 (1M context) <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