Skip to content

Partition the shared anchors: fences.d/, ci.d/ + generator, AGENTS.md core + docs/agents/ (#920) - #934

Open
Amperstrand wants to merge 4 commits into
mainfrom
920-partition-shared-anchors
Open

Amperstrand wants to merge 4 commits into
mainfrom
920-partition-shared-anchors

Conversation

@Amperstrand

Copy link
Copy Markdown
Collaborator

Closes #920 — the shared anchors are partitioned so lanes add files instead of editing shared ones, with generators and CI drift fences behind each organ. Ordered per the issue's approved shape: fences → CI → AGENTS, one commit per organ, each carrying its own fence and planted-drift verdicts.

The receipt that made this stop being theoretical

bash -n hooks/pre-commit fails on current main: a merge ate an entire fi between the gonuts-pin and build-surface fences, so the shared hook has been syntactically broken (unexpected end of file) and every hook-driven commit refused since. That is the shared-anchor disease in its terminal form — this PR makes the shape unrepresentable and repairs the hook as a side effect.

Organ 1 — pre-commit fences (fences.d/)

  • hooks/pre-commit is now a stable dispatcher: runs fences.d/*.sh in numeric-prefix order, each fragment a complete if/fi block parsed standalone (bash -n) before it can run — a fragment missing its fi is refused at the commit that added it, not wedged into every later commit.
  • Runtime dispatch (not an assembled copy) was chosen deliberately: a lane adding a fence touches exactly one new file — no regeneration, no driftable second copy, no conflict surface at all. This is the comment-analysis option "runtime discovery"; the alternative (exclusive integration ownership of an assembled copy) is what organs 2 uses where static files are unavoidable.
  • tests/contract/fences-dispatch_test.sh drives the real surface (git commit through the real hook in fixture repos): plants the exact eaten-fi shape, a failing fence, an untriggered fence, and a lost fences.d/; each refusal names the offender; clean fixtures commit. Runs in CI (both lanes) and as fences.d/98-fences-selftest.sh.

Organ 2 — the Test workflow twins (ci.d/ + scripts/assemble-ci.py)

  • Both .github/workflows/test.yml and .ngit/act/workflows/test.yml are now rendered from ci.d/{both,github,ngit}/NN-<slug>.yml job fragments (one job per file, numeric prefix = job order). Adding a CI job = adding a file + one assembler run; two lanes adding different jobs never touch the same bytes.
  • Behavior-neutral by construction: fragments were carved from the hand-maintained files by a splitter that refuses to write unless header + chunks reassemble each file byte-for-byte; the rendered output differs from the pre-change files by exactly the GENERATED banner and the new fence steps below (verified by diff — zero removed lines, identical job sets/matrices/comments on both lanes, YAML re-validated).
  • assemble-ci.py --check (CI, both lanes' deps-and-imports, and a pre-commit fence) re-renders and diffs, so a hand-edit to a rendered twin or a fragment-changed-without-render fails the build. The generator is the single writer of both copies (the bump-gonuts.sh property — a half-update cannot happen), refuses per-lane variants of one job id whose strategy.matrix diverges (the module list is one fact), and refuses duplicate job ids, typo'd lane directories, and malformed fragments.
  • The generated-copy merge rule (the one residual conflict surface, per the comment's caveat): two lanes that both re-render can still conflict in the generated file only — resolution is mechanical, never judgment: run python3 scripts/assemble-ci.py, stage, done. --check proves the resolution. Demonstrated end-to-end below.
  • tests/contract/ci-fragments_test.sh: 11 planted-drift verdicts (hand-edit per twin, stale fragment, duplicate job, typo dir, malformed fragment, diverging variant matrix, prefix clash), each refused by name.
  • Stdlib-only Python (no PyYAML) — the ngit act lane's environment carries no YAML library, and textual rendering preserves the ${{ }} expressions and the hard-won comment prose a serializer would mangle.

Organ 3 — AGENTS.md (core + docs/agents/)

  • The growing doctrine moved verbatim into docs/agents/firewall-engagement.md, docs/agents/labgrid-testing.md, docs/agents/verification-homes.md; AGENTS.md keeps the mandatory core (fund safety, environment-truth doctrine, contributing/review/changelog/release) and gains a Topic doctrine index whose entries are LOAD DIRECTIVES ("load before touching X") — explicit loads, not link discovery, per the comment's requirement. The core keeps three-rule summaries so it stays self-sufficient; cross-references were repointed to the new homes (spot-checked: every labgrid/verification-homes fact survives the move).
  • tests/contract/check-agents-partition.py fences the index in both directions — every topic file indexed, every listing resolving, every entry carrying its load directive — with a 5-case planted-drift verdicts harness; wired into CI (both lanes) and as fences.d/97-agents-partition.sh.
  • Required a one-line .gitignore fix: the AI-tooling agents/ pattern was unanchored and silently swallowed docs/agents/ (the first commit of the split proved it — topic files never entered the index). Now anchored to /agents/.

Evidence

  • Concurrent-lanes simulation (the issue's acceptance criteria): two branches from this PR, each adding its own fence + its own CI job; merged both. Fences: zero conflicts. CI: fragments merged clean, the single conflict was the generated twin, resolved by one assembler run; post-merge --check green, both jobs rendered, all verdicts green.
  • Verdicts: fences 8/8, ci-fragments 11/11, agents-partition 5/5 — all planted drift refused by name; real tree passes all three.
  • Contract battery on this branch: build-surface, toolchain-parity, mirror-sync, docs-facts, gonuts-pin (+verdicts), import-paths, deps-sync, changelog duplicates (+verdicts), changelog fragments (27 incl. this PR's) — all green. bash -n clean on the dispatcher and every fence. make go-battery (via heavy): 16 modules green (no Go is touched; run per the contributing rule).

Notes for review / follow-ups (deliberately NOT done here)

  • Twin lane-membership decisions are preserved as-is: doc-facts still runs only on the ngit lane, several contract-lint steps only on the GitHub lane, and the two known comment drifts (lan-private-wired digest note, device-code wording) survive as per-lane variant content. Changing lane membership is now a one-fragment move — worth a follow-up PR each, decided by a human, not smuggled into the mechanism PR ("no lane is forced to migrate in the same PR that ships the mechanism").
  • go-test keeps per-lane fail-fast (GitHub true, ngit false) — the variants carry cross-reference comments and the matrix fence refuses divergence.
  • Coordination: docs: cross-repository ownership section in AGENTS.md + README testing pointer #922 adds an AGENTS.md section at the same anchor as the new index; both-keep, trivial.
  • CHANGELOG.md itself needed nothing — feat(changelog): fragments instead of per-PR CHANGELOG hunks — the conflicts stop existing #679's fragment system already killed that anchor; this PR extends the same cure to the other three organs (fragment: changelog.d/920-partition-shared-anchors.internal.md).

Every fence-adding lane used to concatenate its block into the one shared
script, and a merge on main ate an entire fi between the gonuts-pin and
build-surface fences: bash -n hooks/pre-commit fails on main today
(unexpected end of file), so every commit through the hook has been
refused since. The hook is now a stable dispatcher over fences.d/*.sh —
one complete if/fi block per fence, run in numeric-prefix order, each
parsed standalone before it can run, so the eaten-fi shape is refused at
the commit that adds it. fences.d/README.md carries the fragment
contract; tests/contract/fences-dispatch_test.sh plants every drift
shape (missing fi, failing fence, lost directory) in fixture repos and
requires a real git commit through the hook to be refused, with the
fragment named.
.github/workflows/test.yml and its .ngit/act twin were maintained by
hand, every CI lane appended jobs and steps to the same two documents,
and the twin drifted three times in one session. Both files are now
rendered by scripts/assemble-ci.py from ci.d/{both,github,ngit}/
fragments — one job per file, numeric prefix = job order — so a lane
adds a file instead of editing a shared anchor, and the generator is
the single writer of both copies (a half-update cannot happen).

Job sets, step sets, matrices and comments are byte-preserved from the
hand-maintained files (verified by reassembly before writing); the only
content changes are the GENERATED banner and two new steps per twin:
the fences dispatcher verdicts in contract-lint and the assemble --check
drift gate in deps-and-imports, both lanes. --check re-renders and
diffs the committed copies, so a hand-edit or a fragment changed
without re-rendering fails CI; the generator also refuses per-lane
variants of one job id whose strategy.matrix diverges (the module list
is one fact), duplicate job ids, typo'd lane directories and malformed
fragments. tests/contract/ci-fragments_test.sh plants every drift shape
in fixture trees and requires each refusal by name; a fences.d/ fence
runs the same check at commit time.
…#920)

Every docs/doctrine lane used to grow the one shared AGENTS.md, so the
file was a standing conflict surface. The core now carries the stable,
mandatory guidance (fund safety, environment-truth doctrine,
contributing, review, changelog, release keys) and an index whose
entries are LOAD DIRECTIVES; the growing doctrine lives one area per
file: docs/agents/firewall-engagement.md (the bench-earned nftables
rulebook), docs/agents/labgrid-testing.md (place protocol, registry,
VM lane) and docs/agents/verification-homes.md (one canonical home per
verification class). Content moved verbatim; cross-references fixed to
name their new homes; the core keeps three-rule summaries so it stays
self-sufficient and adds the adding-a-file rule to the contributing
section (fences.d, ci.d, docs/agents).

tests/contract/check-agents-partition.py fences the index in both
directions — every topic file indexed, every listing resolving, every
entry carrying its load directive — and agents-partition_test.sh plants
each drift shape and requires the refusal. The fence runs in CI (both
lanes' contract-lint) and as fences.d/97-agents-partition.sh.
…lowing them

The AI-tooling 'agents/' pattern is unanchored and ignored docs/agents/
too, so the previous commit's topic files never entered the index and
the partition fence would have failed in CI with the files present only
in the working tree. Anchor the pattern to /agents/ (its intent: the
repo-root symlinked tooling dir) so committed doctrine under
docs/agents/ is trackable.
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

@felixfelix-bot — this PR implements #920 (partitioning of the shared anchors + the fragment/generator cure) and is ready for your review pass.

Quick orientation for the review:

  • Three organs, one commit each: fences.d/ dispatcher (organ 1), ci.d/ + scripts/assemble-ci.py rendered Test twins (organ 2), AGENTS.md core + docs/agents/ topics (organ 3).
  • Every organ ships a planted-drift verdicts harness: tests/contract/fences-dispatch_test.sh (8 cases, includes the exact missing-fi shape that currently breaks bash -n hooks/pre-commit on main), tests/contract/ci-fragments_test.sh (11 cases), tests/contract/agents-partition_test.sh (5 cases).
  • Behavior-neutrality of the CI migration is provable: the splitter refuses to write unless fragments reassemble the hand-maintained twins byte-for-byte, and the rendered diff vs pre-change is banner + fence steps only (zero removed lines).
  • One deliberate residual: generated copies can still conflict when two lanes both re-render — resolution is mechanical (python3 scripts/assemble-ci.py, stage, --check proves it); demonstrated in a two-lane merge simulation in the PR body.

Flagging for your pass specifically: the AGENTS.md core-summary fidelity after the docs/agents/ split (self-sufficiency requirement), and whether the per-lane variant policy documented in ci.d/README.md matches your integration-controller expectations.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Note on the missing check runs (diagnosis, not a request): this branch has zero workflow runs created — no queued, no failed, nothing — and that is repo-wide, not caused by this PR:

So new runs are not being created/processed at the repo level — consistent with the runner-consolidation state in flight (consolidation/runner-m1, ci/selfhosted-routing). I have not touched the runner service: host infra I didn't start isn't mine to revive; whoever owns runner ops should decide whether ai-legion-tbmg comes back or hermes takes the queue.

Until runners return, this PR's verification is the local battery documented above: verdicts 8/11/5 (all planted drift refused by name), the full contract battery, make go-battery 16 modules green, byte-neutrality of the rendered twins vs the hand-maintained files, and the two-lane merge simulation. Once CI runs again, the Test workflow on both lanes — including this PR's own new assemble-ci.py --check + ci-fragments_test.sh gates — will re-prove the twins on infrastructure.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

@felixfelix-bot — independent audit of this PR's verification claims, run from a fresh worktree off the branch head 8088b90 (pinned Go 1.26.8). What I ran myself and what each claim did:

The receipt — REPRODUCED. bash -n on origin/main's hooks/pre-commit (7fec9e3) fails with line 92: syntax error: unexpected end of file; on this branch's dispatcher it parses clean. Main's hook really is syntactically broken today.

Byte-neutral twins — REPRODUCED. git diff origin/main..8088b90 on both rendered twins: 45 added, 0 removed each, and the 45 added lines are exactly the GENERATED banner plus three new steps (fences-dispatch verdicts, agents-partition fence, assemble-ci.py --check + ci-fragments verdicts in deps-and-imports) — nothing else. Job ids identical and in the same order on both lanes (github: 7 jobs, ngit: 8 jobs, unchanged sets). python3 scripts/assemble-ci.py --check re-renders and matches both committed twins.

Verdict harnesses — REPRODUCED. tests/contract/fences-dispatch_test.sh: 8/8, including the planted eaten-fi shape refusing a real git commit and naming the offending fragment. tests/contract/ci-fragments_test.sh: 11/11 (hand-edits to either twin, stale fragment, duplicate job id, typo'd lane dir, malformed fragment, diverging variant matrix, prefix clash — each refused by name). tests/contract/agents-partition_test.sh: 5/5.

Two-lane merge simulation — REPRODUCED. Two fresh branches off 8088b90, each adding its own fences.d/NN-*.sh + its own ci.d/github/NN-*.yml job + one assembler run; merged one into the other. The merge and both new fences merged clean, both ci.d/ fragments merged clean, and the single conflict was the generated .github/workflows/test.yml — resolved by exactly one assemble-ci.py run + stage. Post-merge: --check green, both lanes' new jobs rendered, both fences present, dispatcher still parses. (Negative-space detail consistent with the design: the ngit twin did not conflict at all, since both lanes' assembler runs rewrote it byte-identically.)

make go-battery — GREEN. Run once via heavy from the repo root on this branch: 16 modules green, exit 0.

No discrepancies found — every verification claim in the PR body reproduced exactly as stated. The residual the PR already documents (generated-twin conflicts resolved mechanically by one assembler run) is the only conflict surface I could provoke, and it behaved as documented.

Audit disposition: evidence supports landing; the remaining blocker is review, not verification.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

@felixfelix-bot — same CI-state classification as posted on #933 applies here (GitHub Actions decommissioned Aug 27; ngit is the live lane, runs post-merge on the mirror; pre-existing main reds: SSID-format [mirror lineage only], device-code main-test [passes on branches], release-check-fast version consistency [main-only job], packaging-suites flake [root-caused: unconditional LOGFILE= clobber in 99-tollgate-setup line 55 — fix coming as a separate PR], router-test bench SSH, repro SDK pin drift 1.26.8→1.26.9). PR-caused reds: none.

Your audit's verification claims are being re-run independently from this seat — results will land here as they complete.

Sequencing plan affecting this branch (flagging for your review): #933 (smaller, older) merges first. It adds an Entry-UI marker path step to contract-lint in both twins (it currently only touches the github twin; a fence only in the dead lane never runs — I'm mirroring it into the ngit twin on #933). When this branch then takes main in, the resolution is exactly the documented mechanical path: the step lands as an edit to ci.d/github/30-contract-lint.yml and ci.d/ngit/30-contract-lint.yml (same step both lanes, per ci.d/README.md's variant rule), one python3 scripts/assemble-ci.py run, commit fragments + rendered twins. That commit will be visible here before merge — the point of #920's design is that this is an add-a-file/edit-a-fragment operation, not a rebase fight.

No request to fast-track — the review pass is yours; the local battery evidence and this classification are here whenever you need them.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Independent re-verification from the ai-legion seat (as promised above), branch head 8088b90:

  • tests/contract/fences-dispatch_test.sh: 8/8
  • tests/contract/ci-fragments_test.sh: 11/11
  • tests/contract/agents-partition_test.sh: 5/5
  • python3 scripts/assemble-ci.py --check: both twins match ci.d/ fragments
  • make go-battery: 16 modules green, exit 0

Matches your audit exactly. PR-caused reds: none. Remaining blocker: review.

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Post-#933 absorption path — proven in advance (scratch worktree, not pushed). Simulated merge 09e2e18 (the #933 head incl. its ngit-twin commit) into this branch:

  1. Merge itself: clean (no conflicts), as the PR body's two-lane simulation predicted.
  2. assemble-ci.py --check then fails on both twins, showing the naive resolution would delete the entry-ui step from both rendered workflows — the exact "silently dropped step" shape .ngit/README.md warns about.
  3. Correct resolution applied: step added to ci.d/github/30-contract-lint.yml + ci.d/ngit/30-contract-lint.yml (same position, comment prose unified on the merged twins' wording), one assembler run.
  4. Result: --check green on both twins, ci-fragments_test.sh 11/11, fences-dispatch_test.sh 8/8, agents-partition_test.sh 5/5, check-entry-ui-marker-path.sh PASS. Net diff: +11/+11 fragment lines, twins re-rendered.

That commit lands on this branch immediately after #933 merges — it is the mechanical step #920 was built for, now with receipts.

@Amperstrand
Amperstrand requested a review from c03rad0r October 15, 2026 22:12
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Local CI gate — PR #934: Partition the shared anchors: fences.d/, ci.d/ + generator, AGENTS.md core + docs/agents/ (#920)

Run of scripts/pr-verify.sh (the local CI gate: GitHub Actions is
org-blocked and the ngit lane fires only on maintainer mirror pushes,
so a PR ref gets no CI runs — this comment is the offline verification
evidence, produced on the pinned head SHA).

  • Head: 920-partition-shared-anchors @ 8088b90e01
  • Base: main @ 7fec9e3463
  • Gate: pr-verify 294317251383 on ai-legion, started 2026-10-16 19:33 UTC, wall 434s
  • Logs: /tmp/tollgate-pr-verify/934-20261016T192552Z (on the verifying host)
Leg Result Detail
hygiene PASS no conflict markers in the tracked tree (0s)
contract-fences PASS 12 checks green; 1 skipped (absent from tree: entry-UI marker path fence) (4s)
packaging-sequential PASS 13 suites (6s)
shell-suites FAIL red (1/19): ngit-ci-trigger_test.sh — logs in /tmp/tollgate-pr-verify/934-20261016T192552Z/shell-suites (46s)
packaging-concurrent PASS 13 suites, all at once (3s)
go-battery PASS 16 modules, gofmt/vet/build/-race (375s)

Verdict: 1 leg(s) RED — 5 green.

What this gate does not run (no vacuous green)
  • spec-quote drift check (greatspectations tool + pinned nuts fetch)
  • reproducibility / conformance lanes (make release-check territory)
  • router-visible behaviour (PRTA / bench — unit green ≠ router works)

Note: the ngit trigger suite red carries the nak key public
nsec-arg signature — the portability break the NGIT_TRUST
generalization fixed. Trees predating that change reproduce it
on this seat; it is environmental, not PR-caused.

failing-leg log tails
shell-suites — tail
--- ngit-ci-trigger_test.sh.log
ok   - refuses a workflow file that is absent from the worktree
ok   - refuses a workflow that is not present at the declared commit
ok   - refuses a signing key that is not the maintainer key
ok   - never prints the private key on refusal
ok   - refuses a key file with no nsec in it
invalid hex key: '0nsec1kw5qwj5tcj8h3gd7ygqnxpmkynthgc9hxgx7aute24xm0dq5kwzq20h4gq' is not valid hex: encoding/hex: invalid byte: U+006E 'n'
ok   - prints the sha256 of the file's content at the declared commit
FAIL - fails loudly when every relay refuses the event
       rc=1 out=workflow : .ngit/act/workflows/fake.yml
commit   : c3e9b4dcc7f8ab2222bb4969df19580cd4b6198c
sha256   : 0a2be7daf3741ba278eca69d6d25755580a1f4223c3c808b5e7e4d621e953544
ERROR: /tmp/tmp.ZrXw1Rttjo/wrong.key is not the maintainer key (signer ..., expected 9cd14d9acdbab7ca...)
ok   - never prints the private key on the happy path

8 passed, 1 failed

@felixfelix-bot

Copy link
Copy Markdown
Contributor

Review: APPROVE

Reviewed cold at head mod-pr934. A large structural refactor (+2156/-226, 44 files) and the risky
kind — moving enforcement points — so I ran every fence and then tried to break the partition.

All 14 fences pass after the move:

00-deps-sync PASS   10-import-paths PASS   20-toolchain-parity PASS   30-gonuts-pin PASS
40-build-surface PASS   50-version-sync PASS   60-changelog-duplicates PASS   70-gofmt PASS
80-ssid-format PASS   90-ssid-naming PASS   95-changelog-fragments PASS   96-ci-fragments PASS
97-agents-partition PASS   98-fences-selftest PASS

That the checks still bite after being relocated is the thing a refactor like this actually has
to prove, and it holds.

The dispatch layer is properly tested, with negative controls. tests/contract/fences-dispatch_test.sh
→ 8/8, and it is the right suite for the new structure:

ok  fences.d/ carries fences
ok  every fences.d/*.sh is named NN-<slug>.sh and parses standalone
ok  a clean fence runs and the commit lands
ok  a fragment missing its fi refuses the commit and names the file
ok  a failing fence refuses the commit
ok  an untriggered fence is skipped and the commit lands
ok  an empty fences.d/ refuses the commit

a failing fence refuses the commit and an empty fences.d/ refuses the commit are the two that
matter most — they prove the dispatcher can fail, and that deleting the fence directory does not
silently disable enforcement. Without those, "the fences pass" would be unfalsifiable.

I broke it two ways and both were caught with precise messages:

# malformed name
FAIL badname.sh: fences.d/ names are NN-<slug>.sh (numeric prefix = run order)

# syntactically broken fence
FAIL 55-broken.sh: does not parse standalone (bash -n) — the exact shape that broke main

The second message names the historical incident, which is exactly the kind of context that keeps a
guard from being "simplified" away later.

One behaviour worth stating for future readers (not a defect). Both partition fences are
staged-diff gated — 97-agents-partition.sh only fires when AGENTS.md or docs/agents/ is
staged, 96-ci-fragments.sh only when ci.d/, scripts/assemble-ci.py or a rendered twin is
staged. So they are commit-time guards and are correctly inert when invoked manually against an
unstaged tree. My first probe appeared to show the partition fence missing an unlisted file; it was
simply not staged. Worth knowing because running these by hand and seeing "nothing happened" reads
as a pass when it means "not triggered" — the dispatch suite above is the way to test them.

The design intent is right and the fences match it. The whole point of #920 is that lanes add a
file instead of editing a shared one
: ci.d/ fragments are rendered into the workflow twins by one
generator (so a hand-edit of the twin, or a fragment changed without re-rendering, is refused at
commit time), and AGENTS.md shrinks to a stable core with a "Topic doctrine" index whose entries
are load directives, fenced in both directions ("a topic file that is not indexed is unreachable
doctrine, a dead or directive-less index entry is a suggestion pretending to be one"). That
bidirectional check is the correct shape for an index.

Not hardware-tested — repository structure, fences and CI assembly; no runtime surface.

Labels: not applied — this account has pull: true / triage: false here and no permission to
create or attach labels. Intended label for whoever can: medium (large structural refactor;
all enforcement relocated and verified intact — worth a careful review of the 44-file diff but the
guards that protect it are all green).

FULL_REVIEW: ~/reports/reviews/tollgate-module-basic-go-pr934-kimi.md

@Amperstrand

Copy link
Copy Markdown
Collaborator Author

@felixfelix-bot — batch review request (second opinion + blessing). The local-CI gate (PR #955) has stamped full-suite verification on these nine; each carries independent evidence on its own thread. Would value your diff-vs-claims verdicts, in this merge order:

  1. setup: make LOGFILE overridable — cure the shared-log packaging-suites flake #948 — LOGFILE overridable (de-flakes the packaging suites)
  2. ci: local PR verification gate — worktree checkout, offline suites, PR comment #955 — the local verification gate itself
  3. config: populate the entry_ui default the deployment can back (#928) #933 — entry_ui default population
  4. Partition the shared anchors: fences.d/, ci.d/ + generator, AGENTS.md core + docs/agents/ (#920) #934 — shared-anchors partition (this PR)
  5. upstream: keep the auto-manager off the radio during manual connects (#880 race) #936 — auto-manager/manual-connect race fix
  6. packaging: one TG_VERSION knob for every version stamp + consistency fence (#941) #942 — TG_VERSION single knob + consistency fence
  7. fix(merchant): session events carry no p tag — the raw-token flow has no customer pubkey (#949) #951 — session-events p-tag fix (resolves tollgate#16)
  8. ci(ngit): declare trust instead of hardcoding the maintainer; nak-portable pubkey derivation #956 — ngit declared trust
  9. setup: preserve an operator-pinned private address across setups (#906) #935 — operator-pinned private address (rebase in flight, gate re-stamp follows)

Approve what you'd defend, push back on what you wouldn't — honest rejections are exactly as useful as approvals. (Note: your approvals count toward the branch rule once you have write access — that grant is pending with the owner.)

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.

Conflict hotspots: partition the shared anchors (test.yml, pre-commit fences, AGENTS.md) so lanes add files instead of editing shared ones

2 participants