The wording ruling holds: the required sentence is verbatim in the baseline and its control, and every
remaining use of "protected" refers to RM-60's external boundary rather than the local baseline. The
history claim reads "structurally incapable of asserting history provenance".
I verified the negative control by making the overclaim myself: clean exits 0, and injecting
"protected, independently anchored" into the baseline purpose exits 84 with INVENTORY_CLAIM_OVERSTATED.
D-48 was an overstated boundary claim that three of us approved; that exact failure mode is now caught
mechanically rather than by attention.
Review dispatched with N2 as the sharpest check: the overclaim control is itself PR-controlled, so can
it be weakened or reworded so an overstatement passes? This PR's history is that every fix relocates the
defect one level down, so the reviewer is asked to assume it did again.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Registered at e910a45a before any reviewer read the diff. Verified first, so the set does not re-spend:
the narrowing sentence is verbatim in both the baseline and its control; the overclaim control fires
(clean exit 0, injected "protected, independently anchored" exit 84 INVENTORY_CLAIM_OVERSTATED); CI
2201 is 10/10 with the machine check bound and clean. D-48's failure mode is now mechanically caught.
N1 and N2 are the sharp checks. N1: the control only inspects the files it inspects, so is the claim
honest everywhere else — docs, comments, observations, CLI output? N2: the overclaim control is itself
PR-controlled, so can it be weakened or reworded so an overstatement passes? If it can, D-52 is re-armed
one level down, which is the recursion this PR has produced at every round. N3 asks whether it detects
semantic overclaim or only literal strings.
H2 and H3 re-check the two traps I found in the code by reading it, because they are the way (d) turns
back into a green: emitting the unverifiable state as an observation exits 0, and coverageBoundary
exclusion is declared rather than enforced. H4 requires the corrected residual, not the D-48 wording.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
coder-mos2 built blocker 2's "protected/base artifact" fix, ran independent code AND security review on
its own fix, and returned HIGH CWE-353 against its own work: the baseline is not protected because
verifier, manifest, baseline, tests and lifecycle code are all PR-controlled, so rewriting them
consistently self-certifies. It stopped rather than invent a fourth local anchor.
Both of us authored the error in the same turn. I dispatched blocker 2 as "must come from a
protected/base artifact"; Mos ruled blockers 2+3 fixable in-PR; and the same brief told the seat that
blocker 1 had no local fix because PR code executes before the gate. My own pre-registered P2 had
already named it — the seven required gate IDs are an anchor list, and if the author can edit it that
is D-45 one level in. I registered the check, rev-974 confirmed it as a blocker, and I dispatched a
remediation reproducing it one level down.
That is the strongest evidence the principle is real: it defeats the people who wrote it, every time,
until the anchor is external. Knowing the rule does not protect you from it.
Ruled (b) honest narrowing, and the sibling distinction is what makes it correct rather than a
climbdown. Blocker 1's local check can be vacuously passed — seam=HEAD certifies over nothing — so the
claim is REMOVED. Blocker 2's baseline genuinely detects accidental drift and fails only against an
adversary rewriting baseline, manifest and verifier consistently, so the claim is NARROWED and the
check kept. Accidental drift is most real-world drift.
The wording is the whole ruling, because this is D-48 territory and neither seat may repeat it: no
"protected" or "independently anchored" over a same-checkout baseline, positive claim and adversarial
gap in the same breath, plus a negative control that goes RED if the check ever claims protection it
does not have.
Increment trajectory surfaced to Jason: two RM-02 anchors now require RM-60 and each thins the
(d)-strict increment. If a third needs the same boundary the increment may thin past worth.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
#1032 merged 58b971ab and #1019 closed, both verified by property. RM-03 is delivered to main. And the
guard that actually executes on this host is unchanged: installed 291 lines with ZERO occurrences of
ASSERTED_NOT_READY, main 482 lines with five. Differing SHA-256. Measured, not inferred — the installed
guard contains none of the tri-state logic.
This is the install-drift disease the postmortem indicts, caught in the act on the mission's own fix.
Every completion signal is green — merged PR, closed issue, terminal CI, merge-gate GO — and the defect
is still live in the thing that runs. D-23's origin was an assumed-propagated-but-didn't guard; this is
that origin reproduced by the delivery of its own remedy.
Merge is not delivery for tool changes. Rules banked from Mos: propagate only via the canonical path
(mosaic upgrade / deploy pipeline), never a hand-copy that could diverge while appearing to fix it; the
installed guard stays zero-information until re-sync, so every state=unknown exit 0 this mission logged
remains correctly uncited; and on re-sync VERIFY THE PROPERTY by running the installed guard against a
known-red pipeline to confirm it exits non-zero and blocks. Closing this by assuming the upgrade worked
would reproduce D-23 a third time.
Generalises: every framework/tool change this mission delivers has the same main-to-installed gap. It
belongs on the completion checklist for tool-affecting tasks and mechanically on RM-63 — the same
systemd-service architecture that mechanises rotation also mechanises propagation.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RM-03 #1032 freshened onto post-RM-61 main and re-cleared at 868b9b87: review 74 APPROVED bound to the
exact head, CI 2199 9/9 including clone, mergeable true. The deliverable is proven against production
rather than asserted — rev-974 invoked the exact-head ci-queue-wait.sh itself against isolated provider
fixtures and got exit 3 with ASSERTED_NOT_READY on terminal failure, no-status, malformed payload and
an indeterminate neutral status, plus timeout 124 on pending. D-23 said the guard returns pass for
every possible input; that is now false at this head. Merge-gate assigned, Jason's approval standing.
My own spot-check went further than the range-diff I asked for: ci-queue-wait.sh and pr-merge.sh blobs
are byte-identical across the old approved head and this one, so no guard implementation moved in the
rebase — the only delta is commit 1's additive test enumeration, a union preserving both sides.
RM-02 round 4 dispatched to coder-mos2, a fresh dedicated identity at 0.5% context, so no D-41
manual-respawn problem. I asserted its capability differentially before dispatch per my own D-11b
addendum rather than accepting it on report: authenticated push True, unauthenticated False.
Its brief carries the two traps I found by reading the code, both binding, because the first is how
anyone would naturally build (d): emitting the unverifiable state as an observation exits 0, and
coverageBoundary.excluded is schema-validated but not enforced. The claim goes away rather than being
annotated, and the exclusion needs a must-fail control or it is a gate with no proven failure path.
It also carries the CORRECTED residual — no local git state is trustworthy because PR code executes
before the gate — not the D-48 wording three of us approved.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Registered before the reviewer read the diff. Review 66 and the merge-gate GO are void at the new head
per D-50; Jason's approval is standing and executes once this re-gates.
Verified independently first so the review does not re-spend on it: range-diff shows commits 2-5
patch-equivalent with only commit 1 differing, and that difference is the test:framework-shell chain
resolved as a union preserving main's terminal-green contract harness AND all four RM-03 suites plus
the pre-existing branch-absent test. Nothing dropped either way. CI 2199 is 9/9 including clone, the
machine check is bound and clean, and mergeable is now true.
R1 is the deliverable: the guard must actually FAIL on asserted non-readiness. RM-03 exists because the
queue guard is zero-information — it returns pass for every possible input — so proving its tests pass
is not the same as proving the guard can fail. R2 is the risk: confirm no queue-guard semantic moved in
the rebase, because one resolved quietly re-breaks exactly what this fixes. R4 checks the tri-state can
represent an indeterminate result without presenting it as a pass, which is the defect verbatim
(state=unknown exit 0).
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-50: #1032 carried review 66 APPROVED and a merge-gate GO both bound to 78ec47cd, and by the time
Jason approved the merge it reported mergeable=false. Verified from the provider: its head does not
contain current main f4fd5967. Two rules we already hold, neither wrong alone, combine badly — a
verdict is commit-bound and voids when the head moves, and main drifts while a PR is held, so resolving
the drift moves the head and voids the verdict that was waiting to be used. Holding it across the whole
#1033 saga is what did it.
The asymmetry worth noting: usually a head moves because the AUTHOR pushed, so the void follows from
their action. Here nobody touched the PR — unrelated progress invalidated it — which is why it is easy
to miss and needs a checklist line rather than vigilance. Mos's line: a GO has a shelf life, re-gate on
main-drift.
RM-63 files Jason's architecture as the mechanization of D-41/RM-50/RM-58/P-LIFECYCLE rather than a new
island: durably-named systemd agent services with lanes, where restart triggers a continuation and the
agent resumes its lane, combined with DB-centric task assignment on the PG spine for autonomous pickup.
That is the enforcement surface D-41 proved missing — a managed service can be watched (token budget,
which is D-49's mechanical ceiling monitor), restarted (rotation, retiring our manual respawns) and
continued (rehydrate from the DB). It closes the f10-coder ceiling gap directly: Jason confirmed that
ceiling was not mechanically flagged and must be.
#1032 freshening dispatched to coder-mos1 — it authored the queue guard, so that context is an asset,
and by D-49 it can still take a lane at 67%. Told to stop and flag on any SUBSTANCE conflict, because a
queue-guard semantic resolved quietly re-breaks the very thing RM-03 fixes.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-48 is Mos's error, banked in Mos's words at Mos's request, and renumbered from the D-46 he suggested
because that number is already the empty-registry finding — the ledger is the primary product and a
duplicated number corrupts it.
Three seats shipped and approved "sound against an author who cannot rewrite main; residual =
compromised main; owner Builds 1-2". A1 does not rewrite main; it repoints a local remote-tracking ref
with one unprivileged git update-ref, which I reproduced in seconds. So the documented residual
described an exotic threat while the real one is trivial. Stating a boundary is not the same as stating
it correctly, and an understated residual is worse than none because it reads as rigorous. Everyone
checked a both-directions statement EXISTED; nobody checked it was TRUE. That is D-19's own move 2
performed with wrong content, and the third distinct way the render/verify principles have failed
inside their own enforcers.
And the correct answer was already written down. f10-coder's handoff holds the wrong claim at line 21
and, at line 29, the suspicion that overturns it — that provider-target ref selection may be
influenced by CI checkout/fetch configuration, marked honestly as unverified. rev-974 then found
exactly that. Had anyone run the suspicion down, D-48 would not have shipped. Requirement banked on
RM-02/RM-34: a labelled suspicion that contradicts a shipped claim must BLOCK that claim until
falsified. Suspicions are currently recorded and ignored, which makes honest labelling free of
consequence — where one targets a boundary or integrity claim it is a pre-registered check nobody ran.
D-49 refines the parking rule and supersedes the rationale of the coder-mos1 (c) ruling: the threshold
is not "is the seat idle" but "can it still take the next lane". f10-coder was idle with work pushed
and frozen, which reads as safe, but at 97.4% could not have taken the remediation the pending verdict
was about to require. Rotating on that asymmetry was the only clean boundary available.
The detection was discipline, not a mechanism — I was not watching seat context in real time and Mos
caught it on a manual sweep. Requirement on RM-58/P-LIFECYCLE/the coordinator daemon: the coordinator
watches token budget across seats and pre-empts at threshold, so a keystone seat's ceiling is never
found by a failed round or a lucky sweep. Interim we both sweep; the mechanism retires the sweep.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The derived boundary f4fd5967 equals an independently computed git merge-base HEAD
refs/remotes/origin/main. It is the same value f10-coder once set by hand: the value was never wrong,
the authority was. That is exactly why Mos insisted A6 be separated from A4 — asking only whether the
value was right would have closed this three rounds ago with the hole intact.
Empty-registry attack now fails in the precondition's own language. The both-directions bootstrap
statement is in the code with the residual bound to Builds 1-2, per D-19's third mandatory move.
Exact-head CI 2198: 10 children all success including clone and gate-verify; my machine check exits 0
bound to the head with anomalies 0, and a wrong-head negative control exits 1. exempted_steps is 0 this
run because ci-postgres succeeded, so the #1000 exemption was present but unneeded.
Review dispatched with A1 and P2 as the sharp checks: both ask whether this fix REMOVED the author's
control point or moved it again. refs/remotes/origin/main is a local ref, and the seven required gate
IDs are an anchor list. Five for five so far, every blocker on this PR came from outside the registered
set.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Registered at fbb61912 before any reviewer read the diff. I reproduced three things first so the review
does not re-spend on them: the derived boundary equals an independently computed git merge-base HEAD
origin/main (f4fd5967 — the same value f10-coder once set by hand, now derived rather than asserted);
emptying the registry populations fails with the precondition's own language; the both-directions
bootstrap statement is present with the Builds 1-2 residual.
A1 and P2 are the sharp ones and they ask the same question the last two rounds answered badly: did the
fix REMOVE the author's control point or MOVE it? A1 — refs/remotes/origin/main is a local ref; who
controls it where the gate runs, and can an author rewrite remote.origin.url or craft that ref in their
own clone? P2 — the seven required gate IDs are an anchor list; if the author can edit it, that is D-45
one level in. A4 asks about the natural failure mode of this fix, over-inclusion causing false reds.
P3 independently confirms the production fixture-profile bypass the author found and removed in their
own pre-commit review — self-disclosed, so it gets verified rather than credited.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Mos ruled both open questions and promoted the pattern to the charter.
PRINCIPLE 1 — the anchor must live outside the audited party's authority. You cannot fix "the author
controls X" by deriving X from something the author also controls; deriving only MOVES the control
point. Third independent arrival of one conclusion, each reached while shipping something else and each
from a different direction: the manifest certifying its own tree (D-19), the sandbox evaluating code
that enters before the boundary exists (D-25), and now the registry seam derived from a path the author
also places (D-45). Three impossibility-derivations of the same conclusion is the strongest
architectural evidence this mission has produced, and it is what forces Builds 1-2 rather than making
them a preference.
PRINCIPLE 2 — no universally-quantified check may pass over an empty set. "All registered cases ran" is
vacuously true when there are none. Non-emptiness and anchoring are preconditions asserted before the
quantified check runs, not properties hoped for after. The identical vacuity appeared twice at two
levels — seam=HEAD emptied the commit range, an emptied manifest emptied the registry population — and
the first was fixed as an instance, so it returned one level up. Corollary, same disease: a clause
written for the instance that produced it is not a clause.
Q1 ruled: the merge-base anchor IS in scope for f10-coder in this PR. It is git-computable against
main, which the author does not control, so reordering or splitting within the branch cannot move it —
an existing non-author-controlled reference, no new infrastructure. RM-60's execution boundary is a
sibling under the same principle but a different mechanism (where gate-verify runs, not what the anchor
is) and stays with Mos and Jason. Honesty required in both directions: the merge-base anchors to main,
whose integrity rests on the merge discipline this registry enforces — a bootstrap, sound against an
author who cannot rewrite main and NOT sound against an attacker who can, with that residual bound to
the Builds 1-2 dependency rather than implied.
Q2 ruled: state the empty-set principle as a general clause now, and generalize the D-38/D-40 criteria
the same way — quantify over the population instead of relabeling the originating instances.
Round 3 dispatched to f10-coder: merge-base anchor plus delayed-introduction must-fail; empty-set
precondition as a general clause with an emptied-registry must-fail; generalized clauses bound across
the gate inventory; each red-first. Number.isInteger accepting zero/negative/unsafe pipeline numbers
banked as a non-blocking follow-up.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Third application of D-43's rule today. Those two bullets restated D-11/D-11a/D-11b, which live
authoritatively in TASKS.md, so they were a second copy waiting to go stale. Rolled verbatim; the board
keeps a pointer plus the one operational line that is not a restatement — assert the differential as
that seat, because a single endpoint can be true for anyone or 403 for an unrelated scope.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Second NO-GO on the keystone at 32b490a7. The pattern is the finding, not the three blockers.
D-45: round 1 the author set activationCommit as a field, so we derived it; round 2 the derivation
depends on WHEN the author introduces gates/gates.manifest.json. rev-974 committed a gate change before
adding the manifest — deriveHistoryBoundary picked that commit as activation, the prospective list held
only the later introduction, and verifyHistory returned no failures. The three registered seam controls
cannot catch it because they compare candidates against whatever introduction the author selected: they
validate the choice, not the choosing. You cannot fix "the author controls X" by deriving X from
something the author also controls. That is D-19/D-25 verbatim — the anchor must live outside the
audited party's authority — arriving a third time, now inside the keystone, and it is exactly why
Builds 1-2 are forced rather than preferred.
D-46: I reproduced it myself. Emptying criteria, gates, proseClaims and compatibilityScenarios — 17, 7,
8 and 2 entries — and pointing gateRoots at an empty directory yields EXIT 0, reporting "all registered
cases ran from this checkout" and "open behavior deltas: 0". "All registered cases ran" is vacuously
true over an empty set. The registry can be reduced to nothing and still report green.
D-45 and D-46 are the same defect at two levels: vacuity through emptiness. Empty the commit range or
empty the registry population — either way a universally-quantified check over an empty set passes.
seam=HEAD was the first instance and we fixed it AS an instance; the principle was never extracted, so
it came back one level up. Proposed general clause pending Mos: no universally-quantified registry
check may pass over an empty set — non-emptiness and anchoring are preconditions, not properties.
D-47: the new criteria are still instance-scoped. RM02-EVIDENCE-SUBJECT-BINDING covers only provider
evidence identity, not whether every registered gate binds evidence to its subject; RM02-TYPE-STRICT-
SCHEMA covers registry schema fields, not each gate's own discriminators. Do not relabel the
originating instances as the general clauses.
Dispatch PAUSED for the first time this session: the fix needs a non-author-controlled anchor, which
touches the provider/merge-base boundary and is adjacent to RM-60, which Mos and Jason own. Asked Mos
whether that anchoring is in scope for f10-coder here or crosses into RM-60, and whether the empty-set
principle becomes a general clause now.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Item 1 has now gone stale three separate times in one session, every time for the same reason: it
restated lane state that the In-flight table already owns. That is D-26's class (restatement is lossy
every time) and D-43's mechanism (a second copy is a second thing to go stale). Fixing the wording a
fourth time would just buy another few hours.
So it no longer carries lane state at all — it points at the table and keeps only the two things that
are NOT restatements: read the table, and re-derive any board claim from the provider before
load-bearing use, because the board is sole-written and has no independent verifier.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
All four D-44 silent-defeat paths remediated in one hardening pass. I re-ran three of the four attacks
myself and they are dead: activationCommit injection is rejected as an author-controlled field with the
boundary now derived from the registry introduction (0b4aa475); the misspelled outputPatern is rejected
as an unknown field by the recursive schema; the global cross-commit duplicate-pipeline check is
present. Three new criteria registered with bound cases.
Exact-head CI 2197 is the notable one: pipeline success with 10 children, 9 success and 1 genuine
FAILURE — ci-postgres, type service, exit_code 0, exact #1000 teardown signature. The RM-61 exemption
fired for real on a gate-ready decision for the first time. I ran the machine check myself (exit 0,
terminal-green, exempted_steps 1, correct exemption id) and a negative control on the same record with
a wrong expected head (exit 1, not-terminal-green, exempted_steps 0). Both halves of RM-61 — the
signature-scoped exemption and the subject binding — demonstrated in a single run.
The pre-registered ACs deliberately do not re-check what I reproduced; they attack the FIX, because
every round on this mission the remediation introduced the next hole. H1 asks whether the new
derivation's inputs are author-controlled, S1 whether the schema closure is a class or an enumeration,
C2 whether the new criteria are clauses or just their originating instances.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Registered at 32b490a7 before any reviewer read the diff. I verified three of the four attacks are dead
by re-running them myself: injecting activationCommit=HEAD now yields "author-controlled
activationCommit is forbidden; history boundary is derived from the registry introduction"; the
misspelled outputPatern is rejected as an unknown field by the recursive schema; the global
cross-commit duplicate check is present. The three criteria are registered with caseRefs 1/3/3.
So the AC set does not spend itself re-checking what I already reproduced. Every round on this mission
the remediation introduced the next hole — the commit-binding fix shipped a type confusion, the
type-strict fix was clean but the registry around it had four defeats — so the value is in whether the
NEW mechanism is defeatable.
Blocker 1's fix replaced an author-settable field with a derivation, and a derivation has inputs. H1
asks who controls them: can an author move the derived boundary by adding, renaming, deleting-and-
recreating or symlinking gates/gates.manifest.json, or by planting a second manifest earlier in
history? If so, blocker 1 is relocated rather than fixed. H2-H4 ask what first-parent traversal,
shallow clones, and path deletion do — fail closed or silently derive a wrong boundary.
S1 asks whether the schema closure is complete or merely enumerated: find a nested object the author
did not list and inject an unknown key there. C2 asks whether the new criteria express D-38 and D-40
generally or only their originating instances — a clause that covers only the instance that produced it
is not a clause.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 NO-GO at 83d2ecb2. A4/A5/A6 confirmed and three further blockers found, two of them outside my
registered set. Each is a silent-defeat path of the registry itself: the activation seam inerts the
history audit (seam=HEAD gives 0 prospective commits and no failures — and HEAD's parent and the
introduction commit also pass, so forbidding equality with HEAD does not close it); a misspelled
outputPattern silently reduces an assertion to exit-code-only because the closed schema is not
recursive; two provider records sharing pipeline number 7 certify two different commits; and the D-38
and D-40 criteria are absent, with blocker 4 a live instance of the very D-38 clause that is missing.
The headline: all four passed CI, passed the canonical gate:verify, and passed 38/38 focused tests.
Greens discharged nothing. My own AC set was incomplete too — A3 corrects the prospective range to
eight commits, not the seven I wrote; I omitted the seam commit itself.
Mos ruled ALL FOUR in this PR, no trim, and sizing does not help: a registry that ships with a known
way to be silently defeated IS the inert gate it exists to detect. There is no core registry that is
integrity-complete without these — they are not hardening on top of the deliverable, they are the
deliverable. Theme: the registry's own checks must not be silently defeatable. Each fix carries a
red-first must-fail control proving the specific defeat is now caught, and that control set IS the
D-38/D-40 coverage work rather than being additive to it.
Blocker 1's fix keeps the value and replaces the mechanism: derive the seam from non-author-controlled
history (parent of the first first-parent commit introducing gates/gates.manifest.json) rather than
asserting it in an author-editable field, plus must-fail controls for HEAD, HEAD's parent, and the
introduction commit. If the work balloons past reviewability the only acceptable split is by
integrity-complete stage, never by deferring a blocker.
Method note banked as a positive: I could not reproduce "full verifier exit 0", traced my first attempt
to my own instrumentation artifact (json.dump reformatting the manifest), and stated the divergence
rather than wielding non-reproduction as a refutation. The vacuity itself reproduced and blocker 3 was
confirmed by construction. Non-reproduction is not refutation. Open thread: why my gate:verify exits 1
on checkout-preflight with outcome 42.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Mos ruled the disposition BEFORE the verdict, contingent on rev-974's independent A4/A5/A6. Recorded
here — not in the frozen AC file, which would be retrofitting, and not relayed to the reviewer, who
must reach A4 themselves or refute it.
The concern: activationCommit is the lower bound of the range required to carry own-tree registry
provenance, advancing it shrinks the audited set, the author advanced it to turn the author's own gate
green, and the only validation is --is-ancestor, which a commit satisfies against itself. The registry
built to detect gates that silently stopped enforcing may contain a field by which its own author can
make it stop enforcing, validated by a tautology.
A6 is the crux and stands alone from A4: even if f4fd5967 is the correct value — and it plausibly is,
since a pre-registry commit cannot carry a registry manifest — the mechanism question is independent.
If the audited party can relax its own audit behind a tautological check, the value being right today
is irrelevant and the next value need not be right.
If confirmed: BLOCKING at D-40's standard, and the fix is NOT reverting the value. It is making the
field non-author-relaxable — activationCommit independently derivable, equal to the real pre-registry
seam by construction rather than assertion — plus a must-fail negative control that a HEAD-valued or
over-advanced seam is REJECTED. The registry must be able to fail on its own seam being gamed.
If refuted: bounded, value stands, hardening tracked as a registry clause, and the refutation is itself
a finding worth having.
f10-coder commended with the distinction stated: it verified identity before committing and disclosed
the non-mechanical change unprompted, which is the only reason this is visible at all. But disclosure
is not sufficiency — an honest author flagging a self-relaxing change still made one. Honesty surfaced
it; the mechanism must catch it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Sequencing section rolled verbatim to BOARD-LEDGER — it was a pointer to a pointer, and MISSION.md
plus TASKS.md 5 remain canonical. Item 1 said RM-02 'needs rebase+CI+review'; rebase and CI are done
and it is in review at 83d2ecb2.
Second application of D-43's rule today: delete-and-reference, never paraphrase-to-save-bytes.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Registered at head 83d2ecb2 before dispatch. Prior reviews 63/65 are superseded and this head carries
no live review of any kind, so the keystone gets a full independent review.
The crux is the one non-mechanical change, which f10-coder disclosed unprompted: activationCommit
advanced f65e9ea6 -> f4fd5967 so that the newly merged pre-registry RM-61 commit is no longer treated
as prospective. activationCommit sets the lower bound of the commit range required to carry own-tree
registry provenance, so advancing it SHRINKS that set. The value is plausibly correct — a pre-registry
commit cannot carry a registry manifest — but the change was made by the author to turn the author's
own gate from red to green, which is the self-verification shape the charter warns about.
Registered as A1-A6 without a verdict from me (D-39). A4 is the sharp one: the only validation on the
field is merge-base --is-ancestor activationCommit head, and a commit is its own ancestor, so setting
activationCommit = HEAD would empty the prospective range and the per-commit provenance loop would
iterate zero times. Whether that makes the history check pass VACUOUSLY is for rev-974 to determine and
report — an author-settable field that can inert the anti-inert-gate registry's own history check would
be the disease inside the cure again. A5 asks whether a must-fail negative control exists for it, since
RM-02's own founding rule is that a gate with no proven failure path manufactures evidence.
B1/B2 carry Mos's scope ruling that the D-38 bind-to-subject and D-40 type-strict clauses land in THIS
PR; B3 confirms D-42's clause is deliberately absent rather than forgotten.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
SCOPE (Mos): D-38 (bind evidence to subject) and D-40 (type-strict discriminator inputs) land IN the
RM-02 PR — cheap registry-coverage clauses, satisfiable now, and RM-02 literally IS the registry, so
codifying what its own delivery learned about how gates fail is the coherent move.
D-42 splits, and the reason is sharper than "needs experimentation": landing its clause in-PR today
would give the registry a clause with NOTHING to satisfy it, so the registry would go red on its own
clause and couple the keystone's landing to infra work. Wrong coupling. So (a) the infra experiment
proving the pin enforces via the negative case, in Mos's D-37/D-41/D-42 cluster; (b) the registry
clause "provider-side enforcement requires an OBSERVED negative control", in a follow-up RM-02
increment once (a) gives it something to check. Named owner, not a vacuous check and not a silent
omission.
D-43 point 3 elevated to the PRIMARY requirement on RM-34, with the table that makes it plain: code has
rev-974, gates have the merge-gate, CI has the JSON scan, and the control plane has NOTHING. Every
other layer earned a verifier from a live failure; the board never did, and it is the artifact every
other decision is staged from. "Validate the checkpoint" must mean re-derive the board's claims from
ground truth — not that the file parses or that a successor can read it. It belongs to the
coordinator-daemon deliverable: the control plane needs its own verifier the way every other layer got
one. Today's catch was discipline, not mechanism.
Interim rule binding on BOTH seats until that mechanism exists, and Mos adopted it against himself:
re-derive a board claim from ground truth before any load-bearing use. The orchestrator does it before
dispatch; the coordinator does it before acting on or relaying a board claim that gates a decision.
D-11b addendum: the false-NEGATIVE twin. /user returns 403 on a least-privilege seat token (no
read:user) and reads exactly like an unprovisioned seat. The real assertion is the DIFFERENTIAL —
authenticated push=true vs unauthenticated push=false proves the token caused the difference. A single
endpoint can be true for anyone or refused for an unrelated scope reason. Would have escalated a
working seat as unprovisioned and stalled the keystone on a phantom.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
#1033 merged f4fd5967, verified by property: merged=true + merge commit, #1034 (delivery) CLOSED,
#1000 (retirement trigger) still OPEN. The exemption is on main and RM-02 is unblocked.
D-43 is mine. Compressing RM-02's board row to meet the 8KB budget, I turned "2 reviews clear" into
"2 live REQUEST_CHANGES" — an inversion. Provider truth: reviews 63 and 65 are at superseded heads and
live REQUEST_CHANGES at f9746b23 is empty. The real state is subtler than either wording: no live
blocker AND no live approval, because the head carries no review at all.
Caught only by re-deriving RM-02's state from the provider before dispatching, not by re-reading the
board. I was one message from briefing f10-coder to remediate two blocking reviews that do not exist.
The transferable part: this board carries a hard <8KB budget, it exceeded that budget four times in one
session, and each time I shaved prose to fit. Compression IS restatement, and this board's own rule is
"REFERENCE, do not restate" (D-26) because restatement is lossy every time. The budget therefore forces
the exact operation the board forbids, on its most load-bearing table. I flagged that as a risk earlier
in the session; it then materialised as an actual error.
Rule banked: meet a size budget by ROLLING content out to BOARD-LEDGER.md, never by rewording what
stays. Deleting a row and pointing at its authoritative home is safe; paraphrasing to save bytes is not.
Applied immediately — this commit rolls the Decisions-log narrative out verbatim rather than trimming
it, and the board is back under budget at 8096.
And the deeper one, onto RM-34: the orchestrator is the board's sole writer, so nothing external checks
the board against reality. Code has rev-974, gates have the merge-gate, CI has the JSON scan — the
control plane has no independent verifier. Handoff validation must include re-deriving the board's
claims from the provider, not merely confirming a successor can read the file.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
merge-gate.md says it outright: a successful merge is NOT evidence the pin worked, only the negative
case is. On mosaicstack Gitea a merge against a WRONG head_commit_id has never been attempted, so every
head-pinned merge to date is equally consistent with the pin working and with the pin being ignored.
That is D-23's class — the queue guard "ran" and returned pass for every possible input; the pin
"holds" on every merge that would have succeeded anyway. By the mission's own standard (every
gate-introducing task carries a registered must-fail negative control; a gate with no proven failure
path manufactures evidence) the head-pin is unregistered in substance however it reads in the runbook.
Not blocking #1033: that head is coordinator-frozen with both the orchestrator and coder-mos1 holding,
so there is no concurrent pusher for the pin to defend against — its protection is only load-bearing on
a contested head. Raised by Mos while assigning the merge; Mos owns it and has filed it as an infra
task in the D-37/D-41 cluster, to be proven before any contested-head merge.
Requirement on RM-02: the negative-control clause must cover provider-side enforcement mechanisms, not
only in-repo checks. "The API accepted our parameter" is not evidence the API honours it — the same
true-answer-to-a-different-question shape as D-24 and D-38.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Merge-gate NO-GO with every code and CI gate verified, including the --expect-commit machine-check it
was told to exercise (9/9 JSON, exempted_steps 0, expected_commit == commit). The blocker was the body:
only "Refs #1000", no "Closes #N", and Mosaic completion requires a linked issue closed.
Third distinct metadata failure on one PR — missing commit binding, then a stale DO-NOT-MERGE body
describing a dead head, now an incomplete link. Each time the code was fine and the metadata was
unbound, stale, or incomplete. Nothing re-binds PR metadata to the head or checks it for completeness
until a gate does, and by then it costs a round trip.
The subtlety that makes it a real ruling: #1033 must NOT Closes#1000. The teardown defect stays open,
and #1000 is the exemption's own retirement trigger — auto-closing it would delete the retirement
trigger for the workaround being merged, turning a bounded exemption into a permanent one by side
effect. Hence a delivery issue distinct from the defect issue: #1034, with Closes#1034 added and
Refs #1000 kept.
Metadata-only, head never moved: verified by read-back that head is still 57cae04f, Closes#1034
present, Refs #1000 present, Closes#1000 ABSENT, review 70 still CURRENT with zero live
REQUEST_CHANGES, #1034 open. coder-mos1 stayed parked; nobody pushed.
Process gap ruled by Mos: every remediation delivery PR needs a delivery issue to Close, created at
OPEN-TIME rather than discovered at gate-time. #1032 already Closes#1019; #1033 had none.
Both provider writes went through the tea->API fallback — the same path that silently dropped #1033's
draft property at creation — so both were read back and verified by property, not by exit code.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 approved at the exact head with no blocking and no non-blocking findings. Verified by me from
the provider rather than relayed: head unmoved, review 70 bound to 57cae04f, pipeline 2194 terminal
success at that commit, 9 children all success including clone (a 9-of-9 scan, not the 8-of-9 D-33 was
banked for). Queue guard run for the push gate and NOT cited — state=unknown exit 0 on branch=main,
D-23 verbatim.
The redundant-clause question I handed rev-974 without a conclusion came back answered by mutation
testing, and I re-ran both mutations myself: deleting the bool clause alone leaves the 17-case harness
green (not load-bearing), deleting the exact-type clause alone fails it (load-bearing, mutation
killed). So the exact-type clause carries the protection and the bool clause is inert-but-defensive
against a future type()->isinstance() edit, which the true/false cases would catch. Removing it alone
cannot reopen D-40. Better answer than either of us had, obtained by asking rather than telling —
D-39 paid for itself on the first review after adoption.
Disclosed to Mos rather than absorbed: JSON `-0` decodes to Python integer zero and passes. rev-974
ruled it not a type confusion because it is a valid JSON integer numerically equal to zero. I agree on
the merits, but it is a reviewer judgement inside the exemption and belongs in front of the gate.
Gate steps 1-3 are satisfied. Steps 4 and 5 — merge-gate verdict and head-pinned merge — are Mos's.
Head frozen; coder-mos1 parked idle per ruling (c) and will not push; neither will I.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Mos ruled (c): coder-mos1 stays idle/parked, not manually rotated. It holds no in-flight work, so
there is nothing to rotate FOR, and a manual in-pane restart would dress a missing mechanism as a
lifecycle operation — the D-41 overclaim itself. It stays AVAILABLE through RM-61's re-review in case
that review needs its context; the next NEW lane goes to a fresh seat, not to a seat at 67%.
RM-62 filed, because a dependency stated as prose with no owner becomes the permanent gap the charter
warns about. Bringing the execution fleet under roster/systemd management BLOCKS RM-50, RM-58 and
P-LIFECYCLE-001 — each is unsatisfiable against its real population until it lands. Banked explicitly
that both would FAIL against their real target today: RM-50 applied now quarantines the seat
implementing RM-50, and RM-58 has no mechanical reset path for any seat that needs one. RM-50 and
RM-58 now carry RM-62 as a hard depends_on edge, and RM-50 carries the requirement that acceptance be
proved against the unmanaged execution fleet rather than the roster-managed canaries.
RECORD CORRECTION, initiated by Mos and banked here: this session's opening rotation validated the
checkpoint+rehydration DESIGN losslessly — the residency attestation genuinely passed from the files —
but the MECHANISM was a manual pane respawn. "The handoff rehydrated losslessly" is earned; "rotation
worked" overclaims a mechanism that does not exist, and that overclaim is D-41. Same distinction as
D-23's inert guard: the step ran, one property was observed, the mechanism was not. Board header now
says so rather than implying a lifecycle rotation occurred.
D-37 and D-41 are one cluster — the execution fleet lacks both its shared-infrastructure and its
lifecycle management. Mos owns both, sequenced at a seam, never mid-lane.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Went to rotate coder-mos1 mechanically; `mosaic fleet restart` cannot reach it. Every seat executing
this mission is systemd inactive/disabled and flagged UNMANAGED — coder-mos1, rev-974, f10-coder,
merge-gate, the pm-scouts, rev-3107b, ultron-3107, and mos-remediation itself. The roster-managed
population is a different set of seats from the ones doing the work.
RM-50 applied today would quarantine the entire remediation fleet including the seat implementing it.
RM-58's mechanical out-of-band reset cannot be performed at all, so the only rotation available is what
D-4 says does not count: asking the agent, or killing a pane by hand. Requirement banked on both: their
acceptance must be demonstrated against the UNMANAGED execution population, because a criterion proved
only on roster-managed canaries is tested on the wrong population — D-17's coverage class one layer up.
Today's rotation is therefore labelled honestly as a manual pane restart with a hand-verified handoff,
not as a lifecycle operation. The step ran; the property was not observed (cf. D-23).
Did not run fleet restart against a disabled unit while the live pane held RM-61 and RM-03 state.
Disposition escalated to Mos.
Also records the handoff artifact as the positive control: typed state, and it surfaced D-12 recurring
live (PR #1033's draft property silently dropped by a wrapper fallback), an unrunnable check declared
rather than substituted, and suspicions labelled as suspicions.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Mos ruled BLOCKING on the exit_code type confusion. Both of us had leaned non-blocking on the
unreachability argument and neither shipped it. The two decisive grounds: the direction is
wrong-ACCEPT (false and 0.0 GRANT the exemption), which is this mission's disqualifying direction and
exactly why RR12 was tolerable; and "Woodpecker is Go with an int type" is structurally the same
incidental-correlate argument RM-61 itself forbade for the signature — you cannot certify the
discriminator against infra-shift and then defend a hole in it with an infra-stability assumption.
Generalised into an RM-02 standing clause, which is the real prize: discriminator and comparison
inputs must be TYPE-STRICT. A comparison that accepts a type it should not is a wrong-ACCEPT hole by
construction; `== 0` matching False and 0.0 is one instance of a class. D-40 is the instance, the
clause is the fix. It sits alongside the D-38 clause (does this gate bind its evidence to its subject).
D-39b banks the roles-swapped half: the author does not adjudicate its own PR's blocker status and
does not move the head to enforce it. Symmetric with D-39 — a seat with a stake in the answer does not
settle the question, and a conservative motive exempts neither direction.
ACs are registered NOW, before coder-mos1 pushes and before any reviewer reads a diff; at registration
the fix exists only as an unpushed local commit I have not read, so there is no diff to retrofit to.
They also state explicitly which cases are genuine RED-FIRST (false, 0.0 — currently wrongly exempting)
and which are only regression guards ("0", null — already blocking), because demanding an impossible
red for the latter two invites weakening something real to manufacture it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-39: I contaminated an open independent review. RR12 was an open registered check and I sent rev-974
my CONCLUSION, not just my observation. rev-974 then downgraded an automated Codex review that had
flagged RR12 as a blocker, in line with my framing. The bias sat in my instrument, not the reviewer's
diligence — a subordinate is agreeable by construction, and the divergence instruction I attached
cannot undo having named the answer first. Mos adopted the rule fleet-wide: a dispatcher may relay
observations and reproductions into an open review, never its own verdict on an open check.
Re-adjudicated by a mechanically fresh seat with no access to my framing: NON-BLOCKING, independently,
on stronger evidence than either prior pass (accept-set matrix proving the accepted set is a strict
subset of a correct case-insensitive comparison's, so it cannot false-certify; exploit path hunted and
ruled out; all 0x110000 codepoints scanned for a fold that could smuggle non-hex onto a valid SHA).
So contamination did not change the answer, and the finding stands anyway — a correct answer reached
by a contaminated process still corrupts the process.
D-40: that same fresh seat looked outside its question and found a type confusion inside the exact
conjunction the exemption rests on. `step.get("exit_code") == 0` is True for JSON false, and
coder-mos1 added the float case. Verified by me on the real #2188 record: false and 0.0 both yield
exit 0 with exempted_steps 1 — the exemption GRANTED; "0" and null correctly block. Two of four
near-miss types satisfy a conjunction whose whole justification is that it is exactly scoped.
Consequence: I reported #1033 gate-ready at 033b2ffb (RR1-RR12 pass, review 69 APPROVED, CI #2193
success 9/9) and then had to hand Mos a finding that may disqualify what I had just certified. Better
that than defending the report. Disposition is Mos's; coder-mos1 escalated it to blocker unilaterally
and moved to push — held, because the author does not adjudicate their own PR and does not void a live
APPROVED review plus terminal-green CI by pushing. Head frozen, fix prepared and unpushed, ACKed.
Board doctrine now carries both rules: never relay a conclusion into an open review, and the author
never adjudicates their own blocker status.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 review 68 @ 033b2ffb: RR1-RR12 all PASS. RR7 is the strong one — all four binding predicates
proved RED against the old e7b29219 verifier, and the original attack under the old interface returned
exit 0 with exempted_steps=1, proving the PRIOR DEFECT rather than merely unsupported syntax.
The remaining blocker was metadata, not code: the live PR body still said "DO NOT MERGE" and "the
exemption will not be implemented", describing an earlier head. Verified independently — the controls
were injected in 3931b0e2/9455cd6a/25ac5971/ef9d23ab and the final delta does not touch
.woodpecker/ci.yml at all. The body was true when written and went stale as the branch evolved. That
is D-38 one layer out: the PR that binds evidence to its subject shipped a description that was not
bound to its subject, and D-36's staleness class on provider metadata rather than a repo file.
Fixed as metadata only. Head confirmed unmoved at 033b2ffb from both provider and git, body SHA-256
b0198d98 matched the author's reported digest byte-for-byte, both stale strings absent. Because the
commit never moved, the twelve results stand and no third review round is owed — only the discharge
of the finding by the reviewer who raised it.
Exact-head CI #2193: success, 9 children, 9/9 success including clone, read from -f json (D-33).
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-38 — RM-61's terminal-green verifier certified the right RECORD for the wrong COMMIT. rev-974
mutated only #2188's commit field and the gate still exited 0. Confirmed by construction: at
e7b29219 the whole verifier had exactly one "commit" line (159), no expected-head parameter, no
comparison, no failure path. D-24 at the gate layer — a true answer to a different question.
The transferable half is the AC set, not the verifier: AC1-AC8 ALL PASSED and the PR was still
NO GO, because every registered check tested whether the signature discriminates and none tested
whether the evidence was bound to its subject (coverage-failure-mode-2, D-17 class). Mos ruled a
STANDING clause into RM-02 coverage: "does this gate bind its evidence to the subject under
review?" — every gate can fail this way and pass its own ACs.
D-37 — one shared .git/config silently re-identified EVERY worktree. extensions.worktreeConfig is
unset, so a worker's repo-local identity write rebinds all linked worktrees at once; the
orchestrator checkout and rev-974's review worktree both authored as coder-mos1. Distinct from
D-34, not an instance: MOSAIC_GIT_IDENTITY was exported and correct and still resolved wrong.
Sharp edge is gate-16 defeated in the artifact — the reviewer would author its pre-registered ACs
as the author of the code under review. No contamination occurred. Containment (explicit -c, no
shared-config rewrites mid-flight) is the standing order; the real fix is authorised and owned by
Mos, sequenced at a quiet seam. #1024 is implicated: repo-local pinning is the colliding mechanism.
Also pre-registers the twelve RM-61 re-review checks at exact head 033b2ffb BEFORE the reviewer
reads the diff, and rebuilds the board for a cold read — RM-61 "building", "nothing implemented
yet", and DECISION-1/2/3 "must be ruled" were all stale, the D-36 class again at the same seam.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
coder-mos1 ran two real ci-postgres failure controls; the orchestrator verified the JSON
records independently. #2189 (startup failure) exit 1 and #2191 (post-readiness crash,
log proves ready then postmaster killed) exit 137 both take the workflow and the test
step down — terminal red. #2188, the genuine artifact, carries exit_code 0 under a
SUCCESSFUL workflow.
So the discriminator is structural — a failed service step with exit 0 under a successful
workflow — not the message string and not an incidental correlate like node or timestamp,
which were explicitly forbidden because they pass today and mask a real failure the moment
infrastructure shifts.
Verifier: exit 0 with one named exemption on the artifact; exit 1 with exempted_steps 0 on
BOTH real controls. No fetch, trigger, retry or re-roll — the coin flip is removed, not
codified.
PR #1033 dispatched to rev-974 with eight acceptance checks pre-registered before the diff
was read; AC2 (both real failures must NOT be exempted) is the crux and a REJECT if it fails.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Preparing the checkpoint for a cold read surfaced three stale sections, each a copy of
text owned elsewhere, each of which would have misled the incoming orchestrator: a
delivery-gate list still omitting the merge-gate step (D-26's own defect, still on the
board after MISSION.md was fixed); a capability rule superseded by D-13/D-15; and
DECISION-1 still marked CONTESTED hours after it was ruled.
None were wrong when written. All three drifted because they were copies — D-14 three
times in one artifact. The failure is not that someone forgot to update three lines; it
is that three lines existed to forget.
The rotation seam is what surfaced it. A familiar section is skimmed for the line you
came for; preparing state to be read cold by a stranger is a different act from
maintaining it, and catches a class ordinary use cannot.
RM-34: a handoff must VALIDATE the checkpoint, not merely write it. RM-02 registers the
general form with a must-fail on divergence. Interim: audit the board against its
sources at every rotation seam.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Orchestrator rotating at ~803k tokens (~4x threshold). A mission built on
rotation-not-compaction should not run its own coordinator past it.
Board rewritten as the rehydration artifact: in-flight state, a read-this-first block
for the incoming seat, and the live rulings it most needs. Under the 8KB cap.
AUDIT AT THE SEAM found THREE stale restatements on this board, each of which would have
misled the incoming orchestrator:
1. the delivery-gate list still OMITTED the merge-gate verdict step (the D-26 defect
itself, still sitting on the board after MISSION.md was corrected);
2. capability described as 'token-file set = authoritative registry', superseded by
D-13/D-15 (capability is per-path; assert permissions.push as that seat);
3. Sequencing still said DECISION-1 was CONTESTED and 'do not treat it as settled'
hours after it was RULED — an incoming seat would have re-litigated a closed decision.
All three were restatements of text owned elsewhere. Replaced with references, per the
mission's own render-not-restate principle: a second copy is a second thing to go stale,
and this board proved it three times in one night.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Pipeline #2188 at the same head: ci-postgres the sole failure, everything else success
including gate-verify. f10-coder independently confirmed and applied the pre-registered
rule itself.
RM-02 blocked. No ad-hoc exemption, no third roll. Recorded why: two failures against one
earlier clean run makes 'best of five' feel reasonable, and the point of declaring the
bound in advance is that it binds when the result is inconvenient. A third roll would
have been indistinguishable from diligence, including to the person doing it.
Data for RM-61: the artifact hit twice consecutively at f9746b23 while #2184 was clean at
38f1b249, so it may cluster rather than be uniformly random — which makes a one-shot
re-roll close to worthless and strengthens the ruling that it must not become policy.
And the illustration: the anti-inert-gate registry is blocked by an artifact that makes
gate evidence unreliable. The thesis demonstrating itself on its own keystone.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The re-trigger guardrail, recorded so it cannot become practice: one bounded,
pre-declared attempt at the same head with the response to each outcome fixed before
triggering. Re-running until the desired answer appears is p-hacking the pipeline, and
silently nobody can tell it from diligence — including the person doing it. Per Mos: a
per-PR free re-roll would be D-21 normalisation wearing a new hat. RM-61 must make the
re-roll unnecessary, not codify it, and a clean re-run does not retire RM-61.
Milestone: RM-03/#1032 completed independent review -> CI terminal-green -> merge-gate GO
-> coordinator -> held for owner, the first time end-to-end. GO posted under the gate's
own minted identity by a seat that structurally cannot merge. Head verified unmoved after
the verdict, so the commit-bound GO stands.
The first delivery through the complete stack is also the last one gated by a check that
could not fail — RM-03 is that check's fix.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
First genuine gate-ready of the mission. Head triple equal at 78ec47cd, latest review
APPROVED at the current head, CI 9/9 by JSON scan with two-observer agreement.
Recorded the shape worth remembering: the queue guard is still inert for this merge
because RM-03 is the fix — the last merge to pass through the broken gate is the one
that fixes it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Charter gains a fifth principle from D-33: two observers of the same evidence,
disagreeing, catch what neither catches alone — applied to evidence GATHERING, not just
judgement. A summary that resembles an enumeration is more dangerous than one that
obviously summarises; prefer the machine-readable record and state counts so divergence
is detectable.
D-35: while editing merge-gate.md to fix D-33, the coordinator recalled the mandate
string instead of reading it and the Edit tool's exact-match REJECTED it. Third instance
for that author, inside the turn fixing another instance of the same class — and the
only one that did not reach a document, because a mechanism caught it.
A rule that fails in its authors but is caught by a tool has told you where it belongs.
RM-02 should enforce verbatim citation by construction, as the Edit tool did by accident
of design.
Fix landed by annotating the source: merge-gate.md mandates 3 and 4 now require the
scan and count from the JSON record. The shared wrapper was deliberately NOT modified
mid-flight — three lanes are reading it; fix at a seam.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 finished its RM-02 review and could not post it: pr-review.sh failed with
'Gitea token not found'. Verified — with MOSAIC_GIT_IDENTITY unset the token does not
resolve; with it set it resolves. The token file was correct throughout.
My own context reset wiped the seat's exported identity and my rehydration brief did not
re-establish it. git config mosaic.gitIdentity is persistent and per-worktree;
MOSAIC_GIT_IDENTITY is ephemeral and per-context. rev-974 works from ~/agent-work with no
git-config fallback, so it had no warning and the loss surfaced only when it next needed
a credential.
Intersection of two banked findings, created by acting on one: D-31 prescribes rotation,
D-11a requires identity coherence, and nothing said rotation is a credential-affecting
operation. The fix for one failure introduced another.
RM-34: rotation must re-establish AND verify seat identity before handing over work.
RM-50: prefer the durable binding (mosaic.gitIdentity in the seat's repo) so identity
survives a reset by construction. Interim: every rehydration brief re-exports identity.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
coder-mos1 scanned pipeline #2186 with -f json and found 9/9 child steps; my scan of the
same pipeline in the wrapper's default text mode showed 8. Verified: text mode omits
clone (JSON shows the ci workflow entry, clone, and the 8 visible ones).
Every full step scan performed tonight enumerated 8 of 9 real child steps and reported
it as complete. clone succeeded throughout so no verdict changes — but the method was
incomplete and its user did not know.
The merge-gate mandate requires a FULL step scan and requires verdicts to enumerate the
step count. A verdict citing 8 where 9 exist is non-conforming evidence, and a gate using
this wrapper's default output would produce exactly that while believing it complied.
The shape is the session's thesis aimed at the detection tool: the doc says read the
artifact not the summary, and text mode IS a summary that looks like an artifact. Compare
D-24 — mergeable was a true answer to a different question; this is a true answer to a
smaller one.
Caught only because two observers' counts disagreed; neither alone would have found it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
First run on this branch where ci-postgres passed, so no exemption question arises.
Recorded that the artifact's intermittency is evidence FOR RM-61's negative-control
requirement: intermittent means delivery is a coin flip until the signature is proven to
discriminate from a real database failure.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 blocked RM-03: pr-merge.sh still ships --skip-queue-guard at five sites
including a worked example. It proved this rather than reading it — stubbed the guard to
exit 99, ran a real fixture merge with the flag, and observed the guard never called,
provider payload created, 'merged successfully', exit 0.
Disqualifying because RM-03 repairs a mandatory gate that has never been able to fail;
shipping that repair with a documented bypass means the gate merely requires one flag
instead of zero, and every merge-side CANNOT_ASSERT/HOLD semantic is skipped. L0 bars
equivalent skip switches, and the merge-gate role doc names this exact hazard about
force_merge — adjacent to the field you came to edit, at the moment you are most
motivated to reach for it.
Generalizable: repairing a gate is incomplete while any supported path skips it. The
failure changes from 'cannot block' to 'can be told not to' — the same outcome one
keystroke later. Removing the bypass is part of the repair.
Everything else passed independently, including the 160 KiB stdin transport where #1023
regressed.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
coder-mos1 hit 102.5%/372k on RM-03 with 28 modified files uncommitted and its branch
still at base — a full context window spent with nothing durable. Caught by polling seat
state, not by any system signal. Nothing warned anyone: no threshold alert, no
pre-compaction hook.
The founding failure mode of this mission — compaction destroying in-flight work —
nearly hit the mission's own delivery twice in one night. RM-01 survived because it had
committed work when checkpointed; this one had none. Commit-then-rotate works;
rotate-without-commit loses everything.
RM-34: a context threshold is not enough — rotation must force a durable checkpoint and
refuse to rotate a seat with uncommitted work. RM-50: seat context is observable state
and must be monitored; relying on an orchestrator to poll is instructions-not-enforcement
applied to lifecycle. Brief doctrine: commit early, commit WIP.
Adjacent: the diff spans 5 guides and 11 templates against a brief scoped to one script.
Queried, not assumed — but a 28-file diff from a one-script brief is a scope signal
regardless of the answer.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Condition 1 tested: the signature is stable — pods "wp-svc-<ULID>-ci-postgres" not
found across all five observed failures, an orchestration-layer lookup miss structurally
unlike a service-level failure.
But discrimination is UNPROVEN: every observation co-occurs with a demonstrably working
database, and we have never seen a real ci-postgres failure on this provider. If
PostgreSQL crashes and the pod is GC'd, the status query may also return pod-not-found —
the exemption would over-match and mask a real failure.
So conditions 1 and 2 are not independent: 2 is the evidence for 1. Build the negative
control first, prove a real failure produces a different signature, then adopt the
exemption. Kill criterion stated in advance: if an injected real failure also yields
pod-not-found, B is unsafe and we do A.
Filed as RM-61, unassigned — no free write-capable seat; flagged rather than stacked
onto a busy lane.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-29: rev-974 moved two criterion bindings to an unrelated case, reformatted the
manifest, and gate:verify still exited 0. The registry verifies a criterion HAS a
binding, not that the bound case can fail for that criterion's stated reason —
RM02-REQ-03 unsatisfied. The keystone reproduced the defect it exists to eliminate, in
its own coverage check. Caught only by mutating and re-running; inspection would have
passed it.
D-30: my review brief claimed the ci-postgres FAIL appeared on #2167 — it did not
(#2167 is OK). My own banked D-21 says so explicitly; I restated from memory instead of
reading my record. D-26 recurring, in a reviewer brief, hours after promoting
render-not-restate to the charter.
The harm is not the inaccuracy but that I supplied a reviewer with fabricated supporting
evidence for my own reading. It refuted me — query-for-refutation paying for itself in
the same document that introduced it.
Requirement: briefs cite evidence from the record with identifiers, never from recall.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Head triple verified independently twice (orchestrator + f10-coder): local = origin =
PR #1030 = pipeline #2182. Full step scan rather than a tests-complete inference.
Queue guard not offered as evidence.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos. A subordinate asked to confirm a hypothesis will agree — the bias is induced by
the query, not by the answerer's diligence. 'The DB flaked, please confirm' and 'confirm
or refute, with the log line that proves it' are different instruments returning
different answers to the same question.
This matters most with agent subordinates, which are agreeable by construction, so the
discipline cannot rest on the answerer being rigorous — it must be built into how the
question is asked.
Origin: the orchestrator's ci-postgres hypothesis was refuted by the implementing seat
with log evidence and then settled by reproduction. Phrased for confirmation, it would
have returned agreement and D-21 would have been re-classified on a false premise,
silently corrupting a banked finding.
Operational form: state the hypothesis as yours, ask for evidence that kills it, say
what would change your mind, and reproduce when the answer is consequential. Added to
KICKSTART's standing invariants so it survives compaction.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Resolves an ambiguity in the orchestrator's own brief, which required CANNOT_ASSERT to
neither silently pass nor permanently block without distinguishing push from merge.
coder-mos1 stopped and asked rather than inferring authorisation; Codex security
independently flagged CWE-693.
Ruled B: merge fails CLOSED (proceeding without exact-head CI evidence is D-23's
condition in a narrower costume), push degrades AUDITED (blocking during an outage
bricks delivery — the Pi-brick class we already banked). A temporary block pending
evidence is not a permanent block, and merge is separately gated by the merge-gate and
coordinator, so nothing is stranded.
Conditions: distinct exit code or the tri-state is destroyed; the audit record asserted
by a registered case, not assumed, or 'audited' is a claim dressed as a property;
retryable and self-clearing; cases observed RED first; no silent degraded merge path —
break-glass belongs to RM-05.
Option C rejected: it bricks push during an outage and defers the degraded path, which
in this codebase means a silent bypass appears under incident pressure. Three are
already on the books.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RM-02 CI failed on four sandbox tests. Bubblewrap is installed by the gate step but not
the test step, so spawnSync returned ENOENT; replayCommit replaced the spawn diagnostic
with an empty string, so the classifier could not prove Bubblewrap provenance and
correctly refused to treat it as expected sandbox unavailability.
The refusal was right; the information loss was the bug. For a classifier, error text is
not decoration — it is the input. Widening acceptance to make the test pass would be a
finding, not a fix.
Linkages: D-16 again (local has bwrap, CI does not — local and CI disagree a third
time), and diagnostic-preservation registered as an RM-02 gate requirement with a
must-fail control proving a swallowed message is detected.
Process note: the orchestrator's hypothesis that the coincident ci-postgres FAIL caused
this was WRONG and was refuted with log evidence. D-21 stands unchanged as a teardown
artifact and is NOT upgraded — it was about to be re-classified on a false premise.
Asking the seat to confirm or refute rather than accept is what prevented that.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos's item 4. The gate definition was incomplete from setup because the gates were
restated from memory rather than referenced, and the omission propagated into every
worker brief since — then recurred inside the correction itself (D-26).
Board now carries the referenced sources, the gate order, freeze-after-GO, the
coordinator/orchestrator split, and the queue-guard zero-information field form.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 found this while loading the canonical gate sources, i.e. because we switched
from restating to reading. Verified in roles.local/merge-gate.md: mandate 3 requires
verifying 'CI queue guard clear', mandate 4 requires enumerating 'the queue-guard
outcome' in every durable verdict, the merge path annotates it 'real', and the control
table at :290 certifies it '✅ enforced … genuinely aborts'.
The document is correct about the WIRING and wrong about the CONTROL: set -euo pipefail
with an unguarded exit does abort, but the guard cannot produce a non-zero exit for any
input (D-23). A mechanism correctly wired to a sensor that never fires, certified as
enforced.
Compounding: the verdict format REQUIRES the queue-guard outcome as evidence, so a
conforming verdict must include a meaningless field — the role file instructs the gate
to manufacture evidence, in the mandate that exists to stop bare conclusions.
Interim: record the field labelled ZERO-INFORMATION (inert, owner RM-03) rather than
omitting it — omission makes the verdict non-conforming; silent citation is worse.
Escalated: operator-owned file, coordinator's to fix. Recommend annotation not deletion
— the requirement is correct once RM-03 lands.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The delivery-gate definition was incomplete from setup — the merge-gate verdict step
was missing because the gates were restated from memory rather than referenced, and the
omission propagated into every worker brief since.
Then it recurred inside the correction: the correcting message was itself a restatement
and dropped five things, including a security precondition (an unminted gate seat fails
open to the owner's admin account — the #3084 breach mechanism) and a citation to a file
that does not exist.
D-26 is the definitive case because every condition favoured success: attention maximal,
actor knew the rule best, subject was the rule itself. A rule its own enforcer cannot
follow while enforcing it is not a discipline — it is a requirement for a mechanism.
MISSION.md and KICKSTART.md now reference fleet/roles.local/merge-gate.md and
fleet/roles/validator.md and state only the gate ORDER. Every reference resolves; a
dangling pointer is worse than a restatement.
Precondition independently verified: merge-gate token is least-privilege,
pull=True push=False admin=False — structurally cannot merge.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos. The choke-point executor and PG spine are not a design preference — they are
forced. Twice during the mission's own first deliveries, work stopped against a security
property that cannot exist at the layer needing it: D-19 (audited party controls the
manifest certifying it — artifact integrity) and D-25 (audited party controls the code
entering the sandbox — execution integrity). Both reduce to self-verification by the
audited party is not verification, and both resolve only via an authority outside its
control.
Neither proof was sought; both arrived while shipping something else, from different
directions, at different layers. An architecture forced by two independent impossibility
proofs is stronger evidence than one argued for.
RM-60 records Mos's sharper option analysis: A (unprivileged userns) does NOT fix the
vulnerability — it grants a capability and leaves the ORDERING defect untouched, so B is
required regardless; A without B is kernel exposure bought for nothing. B is the correct
primitive, generalises to RM-59 and the choke-point executor, and may not need A at all.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RM-02's per-commit replay needs isolation; the privileged CI step was correctly refused
because PR-controlled code executes before Bubblewrap establishes any boundary — the
untrusted party would obtain the capability meant to contain it. Ordering defect, not a
hardening problem.
D-25 is D-19 one layer down: audited party controls the manifest (artifact integrity)
became audited party controls the code entering the sandbox (execution integrity). Both
reduce to self-verification by the audited party is not verification, and both resolve
via an authority outside its control — twice now this mission has derived Builds 1-2
from a security impossibility rather than design preference.
Option C ruled independently by the orchestrator and rev-974 before either saw the
other. rev-974 added the condition I missed and it matters most: post-merge replay is
DETECTION, not PREVENTION, with a defined quarantine/revert response, and must never be
presented as equivalent to a pre-merge gate.
RM02-REQ-10 revision formally recorded and accepted under RM-02's own clause 4 —
original text, restatement, reason — discharging rev-974's review-readiness condition.
RM-60 opened for the external pre-execution trust boundary, cross-referenced with RM-59.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Mos verified #1023 before relaying and corrected my framing. Verified independently:
review id-58 REQUEST_CHANGES at f6334080, the current head — the block is live, and its
findings are that #1023's own fix is defective (unknown still exits 0, payload-as-argv
hits ARG_MAX, tests do not assert exits).
mergeable:true is git-mergeability, not readiness. The charter's first principle
committed by the orchestrator inside an escalation about proxies. Fifth instance of mine.
The mechanism, named because it will recur: mergeable is not a lie, it is a true answer
to a different question. A false field would have been caught; a true-but-adjacent field
passes every sniff test — which is why the discipline must be mechanical, not attentive.
D-23 corrected backward per D-14-as-amended, original framing quoted.
RM-02: readiness cases must assert the decision-relevant property — latest review state,
CI terminal status on the exact head, required approvals — never a structural proxy.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RM-02's registered fixtures found this before the registry was built. Worse than the
banked unknown=>exit 0: ci-queue-wait.sh:266 pipes the payload into a classifier
starting 'python3 - <<PY', so python reads its PROGRAM from stdin and json.load never
sees the JSON. Proven empirically — success, failure, pending and malformed all
classify as unknown, and unknown exits 0.
The mandatory pre-push/pre-merge guard therefore returns PASS for every possible input,
including a genuinely failing CI. The six observed meaningless greens were not an edge
case; they are the only output it can produce.
PR #1023 is exactly this fix, open and parked, and its body diagnoses it precisely.
Two aggravating details from that body: pr-ci-wait.sh:38 documented the bug and remedy
and it was never backported to the mandatory sibling (D-14 propagation with a
security-adjacent blast radius); and the fix was opened without re-verification.
Escalated for Jason's #1023 disposition. RM-02 records required-vs-actual with
DEFECT (owner: RM-03); no RM-03 lane opened, guard not edited.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The gate that actually runs is the installed copy at ~/.config/mosaic/tools/git/;
a repo-scoped registry would test packages/mosaic/framework/tools/git/. Byte-identical
today (sha256 19cda2f7009c536e both) but nothing asserts it.
A registry that verifies the wrong artifact is worse than none — it manufactures
confidence, and the divergence would be invisible from every signal we have: registry
green, CI green, deployed gate arbitrarily different.
D-1/P-ACTIVATION applied to the enforcement mechanism itself rather than to config.
RM-02 gains: for every registered gate with a deployed counterpart, assert
repo-source == deployed-copy with a must-fail control; where none exists, record that
explicitly.
Found by design review before implementation — the only finding tonight not found by
execution, which is the design-first gate on keystone tasks paying for itself.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
ci-postgres reports FAIL under an overall-success pipeline on #2170 and #2175 while all
eight functional steps pass. Most likely a service-pod teardown artifact; the test
step's pg_isready fail-fast guard passed, so the database was available during the run.
Low severity, but the pattern matters: a red that is routinely present and routinely
correct to ignore trains operators and agents to discount reds, and the discounting
generalises. Six instances of a meaningless green are already banked; a meaningless
FAIL is the same erosion with the sign flipped.
Recorded rather than normalised — the first time it is waved through without a note is
when it becomes background noise.
RM-55: treat a non-terminal-success sub-step under an overall-success pipeline as a
reportable anomaly, and make the CI contract state which steps may fail without failing
the pipeline. An implicit allowance is indistinguishable from a bug.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Both the orchestrator and the coordinator read D-14 as forward propagation only: a
ruling reaches the documents stating the new rule. D-20 proved that insufficient —
when D-19 superseded part of D-18, the consequence went forward into the charter and
the delivery conditions but never backward into D-18, which kept asserting a withdrawn
claim until a reviewer disproved it by experiment.
A supersession must update BOTH the documents rendering the new rule AND the finding it
retires, with the retired wording quoted rather than deleted. Interim rule updated
accordingly.
Applying D-14's own rule to D-14, in the same commit that records the amendment.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 blocked #1027 a second time, and the defect was in this file, not the code.
D-18's AC2 restatement omitted the accidental/independent scope D-19 made mandatory,
and D-18 asserted the tampered-manifest control turns integrity 'from a claim into a
property'. It does not and cannot — that sentence predated D-19 and was never revised
when D-19 landed. rev-974 disproved it by experiment: a same-UID consistent
manifest+marker rewrite passed preflight.
Two banked findings firing on the orchestrator at once: the integrity-claim corollary
(I wrote a claim in the voice of a property, in the document defining the rule against
it), and D-14 propagation — I carried D-19's consequence forward into the charter and
the delivery conditions but never back into the finding it superseded. Backwards
propagation is the same defect as forwards and I did not audit for it.
Corrected in place with the original wording quoted and the disproof recorded, not
silently rewritten.
RM-02 fifth clause: documentation asserting a security or integrity property is itself
a claim requiring a negative control observed red. Prose is not exempt from the
mission's evidentiary standard, and governing prose least of all.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos, with his two additions.
Charter gains a fourth first-class principle: when a property cannot exist at the layer
that specified it, three honest moves are mandatory — implement what the layer can
guarantee; state the boundary in BOTH directions (what it does not defend AND what it
does, since either alone misleads); and record the real guarantee as a TRACKED
DEPENDENCY, not prose. A written-down gap is acceptable engineering; an implied-fixed
gap is the mission's core failure in a new costume.
The residual risk is now RM-59 (depends_on RM-12, RM-21, RM-25) — a real backlog task
owned by the choke-point executor and spine, which verify from outside the worktree's
authority. Mos's point: 'record where the guarantee comes from' only holds if the record
is a live dependency someone must close; a documented gap with no owner becomes a
permanent gap that reads as intentional.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Implementing the D-18 manifest hit CWE-345: manifest and marker share one same-UID
writable tree, so that actor can regenerate both and pass. No local construction fixes
self-authentication without a key outside its authority. The seat escalated rather than
calling it tamper-resistant — the charter corollary working on its first real test.
Ruling: Option A, scope AC2 to accidental/independent/stale mutation, retain the design.
The undefendable boundary is not the weak link (same-UID write already owns the source,
the tests, preflight.mjs and .husky); AC2 exists for the D-5 staleness class, against
which the design works; and a real anchor arrives with the choke-point executor and PG
spine, which verify outside the worktree's authority.
Acceptable only with honest labelling: threat model verbatim in code and PR, the words
tamper-proof/tamper-evident/secure barred there, scope carried in AC2's restatement,
residual risk named as backlog, all controls kept RED-first.
Generalizable: when a property cannot exist at the layer it was specified, implement
what the layer can guarantee, state the boundary precisely, and record where the real
guarantee comes from. A known gap written down is acceptable; a gap implied fixed is not.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos. Two first-class principles alongside observe-the-property:
1. Pre-registration prevents RETROFITTING and nothing else. A pre-registered check set
can be WRONG (D-8), INCOMPLETE (D-17), or INTERNALLY INCONSISTENT (D-18). It confers
neither correctness nor coverage nor consistency. All three modes were found on this
mission's own first delivery, by the machinery applied to its own work. RM-02's four
clauses are the enforceable form.
2. Never ship an integrity claim dressed as a property. A verification artifact
writable by the actor whose behaviour it certifies certifies the attack. It must sit
inside its integrity envelope, publish atomically, and carry a tamper negative
control observed red. If it cannot be made tamper-evident, say so and reconsider —
an honest 'this cannot be verified' is always available and always preferable to
laundering foreign content as certified.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Implementing D-17's fix surfaced a conflict between criteria, not within one. AC2
(reject symlinked generated state) cannot hold alongside AC4 (canonical build
succeeds): verified independently that apps/web sets output:'standalone' and the built
tree carries 42 legitimate pnpm symlinks under .next/standalone. Only building the
tree reveals it.
Completes the chain: a pre-registered check set can be WRONG (D-8), INCOMPLETE (D-17),
or INTERNALLY INCONSISTENT (D-18).
Ruling: build-certified symlink manifest — .next itself still rejected; descendants
rejected unless exactly certified. Stronger than blanket rejection because it catches
retargeting. AC2 restated and RECORDED with provenance rather than absorbed, since
silently resolving a conflict between pre-registered criteria destroys the point of
registering them.
Hardening: the manifest is generated state, so a manifest writable by whoever plants a
rogue symlink certifies the attack. Must be inside the integrity envelope, published
atomically, with negative controls observed red first including manifest-tampered.
RM-02 fourth clause: the registry must detect conflicts between criteria, and retain
original text plus restatement plus reason when a criterion changes meaning.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
rev-974 blocked PR #1027: AC2 was pre-registered and explicitly required rejecting
symlinked generated state; the implementation accepts it (ln -s into .next, preflight
exits 0 instead of 43) while the acceptance suite ran 21/21 green throughout.
Confirmed independently: preflight.mjs:82-92 rejects symlinks on the SOURCE path,
:28 merely skips symlinked dirs, and the generated-state path :141-163 checks uid but
never isSymbolicLink(). The suite's only symlink cases cover the turbo binary and a
source file.
Sharpens D-8 rather than repeating it. D-8: pre-registration does not confer
CORRECTNESS. D-17: it does not confer COVERAGE — a suite can be green with every
criterion appearing satisfied while a criterion's actual requirement is untested.
RM-02 third clause: the registry must bind each criterion to the specific case that
exercises it and prove that case red before trusting its green. Mutation testing
pointed at the criterion-to-case mapping, not just the gate.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Propagating Mos's ruling in the same commit that records it, per D-14's interim rule.
The hermeticity fix IS PR #1024, which sits in Jason's parked delivery stack, so its
disposition is his — marked SUPERSEDED-PENDING-JASON alongside #1023. D-16 strengthens
urgency without transferring ownership; we do not open a third lane on a parked PR.
Class sharpened: a pre-push gate an operator cannot run locally is a gate only CI
enforces, so pointing .husky/pre-push at it misrepresents where the gate lives.
Non-hermetic gates make every green host-dependent.
#1027 proceeds on CI-green; the local exit-97 is a host-specific guard abort and is
not a merge consideration.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Ran down Mos's flag. Both hypotheses were wrong: CI runs exactly 'pnpm test' (same
command, path IS exercised) and is green, while this host exits 97 on an
environment-dependent BASH_LINENO guard (#973) that aborts on bash 5.2.15 and not in
CI's container. PR #1027 touches zero files under packages/mosaic, so the guard is
genuinely pre-existing — f10-coder's report was accurate in every particular, and main
is equally affected here.
Not 'merges step around a red' but something worse in one respect: the local gate and
the CI gate disagree about what passing means. No agent on this host can get a green
pnpm test on any branch. A gate only CI can run cannot be a pre-push gate.
Second defect found while establishing this: in the main checkout the same package
fails differently (exit 1) because a test scans the working tree and picks up
apps/coordinator/venv third-party site-packages. A test whose verdict depends on
untracked files is not hermetic — same contamination source that broke format:check,
one foreign tree breaking two independent gates.
Coordinate with #1007/#1024 rather than opening a third lane.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
f10-coder holds gitea-mosaicstack-f10-coder.token with write:repository scope and was
verified at mint by 'repo access returns 200'. It cannot push: collaborator lookup
404s and its own token reports push:false, pull:true.
Capability has three independent layers — token file (raw-API auth), tea login (tea
path), repository permission (actual write authority) — and satisfying two proves
nothing about the third. Scope bounds what a token may ATTEMPT; repository permission
decides what the user may DO.
The charter principle failing on the check meant to confirm capability: a 200 on a
READ was accepted as evidence of WRITE. written-unverified treated as verified, by
both provisioner and orchestrator, one layer above D-12.
RM-50's pre-dispatch check must assert effective permission for the intended operation
(permissions.push == true as that seat), not token existence or a read returning 200.
A capability check that cannot fail on a seat lacking write permission is itself an
inert gate.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Applied D-14's own interim rule immediately instead of waiting to be bitten again, and
it caught a second instance within minutes: MISSION.md's standing directives still
stated the DB hard-cutover with no mention of Mos's binding qualification that the
spine must not be a single-point hard-stop (degraded mode + rollback artifact
required). A seat reading the charter would have designed toward an availability
posture the coordinator had explicitly rejected, with the superseded 'no DB means the
fleet stops' recommendation nowhere contradicted.
Two un-propagated rulings out of two that touched charter text. Without a mechanism,
propagation failure is the default outcome, not an oversight — which is the argument
for D-14 being a requirement rather than a discipline.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
D-14: DECISION-1 was ruled and applied to TASKS.md while MISSION.md — the charter a
cold-starting seat reads first — kept the superseded wire-in target for hours, flagged
CONTESTED on the board but never corrected in the source of truth. A resuming seat
would have read the rejected target as authoritative. Caught by hand during an
unrelated edit; nothing would have caught it otherwise.
P-MISSION-001 turned on ourselves: a decision that lives in a ruling and a board note
but not in the authoritative record has been AGREED, not MADE. Requirement: rulings
must propagate mechanically — decisions as records, documents rendering rather than
restating, and a must-fail check where duplication is unavoidable. Interim rule until
the DB cutover: the same commit that records a ruling updates every document stating it.
D-13 updated with Mos's reconciliation: 5 of 6 mosaicstack-provisioned seats have no
tea login — systemic, not a one-off. Supersedes D-11(b): the token-file set is
necessary but NOT sufficient; capability is per-path.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The charter's build-1 cell still named mosaic_orchestrator.py::run_single_task after
DECISION-1 ruled against it. Leaving the charter contradicting the ruled decision would
mislead any future reader who starts there — corrected in place with the rationale and
provenance, rather than only in TASKS.md.
Also: status PLANNING -> EXECUTING; scout TODO discharged (file is in-tree) with a note
that its findings stand but its recommended wire-in point is superseded.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Charter gains a first-class principle per Mos: no write is done until the requested
PROPERTY is observed; a success exit code is not evidence. Success output is designed
to be believed — that is why the inert-gate class exists and why the P-WRAPPER
tri-state is not optional. Recorded with its provenance: the orchestrator committed
this exact error (D-12), and three of the session's twelve instances were its own.
D-13: diagnosing D-12 found two parallel credential registries that can disagree.
gitea-mosaicstack-mos-dt-0.token EXISTS, but tea has no mosaicstack login for that
identity — so raw-API paths work while tea-dependent wrapper paths silently degrade.
tea is not stale; the login does not exist. Capability declared authoritative by the
token-file set does not govern the tea path.
RM-04 must reconcile the registries (or assert agreement at startup, with a must-fail
control). RM-50's pre-dispatch check must verify capability for the path actually used.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
PR #1027 was created with -d because it carries partial unproven work. tea auth was
stale, the wrapper fell back to the raw API which cannot set draft, warned on stderr,
and exited 0. The PR sat open and mergeable for ~25 minutes, protected only by the
word DRAFT in its title and body. I had reported it to the coordinator as a draft.
Three failures: a fallback silently degrading a SAFETY flag (nuisance for --labels,
dangerous for --draft); a correct warning nobody consumed; and my own failure to
verify the flag took effect — I checked the PR existed, not that it had the property
I required. That is written-unverified treated as verified, by me, on exactly the
class of tool this mission exists to distrust.
Fixed via the WIP: title prefix; draft:True verified after.
RM-02 gains a must-fail control: a wrapper that cannot honour a safety-relevant
argument must exit non-zero. RM-24 gains this as its canonical tri-state example.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Board now carries the pre-dispatch capability check (token-file existence), the
token/authorship coherence rule, and the accreted worker-brief doctrine, so a
compacted or fresh seat inherits them mechanically rather than by recall.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Per Mos. MOSAIC_GIT_IDENTITY resolves the TOKEN; commit author comes from git config
user.name/user.email, a separate setting that fell back to the generic value. The
export could never have fixed authorship — my worker brief instructed only the export,
so the seat did what it was told and the commits were still mis-attributed. The error
was in the brief before it was in the finding.
Requirement is COHERENCE: token and authorship must agree; either half alone is drift.
Belongs in seat setup, verified by an exit-asserting test that commits and asserts the
author — not in prose a seat can follow correctly and still end up wrong.
Capability check mechanized: capability IS token-file existence. Before dispatching
seat X to provider Y, test gitea-<Y>-<X>.token exists. The token-file set is the
authoritative capability registry.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RM-01's seat was briefed to export MOSAIC_GIT_IDENTITY; its commits are authored as
the generic mosaic-coder fallback, so git history cannot say which seat did the work.
P-WRAPPER-001 reproduced on our own delivery.
Separately, nothing at dispatch time revealed the seat lacked a credential for the
target provider — discovered only when it failed mid-task after ~$9 and 69% context.
get_gitea_token behaved correctly by refusing to borrow another slot's token; the
dispatch-time information simply did not exist.
RM-50 gains per-seat capability declaration + pre-dispatch check; RM-04 gains
identity-binding verified by an exit-asserting test rather than assumed from an
export in a brief.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Same required gate, both directions wrong: fails OPEN on state=unknown (5 meaningless
greens this session, wrong branch too), and fails CLOSED on credential resolution,
hard-blocking a worker's completed work. The identical command succeeded from that
worker's own worktree in another shell, so the checkout was fine.
A gate that waves through unchecked work and blocks ready work has its failure modes
backwards. Availability failures must degrade to a loud, audited CANNOT_ASSERT;
correctness failures must block. Also the Pi-brick shape: a gate whose unavailability
prevents recovery from it.
RM-03 extended to a third requirement: distinguish CANNOT_ASSERT from
ASSERTED_NOT_READY, both registered with must-fail controls, neither exiting 0 silently.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
KICKSTART resume now points at origin/remediation/state for the live board, per Mos's
cadence ruling: every tick is pushed to the rolling branch immediately (durable
per-tick, no per-tick main PR); main carries periodic snapshots only. Reading the
board from main would silently yield a stale tick, so the step warns explicitly.
D-9 hardened: comms/v1 must use file/stdin transport, never argv interpolation.
Standing interim rule until then — agent-send -f for any body with special
characters, never -m. Mandatory in every worker brief alongside the D-8 clause.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
agent-send.sh -m with backticks executes them as command substitution: the recipient
got a mangled body plus a shell error, and the send still reported success. Same class
as the known pr-create.sh backtick bug — two tools in the comms path treating a
message body as shell input.
The failure mode is the mission's own pattern: silent corruption with a success
receipt.
Requirement on RM-40/RM-42: comms/v1 must carry payloads verbatim with no shell
interpretation at any hop, with a byte-identical round-trip test (backticks, command
substitution, quotes, newlines) registered under RM-02 including a must-fail control.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Closes#953.
GATE RECORD: review CLEAR at this exact head (author != reviewer, pre-registered diff-blind checks) + terminal-green CI at this exact head + queue guard clear.
CI CAVEAT (#973): green on the wake suites is currently WEAKER THAN IT LOOKS, IN BOTH DIRECTIONS. grep error/spawn exit codes are read as absence across 257 assertion sites in six idiom forms; 36 inverted (&&-fail) sites — including 19 credential-security canaries — fail toward GREEN under load. These greens were obtained on solo reruns after load-correlated FALSE reds (2115/2116/2118; main itself was red). This merge's safety therefore rests on the CONTENT review, not on the green. Remediation charter fa551c2d0 is authored and in flight.
Co-authored-by: mos-dt-0 <[email protected]>
Closes#952.
GATE RECORD: review CLEAR at this exact head (author != reviewer, pre-registered diff-blind checks) + terminal-green CI at this exact head + queue guard clear.
CI CAVEAT (#973): green on the wake suites is currently WEAKER THAN IT LOOKS, IN BOTH DIRECTIONS. grep error/spawn exit codes are read as absence across 257 assertion sites in six idiom forms; 36 inverted (&&-fail) sites — including 19 credential-security canaries — fail toward GREEN under load. These greens were obtained on solo reruns after load-correlated FALSE reds (2115/2116/2118; main itself was red). This merge's safety therefore rests on the CONTENT review, not on the green. Remediation charter fa551c2d0 is authored and in flight.
Co-authored-by: mos-dt-0 <[email protected]>
Closes#958.
The preimage definition (source-adapter.sh) is the single most consequential file in the wake pipeline — every observed_hash is a sha256 of what it emits — and it was UNVERSIONED: no git history, no backup. When it was edited at 07:27 on 2026-07-30, attribution was recoverable only because an agent transcript happened to still be on disk. A11 gives that file durable provenance (option (2) of #958: recorded content-addressed, not in-band).
DESIGN, per the pre-registration:
- Provenance is OUT-OF-BAND (never on the adapter's stdout) — an in-band record would advance observed_hash for every source at once and manufacture the re-baseline it exists to explain (B3).
- The obligation never depends on the provenance path: a missing/corrupt store cannot halt the detector or swallow a wake (#940 advisory-fields precedent; B4).
- DESC_FMT=d1 is NOT the provenance record — the tag versions the descriptor FORMAT; a behaviour change that keeps descriptor shape re-baselines every hash and leaves the tag unchanged (B6).
CREDENTIAL HARD GATE (rebuilt after the first verdict FAILED it): byte capture is now RECORD-ONLY BY DEFAULT (extras opt in via WAKE_PREIMAGE_CAPTURE), not allow-by-default-refuse-on-shape — because a shape list can only refuse the secrets someone already enumerated, and the tool's own usage text recommended adding detector.env (where HMAC material lives). Deny is evaluated on BOTH raw and resolved path forms with resolved anchors, ordered before allow — closing the realpath-before-deny ordering defect that let a renamed symlink target through.
VERIFICATION (reviewer, mos-dt, independent of the author's claims):
- Seven decoy cases by planted-marker-then-grep-whole-state-dir: known cred path / same-name symlink / RENAMED-target symlink / prefixed secret / prefixless secret / opted-in-symlink-to-DENIED-target all REFUSED; opted-in-symlink-to-ALLOWED-target CAPTURED (positive control that the harness can capture at all, and that C was not closed by breaking every symlink).
- Polarity-completeness self-test RE-RUN with the shape list stubbed always-allow AND both deny lists stubbed — case D still safe: the flip is complete, the shape list is not load-bearing. Each stub proven live first (a stub that silently fails to apply reports the dangerous state as safe).
- B11: rm-then-change fails LOUD (rc=1), refuses to re-baseline, leaves the ledger absent; absent-with-emptied-objects still first-installs cleanly (absent-is-not-corrupt not paid for by breaking first install).
- RED-first reproduced exactly P13-P16 pre-fix; each refusal corroborated three ways (loud stderr, ledger row captured:false WITH a hash so attribution survives refusal, objects/ holding only the adapter).
KNOWN RESIDUAL (filed #969, non-gating): the deny check is both-forms but the suite needles only the resolved form — a deny reduced to resolved-only survives 17/17 green and would leak a renamed-symlink case. No reachable leak at this head (shipped code correct on all seven decoys); it constrains a FUTURE edit. Doctrine: a both-forms fix needs a needle per form; a fixture that satisfies its assertion through a DIFFERENT rule is testing the rule it did not mean to test.
Authored by pepper (sb-it-1-dt); independently reviewed by mos-dt (sb-it-1-dt) under diff-blind pre-registration (7242688b1, predating first read) — NOT CLEAR on the first verdict (B2/B11 failed by decoy), CLEAR at 8aff7d8 after the polarity rebuild. Manifest version 0.7.0.
Co-authored-by: mos-dt-0 <[email protected]>
Closes#946.
The digest omitted a quarantined entry's claim from disclosure while still advancing the ack watermark it instructed the consumer to run — converting a fail-safe HOLD into a silent DISCARD, through the documented normal path. Measured: the burial instruction was re-issued FIVE times, four fresh digests plus one system-initiated redelivery fired purely because the entry had gone unconsumed for 1826s. That redelivery is the proof of the 'indefinitely' half: the mechanism re-asserted itself with no new information.
SCOPE — this was NOT a missing check in the consume path. Measured before the fix: dead-letter occurrences were digest.sh 27, store.sh 0, ack.sh 0, detector.sh 0, reconcile.sh 0. Quarantine was owned ENTIRELY by the renderer; the store that advances the watermark had zero knowledge the ledger existed, so an entry could be quarantined by one subsystem and consumed by another with no possible interaction. The fix is therefore a deliberate cross-module decision — option (b), quarantine recorded into a store-owned file, preserving the existing direction of dependency — pre-registered by the consumer before any diff existed.
VERIFICATION
- Pipeline 2107 terminal SUCCESS at e11bc6622, read clone-inclusive from the provider API rather than through `pipeline-status.sh` (which filters `.type != "clone"` per workflow and would hide a clone failure behind an all-green table): 9/9 children success, exit 0 each, no non-success member. Its `test` step runs all nine wake harnesses via turbo -> packages/mosaic `test` -> `test:framework-shell`.
- Independent review by the consumer on the affected lane: eleven pre-registered acceptance checks, authored and delivered BEFORE the diff was read — the file list deliberately unlooked-at, because a filename alone would have disclosed which option was chosen. All eleven resolved, no blocker.
- The check that decides it: a RAW `ack.sh consumed --upto N` with no digest involved must refuse to advance past a quarantined seq — the case an agent hits when a digest is MISSED, and the one that would have sunk a disclosure-only fix. Covered at the head as a named assertion (T13 ordinary-path bypass), written independently of the reviewer's list.
- Mutation: one asserted site disabled -> TWELVE assertions die, every one BEHAVIOURAL, ZERO count assertions, including one killing across the module boundary the fix spans.
- RED control at base a6b5f6a: 34 and 10 assertions fail, matching the body exactly.
- Coordinator re-verify by a different instrument than the reviewer used: static reference counts across the base/head boundary — store.sh 0 -> 49, ack.sh 0 -> 6, `--agent` unchanged at 4 (so #949 correctly stayed out). A fix present-but-inert passes the count and fails the mutation; a fix behaviourally correct but smuggling #949 passes the mutation and fails the count. Neither result is reachable by repeating the other.
KNOWN RESIDUALS
- The `consumed-hashes` repair criterion is met only for keys that RE-EMIT. A corrupted row whose key never recurs stays false indefinitely; the sweep covers those, and the known-false row named in the acceptance criteria had already self-healed by re-emission rather than by design — safe by population, not by design.
- The audit's clean-sweep message names one unprovable class; a second exists (a surviving dead-letter row with an empty `observed_hash` cannot be convicted either). Wording, not logic. Filed separately.
- The audit's provability bound makes dead-letter RETENTION load-bearing for auditability. Nothing prunes it today, so this is latent — but any future rotation or size cap silently converts provable rows into unprovable ones with no signal at either end. This is not a defect; it is a property that BECAME load-bearing and is recorded nowhere. Filed separately.
- `test-wake-detector.sh` D4 fails at this head AND identically at base, with an empty diff over detector files — pre-existing, tracked, not introduced here.
Authored by pepper (sb-it-1-dt); reviewed independently by mos-dt (sb-it-1-dt). The mos-dt-0 commit and fork identity does not identify the author — attribution collapse tracked separately.
Co-authored-by: mos-dt-0 <[email protected]>
Closes#944.
_has_hard_locator accepted only repo+issue / 40-hex sha / file — the forge vocabulary. The detector emits path (+snapshot_sha when attested) and NEVER emits file/issue/sha, so predicate and sole producer shared ZERO keys and every class=actionable board_file entry dead-lettered. Latent since #920, whose harness pinned the detector's own emission shape as its malformed example — the suite certified the gap it was written to guard.
Fix: `path` becomes a hard-locator arm, and ONLY path. Bare path-less snapshot_sha is a deliberate NON-arm (would widen past the board_file vocabulary); Q11(d) asserts it still quarantines at 7/40/64 chars, spanning the detector's ^[0-9a-f]{7,64}$ attestation range.
VERIFICATION
- Pipeline 2105 terminal-green at fa36da8. Its `test` step reaches all nine wake harnesses via turbo -> packages/mosaic `test` -> `test:framework-shell`, which names each suite explicitly. The two-levels-down indirection matters: no search of .woodpecker/* can see it, and that is exactly why this question was got wrong earlier today and then corrected. CI therefore DOES attest the quarantine suite and the detector suite at this head.
- Independent review (mos-dt, consumer on the affected lane) PASS at fa36da8, from a detached worktree: predicate provably unmoved from fb3c3c3 (comment-stripped sha256 identical, _has_hard_locator body byte-identical), RED control at base reproduced exactly 8 failures all Q11 including "got 6".
- Third reviewer (wake-judge) ACCEPT on both judgment calls: the Q1 assertion reversal is a legitimate correction (the flip was forced, not elective — base fixtures red 19 assertions against the head predicate) and path-alone satisfies §2.1, whose operative test is "one targeted call, never a search" — two of its four named exemplars already resolve to current state. Requiring path+snapshot_sha jointly would permanently dead-letter a declared source class and conflict with #940's advisory-fields ruling.
- Judge's mutation criterion met: with the reconciled exemption disabled, the gate-level assertion ("an ORIENTATION-tier enumeration must NOT be quarantined") dies at this head and did not exist as a casualty before F1.
- D4 (detector lock re-acquisition) fails intermittently at base AND head; git diff base..head over the detector files is EMPTY, so it is out of this PR's surface on structural grounds rather than on a re-roll. Known defect, fix identified (detector.sh:516, fd 9 leaked into sleep), tracked separately.
KNOWN RESIDUALS
- ENUM-B is now the sole address-free reconciled fixture, so the exemption's gate-level guard is a population of one. Safe by population, not by design. Author follow-up: assert ENUM-B carries no hard-locator arm so the harness guards its own premise.
- The binding spec (CONVERGED-DESIGN.md §2.1, separate repo) still enumerates four forge tokens and reads narrower than the shipped gate. Tracked as #948, sequenced after the dragon-lin reseed.
- Hard-locator arms are type-loose: repo/file/path accept any non-null JSON value. Pre-existing; `sha` fails closed only by accident of test(). Tracked separately.
Authored by pepper (sb-it-1-dt); reviewed independently by mos-dt (sb-it-1-dt) and wake-judge. The mos-dt-0 commit/fork identity does not identify the author — attribution collapse tracked in #3092.
Co-authored-by: mos-dt-0 <[email protected]>
grep is line-oriented, so a multi-line knob value passed the per-line anchors and was still fatal in arithmetic. Replaced with a case pattern matching the whole string, so an embedded or leading newline rejects. Manifest bumped 0.6.12 -> 0.6.13: three materially different detectors had shipped under one version string, and version= is the component sole self-identity claim.
Authored-by: pepper
Reviewed-by: mos-dt (independent, at this head; transfer proven by blob-hash equality)
Merged-by: Mos
Co-authored-by: mos-dt-0 <[email protected]>
The slack knob was interpolated raw into $((...)) under set -u: a malformed value was FATAL to the poll, falsifying the poll-never-fails invariant, and a negative value inverted the guard to deny-all. Shape validation alone was insufficient — bash reads a leading zero as octal, so 08/09 passed the regex yet were fatal and 0300 silently meant 192. Now validated ^[0-9]{1,9}$ with a loud fallback to 300, then forced to base-10 via 10# so the knob means what the operator wrote.
Authored-by: pepper
Reviewed-by: mos-dt (independent, found both the original defect and the radix residual)
Merged-by: Mos
Co-authored-by: mos-dt-0 <[email protected]>
Adapter emits snapshot sha/ts out-of-band on fd 3 so a changing value never enters the delta-gate hash. Detector validates advisorily (sha regex, epoch sanity before arithmetic, future-skew slack); malformed metadata is dropped loudly and never gates the wake. Digest renders snapshot_sha/snapshot_ts plus a git-show re-verify hint. Adapters that never write fd 3 are byte-identical.
Reviewed-by: Mos (design, independent)
Reviewed-by: mos-dt (artifact, hardening §2)
Co-authored-by: mos-dt-0 <[email protected]>
Part of #869
Mos (id-11) Gate-16 merge: independent APPROVE @b6f36564 (8/8, verified vs real production settings template), author id2 != approver id11, clean mosaic-coder author, CI green wp1988.
Co-authored-by: jason.woltje <[email protected]>
Co-committed-by: jason.woltje <[email protected]>
Part of #869
Mos (id-11) Gate-16 merge: independent APPROVE @75235ef8 (9/9, no live host mutation), author id2 != approver id11, CI green wp1971.
Co-authored-by: jason.woltje <[email protected]>
Co-committed-by: jason.woltje <[email protected]>
GLPI helpdesk workflow skills written against the portable
tools/glpi/ tooling (session-init.sh, ticket-list.sh, ticket-create.sh),
cross-linked via [[glpi-*]]:
- glpi-solve — close a ticket by setting status Solved (5); GLPI auto-closes
- glpi-followup — add a followup via the top-level /ITILFollowup endpoint
- glpi-sweep — read-only hunt for done-but-open tickets needing Solve
- glpi-list — query tickets by status/recency
- glpi-create — open a new ticket
Core rule encoded: completing work means setting status Solved, not just
posting a resolution followup (a followup documents; only Solved auto-closes).
Note: illustrative examples in the bodies are USC-flavored (M2M / helpdesk
ticket numbers) and can be genericized in review if preferred.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_019GjBgrb9tHgvq414Fqj37c
Fresh `mosaic gateway install` (npm) left the gateway DB schema empty —
sign-in 500'd with `relation "users" does not exist`, and every entry
point (auth, bootstrap setup) failed because they all query the users
table first. Five stacked bugs on the local (PGlite) tier:
1. `packages/db/package.json` `files: ["dist"]` excluded the `drizzle/`
SQL migrations from the published tarball.
2. `runMigrations()` only supports postgres-js — unusable for embedded
PGlite.
3. `apps/gateway/src/database/database.module.ts` never invoked
migrations at startup.
4. `createPgliteDb` didn't load pgvector, so migration 0001's
`CREATE EXTENSION vector` failed.
5. Drizzle's PG migrator wraps every migration in one outer
transaction, which trips Postgres' `check_safe_enum_use` on
migration 0009 (`ALTER TYPE ADD VALUE 'pending'` → `SET DEFAULT
'pending'` in the same tx).
Changes:
- Ship `drizzle/` in the published tarball.
- `createPgliteDb` loads `@electric-sql/pglite/vector`.
- New `runPgliteMigrations(handle)` walks the Drizzle journal and
runs each statement-breakpoint chunk through PGlite's `client.exec()`
(autocommit per statement). Records into `drizzle.__drizzle_migrations`
for interop with the postgres-js path. Per-statement try/catch
surfaces which statement of which migration failed.
- `DatabaseModule` runs migrations in `OnModuleInit` before
`app.listen()`. Local tier: explicit `runPgliteMigrations` then
`storageAdapter.migrate()`. Postgres tier: just `storageAdapter.migrate()`,
which already calls `runMigrations(url)` internally — no double-call.
- Removed `packages/storage/src/test-utils/pglite-with-vector.ts`. The
"intentionally not exported" rationale is moot now that migration
0001 forces pgvector load anyway. The integration test uses
`createPgliteDb` + `runPgliteMigrations` from `@mosaicstack/db`.
Tests: BetterAuth tables exist after migrate; idempotent (re-runs 0009);
partial-failure surfaces statement-level context and leaves no ledger row.
QA on a fresh PGlite install:
- `Applying PGlite schema migrations...` then `Initializing storage
adapter (pglite)...` in startup log.
- `GET /api/bootstrap/status` → `{"needsSetup":true}` HTTP 200 (was 500).
- `POST /api/bootstrap/setup` reaches Zod validator (was 500).
Scope: this PR fixes the local (PGlite) tier. Postgres-tier first
install still has the outer-transaction problem and a journal ordering
bug (0009's `when` < 0008's). Documented inline as TODO and in the
scratchpad — needs a separate change with real-Postgres validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- CRIT-1: Validate cert subjectUserId against grant.subjectUserId from DB;
use authoritative DB value in FederationContext
- CRIT-2: Add @Inject(GrantsService) decorator (tsx/esbuild requirement)
- HIGH-1: Validate UTF8String TLV tag, length, and bounds in OID parser
- HIGH-2: Collapse all 403 wire messages to a generic string to prevent
grant enumeration; keep internal logger detail
- HIGH-3: Assert federation wire envelope shape in all guard tests
- HIGH-4: Regression test for subjectUserId cert/DB mismatch
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds FederationAuthGuard that validates inbound mTLS client certs on
federation API routes. Extracts custom OIDs (grantId, subjectUserId),
loads the grant+peer from DB in one query, asserts active status, and
validates cert serial as defense-in-depth. Attaches FederationContext
to requests on success and uses federation wire-format error envelopes
(not raw NestJS exceptions) for 401/403 responses.
New files:
- apps/gateway/src/federation/oid.util.ts — shared OID extraction (no dupe ASN.1 logic)
- apps/gateway/src/federation/server/federation-auth.guard.ts — guard impl
- apps/gateway/src/federation/server/federation-context.ts — FederationContext type + module augment
- apps/gateway/src/federation/server/index.ts — barrel export
- apps/gateway/src/federation/server/__tests__/federation-auth.guard.spec.ts — 11 unit tests
Modified:
- apps/gateway/src/federation/grants.service.ts — adds getGrantWithPeer() with join
- apps/gateway/src/federation/federation.module.ts — registers FederationAuthGuard as provider
Closes#462
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- HIGH-A: resolveEntry now uses promise-cache pattern so concurrent
callers serialize on a single in-flight build, eliminating duplicate
key material in heap and duplicate DB round-trips
- HIGH-B: flushPeer destroys the evicted undici Agent so stale TLS
connections close on cert rotation
- MED-C: add regression test for PEER_MISCONFIGURED when
STEP_CA_ROOT_CERT_PATH is unset
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
CRIT-1: regenerate pnpm-lock.yaml so apps/gateway resolves [email protected].6
(prior PR pushed package.json without lockfile update; CI failed with
ERR_PNPM_OUTDATED_LOCKFILE). Incidentally cleans 57 lines of stale
peer-dep entries.
CRIT-2: cache-hit test no longer swallows resolveEntry errors. Calls the
private method directly twice and asserts identity equality plus a
single DB select, removing the silent-failure path the prior assertion
allowed.
HIGH-1: mTLS Agent now pins Step-CA root via STEP_CA_ROOT_CERT_PATH.
Without the env var resolveEntry throws PEER_MISCONFIGURED, refusing to
dial peers against the public trust store. PEM is read once and cached
on the service instance.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Implements FederationClientService — a NestJS injectable that dials peer
gateways over mTLS (undici Agent with cert+sealed-key from federation_peers),
invokes list/get/capabilities verbs, validates responses via Zod, and surfaces
all failure modes as typed FederationClientError with a coherent error code
taxonomy (PEER_NOT_FOUND, PEER_INACTIVE, PEER_MISCONFIGURED, NETWORK,
FORBIDDEN, HTTP_{status}, INVALID_RESPONSE).
Per-peer Agent instances are cached in a Map for the service lifetime;
flushPeer(peerId) invalidates the cache for M5/M6 cert rotation and
revocation events.
Wired into FederationModule providers + exports so QuerySourceService
(M3-09) can inject it.
13 unit tests covering all required scenarios via undici MockAgent +
real sealClientKey/unsealClientKey round-trip.
Closes#462
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Picks up auth command and spec written by parallel agent, and updated
mosaic cli.ts wiring from parallel development during cli-unification.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds mosaic forge run|status|resume|personas list subcommands to
@mosaicstack/forge, wires registerForgeCommand into the root mosaic CLI,
and ships a smoke test asserting command structure. Ref CU-05-01
cli-unification-20260404.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Remove @mosaicstack/cli (absorbed into @mosaicstack/mosaic)
- Add all 21 remaining workspace packages so the multi-package
update checker actually covers every published package
@mosaic/mosaic is now the single package providing both:
- 'mosaic' binary (CLI: yolo, coord, prdy, tui, gateway, etc.)
- 'mosaic-wizard' binary (installation wizard)
Changes:
- Move packages/cli/src/* into packages/mosaic/src/
- Convert dynamic @mosaic/mosaic imports to static relative imports
- Add CLI deps (ink, react, socket.io-client, @mosaic/config) to mosaic
- Add jsx: react-jsx to mosaic's tsconfig
- Exclude packages/cli from workspace (pnpm-workspace.yaml)
- Update install.sh to install @mosaic/mosaic instead of @mosaic/cli
- Bump version to 0.0.17
This eliminates the circular dependency between @mosaic/cli and
@mosaic/mosaic that was blocking the build graph.
git pull --rebase fails with 'cannot pull with rebase: You have
unstaged changes' when the skills repo has local modifications.
Fix: detect dirty index/worktree, stash before pull, restore after.
Also gracefully handle pull failures (warn and continue with existing
checkout) and stash pop conflicts.
Two bugs causing 'EACCES: permission denied, copyfile' when source
and target are the same path (e.g. wizard with sourceDir == mosaicHome):
1. No same-path guard — syncDirectory tried to copy every file onto
itself; git pack files are read-only (0444) so copyFileSync fails.
2. excludeGit only matched top-level .git — nested .git dirs like
sources/agent-skills/.git were copied, hitting the same permission
issue.
Fixes:
- Early return when resolve(source) === resolve(target)
- Match .git dirs at any depth via dirName and relPath checks
- Skip files inside .git/ paths
Added file-ops.test.ts with 4 tests covering all cases.
- mosaic-init bash script: detect existing SOUL.md/USER.md/TOOLS.md and
prompt user to keep, import (re-use values as defaults), or overwrite.
Non-interactive mode exits cleanly unless --force is passed.
Overwrite creates timestamped backups before replacing files.
- launch.ts checkSoul(): prefer 'mosaic wizard' over legacy bash script
when SOUL.md is missing, with fallback to mosaic-init.
- detect-install.ts: pre-populate wizard state with existing values when
user chooses 'reconfigure', so they see current settings as defaults.
- soul-setup.ts: show existing agent name and communication style as
defaults during reconfiguration.
- Added tests for reconfigure pre-population and reset non-population.
The #351 merge landed before the force-push with full commands.
This adds the missing subcommands:
- mosaic coord {init,status,mission,continue,run,smoke,resume}
→ delegates to tools/orchestrator/*.sh with --claude/--codex/--pi/--yolo
- mosaic prdy {init,update,validate,status}
→ delegates to tools/prdy/*.sh with --claude/--codex/--pi
- mosaic seq {check,fix,start}
→ sequential-thinking MCP management (native TS)
- mosaic upgrade {release,check,project}
→ delegates to tools/_scripts/mosaic-release-upgrade and mosaic-upgrade
Also removes duplicate prdy registration (was in both launch.ts and
the old registerPrdyCommand — now only in launch.ts).
Templates moved from packages/mosaic/templates/ to
packages/mosaic/framework/templates/ in #345. The test's
existsSync guard silently skipped the copy, causing writeSoul
to early-return without writing SOUL.md.
The @mosaic scope registry is configured in ~/.npmrc. Passing --registry
on the install command overrides the default registry for ALL packages,
causing non-@mosaic deps like @clack/prompts to 404 against Gitea.
Completes the bootstrap repo migration with remaining files:
- PowerShell scripts (.ps1) for Windows support (bin/ + tools/)
- Runtime adapters (claude, codex, generic, pi)
- Guides (17 .md files) and profiles (domains, tech-stacks, workflows)
- Wizard test suite (6 test files from bootstrap tests/)
- Memory placeholder, audit history
Bootstrap repo (mosaic/bootstrap) is now fully superseded:
- All 335 files accounted for
- 5 build config files (package.json, tsconfig, etc.) not needed —
monorepo has its own at packages/mosaic/
- skills-local/ superseded by monorepo skills/ with mosaic-* naming
- src/ already lives at packages/mosaic/src/
Kaniko fails when COPY --from=builder references a path that doesn't
exist. The web app had no public/ directory, causing build-web to fail
with 'no such file or directory' on the public assets COPY step.
Publish pipeline:
- Add publish-npm step to .woodpecker/publish.yml — publishes all
@mosaic/* packages to Gitea npm registry on main push/tag
- Requires gitea_npm_token Woodpecker secret (package:write scope)
- publish-npm runs after build, parallel with Docker image builds
- pnpm publish resolves workspace:* to concrete versions automatically
Package configuration:
- All 20 packages versioned at 0.0.1-alpha.1
- publishConfig added to all packages (Gitea registry, public access)
- files field added to all packages (ship only dist/)
- @mosaic/forge includes pipeline/ assets in published package
Meta package (@mosaic/mosaic):
- Now depends on @mosaic/forge, @mosaic/macp, @mosaic/prdy,
@mosaic/quality-rails, @mosaic/types
- npm install @mosaic/mosaic pulls in the standalone framework
Build fixes:
- Fix forge and macp tsconfig rootDir: '.' -> 'src' so dist/index.js
resolves correctly (was dist/src/index.js)
- Exclude __tests__ and vitest.config from build includes
- Clean stale build artifacts from old rootDir config
Required Woodpecker secret:
woodpecker secret add mosaic/mosaic-stack \
--name gitea_npm_token --value '<token>' \
--event push,manual,tag
Skills:
- Rename all repo skills to mosaic-<name> convention (jarvis -> mosaic-jarvis, etc.)
- Update frontmatter name: fields to match directory names
- New mosaic-board skill: standalone Board of Directors multi-persona review
- New mosaic-forge skill: standalone Forge specialist pipeline
- New mosaic-prdy skill: PRD lifecycle (init/update/validate/status)
Wizard (packages/mosaic):
- Add mosaic-board, mosaic-forge, mosaic-prdy, mosaic-standards, mosaic-macp
to RECOMMENDED_SKILLS
- Add new skills to SKILL_CATEGORIES for categorized browsing
Framework scripts (~/.config/mosaic/bin):
- mosaic (launcher): load skills from both skills/ and skills-local/ for Pi
- mosaic-doctor: add --fix flag for auto-wiring skills into all harnesses,
Pi skill dir checks, Pi settings.json validation, mosaic-* presence checks
- mosaic-sync-skills: add Pi as 4th link target, fix find to follow symlinks
in skills-local/, harden is_mosaic_skill_name() with -L fallback
- mosaic-link-runtime-assets: add Pi settings.json skills path patching,
remove duplicate extension copy (launcher --extension is single source)
- mosaic-migrate-local-skills: add Pi to skill_roots, fix find for symlinks
YAML fixes:
- Quote description values containing colons in mosaic-deploy and
mosaic-woodpecker SKILL.md frontmatter (fixes Pi parse errors)
The insights table uses vector(1536) but no migration enables the pgvector
extension. CI postgres (pgvector/pgvector:pg17) has the extension available
but it must be explicitly created before use.
Adds CREATE EXTENSION IF NOT EXISTS vector at the top of
0001_cynical_ultimatum.sql (the first migration referencing vector type).
The migration file 0001_cynical_ultimatum.sql existed on disk but was not
registered in the Drizzle journal (_journal.json). This caused fresh-database
migrations (CI) to skip creating tables (agent_logs, insights, preferences,
skills, summarization_jobs), then 0002_nebulous_mimic.sql would fail trying
to ALTER the non-existent preferences table.
Fix: insert cynical_ultimatum at idx 1 in the journal and shift all
subsequent entries (idx 2-7).
Verified: pnpm test passes (347 tests, 35 tasks).
BullMQ v5 RedisConnection constructor does:
Object.assign({ port: 6379, host: '127.0.0.1' }, opts)
When opts is a URL string (via 'as unknown as ConnectionOptions'),
Object.assign only copies character-index properties from the string,
so the default port 6379 was never overridden — causing ECONNREFUSED
against the wrong port instead of the configured 6380.
Fix: parse VALKEY_URL with new URL() and return a plain RedisOptions
object { host, port, ... } so Object.assign merges it correctly.
- plugins/macp/src/index.ts: use createRequire + dynamic import() for OC SDK
- plugins/macp/src/acp-runtime-types.ts: local ACP runtime type definitions
- plugins/macp/src/macp-runtime.ts: DEFAULT_REPO_ROOT and PI_RUNNER_PATH use
os.homedir() instead of hardcoded /home/user/
- plugins/mosaic-framework/src/index.ts: removed hardcoded SDK import
- No hardcoded /home/ paths remain in any plugin source file
- Plugin works on any machine with openclaw installed globally
The `agent` column specifies the required model for each task. **This is set at task creation by the orchestrator and must not be changed by workers.**
Self-hosted, multi-user AI agent platform. One config, every runtime, same standards.
Mosaic gives you a unified launcher for Claude Code, Codex, OpenCode, and Pi — injecting consistent system prompts, guardrails, skills, and mission context into every session. A NestJS gateway provides the API surface, a Next.js dashboard gives you the UI, and a plugin system connects Discord, Telegram, and more.
After install, the wizard runs automatically or you can invoke it manually:
```bash
mosaic wizard # Full guided setup (gateway install → verify)
```
### Requirements
- Node.js ≥ 20
- npm (for global @mosaicstack/mosaic install)
- One or more runtimes: [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenCode](https://opencode.ai), or [Pi](https://github.com/mariozechner/pi-coding-agent)
## Usage
### Launching Agent Sessions
```bash
mosaic pi # Launch Pi with Mosaic injection
mosaic claude # Launch Claude Code with Mosaic injection
mosaic codex # Launch Codex with Mosaic injection
mosaic opencode # Launch OpenCode with Mosaic injection
mosaic yolo claude # Claude with dangerous-permissions mode
mosaic yolo pi # Pi in yolo mode
```
The launcher verifies your config, checks for `SOUL.md`, injects your `AGENTS.md` standards into the runtime, and forwards all arguments.
Pi launches default to a token-lean skill posture: `mosaic pi` passes `--no-skills` so Pi does not preload every global skill description into the system prompt. Use `MOSAIC_PI_SKILL_MODE=all mosaic pi` for the legacy all-skills catalog, or `MOSAIC_PI_SKILL_MODE=discover mosaic pi` to let Pi use its native settings/project skill discovery.
### TUI & Gateway
```bash
mosaic tui # Interactive TUI connected to the gateway
mosaic gateway login # Authenticate with a gateway instance
mosaic sessions list # List active agent sessions
```
### Gateway Management
```bash
mosaic gateway install # Install and configure the gateway service
mosaic gateway verify # Post-install health check
mosaic gateway login # Authenticate and store a session token
mosaic gateway config rotate-token # Rotate your API token
mosaic gateway config recover-token # Recover a token via BetterAuth cookie
```
If you already have a gateway account but no token, use `mosaic gateway config recover-token` to retrieve one without recreating your account.
### Configuration
Mosaic supports three storage tiers: `local` (PGlite, single-host), `standalone` (PostgreSQL, single-host), and `federated` (PostgreSQL + pgvector + Valkey, multi-host). See [Federated Tier Setup](docs/federation/SETUP.md) for multi-user and production deployments, or [Migrating to Federated](docs/guides/migrate-tier.md) to upgrade from existing tiers.
```bash
mosaic config show # Print full config as JSON
mosaic config get <key> # Read a specific key
mosaic config set <key> <val># Write a key
mosaic config edit # Open config in $EDITOR
mosaic config path # Print config file path
```
### Management
```bash
mosaic doctor # Health audit — detect drift and missing files
mosaic sync # Sync skills from canonical source
mosaic skill list # Audit Claude skill registrations and conflicts
mosaic skill register <name> # Register one canonical skill with Claude Code
mosaic skill unregister <name> # Remove one Mosaic-owned Claude link
mosaic update # Update CLI/framework and auto-register canonical skills
mosaic wizard # Full guided setup wizard
mosaic bootstrap <path> # Bootstrap a repo with Mosaic standards
mosaic coord init # Initialize a new orchestration mission
mosaic prdy init # Create a PRD via guided session
```
### Sub-package Commands
Each Mosaic sub-package exposes its API surface through the unified CLI:
```bash
# User management
mosaic auth users list
mosaic auth users create
mosaic auth sso
# Agent brain (projects, missions, tasks)
mosaic brain projects
mosaic brain missions
mosaic brain tasks
mosaic brain conversations
# Agent forge pipeline
mosaic forge run
mosaic forge status
mosaic forge resume
mosaic forge personas
# Structured logging
mosaic log tail
mosaic log search
mosaic log export
mosaic log level
# MACP protocol
mosaic macp tasks
mosaic macp submit
mosaic macp gate
mosaic macp events
# Agent memory
mosaic memory search
mosaic memory stats
mosaic memory insights
mosaic memory preferences
# Task queue (Valkey)
mosaic queue list
mosaic queue stats
mosaic queue pause
mosaic queue resume
mosaic queue jobs
mosaic queue drain
# Object storage
mosaic storage status
mosaic storage tier
mosaic storage export
mosaic storage import
# Schema migration is unavailable in this release. The current storage wrapper shells
# directly to `pnpm --filter @mosaicstack/db db:migrate`; it is legacy N-1,
# uncertified, and MUST NOT be invoked pending KBN-101-02/-03/-06/-08 activation.
`mosaic-db-migrator --verify` → Gateway/Compose readiness. The future deployment artifacts—not
this README—will provide the reviewed commands and secret-consumer interface.
For local data-layer work, PGlite needs no PostgreSQL service. The optional Compose command above
starts only Valkey; OTEL Collector and Jaeger may likewise be started individually if needed,
without starting PostgreSQL. A Gateway/Web local process is not currently a safe PGlite route:
its unguarded dotenv loader may inherit a daemon PostgreSQL DSN. Do not use root `pnpm dev` or a
Gateway start command until KBN-101-02 makes that state fail closed.
### Quality Gates
```bash
pnpm typecheck # TypeScript type checking (all packages)
pnpm lint # ESLint (all packages)
pnpm test# Vitest (all packages)
pnpm format:check # Prettier check
pnpm format # Prettier auto-fix
```
### CI
Woodpecker CI runs on every push:
-`pnpm install --frozen-lockfile`
- **Legacy N-1 CI status only — active, uncertified, and non-authorizing as an operator route:** the checked-in job currently invokes `pnpm --filter @mosaicstack/db run db:migrate` with `DATABASE_URL` against an isolated disposable PostgreSQL CI database. It performs direct DDL in that CI database, is not approved ordinary behavior or an operator route, and remains a known exception pending KBN-101-06 removal/replacement by the certified runner-backed CI path.
-`pnpm test` (Turbo-orchestrated across all packages)
npm packages are published to the Gitea package registry on main merges.
Each stage has a dispatch mode (`exec` for research/review, `yolo` for coding), quality gates, and timeouts. The board review uses multiple AI personas (CEO, CTO, CFO, COO + specialists) to evaluate briefs before committing resources.
## Upgrading
Run the installer again — it handles upgrades automatically:
DTOs go in `*.dto.ts` files at module boundaries. Scratchpads (`docs/scratchpads/`) are mandatory for non-trivial tasks. See `AGENTS.md` for the full standards reference.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.