Skip to content

feat(harness): every coverage difference between the engines is registered per rule, both ways (GT-716 AC3) - #795

Merged
beyondnetPeru merged 3 commits into
developfrom
feat/gt-716-coverage-ratchet
Sep 21, 2026
Merged

beyondnetPeru merged 3 commits into
developfrom
feat/gt-716-coverage-ratchet

Conversation

@beyondnetPeru

@beyondnetPeru beyondnetPeru commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

68-validate-engine-verdict-parity.mjs holds the engines to agreement on the rules both decide and prints what only one decides as two counts gated on nothing. ADR-0041 never promised equal reach; this PR does not ask for it. It asks that every coverage difference be a diff somebody reads. GT-716 criterion 3.

  • 73-validate-engine-coverage-parity.mjs, a sibling of 68 reusing its deriveOutcomes precedence. Both engines run on an export of the tracked tree and on a satellite the guard creates with evolith init in a temporary directory (--core pointed at the export) — because the two do not skip the same rules: here native handlers decide the DoD, compliance-baseline, manifesto and taxonomy rules whose policies read a context nobody supplied; there almost everything either engine decides is decided by one of them.
  • Every rule decided by exactly one engine is held to engine-coverage-parity.baseline.json: per scenario, per direction, per rule, with the reason the other engine gave. Measured first — the class its report states and, for the OPA side, the facets its skip row names — then derived: an id no reachable policy emits, or a policy reading facets opa-input-builder.ts never emits (absent on a bare run whatever their provenance: layers is observed in nature and supplied in practice; input.repository is produced by nothing). A native-side class stated on the OPA side is filed as opa-gave-no-reason (the enforcer-routed HXA-01/02/04/05 skip without a class of their own), not as handler debt.
  • The ratchet closes both ways: an unregistered rule fails, a registered rule both engines now decide fails, an entry whose class changed fails. --write regenerates the file for review; --json publishes the counts. Runs in the Test job after 68 (≈17 s + ≈3 s).

Measured (2026-09-20)

scenario native-only of which opa-only of which
repository 82 52 supplied-facet-absent, 26 no-policy-in-bundle, 4 opa-gave-no-reason 7 MCP-01..03 unimplemented-native, OBS-EVD-01/02 needs-runtime, OBS-EVD-03 needs-external-system, MCP-05 handler declined
init satellite 49 34 / 11 / 4 7 the same seven

Both scenarios read the Core from an export of the tracked tree plus the compiled bundle (afc804bc). The first CI run disagreed with the laptop that wrote the baseline: a local coverage/ directory decided EM-Y-01/QT-01, git history decided DRIFT-01, the CLI's bundled corpus copy predated facets.json, and the working-tree run resolved the 138 ADR-conformance rules' decision-record references against that copy and failed them all falsely. No coverage directories, no dists, no .git, on every machine alike.

Falsifier, both outputs recorded. The criterion's own probe — removing an import from main.rego — cannot run since GT-675: the bundle build refuses a reachable policy nobody imports. The equivalent probe is renaming OBS-EVD-03 in telemetry-evidence.rego so the bundle stops deciding it: the guard went red on both scenarios naming the id (❌ repository: 1 registered entry(ies) are no longer coverage-only — opaOnly OBS-EVD-03), and green again once restored.

Guard 42 classifies it INSTRUMENTED (89 guards); guard 43 observed it red on the empty fixture (65/65); 11 unit tests.

Docs and board

known-limitations and the OPA README now say CI registers coverage differences per rule rather than merely allowing them. AC3 ticked in both catalogs with the measurement and the recorded falsifier; closure appended to the row; Last Updated line; counters unchanged (688 / 715). AC4–AC5 stay open.

Verified locally

73 tests 11/11 and the guard green against its baseline; 42, 43, 01, 04, 08, 46 (fixed point), 66.

Advances GT-716.

🤖 Generated with Claude Code

beyondnetPeru and others added 2 commits September 20, 2026 12:06
…tered per rule, both ways (GT-716 AC3)

`68-validate-engine-verdict-parity.mjs` holds the engines to agreement on the
rules BOTH decide and prints what only one decides as two counts gated on
nothing. ADR-0041 never promised equal reach; this does not ask for it. It asks
that every coverage difference be a diff somebody reads.

- `73-validate-engine-coverage-parity.mjs`, a sibling of 68 reusing its
  `deriveOutcomes` precedence: both engines run on the repository root and on a
  satellite the guard creates with `evolith init` in a temporary directory
  (`--core` pointed at the root), because the sign flips between the two — here
  the native engine decides 138 ADR-conformance rules no policy names; there
  the policies that decide anything read facets nobody supplied.
