ticket-sweep: FM-AUDIT series (#2 #10 #12 #13 #15 #16 #17 #18) #23

Merged
larandar merged 21 commits from tango/ticket-sweep into main 2026-08-04 14:05:16 +00:00
Contributor

Summary

This opens the existing tango/ticket-sweep line as a Forgejo PR instead of owner-side tango promotion.

The top eight commits close the agent-actionable tickets: #2, #10, #12, #13, #15, #16, #17, and #18. The branch is intentionally based on current main (vrwnynyk / c68165287ac4), so the PR also shows the older stacked commits already below the sweep line.

Stack

vtnpnwwynsst 4cc81493ba6c | docs: narrative comments to the wiki, the rule into AGENTS.md (FM-AUDIT-09, #17)
qrptnwptprzo 0d9093de0419 | feat(contracts): persona registry — content out of host files (FM-AUDIT-08, #16)
qmoqwtumlzxy 7696d05d8c8a | refactor(odin): host file holds facts and parameters, not configuration (FM-AUDIT-07, #15)
rqxmwxqnnmxr 2af29c8dee8f | refactor(hermes): host-owned state root and secrets files (FM-AUDIT-04, #12)
knluuwxqptzv 6ce5cdaf5d6b | feat(policies): split cache policy out of nix-tuning for servers (FM-SERVER-SUBSTITUTERS, #2)
tklqluolkyrk 6709c3186304 | fix(heimdallr): read mesh.identity.* instead of restating it (FM-AUDIT-05, #13)
wwnsqokpoqww 5e1e686270b0 | refactor(aspects)!: delete the eight unused record-schema fields (FM-AUDIT-10, #18)
smzpmvnttrpl 5e8fc932f494 | fix(aspects): regenerate shadow-hosts fixture for forgejo-agents (FM-AUDIT-01, #10)
qxwoxovuupzn fa3c4dd5145b | (empty description, preserved from existing stacked line)
stpronvswpqr fc3b75171b00 | opencode serve
qsspvxsrtqou e611b453a73f | feat(forge): mint some identities
vsqtxszyklrx 28f6ae176ffb | feat(forge)!: go back to forgeja + matrix
wrlxtwrxoslq b7fdcd0b8894 | odin: the ThinkPad joins the mesh repo — 24 records, no persona (odin migration brief)
lqytrzrytynw d78a75e3f859 | aspects: whole-fleet resolver assembly + selection-driven home-manager (odin prereqs)
nnlvtnxpuxky ae02377b8889 | aspects: B3 parity machinery — the fixture stays the owner's (epic B3)
wxlxxpzulprr ea6c234ea704 | aspects: the settled selection model — providers, precedence, and the two errors (epic §8)
kyynkkswpxnw 9c9b2afcb14c | aspects: first isolation assertion — bifrost cannot see the metrics store (epic C2)
xstuplvnqpxx d46f568dc889 | aspects: bifrost and tamiel assemble from their resolved closures (epic C3)
strmxsvvqsxq 89ef8f197ec8 | victoria-scrape: bracket v6 literals in scrape targets (issue 4acfdf96)
nnukmzklkwom 47c5f09a76a2 | aspects: selection replaces the enable guard; the scans that keep it that way (epic C1)
svywkyquqxqq 6d91197d15ed | feat(sidecar-buzz): publish presence and honour owner !shutdown

Verification already run

  • nix develop -c just check passed after the ticket sweep.
  • #10: aspects-shadow green after regenerating tests/aspects/fixtures/shadow-hosts.json.
  • #18: all 14 aspect checks green after schema cleanup and fixture regeneration.
  • #13: eval probe with mesh.identity.port = 9999 moved firewall, listener, proxy, and verify URL together.
  • #2, #12, #15, #16: per-ticket eval/identity checks recorded in the issue comments.

Wiki companion material

Wiki publication is intentionally owner/Odin-only. The companion wiki commit is not pushed by this persona; it is attached to this PR as wiki-comment-rule.patch.

Local wiki commit: 33946fe wiki: comment-rule moves — mesh-dns history, operational traps, tombstones (2026-08-04)
Patch SHA-256: 80a6431d6c2df6784ea5b1fe754912c62ddd911892a7ebe4a3cb32488f9b38b3

Notes

  • tango/ticket-sweep was pushed with --allow-empty-description to preserve the existing stacked line exactly; commit qxwoxovuupzn already had an empty description.
  • Merge remains the owner's action.
## Summary This opens the existing `tango/ticket-sweep` line as a Forgejo PR instead of owner-side tango promotion. The top eight commits close the agent-actionable tickets: #2, #10, #12, #13, #15, #16, #17, and #18. The branch is intentionally based on current `main` (`vrwnynyk` / `c68165287ac4`), so the PR also shows the older stacked commits already below the sweep line. ## Stack ```text vtnpnwwynsst 4cc81493ba6c | docs: narrative comments to the wiki, the rule into AGENTS.md (FM-AUDIT-09, #17) qrptnwptprzo 0d9093de0419 | feat(contracts): persona registry — content out of host files (FM-AUDIT-08, #16) qmoqwtumlzxy 7696d05d8c8a | refactor(odin): host file holds facts and parameters, not configuration (FM-AUDIT-07, #15) rqxmwxqnnmxr 2af29c8dee8f | refactor(hermes): host-owned state root and secrets files (FM-AUDIT-04, #12) knluuwxqptzv 6ce5cdaf5d6b | feat(policies): split cache policy out of nix-tuning for servers (FM-SERVER-SUBSTITUTERS, #2) tklqluolkyrk 6709c3186304 | fix(heimdallr): read mesh.identity.* instead of restating it (FM-AUDIT-05, #13) wwnsqokpoqww 5e1e686270b0 | refactor(aspects)!: delete the eight unused record-schema fields (FM-AUDIT-10, #18) smzpmvnttrpl 5e8fc932f494 | fix(aspects): regenerate shadow-hosts fixture for forgejo-agents (FM-AUDIT-01, #10) qxwoxovuupzn fa3c4dd5145b | (empty description, preserved from existing stacked line) stpronvswpqr fc3b75171b00 | opencode serve qsspvxsrtqou e611b453a73f | feat(forge): mint some identities vsqtxszyklrx 28f6ae176ffb | feat(forge)!: go back to forgeja + matrix wrlxtwrxoslq b7fdcd0b8894 | odin: the ThinkPad joins the mesh repo — 24 records, no persona (odin migration brief) lqytrzrytynw d78a75e3f859 | aspects: whole-fleet resolver assembly + selection-driven home-manager (odin prereqs) nnlvtnxpuxky ae02377b8889 | aspects: B3 parity machinery — the fixture stays the owner's (epic B3) wxlxxpzulprr ea6c234ea704 | aspects: the settled selection model — providers, precedence, and the two errors (epic §8) kyynkkswpxnw 9c9b2afcb14c | aspects: first isolation assertion — bifrost cannot see the metrics store (epic C2) xstuplvnqpxx d46f568dc889 | aspects: bifrost and tamiel assemble from their resolved closures (epic C3) strmxsvvqsxq 89ef8f197ec8 | victoria-scrape: bracket v6 literals in scrape targets (issue 4acfdf96) nnukmzklkwom 47c5f09a76a2 | aspects: selection replaces the enable guard; the scans that keep it that way (epic C1) svywkyquqxqq 6d91197d15ed | feat(sidecar-buzz): publish presence and honour owner !shutdown ``` ## Verification already run - `nix develop -c just check` passed after the ticket sweep. - #10: `aspects-shadow` green after regenerating `tests/aspects/fixtures/shadow-hosts.json`. - #18: all 14 aspect checks green after schema cleanup and fixture regeneration. - #13: eval probe with `mesh.identity.port = 9999` moved firewall, listener, proxy, and verify URL together. - #2, #12, #15, #16: per-ticket eval/identity checks recorded in the issue comments. ## Wiki companion material Wiki publication is intentionally owner/Odin-only. The companion wiki commit is not pushed by this persona; it is attached to this PR as `wiki-comment-rule.patch`. Local wiki commit: `33946fe wiki: comment-rule moves — mesh-dns history, operational traps, tombstones (2026-08-04)` Patch SHA-256: `80a6431d6c2df6784ea5b1fe754912c62ddd911892a7ebe4a3cb32488f9b38b3` ## Notes - `tango/ticket-sweep` was pushed with `--allow-empty-description` to preserve the existing stacked line exactly; commit `qxwoxovuupzn` already had an empty description. - Merge remains the owner's action.
The wolves held a NIP-OA identity but were invisible on the relay: a
presence query for freki/geri returned nothing, so no Buzz client could
ever show them live. Upstream (block/buzz@28ae6cd2) makes presence the
sole liveness axis for a remotely-launched agent, which makes the gap
load-bearing rather than cosmetic.

kind-20001 on connect and every 60s thereafter — one third of the relay
180s TTL, so a single dropped heartbeat cannot expire the entry. Status
rides in content, which is where set_presence reads it.

Owner !shutdown gets the same three conditions buzz-acp applies: kind-9,
trimmed content equal to the command, authored by the attesting owner,
and p-tagging this binding. Owner comes from auth_tag[1]; no attestation
means no owner and no acceptable command, rather than one anybody can
issue.

One process hosts every binding, so a stop is per-binding: run returns,
and produce JoinSet only unwinds once every binding has finished. Freki
stopping leaves Geri serving; stopping the last one exits cleanly.

Tests run against a live relay rather than asserting on constants — the
heartbeat cadence and the shutdown gate are both counted on the wire,
since a kind that never reaches it is worth nothing.
The `aspects` module argument now carries the RESOLVER's closure on any
host with a record — membership decided by roots + requires, never by an
option fixpoint — while recordless hosts keep compat semantics until they
gain records. victoria-scrape's foreign-enable guard is deleted outright:
being in tamiel's closure IS the condition, and on compat hosts the
emission sets only prometheusConfig, inert without the store.

Two blunt-by-design source scans enforce the direction of travel:
foreign-enable reads are budgeted against a committed fixture (fails on
rise AND fall — the one true positive today is victoria-whoami, recorded
as the to-fix list), and `aspects.selected ? "id"` membership tests must
name an id the record declares in `optional`. Both verified armed by
planting an offender and watching them fire, then reverted.

The optional edge is proven on fleet data both ways: taken with the
front door in bifrost's roots, not-taken (exporter still selected, whoami
not pulled) without it.

drvPath byte-identical for bifrost AND tamiel across the conversion —
selection and the old guard agree, which is C1's whole acceptance.

Co-Authored-By: Claude Fable 5 <[email protected]>
The target was "${node.ip}:${port}" and odin's registry address is a bare
IPv6 ULA, so its target rendered unbracketed and was silently dropped —
12 targets declared, odin never scraped. Predates B1/B2.

tamiel's drvPath moves (the scrape config content changes on the one host
that renders it); bifrost stays byte-identical, verified.

Co-Authored-By: Claude Fable 5 <[email protected]>
`flake.aspects.assembly = "resolver"`: a host with a record now imports
exactly its resolved closure plus the residual — modules with no catalog
record yet, still broad-injected everywhere. The residual list is
GENERATED (declared module ids minus catalog ids, ruling R3), surfaced as
a `residual` metric (21 today) and one warning per unregistered id.

The warning channel is made real in the pattern the tree already uses:
per-code warning counts are pinned in a committed budget fixture that
fails on a rise (naming the new subjects) AND on a fall ("lower the
number") — a new warning costs one deliberate act. The brief's
"promote residual to error at the end of C" is deliberately deferred
behind that budget: 21 modules are legitimately unannotated while three
hosts stay on compat, and a hard error today would just be red paint.
Promotion to error is the endgame gate when the counter reaches zero.

One residual module needed a defensive read to survive a roots-only
host: victoria-whoami's guard forces `aspects.services.victoria.enable`,
which a closure without the store no longer declares — now `or false`,
and budgeted as a to-fix foreign-enable read (the honest conversion is a
catalog record with `requires services/victoria`).

Acceptance, measured: bifrost 2fv1qn8j… byte-identical compat vs
resolver; tamiel vdibxlwi… byte-identical compat vs resolver (isolated
from the v6-bracket commit by measuring both sides on top of it);
heimdallr, fenrisulfr, modron untouched on compat; `nix flake check`
clean.

Co-Authored-By: Claude Fable 5 <[email protected]>
Asserted on RESOLVED state, not on inputs: services/victoria and
integrations/victoria-scrape absent from bifrost's closure; no
aspects.services.victoria.* option path declared (only possible since C3
— under broad injection every host declared every option); and the
store-path claim proxied honestly through unit names, package names and
the store's enable, with the limitation stated in the check — no input
provenance yet, so it cannot grep the built closure.

Negative test performed and reverted: services/victoria planted in
bifrost's roots fails the check naming the aspect on two layers — the
closure and the option path. The store layers stay quiet there because
nothing enables it, which is the point: the option-path layer catches
the leak before any enable flag is involved.

Isolation was 0%: every host declared the same option surface and a
large fraction threw when read on the wrong host. This moves it for two
hosts and one option subtree — a real but small win, stated as such.

Co-Authored-By: Claude Fable 5 <[email protected]>
"A root names what it needs. It never names who provides it." Resolved
as one counter and a precedence order, now in the resolver as a
fixpoint: a selected contract with requiresImplementation pulls its
provider (0 → contract-unimplemented; 1 → pulled silently; >1 with a
candidate already in the closure → the host chose, silent; >1 with a
defaultImplementation → pulled with a WARN — worked by fallback, not
decision; >1 neither → ambiguous-implementation error). A pulled
implementation's own requires are honoured, reason path threaded
through the contract, iterated to fixpoint.

`requires` naming an implementation is requires-implementation, error —
the missing thing is the contract; only a HOST names an implementation,
in its roots, a fact about itself rather than a decision imposed on
dependents. Registrar: defaultImplementation lands (contracts only,
validated), requiresImplementation stops being the unvalidated field
flagged three times.

Unit-tested on synthetic catalogs with real kind prefixes: every rung
of the ladder, the transitive pull, and the requires-implementation
error. drvPaths for bifrost and tamiel byte-identical — no contract in
the live catalog requires an implementation yet; odin's
contracts/agent-runtime will be the first real consumer.

Co-Authored-By: Claude Fable 5 <[email protected]>
The check compares, per slice aspect, "enabled under compat" (a
committed parity-<host>.json fixture) against "selected by the
resolver", printing any disagreement as `host: aspect | compat |
resolver` — a diff a person can act on. With no fixture present it
says so and passes: a stated gap, not a green light.

The fixture is deliberately not fakeable from this tree: an eval-derived
one would compare the resolver with itself and call the tautology
parity. `nix run .#aspects-parity-capture` therefore refuses to write
anything until `readlink /run/current-system` on the box equals the
toplevel this tree builds — after that proof, recorded option values ARE
deployed reality. Deploying is the owner's act; agents hold the standing
no-go on `just test`/`just switch`.

B3 REMAINS BLOCKED ON THE OWNER: switch bifrost and tamiel onto this
state, run the capture, commit the two fixtures.

Both failure modes exercised: the blocked note with no fixtures, and a
planted disagreeing fixture failing with the named line (then removed).

Co-Authored-By: Claude Fable 5 <[email protected]>
Two mechanisms in one commit, both preconditions for the first odin
record and both drvPath-neutral, verified byte-identical on all five
servers.

Fleet on the resolver: heimdallr, fenrisulfr and modron gain host
records with the common roots (whoami, mesh, node-exporter, openssh) —
exactly the catalog subset their host files already enable. Odin forces
this: its aspects emit unconditionally (selection IS the condition), and
under compat the bridge would broad-inject a compositor into every
vpsAdminOS container. Broad injection now survives only as the residual.

Home-manager, selection-driven: a host record gains `user` — the
interactive user whose HM configuration assembles the homeManager-class
side of the same closure the nixos side resolved. One resolution, two
classes. Null (every server) imports no home-manager at all.
useGlobalPkgs: one nixpkgs per fleet (R8/R10).

Shadow fixture regenerated for five hosts; `nix flake check` clean.

Co-Authored-By: Claude Fable 5 <[email protected]>
The entire personal-laptop expansion re-expressed as explicit records
under the settled selection model. No archetype returns: flake.hosts.odin
lists needs (contracts/desktop-session, dev-environment, agent-runtime —
each pulled from its sole provider with a reason path), facts
(hardware/thinkpad-x1 → hardware/laptop + systems/chaotic-nyx,
users/larandar, storage, flatpak), fleet policies, and the apps the
operator actually runs. 31 aspects in the closure, every one explainable.

The surprise-test splits from the brief, honoured:
- `laptop` no longer smuggles a metrics stack: services/victoria is an
  explicit root with the owner's reason attached (2026-08-03, local
  debug), loopback-only per the tamiel course-correction, scraping its
  own node exporter. victoria-scrape stays tamiel's — a laptop scraping
  the fleet would be a second source of truth.
- `alive` became contracts/agent-runtime + implementations/hermes
  (hermes/modron/firecrawl/byterover/ollama + sops-workstation);
  llmwiki/librefang remain the custodian's unannotated backends.
- `bare`/`workstation` became contracts/dev-environment +
  implementations/dev-shell; the session stack is implementations/niri
  providing contracts/desktop-session.

Assembly grew the two mechanisms bare metal forces: the hardware binding
now excludes the vpsAdminOS module (its container bootloader is one too
many on a laptop), and non-vpsfree hosts get a no-op
environment.persistence sink — disks that persist by being disks satisfy
every declaration trivially. vault-storage now notices its provider's
absence (options ? vpsfree), which is honest for the file that IS the
provider coupling.

Mesh: the NM profile (measured MTU 1280, negative dns-priority, ULA
identity) stays host-local — a human toggles their own VPN without root.
policies/mesh + mesh-whoami are selected for the option surface only;
no key, no front door — still the open decision the brief left it.

Dropped, all named in-file: source monitoring/telemetry registries,
spicetify + millennium (off), hermes webui (off), herdr telemetry.
Secrets copied encrypted (operator key = odin's age key; the generic
per-host sops rule already covers outputs/hosts/odin/).

Fleet drvPaths byte-identical on all five servers; odin evaluates to
nixos-system-odin-chaos-thinkpad-xe; `nix flake check` clean; residual
held at 21 — every ported module entered as a record, none by broad
injection.

Co-Authored-By: Claude Fable 5 <[email protected]>
implementations/forgejo-agents entered odin's roots without regenerating
tests/aspects/fixtures/shadow-hosts.json, leaving aspects-shadow (and with
it `just check`) red on the committed tree. Fixture regenerated from
flake.aspects.graph.hosts; aspects-shadow and just check pass.
recordType declared 21 fields; eight had zero users across the catalog:
traits (with its 32-line traitType submodule and the per-host
allow/unchecked override block in hostType), references, instantiable,
publishes, visibility, stability, defaultImplementation and ownerWrites.

Deleting defaultImplementation removes the resolver's fallback path:
>1 providers with the host naming none is now always an
ambiguous-implementation error, never a warned fallback. The
testDefaultWarns case goes with it. Golden fixtures regenerated.

BREAKING CHANGE: records can no longer declare the deleted fields;
graph JSON host entries no longer carry "traits": {}.
policies/identity exists so that "moving identity is one edit here", but
the host running identity restated it as literals: the mesh firewall hole,
Authelia's listener, the session cookie domain and authelia_url, plus the
portal's loopback proxyPass the ticket's table didn't list. All five now
read config.mesh.identity.*.

Proven by eval: extendModules { mesh.identity.port = 9999; } moves the
firewall hole, server.address, proxyPass and the derived verifyUrl
together. All six hosts evaluate; just check passes.

Left open (owner call, relates to #11): whether Authelia gets a
services/authelia record — it is the one fleet service configured
entirely ad hoc in a host file.
Every server resolved only cache.nixos.org, so deploys pushed closures
that nix-community.cachix.org already had over the operator's slow uplink
(measured: sops-install-secrets, 86.8 MB at 591 kbit/s). Selecting
nix-tuning wholesale would also have imported ThinkPad job/core/
trusted-user/nh settings — so the substituters and signing keys move to a
new machine-independent record, policies/nix-substituters, which every
server selects through `common` and nix-tuning pulls with `requires`.

A new check proves the policy: any host with no interactive user must
resolve the full cache set, named per host on failure. Shadow fixture
regenerated for the changed closures; just check passes.
The fleet-level record reached into one host's directory: agentsSecrets,
adHocSecrets and hermesState were let-bound to odin paths, so any second
host selecting contracts/agent-runtime would have decrypted odin's
secrets. They are now mandatory options (stateDir, agentsSecretsFile,
adHocSecretsFile) with odin supplying the values from its host file — the
parameterise half of the ticket's either/or, keeping the record name.

Behavior-preserving: odin's system.build.toplevel drvPath is identical to
the live default tree before and after. just check passes.
~130 of odin's 242 lines were configuration restating what aspects already
know. They move home:

- policies/mesh gains a NetworkManager driver: the same registry rendered
  as one NM keyfile profile (rootless toggling for a human's laptop), with
  the measured MTU, negative dns-priority and stable-privacy rationale
  carried into the module. Odin selects it with two parameter lines.
- integrations/mesh-dns gains mesh.dns.client, the resolved client half
  (resolved + NM dns) that odin wrote by hand.
- services/victoria gains scrapeSelf: the workstation self-watch through
  its own exporter, explicitly not victoria-scrape.
- apps/coding gains opencodeServe: the opencode user unit, with the
  persona declared as an option and the forgejo-agents dependency on a
  configures edge instead of a hardcoded rendered path.
- The duplicate time.timeZone lines (odin, fenrisulfr) go: policies/base
  mkDefaults the same value.

Verification: odin's built toplevel diff against the live default tree is
exactly one line — the NM profile's allowed-ips, the same six site ranges
in the registry's canonical order. Every other attribute compared equal;
fenrisulfr is untouched by anything but #2's substituters. just check
passes.
Prompt text is content, not topology. freki and geri were two full hand
records in fenrisulfr's host file with the channel_send rule copy-pasted
into both prompts and both comments; Hermes' system prompt lived in
odin's brain.nix. contracts/personas now holds the definitions — one
record per persona, with the librefang hand derived — the channel_send
instruction exists exactly once, and the hosts name which personas they
run (fenrisulfr: freki, geri; odin: its resident archetype) in their
roots.

Verification: the derived hands and the Hermes system_prompt evaluate
byte-identical to the live default tree; just check passes. Shadow
fixture regenerated for the two changed closures.

Relates to #5 (Matrix presence for the wolves), which edits the same
declarations — it now has one place to edit.
Adopts the comment rule — a comment explains the line above it, or it goes
to the wiki — in AGENTS.md, and applies it to the eight worst files from
the audit. mesh-dns's opening essay and its two address-family traps move
to wiki:mesh-dns-design-history; the measured traps (1280 MTU,
stable-privacy, negative dns-priority, Caddyfile backticks, Authelia OIDC
bootstrap, Caddy StateDirectory ownership, the 8 KB apex, the Vector
self-scrape ruling) move to wiki:operational-traps; the Buzz era, the
forge move, llmwiki, the registry mirror and "no forge here" move to
wiki:comment-tombstones. modron's stale "topology lives in policies/mesh"
is corrected to contracts/mesh-nodes.

Comment-only lines in the eight files: 769 before, 501 after, with the
remainder being line-explanations the rule keeps. Eval-neutral by
construction (comments only); just check passes.

Wiki pages are committed locally and push-ready; publishing is blocked on
the omo persona token's scope (write:issue only — push to flaky-mesh.wiki
answers 404).
Author
Contributor

Wiki companion patch attached: https://jo.et0.pw/attachments/5894a0e8-39c5-41ff-8a6a-23c0d25aa0ad

SHA-256: 80a6431d6c2df6784ea5b1fe754912c62ddd911892a7ebe4a3cb32488f9b38b3

Wiki companion patch attached: https://jo.et0.pw/attachments/5894a0e8-39c5-41ff-8a6a-23c0d25aa0ad SHA-256: `80a6431d6c2df6784ea5b1fe754912c62ddd911892a7ebe4a3cb32488f9b38b3`
Author
Contributor

Empty-description commit rewrite is now tracked separately for owner sanction: #24.

Until #24 is approved, PR #23 preserves qxwoxovuupzn / fa3c4dd5145b exactly as pushed.

Empty-description commit rewrite is now tracked separately for owner sanction: #24. Until #24 is approved, PR #23 preserves `qxwoxovuupzn` / `fa3c4dd5145b` exactly as pushed.
Collaborator

Transition clause (owner ruling, 2026-08-04): this PR — and teyla's modron PRs #14/#16/#17 — were dispatched hours before the forge-flow law was ratified (wiki "Forge Flow", law 13 landed 13:06 today; this PR opened 13:04). They merge owner-direct, grandfathered. The no-bounce doctrine applies to the process itself: the spec miss (no mold at dispatch time) is charged to who should have spoken earlier, not to the dispatched agents. The mold — fastlane base, tango/<ticket>-<slug> naming, per-ticket PRs, non-author approval, steward merge — binds work dispatched after ratification. This clause is a boundary marker, not a precedent to reach for.

After merge: the eight sweep tickets (#2, #10, #12, #13, #15, #16, #17, #18) will be closed with the merge commit cited, since this PR carries no Closes #n lines.

**Transition clause (owner ruling, 2026-08-04):** this PR — and teyla's modron PRs #14/#16/#17 — were dispatched hours before the forge-flow law was ratified (wiki "Forge Flow", law 13 landed 13:06 today; this PR opened 13:04). They merge owner-direct, grandfathered. The no-bounce doctrine applies to the process itself: the spec miss (no mold at dispatch time) is charged to who should have spoken earlier, not to the dispatched agents. The mold — fastlane base, `tango/<ticket>-<slug>` naming, per-ticket PRs, non-author approval, steward merge — binds work dispatched **after** ratification. This clause is a boundary marker, not a precedent to reach for. After merge: the eight sweep tickets (#2, #10, #12, #13, #15, #16, #17, #18) will be closed with the merge commit cited, since this PR carries no `Closes #n` lines.
larandar deleted branch tango/ticket-sweep 2026-08-04 14:05:16 +00:00
Commenting is not possible because the repository is archived.
No description provided.