Skip to content

🔒 fix(cmd): discriminate absent provider declaration from forge failure (REL-03) - #78

Merged
konih merged 2 commits into
mainfrom
lane/aud2-s02-integrate
Aug 18, 2026
Merged

🔒 fix(cmd): discriminate absent provider declaration from forge failure (REL-03)#78
konih merged 2 commits into
mainfrom
lane/aud2-s02-integrate

Conversation

@konih

@konih konih commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

AUD2-S02 — REL-03 ErrNotFound discrimination

resolveRunFacts in cmd/assent/provider_host.go used to continue on any error from
the forge file read, so a 503, a throttle, or a token scoped away from the governance repo was
indistinguishable from "this provider declares no host". The fail-safe direction held (no fact
binds → CEL sees an absent attribute → REVIEW), but the path was invisible: the operator got a
missing-attribute predicate error naming nothing, and a has()-tolerant policy silently took its
fallback branch.

Now only errors.Is(err, forge.ErrNotFound) — the neutral port sentinel every adapter wraps with
%w — is treated as absence; every other error ends the run with a wrapped, named diagnostic,
consistent with how a malformed declaration already fails in the very next statement.

Changes

  • cmd/assent/provider_host.go — discriminate absence from failure (REL-03 / D-130).
  • cmd/assent/provider_host_test.go — both polarities, plus the error-wrap assertion.
  • CHANGELOG.md — Integrator-owned regeneration (task changelog-write).

Gate

CI=true task check exits 0 on this rebased head, including changelog-verify and
release-changelog-gate-test.

Independent review: APPROVE, no P0/P1, no merge conditions.

Branch note: opened from lane/aud2-s02-integrate rather than lane/aud2-s02 because the rebase
onto origin/main plus the CHANGELOG amend rewrote the published lane SHAs, and force-push is not
authorized in this repo.

konih added 2 commits August 19, 2026 00:29
…re (REL-03)

resolveRunFacts treated ANY FileAtRef error on providers/<name>.json as
"declaration absent" and continued, so a retry-exhausted 5xx, a throttle or a
token scoped away from the governance repo was indistinguishable from "this
provider declares nothing". The fail-safe DIRECTION held — no fact binds, CEL
sees an absent attribute, the run degrades to REVIEW — but the PATH was
invisible: the operator got a missing-attribute predicate.error naming nothing,
and a has()-tolerant policy silently took its fallback branch. A wrong decision
reached without a diagnostic.

continue now happens only for errors.Is(err, forge.ErrNotFound), the neutral
port sentinel every adapter wraps with %w. Every other error ends the run with
an error naming the provider, the declaration path and the ref.

No new discriminator idiom: this is D-130's ruling applied to its sibling call
site. loadResourceOwnerRegistry (same file) fixed exactly this conflation for
the who-may-approve registry; fileAtRefOrAbsent (run.go) is the same shape.
Hard-failing is also the consistent choice locally — an UNREADABLE declaration
now ends the run exactly as a MALFORMED one already did six lines down
(GUIDELINES Safety 2).

REQ-AUD2-S02-01 absence still skips (through the real gitlab 404 wrap chain)
REQ-AUD2-S02-02 5xx returns an error naming provider, path and ref
REQ-AUD2-S02-03 401/403 returns an error; a scope misconfiguration is never absence
REQ-AUD2-S02-04 non-vacuity: reverting the guard to a bare continue reddens -02
                and -03 while -01 stays green
REQ-AUD2-S02-05 no shipped decision outcome changes; task check (which runs
                dogfood-examples over all three packs) exits 0
REQ-AUD2-S02-02 asks the error to name the provider, the declaration path AND
the ref. The first version checked those as three separate strings.Contains
calls — but brokenForge (the fake's adapter-shaped error) already renders the
path and the ref inside the wrapped cause, so two of the three assertions could
not fail: a bare `provider %q: %w` wrap would have passed them. The test was
measuring the fake, not the code.

Asserted now as ONE contiguous substring that only the outer wrap can produce,
plus a separate check that the forge's own cause survives the %w.

Non-vacuity re-proved: mutating the implementation to `provider %q: %w` reddens
TestProviderDeclarationForgeErrorAbortsResolveRunFacts and nothing else. The
first version of the test stayed GREEN under that same mutation.

Also corrects the code comment: the malformed-declaration hard-fail this change
is made consistent with is in the very next statement (LoadProviderConfig), not
"six lines down" as the previous commit body and comment both said.
@konih
konih merged commit f886381 into main Aug 18, 2026
7 checks passed
@konih
konih deleted the lane/aud2-s02-integrate branch August 18, 2026 22:35
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