- Every rule decided by exactly one engine is held to
  `engine-coverage-parity.baseline.json`: per scenario, per direction, per rule,
  with the reason the OTHER engine gave. Measured first — the class its report
  states and, for the OPA side, the facets its skip row names — then derived:
  an id no reachable policy emits, or a policy reading facets
  `opa-input-builder.ts` never emits (absent on a bare run whatever their
  provenance: `layers` is observed in nature and supplied in practice,
  `input.repository` is produced by nothing). A native-side class stated on
  the OPA side is filed as `opa-gave-no-reason` (the enforcer-routed
  HXA-01/02/04/05 skip without a class of their own), not as handler debt.
- The ratchet closes both ways: an unregistered rule fails, a registered rule
  both engines now decide fails, an entry whose class changed fails. `--write`
  regenerates the file for review; `--json` publishes the counts.

Measured 2026-09-20 — repository: 220 native-only (164 no-policy-in-bundle,
52 supplied-facet-absent, 4 opa-gave-no-reason) and 8 opa-only (7
unimplemented-native, 1 handler declined); init satellite: 50 native-only
(35 / 11 / 4) and 4 opa-only (2 needs-runtime, 1 needs-external-system,
1 handler declined). ≈17 s + ≈3 s, in the `Test` job after 68.

Falsifier, both outputs recorded: the criterion's own probe (drop an import
from `main.rego`) cannot run since GT-675 — the bundle build refuses a
reachable policy nobody imports — so the equivalent is renaming `OBS-EVD-03`
in `telemetry-evidence.rego`: the guard went red on both scenarios naming the
id (`opaOnly OBS-EVD-03 … no longer coverage-only`), and green once restored.
Guard 42 classifies it INSTRUMENTED (89 guards); guard 43 observed it red on
the empty fixture (65/65); 11 unit tests.

`known-limitations` and the OPA README now say CI registers coverage
differences per rule rather than merely allowing them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: aarroyo <beyondnet.peru@gmail.com>
…er-rule ratchet now

Ticks criterion 3 in both catalogs with the measurement and the recorded
falsifier (the criterion's own probe cannot run since GT-675; the equivalent
one — renaming OBS-EVD-03 in its policy — turned the guard red on both
scenarios naming the id), appends the closure to the board row and adds a
`Last Updated` line. Counters unchanged (688 / 715). 08, 01, 04 and 46 green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: aarroyo <beyondnet.peru@gmail.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner September 20, 2026 17:07
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown
Contributor

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 5
  • New EN files needing ES translation: 2

Repository Coverage

Metric Value
Total EN files 527
Total ES files 497
Paired files 0
Coverage 0%

⚠️ Action required: 2 new EN file(s) added without ES counterparts.

To create skeletons:

node .harness/scripts/generate-es-skeleton.mjs <file.md>

Generated by GitHub Actions

…cked artifacts flipped six rules between machines

The first CI run of the coverage ratchet disagreed with the laptop that wrote
its baseline: EM-Y-01 and QT-01 were decided natively here (a `coverage/`
directory from a local jest run) and skipped there; DRIFT-01 was decided here
(git history) and skipped on a shallow clone; MCP-01..03 the other way round;
and OBS-EVD-01..03 changed class because the CLI's bundled corpus copy predated
`facets.json` and classified them by the old defaults.

Both scenarios now read the Core from an export of the tracked files
(`git ls-files`, local modifications included) plus the compiled `policy.wasm`:
no coverage directories, no dists, no `.git`, on every machine alike. A rule
whose native verdict needs one of those is skipped identically everywhere,
which is the fact the baseline should carry.

The export also corrected a measurement this branch had recorded: the 138
ADR-conformance rules were never "decided by native alone" — the working-tree
run resolved their decision-record references against the CLI's bundled copy
and failed all 138 falsely; against the export they are documentation on both
engines, decided by neither. Repository: 82 native-only (52 supplied-facet-
absent, 26 no-policy-in-bundle, 4 opa-gave-no-reason) and 7 opa-only; init
satellite: 49 and the same 7. Catalog, board and the guard's header say so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: aarroyo <beyondnet.peru@gmail.com>
@beyondnetPeru
beyondnetPeru merged commit 2e2a552 into develop Sep 21, 2026
52 checks passed
@beyondnetPeru
beyondnetPeru deleted the feat/gt-716-coverage-ratchet branch September 21, 2026 13: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.

1 participant