fix: three P0s, and twelve rules that promised enforcement and delivered nothing (GT-572, GT-595, GT-604) - #273
Merged
Merged
Conversation
fix: align main — the eleven paths the src/ move left behind (GT-632 c2)
fix(ci): guard the paths that are BUILT, not written (GT-632 c3)
…5 AC2) A rule declared `blocking: true` that the engine reports `skipped` produced exactly the same report as a blocking rule that passed — no finding — so "0 blocking findings" and "the blocking rules never ran" were indistinguishable and the verdict claimed coverage it did not earn. GT-569 made the denominator honest and thereby exposed this. The invariant is a `blocking: true` ValidationIssue, not a thrown error: `RulesetValidatorService.validate` catches engine exceptions and downgrades them to a logged warning, so a throw would be silently suppressed by the only caller in the product, and it would abort the corpus at the first offender instead of enumerating all of them. It is emitted from `toValidationIssues`, the single place both engine callers build their verdict from, and unlike the GT-569 `maxSkippedFraction` floor there is no option that turns it off. The rule's own outcome stays `skipped`, so `rulesChecked` is not re-inflated with rules that did not run. Live defect this exposes: 181 corpus rules were blocking-and-skipped. 96 are auto-generated ADR-conformance placeholders whose own validationQuery ends "Concrete checks to be wired into the harness" — the corpus claiming enforcement it never wired — so `generate-adr-rulesets.mjs` now emits them `blocking: false` (severity MUST and enforcement `executable` are kept: those describe the ADR). The remaining 85 are pinned and enumerated in `rule-corpus-triage.spec.ts`: 48 need a handler, 14 an external-system adapter, 12 a runtime adapter, and 11 were declared blocking by a human with no validationQuery at all (EC-SEC-01/02, SV-SEC-01/02, KI-R01..07). Those 11 are a governance decision — author the check or drop the flag — and are deliberately left failing rather than silenced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ke fail (GT-572) Verified against a PACKED TARBALL installed outside the workspace, because that is the only place the previous evidence could have been wrong. `npm pack` in src/packages/mcp-server, `npm install <tgz>` into a fresh temp dir with no workspace above it, drive stdio directly. **The FORBIDDEN is gone in the documented configuration.** 50 tools announced, `evolith-metrics` and `evolith-gate-evaluate` both execute, audit trail records `userId=local-stdio-session status=success`. The 47-of-47 denial the gap opened on does not reproduce at HEAD. **It is NOT gone in production, and could not be.** With NODE_ENV=production the same tarball denies every call: `FORBIDDEN / ABAC_POLICY_MISSING`. Both lookup paths are repository layouts resolved against the process cwd, and `policy.wasm` is a gitignored build artifact living OUTSIDE the package directory — no `files` setting can put it in the tarball. So an npm consumer had a fail-closed rule with no way to satisfy it, which is not a stricter rule. `EVOLITH_ABAC_POLICY_PATH` is the way: it manufactures nothing, and a path that does not exist falls to the same deny branch. Measured on the tarball: FORBIDDEN before, real verdict after. Shipping the bundle inside the package remains a release-pipeline decision, and is stated as such in the README rather than worked around. **The oracle was worse than blind — it was reporting the failure as OK.** The stdio smoke asserted `envelope.success !== undefined`, which holds for every response the server can emit. It was receiving `RULESET_NOT_FOUND` and printing `tools/call OK (evolith-gate-evaluate stdio)`. That error was real: gate discovery walks up for a `rulesets/` marker, the src/ refactor moved that directory under `src/` while the gate definitions stayed at the root, so the walk stopped one level too deep at `<repo>/src`. Same family as GT-632 — a path the refactor left behind, kept alive by an assertion that could not fail. Now a `tools/call` step passes only on a governance VERDICT: not isError, not FORBIDDEN (called out separately — it is this gap's symptom), success true, `data.verdict` in the vocabulary, `data.gateId` naming the gate. The assertion lives in its own module with 11 unit tests over payloads this server has actually produced, and CI self-tests it BEFORE running the smoke: a gate whose own falsifiability is untested is indistinguishable from no gate. Proof it fails: with the old dist, the new smoke exits non-zero with "the server flagged the result isError — [RULESET_NOT_FOUND] … /evolith/src/…". With the fix, both transports report verdict "failed" on gate business-sign-off, and the evidence artifact now carries the verdict instead of a boolean. Also: `corePath` was read by evolith-gate-evaluate and never declared, so an agent reading the schema could not learn about the one argument that makes the tool usable off-repo. And the smoke's 5s timeout was a cold-start flake that teaches people to re-run a gate. Verified: mcp-server 437/437, core-domain 1384/1384, guards 40/47/28 exit 0, both new specs shown failing when their fix is reverted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…T-604 c1)
GT-604 acceptance criterion 1 only. Criteria 2 and 3 are not buildable in this
repository and are handed over instead:
- the shared client has no route to call. The Tracker exposes only
GET /core-evaluation-transactions and GET /core-evaluation-transactions/{id};
a client written against a nonexistent route passes its own tests forever.
- the RoboSoft robot lives in beyondnetcode/evolith_tracker.
The contract (src/packages/contracts/src/ingest/evaluation-ingest.ts) carries the
five things the criterion names — correlationId, the true engine per executed
rule, the executed rules, the violations and the owner — with two decisions made
explicit in the shape:
- correlationId is REQUIRED on the wire. It is optional on EvaluationResult and
both existing producers already synthesize one; resolveIngestCorrelationId
does it deterministically at the boundary, which is what makes the endpoint's
idempotency enforceable rather than decorative.
- the "owner" of the criterion is TWO owners. requestedBy.actorId is who asked;
violations[].accountableOwner is who must fix it. They are different people,
they are named so they cannot be confused, and an unattributed violation is
never back-filled from the requester.
Derivation is enforced by EVALUATION_INGEST_FIELD_SOURCES: every wire field names
its EvaluationResult / Violation source path, and the spec walks all 35 of them
against objects produced by the REAL evaluateDriftGate pipeline. A rename upstream
stops a path resolving and the suite goes red at runtime — which matters, because
this package's ts-jest runs with diagnostics off, so a type-only guard would be
silently skipped. The type-level half is checked by tsc --noEmit on tsconfig.spec.
The module stays import-free like evidence-edge.ts (GT-605) and the GT-573
fixtures: core-domain is a devDependency on purpose, and a type import from it
would leak into the published .d.ts and resolve to `any` for any consumer that
does not install the engine.
Contract 26 tests / package 104 total, all green. Reverting the mapper in place to
the naive pre-contract shape (collapsed owner, engine coerced to a known value,
tenantId on the body) takes it to 12 failed / 14 passed.
Handover for the Tracker team in both languages, 22 headings each:
reference/core/control-center/opportunities/tracker-handover-gt604{,.es}.md —
the endpoint, the machine-key auth with tenant derived from the matched key
(precedent: tracker-approval.http-client.ts), the tests that would prove it, and
one question: GT-604's declared dependency on GT-603 names a different table
(audit_entries vs core_evaluation_transactions) and the Core-side attribution it
implies already shipped under GT-586. Raised as a question, not a correction —
the Tracker schema cannot be checked from here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nth (GT-595)
GT-595 made `blocking` + `skipped` fail the run and left 85 rules that promise
enforcement and never execute. Nine of them were triaged as config-shaped. Eight
now have a real check with a passing AND a failing fixture; the ninth is
declined, with the reason recorded as an executable measurement.
GROUP A — four topology flags (ArchitectureRuleHandler)
ED-R04, ED-R05, ED-R06 and DAM-R05 were already the exact shape
`evaluateConfigRule` reads: a boolean in `<topology>.config.json`. They fell
through on naming alone — their siblings carry prefixed categories
(`event-driven-config`), these carry bare ones (`event-ordering`, `retention`).
The dispatch keys on the RULE ID, not the category, because two of those bare
categories are SHARED across topologies with different files and different
flags: `schema-evolution` is ED-R06 (event-driven.config.json) *and* DAM-R08
(data-mesh.config.json); `retention` is DAM-R05 *and* ED-R07. A category-keyed
dispatch would claim all four and answer for a file it never opened. Two
fixtures pin that mis-claim directly — DAM-R05 against a satellite carrying only
`event-driven.config.json` must FAIL, and vice-versa — and a third asserts
ED-R07/DAM-R08 stay unclaimed rather than answered incorrectly.
ED-R05 is PARTIAL and says so in its own verdict: only the flag clause is
implemented, and the pass message carries "the second clause (AST scan of
consumer handlers for a deduplication guard) is NOT implemented".
GROUP B — four config assertions (GovernanceRuleHandler)
GIT-08 commitlint config present AND the package installed. The second
half is the GT-623 failure mode: a commit-msg hook whose `else`
branch printed "commitlint is not installed — skipping" and exited
zero. A config nobody installed fails open, so it fails here.
SEC-RL-01 an HTTP surface in the tree obliges a rate limit read from
`process.env`. A hard-coded `RATE_LIMIT_MAX = 100` FAILS: the rule
asks for configurability, not for the words.
SEC-RL-02 same, for `MCP_MAX_BODY_BYTES` or an equivalent env read.
Both scan implementation sources only — a `.spec.ts` mentioning the
env var must not satisfy the rule, and a fixture pins that.
MTN-05 PARTIAL, and the reason is a conflict in the corpus. Its
validationQuery wants `boundedContexts[].persistence` to be one of
`shared-schema | schema-per-tenant | db-per-tenant`, but
evolith-yaml.schema.json declares that field with
`enum: [PostgreSQL, MongoDB, SQLServer, SQLite]` under
`additionalProperties: false`. A satellite that satisfies MTN-05 as
worded FAILS contract-schema validation. Implemented: the half both
documents agree on — the decision must be declared, per bounded
context. The enum clause is unenforced and the verdict says so.
OCB-02 — DECLINED, and this is the point of the exercise
No artifact in src/rulesets carries an `availability` marker of either value.
The rule quantifies over the empty set, so a faithful handler returns `passed`
on every repository that could ever exist: the only way to produce a violation
is to first add the marker whose absence is the actual defect. Wiring it would
have moved OCB-02 into the `native-handler` count and bought a number instead of
a check — the exact false green GT-569 and GT-595 exist to remove. It stays in
the backlog, flagged for re-authoring against `openCoreMatrix` (which IS
populated, and the spec proves it) alongside OCB-05.
MEASUREMENT
rule-corpus-triage.spec.ts, recomputed against the real corpus:
blocking-and-skipped 85 -> 77
unimplemented-native 60 -> 52 (offenders 48 -> 40)
native-handler 139 -> 147
unclaimed 114 -> 106
All eight came out of `unimplemented-native`, which is what that class always
meant: a handler was the only thing missing. No adapter was written and no rule
was re-authored, so the other three classes are untouched. The eight ids are now
named in the spec, not merely counted — a count cannot tell a closure from a
rule that left the corpus.
core-domain: 1418 passed / 126 suites. tsc --noEmit clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l (GT-632) HXA-01/02/04/05 declared `blocking: true` and carried a complete dependency-cruiser `from`/`to` clause, and reported `skipped` on every run. The cause was not a missing check — it was that `DiskRulesetRepository` never copied `enforce` onto the normalized rule. `rule.enforce` was therefore always undefined, `EnforcerEvaluator.isEnforcerRule()` always false, and the entire enforcement subsystem (adapters, sandbox, composite, and the `NodeProcessRunner` all three surfaces already inject) was unreachable code. Rather than install dependency-cruiser, the clause is evaluated natively: - the Core is stateless and reads satellites through an overlay filesystem (ADR-0101) that a subprocess cannot see, so shelling out works on CLI and silently does nothing on REST; - `to.path: node_modules/...` only matches when the satellite's node_modules is installed, so a cloned satellite would resolve nothing and mark four blocking rules `passed` — a false pass, strictly worse than the skip; - the check is an import-graph predicate `ast-rules.ts` already implements for MM-R11/MM-R12; it was merely hard-coded to two rule ids instead of driven by the clause the rules carry. Changes: - carry the authored `enforce` block through normalization (config, configRef, mode, severityMap), and widen `EnforceDescriptor` to hold it; - add `ModuleBoundaryRuleHandler` + `module-boundary-rules.ts`: a rule-driven from/to module-graph engine over the TypeScript AST, claiming by CLAUSE (not by rule id) within the boundary categories the PolicyCompiler already defines; - `CompositeRuleEvaluator` re-dispatches any `skipped` enforcer result to the native strategy — the `fallback: 'native'` contract policy-compiler has documented since GT-516, finally connected. An unavailable analyzer now costs precision, never coverage; a rule both engines decline stays `skipped` with both reasons and still fails the run under GT-595 AC2; - `ShellEnforcerAdapter.certificationGap` + a dependency-cruiser guard: running depcruise without a config compiled from the routed rules evaluates the satellite's own ruleset and reports names that match no routed toolRuleId, which would have marked all four `passed` on unrelated evidence. It now refuses and degrades instead. The pinned corpus measurement moves 85 -> 81 blocking-and-skipped rules (unimplemented-native 48 -> 44); the triage loader was also dropping `enforce`, so it could not have seen the change otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Branch 1 of 5 into integration/gt-572-595-604. No conflicts.
Branch 2 of 5. No conflicts (it already contained branch 1).
Branch 3 of 5. One conflict, in rule-corpus-triage.spec.ts: branches 2 and 3 each re-pinned the same corpus counts independently, over disjoint rule sets. Resolved by RECOMPUTING against the real corpus, not by picking a number. Both branches' loader and assertions are kept — branch 3's `enforce` pass-through in loadCorpus (without it the four HXA rules are invisible to the measurement) and branch 2's named-id closures and OCB-02 vacuity block. Measured, not assumed (jest reports Received length: 73 when the pin is perturbed to 74): native-handler 139 -> 151 (+8 config-shaped, +4 module-boundary) unimplemented-native 60 -> 48 unclaimed 114 -> 102 blocking-and-skipped 85 -> 73 offenders/unimplemented 48 -> 36 A parallel named test was added for the four module-boundary closures (HXA-01/02/04/05, with HXA-03 as the unclaimed control), matching branch 2's 'name them, do not merely count them' treatment.
Branch 4 of 5. No conflicts. Note it branched from main directly and did NOT contain branch 1, contrary to the integration brief.
Branch 5 of 5. No conflicts. Also branched from main directly and did NOT contain branch 1.
…w reality
Three failures, each a consequence of a behaviour change that landed
deliberately. None was fixed by weakening an invariant.
1. infra-providers/rule-applicability.integration.spec.ts (GT-571)
GT-595 AC2 makes `blocking` + `skipped` fail the run, so a fresh satellite
now reports 72 blocking findings where the spec asserted []. Asserting [] here
would be asserting that the GT-595 criterion does NOT work.
The old assertion conflated two claims: (1) no rule addressed to somebody else
fires — the GT-571 invariant — and (2) no blocking rule fails at all. Only (2)
stopped being true. (1) is now asserted directly and more strictly than before:
a new test requires that NO blocking finding belongs to a rule applicability
excluded, over the same run. The corpus is not excluded and no applicability
assertion was touched.
Measured breakdown of the 72: 70 are the GT-595 invariant, and the other 2 are
REAL verdicts from rules that really executed —
MTN-05 scaffolded evolith.yaml declares no `spec.boundedContexts`
GIT-08 the scaffold writes no commitlint configuration
Both were closed by the GT-595 config-shaped slice and both fail on a freshly
scaffolded satellite. That is a live defect in InitializeProjectUseCase, not a
testing artifact; it is pinned by name so closing it moves the list to [].
2. src/tests/contract/sdk-type-contract.types.ts (GT-565)
Branch 1 added `blockingSkippedRuleIds` to ValidationResult and core-api emits
it, but the wire-contract map that enumerates the type was never updated, so
the SDK type no longer described the wire. Declared it. This is the contract
test doing its job.
3. reference/core/interfaces/how-to-*.md
Derived artifacts, regenerated with gen-howto.ts. The drift is GT-632's:
HXA-01 is claimed now, so HXA-03 takes its slot in the generated listings.
GT-595 closes on AC2: a rule declared `blocking` that returns `skipped` now fails the run, emitted as a blocking ValidationIssue rather than a throw -- `RulesetValidatorService.validate` catches engine exceptions and downgrades them to logger.warn, so a throw would have been swallowed by the only product caller. Enforcing it exposed 181 blocking-but-dead rules: 96 auto-generated ADR placeholders (the generator now emits `blocking: false`), and 12 of the remaining 85 closed for real. The pin stands at 73, recomputed and observed failing when perturbed to 74. Two root causes recorded: `normalizeRuleset` never copied `enforce`, which made the whole enforcer subsystem unreachable for lack of data; and the triage spec's own loader dropped it too, so the measuring instrument measured a different corpus than production evaluates. dependency-cruiser and OCB-02 are recorded as deliberate non-adoptions. GT-572 closes against an `npm pack`ed tarball installed OUTSIDE the workspace. The default configuration returns a real verdict; under NODE_ENV=production the denial was a packaging defect (policy.wasm is gitignored and written outside the package), satisfiable via EVOLITH_ABAC_POLICY_PATH. Shipping the bundle inside the package stays a release-pipeline decision, documented and not done. GT-604 moves PENDING -> IN-PROGRESS on criterion 1 only; criteria 2 and 3 are not buildable in this repository and were handed over instead. Its declared dependency on GT-603 is recorded as unconfirmed, not as a finding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📊 Bilingual Coverage ImpactPR Changes
Repository Coverage
To create skeletons: node .harness/scripts/generate-es-skeleton.mjs <file.md>Generated by GitHub Actions |
This was referenced Jul 29, 2026
beyondnetPeru
added a commit
that referenced
this pull request
Jul 29, 2026
main moved while this PR was open: #273 merged `integration/gt-572-595-604` and #274 followed. That is the branch which closed the twelve rules — HXA-01/02/04/05 (GT-632), MTN-05, GIT-08, SEC-RL-01, SEC-RL-02, ED-R04/R05/R06, DAM-R05 (GT-595) — so Core now pins `native-handler: 151` / `unimplemented-native: 48`. GitHub could not build a merge ref for a conflicting PR, so NONE of the five required workflows ran on #276; only CodeQL reported. The conflict was in `rule-corpus-triage.spec.ts`, the one file both sides rewrote. Resolved by taking main's spec whole and re-applying the extraction on top, not the reverse: main's version is the newer truth and carries assertions this branch never had (the AC2 blocking-and-skipped list, the named module-boundary and config-shaped closures, the OCB-02 vacuity measurement). Two things had to travel with it: - `enforce` in the corpus loader. GT-632 added it to the spec's loader because dropping a field the handlers dispatch on measures a DIFFERENT corpus — four rules carried a complete module-graph clause nothing read. The extracted harness is that loader now, so it carries the field too. Without this the capture would have reproduced the pre-GT-632 classification. - `PINNED_CLASS_COUNTS` re-pinned to main's numbers, and main's inline literal replaced by a reference to it, so the .mjs guard keeps reading one pin. Then recaptured and rebuilt, in that order. The new guard earned itself on the first real drift it faced: before recapturing it named all twelve rules, one line each — `HXA-01: snapshot says unimplemented-native, triage says native-handler`. That is precisely the case the old guard passed on, twice. Backlog 60 -> 48, and the READMEs move with it: analyser-adoptable 9 -> 5, partial 8 -> 5, to-author 43 -> 38, so adoption is worth 10.4% / 20.8% of the backlog rather than 15% / 28%. The shares FELL because four of the nine rules that table offered to an analyser (HXA-01, HXA-02, HXA-04, GIT-08) now have a native handler — Evolith wrote them first, and what remains leans further toward authoring. Verified after merge: core-domain jest 1436 passed / 127 suites; mapping guard 9/9; `capture --check` faithful at 386 rules; guards 01, 04, 46 green (46 still at a fixed point).
beyondnetPeru
added a commit
that referenced
this pull request
Jul 29, 2026
…275 Investigation only, at the user's explicit request -- no fix applied. Each of the three CI checks still red on PR #275 (Security Audit, E2E Tests, Evolith Core Validation) is root-caused with evidence, not assumed pre-existing on the strength of "it was probably already broken": - Security Audit: 31 npm audit findings (29 high, 2 moderate) in transitive deps of jest-snapshot/fast-uri. No package.json or lockfile touched here. - E2E Tests: one test of 132, "should validate architecture with --arch flag", exits 1 where it expects 0 or 2. #273's own CI run -- the same branch's prior merged PR, before any commit in this session existed -- shows the identical failure by name and by count. - Evolith Core Validation: fails at Bilingual Terminology Lint, 5 inconsistencies across four .es.md files, none touched by any commit in this PR (checked with git log per file, not assumed). Left PENDING with unchecked acceptance criteria: fixing the npm audit findings risks a breaking dependency bump, the E2E exit-code mismatch needs a decision about which side (CLI or test) is wrong, and the terminology fixes need a human call on which suggestions are correct versus a deliberate exception -- none of that is this closure's call to make unilaterally. Counters recomputed: 635 gaps EN/ES (594 done, 23 pending), 611/611 catalog sections, 576 closure records (unchanged -- GT-636 is open, so it carries none). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five branches integrated.
GT-595andGT-572close;GT-604advances to its first criterion and stops at a boundary this repository cannot cross.GT-595 — the honest denominator, one level up
blocking+skippednow fails the run. A blocking rule that silently skips is indistinguishable from one that passed, so the suite reported a verdict it had not earned.It exposed 181 rules declared
blockingthat never execute. 96 were auto-generated ADR placeholders whose ownvalidationQueryends "Concrete checks to be wired into the harness" — the generator assertedblocking: trueon a check it never wired. Of the remaining 85, 12 are now genuinely closed and the corpus count is 73, recomputed rather than assumed: the pin was perturbed to 74 and observed failing.Two findings are the same shape as everything else this session:
HXArules were dead becauseDiskRulesetRepository.normalizeRulesetnever copied theenforcefield. The schema has accepted it since GT-516, ADR-0002 authors six blocks, the PolicyCompiler compiles them — normalization dropped it, so the entire enforcer subsystem was unreachable for lack of data, not wiring.rule-corpus-triage.spec.ts's own loader dropped it too, under a docstring claiming it loads "exactly asDiskRulesetRepositorydoes". The measuring instrument was measuring a different corpus than production evaluates.Two things were deliberately not done, and both are recorded with their reasoning:
dependency-cruiserwas rejected. The Core is a stateless engine reading satellites through an overlay filesystem, which a subprocess cannot see, and the tool's config matchesnode_modules/— an uninstalled satellite yields zero violations and passes four blocking rules. A false pass is worse than the skip being fixed.OCB-02was not closed. Zero artifacts anywhere carry anavailabilitymarker, so it quantifies over the empty set and can only pass. Closing it would have bought a number instead of a check.GT-572 — a smoke that could not fail
Verified against an
npm packed tarball installed outside the workspace, because the symlink is what hid GT-625. The old assertion wasenvelope.success !== undefined— true of every response the server can emit — and was observed printingtools/call OKwhile receivingRULESET_NOT_FOUND. The new one is proven falsifiable three ways, including a live run against the pre-fixdist.Also fixed:
findCorePathresolved<repo>/srcafter thesrc/refactor (aGT-632sibling), andcorePathwas read but undeclared in the tool'sinputSchema.Caveat recorded, not papered over: shipping
policy.wasminside the package remains a release-pipeline decision.EVOLITH_ABAC_POLICY_PATHmakes the production posture satisfiable; it does not make the artifact present.GT-604 — one criterion, and an honest boundary
The ingest contract ships with
correlationIdrequired and both owners carried distinctly —requestedBy.actorId(who asked) andviolations[].accountableOwner(who must fix). Criterion 2 has no endpoint to call; criterion 3 needs RoboSoft, which lives inevolith_tracker. A bilingual handover names the endpoint, payload, auth and tests the Tracker team would need.Recorded as unconfirmed: GT-604's declared dependency on GT-603 appears wrong — GT-603 migrates
audit_entrieswhile GT-604 namescore_evaluation_transactions, and the Core-side attribution already shipped under GT-586.Verification
core-domain 1433 · CLI 1436 · mcp-server 433 · core-api 179 · agent-runtime 406 · infra-providers 128 · contracts 104 · sdk-client 63 · core 62 · contract 43 · exploration 6.
ci-runner governance17/17 in a real checkout. Guards 34, 40–47 green with their self-tests; bilingual parity and docs green; guard 46 reports the chain at a fixed point.Every remaining failure was baselined against
mainby measurement and confirmed pre-existing.Board: 592 / 632.
🤖 Generated with Claude Code