📝 docs: unlock E10 (GitHub adapter) + E11 (Rego) and decompose both spec-first - #45
Merged
Conversation
E10 was Locked under D-012 and reaffirmed locked by D-017 and D-019, so the operator instruction to implement the GitHub adapter is a new unlock event, not the exercise of an existing one; it is recorded before any spec text exists (AGENTS.md rule 6). D-141 lifts D-017's per-rule evidence gate on the Rego backend without waiving the design need behind it. ADR-0021 decides the three things the 2026-08-09 audit found under-scoped (ARCH-18/ARCH-19): the named forge.RunPort composite port, an importable conformance suite, a neutral capability model in which unknown never arms, and port-level transport policy. The original finding text is not in the repo, so the two design buckets are recorded as a re-derivation, not a citation.
E10 gets 18 stories (S01-S17 autonomous, S18 infra-gated) with normative ordering: the seam lands before the first GitHub API call, because all ~1,166 lines of the conformance suite live in _test.go files Go cannot import, so an adapter written today would have no executable contract and D-084's github-deferred catalog rows would be unflippable by construction. E11 gets 13 stories. Two constraints found during design are recorded in the spec rather than discovered during implementation: it is the first epic whose DoD is 'git diff schemas/' != 0 (the rego: leaf is an announced additive change to merge-policy.schema.json; EvaluationInput is untouched, so P3-E1-S02's neutrality guarantee holds), and bounding evaluation with a wall-clock timeout would itself violate rule 7, so the budget must be machine-independent and exceeding it is a process error rather than a policy outcome.
An independent roast of the first draft read the port against the code and found two representation defects that the endpoint-level GitHub dossier could not have surfaced. Fork-PR head addressing: run.go:274 reads head content by branch name inside one project and forge.MRInfo carries no source-repository identifier, so a GitHub fork PR 404s, fileAtRefOrAbsent maps that to nil, and OneSidedLifecycle returns KindDelete -- every fork PR would be evaluated as a whole-file deletion the contributor never made. The port now addresses content relative to the merge request and a conformance case pins it. Capability record surface: $defs.pins is additionalProperties:false with a single-string capabilityGap required iff mergeResultDigest is null, so an eleven-capability report has nowhere valid to live. ADR-0021 scopes the report to doctor output and arming-refusal reasons and states the audit-trail cost rather than widening a frozen schema or hiding the field in an unvalidated one. Also corrected: S02 could not close (cmd/assent constructs gitlab.New, and depguard_test.sh hard-fails unless it sees that call-site, while task lint is S02's own Verify) -- the neutral factory moves from S13 into S02 and the scanner's positive control is replaced rather than deleted. Every merges==0 assertion gains a paired merges==1 positive control, because an adapter that never arms would otherwise satisfy all of them. S14 now requires an adapter disposition on every catalog row, not only the deferred ones, which would otherwise have shipped GitHub with zero trust-boundary cases proven. Two false claims are retracted in place: the github-deferred rows are L3 live-infra proofs that importability cannot unblock, and capabilityGap never modelled the general absent-capability case.
… of PR #45 F1 -- the fork-PR P0 fix was half-landed. ADR-0021 item 1 still froze FileAtRef(project, path, ref) as the port's content accessor while item 5 said the port stops addressing by (project, branch-name); REQ-E10-S02-01 and -05 inherited the contradiction, so an implementer could satisfy -01 literally and preserve the fabricated-DELETE defect with a green build. Reconciled by stating what the narrowing actually binds: the GOVERNED SUBJECT (run.go:270,274) moves to FileAtBase/FileAtHead; the POLICY loads (run.go:203,211,253) deliberately keep FileAtRef because ADR-0015 s1 requires them to read the protected target ref by name -- migrating those would be a trust-boundary regression, not a cleanup. Both directions are now asserted. F2 -- E11's ordering was stated three incompatible ways and the backlog carried an E11-S06' row with no story section, no DoD and no REQ IDs (14 rows against a stated 13 stories). Promoted to a real story E11-S00, the deterministic-budget feasibility spike, built in a nested throwaway module so it answers the question without adding OPA to the root go.mod (no go.work exists, so Go excludes it from ./...). E11 is now 14 stories, S00-S13, and S02 is blocked on S00 because an observe-only rego: leaf is a different published contract from a gating one. F3 -- the spec said E11-S01..S03 were unblocked while judgment call (d) was pending. False, and the most consequential of the three: S03 is the story that EFFECTS the narrowing (d) governs -- it adds OPA to go.mod and its test paths sit in the D-123 guarded tree. Because neither purity gate is transitive, S03 would have landed green while converting rule 7's guarantee from link-enforced to capability-enforced, i.e. narrowing a hard rule by merging a story. S03 is now blocked on (d) alongside S04, and gains REQ-E11-S03-04 requiring the transitive purity guard under (d1). Also: recorded the operator's two 2026-08-10 answers in D-140/D-141 rather than leaving them only in the gitignored INBOX (rule 6), and made explicit that 'accept and pin OPA' settles (d)'s supply-chain half only -- (d2) also accepts and pins OPA, so the mechanism half is still open and is what blocks S03. Corrected Verify commands naming gates that do not exist (task scrub), packages that do not exist (internal/core/{lint, catalogue,testharness}), a shell drift guard that is actually Go (internal/schemadrift), a guard task check never runs (check-migration-invariants.sh), and two false counts (1,166 -> 1,155 conformance lines; 15 -> 14 non-deferred catalog rows).
konih
added a commit
that referenced
this pull request
Aug 10, 2026
… of PR #45 F1 -- the fork-PR P0 fix was half-landed. ADR-0021 item 1 still froze FileAtRef(project, path, ref) as the port's content accessor while item 5 said the port stops addressing by (project, branch-name); REQ-E10-S02-01 and -05 inherited the contradiction, so an implementer could satisfy -01 literally and preserve the fabricated-DELETE defect with a green build. Reconciled by stating what the narrowing actually binds: the GOVERNED SUBJECT (run.go:270,274) moves to FileAtBase/FileAtHead; the POLICY loads (run.go:203,211,253) deliberately keep FileAtRef because ADR-0015 s1 requires them to read the protected target ref by name -- migrating those would be a trust-boundary regression, not a cleanup. Both directions are now asserted. F2 -- E11's ordering was stated three incompatible ways and the backlog carried an E11-S06' row with no story section, no DoD and no REQ IDs (14 rows against a stated 13 stories). Promoted to a real story E11-S00, the deterministic-budget feasibility spike, built in a nested throwaway module so it answers the question without adding OPA to the root go.mod (no go.work exists, so Go excludes it from ./...). E11 is now 14 stories, S00-S13, and S02 is blocked on S00 because an observe-only rego: leaf is a different published contract from a gating one. F3 -- the spec said E11-S01..S03 were unblocked while judgment call (d) was pending. False, and the most consequential of the three: S03 is the story that EFFECTS the narrowing (d) governs -- it adds OPA to go.mod and its test paths sit in the D-123 guarded tree. Because neither purity gate is transitive, S03 would have landed green while converting rule 7's guarantee from link-enforced to capability-enforced, i.e. narrowing a hard rule by merging a story. S03 is now blocked on (d) alongside S04, and gains REQ-E11-S03-04 requiring the transitive purity guard under (d1). Also: recorded the operator's two 2026-08-10 answers in D-140/D-141 rather than leaving them only in the gitignored INBOX (rule 6), and made explicit that 'accept and pin OPA' settles (d)'s supply-chain half only -- (d2) also accepts and pins OPA, so the mechanism half is still open and is what blocks S03. Corrected Verify commands naming gates that do not exist (task scrub), packages that do not exist (internal/core/{lint, catalogue,testharness}), a shell drift guard that is actually Go (internal/schemadrift), a guard task check never runs (check-migration-invariants.sh), and two false counts (1,166 -> 1,155 conformance lines; 15 -> 14 non-deferred catalog rows).
This was referenced Aug 10, 2026
konih
added a commit
that referenced
this pull request
Aug 10, 2026
G1 -- REQ-E11-S00-01's isolation guard rewritten to '! go list ./... | grep -q spikes/rego'. NOTE the review's stated mechanism did not reproduce: in a default shell a pipeline's exit status is the LAST command's, so 'grep -c ... | grep -qx 0' actually exited 0 when isolated and 1 when leaked -- the correct polarity. It inverts to a false red only under 'set -o pipefail'. Verified both polarities under bash -o pipefail before and after. Conclusion right, reasoning wrong; fixed because a guard that flips meaning with a shell option is not a guard, and this one protects the 'no OPA in go.mod while (d) is open' invariant. G2 -- REQ-E11-S03-04's Verify used '-run TestPurity', which matches NO test (the real names are TestCorePurity*). Confirmed vacuous: it printed 'no tests to run' and exited 0. That was the verification for the single REQ enforcing the (d1) hard-rule narrowing -- exactly the tests-that-cannot-fail class this repo keeps re-finding, and it would have read as governed while checking nothing. Now -run TestCorePurity, confirmed to execute. G3 -- the retained-FileAtRef enumeration was incomplete and one line number was wrong: actual ref-addressed policy loads are run.go:203 (MergePolicy), :211 (RulesetBinding), :230 (Config) and :249 (pack); :253 is LoadPack, not a FileAtRef call. The omitted :230 is the most damaging to get wrong -- .assent/config.yaml declares the provider hosts, so migrating it to an MR-relative accessor would let a fork's head redefine its own fact semantics, the precise trust-boundary regression the F1 fix exists to prevent. G4 -- renamed the spike doc to spike-e-rego-budget.md; 'Spike D' is already the Kubernetes CRD feasibility spike that E14 is gated on. G5 -- E11-S13's story dependencies corrected to S00-S12 to match the backlog row.
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.
What this is
A design session, not an implementation lane. The ask was "implement the GitHub adapter
and consider the Rego parser unblocked — are the proper stories and specs in place?"
They were not. Neither E10 nor E11 had a spec directory or a single backlog story; both
existed only as ~5-line paragraphs in
later-phases.md. E10 was moreover still Locked(D-012, reaffirmed by D-017 and D-019), so the instruction is a new unlock event rather than
the exercise of an existing one. Per
/agent-loop-auto's own fallback, this ran the designsession instead of the loop. No engine code is touched.
Contents
forge.RunPort, importable conformance suite, neutral capability model, transport policy, and the addressing/representation decisionsp5-e10-github-forge/spec.mdp5-e11-rego-backend/spec.mdlater-phases.md/meta-plan.md/ ADR index / E10 design noteIndependent adversarial review ran, and changed the design
A roaster subagent that never saw the author's reasoning reviewed the first draft and returned
2 × P0. Both were verified against the code before acting, and both are fixed here:
run.go:274reads head content bybranch name inside one project, and
forge.MRInfohas no source-repository field. AGitHub fork PR 404s →
fileAtRefOrAbsentmaps tonil→OneSidedLifecyclereturnsKindDelete. Every fork PR would be evaluated as a deletion the contributor never made.$defs.pinsisadditionalProperties:falsewith a single-stringcapabilityGap, required iffmergeResultDigestis null. ADR-0021 scopes the report todoctor/refusal reasons andstates the audit-trail cost, rather than widening a frozen schema or hiding a
safety-bearing field in an unvalidated one.
Also fixed: S02 could not close (
main.go:72,83callgitlab.New, anddepguard_test.sh:356-363hard-fails unless it sees that call-site — whiletask lintwasS02's own Verify); every
merges == 0assertion gained a pairedmerges == 1positive control;S14 now requires an adapter disposition on every catalog row, not only deferred ones.
Two false claims are retracted in place rather than quietly edited away: the
github-deferredcatalog rows arelevel: L3, package: test/e2e— live-infra proofs thatimportability cannot unblock — and
capabilityGapnever modelled the general absent-capabilitycase.
🔴 Two blocking operator questions (details in the INBOX entry)
Verified:
internal/core/purity_test.goflags only each file's own imports/selectors,and
.golangci.yml'spure-treedepguard is deny-only over direct imports. Neither istransitive — so importing OPA into the guarded tree passes both gates green while
transitively linking
net/http. Options d1/d2/d3 are in D-141; d1 needs an ADR amendment.and independently droppable.
Gates
task checkgreen locally at the final commit (exit 0), includingchangelog-verifyafterregeneration. Note RELSE-08:
release-exitgateskips on PRs, so PR-green is nottask check-green — the local run is the real evidence.Not merging autonomously
/agent-loop-auto's stop conditions require surfacing core-contract changes for a human lookeven when gates pass, and two blocking questions are open. Review requested.