Skip to content

v0.12.0 integration 24b3: the build loop lands and records, drain lanes, gitleaks opt-in, terminal check, record-lint edge and ADR-id rules, follow-ups - #761

Merged
REPPL merged 110 commits into
mainfrom
integ/land-24b3
Sep 30, 2026
Merged

REPPL merged 110 commits into
mainfrom
integ/land-24b3

Conversation

@REPPL

@REPPL REPPL commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Integration 24b-3 lands seventeen reviewed branches in one change, still inside the unreleased, breaking v0.12.0. For a user: a repository that opts into gitleaks gets it in every scan abcd makes; the build loop can take a lane from review to a merged pull request by itself and hands a lane back to the person after its fix rounds; abcd drain now works issues through that loop one lane at a time; record-lint catches stale and circular dependency edges between intents and two files claiming one ADR id; prompts no longer appear when stdin is /dev/null; and abcd --version says when it answers from a superseded plugin root.

gitleaks in every scan (gitleaksAug). A repository that opts into gitleaks now gets it in every scan abcd makes, not in transcript capture alone: the launch scan, the privacy lint, a lifeboat pack, and the transcript, issue-capture, memory, decision, intent, ideate, reading and release write paths all report what gitleaks finds, beside the native scanner. When the repository armed gitleaks and the binary is not installed, a release and a lifeboat pack refuse, the privacy lint reports an error, and the write paths still write on the native scanner and name the gap in their receipt. What gitleaks hands back is treated as untrusted input: capped, position-checked, forced to hard_fail, and never shown in clear.

One lock primitive for every flock (flockSites, carried by gitleaksAug). The five record stores that ran their own flock loops (ADR, intent and spec mint locks, the memory store, the history records) now go through fsutil's two lock primitives, with byte-identical lock paths, and a detector stops a sixth from appearing. The history records lock's wait is now bounded at two minutes instead of hanging, and lock descriptors are close-on-exec, so a child a holder started no longer keeps the lock.

Guard-teaching follow-ups (teachFollow). A repository's own guard lesson has its why and its successor capped at 1,024 bytes each, and a longer one refuses the guard file loudly. When the rules overflow the injection budget, the notice names the file that filled it. Under a committed guard switch-off every taught lesson says the guard is off, and when a rules override leaves out a lesson the repository's guard file teaches, the load names that lesson too.

A malformed repository route is skipped (routeNameSkip). A repository's .abcd/config.json can no longer stop abcd ahoy credential, ahoy connect and the providers board by giving a route a name that is not a plain lower-case name, or a value that is not <provider>/<model>: per ruling CD2 the route is skipped with one sanitised warning naming the file and the name, and everything else keeps working. The same mistakes in the person's own ~/.abcd/config.json still refuse.

Filing-match follow-ups (filingFollow). reverses: is a checked cross-reference field in record-lint, the promote route's ADR states its lock exactly, and dead code in the reading match is removed.

Terminal detection (ttyDetect). abcd asked install and identity questions when stdin came from /dev/null, because its terminal check treated any character device as a terminal. It now asks the kernel whether the descriptor is a terminal, so a redirected or closed stdin declines with "no terminal to ask at". A program PATH finds inside the checkout no longer counts as installed. By design, abcd update </dev/null now proceeds as it does from a pipe.

One ADR id, one file (adrIdUnique). record-lint's new adr_id_unique blocker refuses two ADR files that claim one id, and the resolver and abcd adr-N refuse such an id naming both files instead of taking whichever sorts first.

Stale intent edges relinked (recDefects). A sweep of planned and draft intents relinks or drops nine builds_on/blocked_by edges that named a superseded intent, each dropped edge saying why under the intent's Audit Notes.

Dependency-edge lint (lintEdges). record-lint warns on a planned or draft intent whose builds_on or blocked_by names a superseded intent (stale_edge), naming where the chain ends, and on a cycle through those edges (edge_cycle), naming every record on it.

Predecessor-store citations qualified (drainCitations). Intents that cite spec numbers from the retired predecessor store now say so with a "(predecessor store)" qualifier where the number also names a different live spec; citations of live specs are unchanged. Records only.

Reach baseline shrunk (drainReach). In eighteen packages, exported core functions nothing outside their package calls are made package-private or marked as test seams. Nothing is deleted and no behaviour changes.

Superseded plugin root named (supersededRoot). abcd --version (and update --check) and bare abcd ahoy add a superseded_root note when the running binary sits in a plugin root other than the one this session resolves, so an old binary a pinned command page still runs no longer answers confidently with a version that is false of this machine.

Pinned-action bump intent filed (iss209). A draft intent records the automation ruling M3 asked for: a dependabot bump of an action in a scaffolded workflow syncs its template and lands re-authored. Nothing is built; it waits on its planning interview.

Undecided audits reopen the work; fix rounds are capped (fixLoop, carrying fidelityOnce). When the build loop's fidelity audit cannot decide whether a criterion is met, the work goes back to a fresh implementer as it does for a not-met criterion; it never lands on an undecided audit (ruling DQ1a). A lane takes at most the run's fix rounds (3 unless --fix-rounds or pace.fix_rounds says otherwise, ruling DR1); past that it is handed back to the person as unachievable with the last round's findings, and the run starts nothing further for it.

The loop lands its own lanes (loopLanding). Once a lane's reviewers pass, abcd implement step closes the spec, resolves the captures the lane fixed, commits those records with the repository's hooks running and an Assisted-by: naming the model the lane's receipts reported, waits for the preflight receipt, pushes, opens the pull request, arms the merge as the ruleset says, and cleans up only after the commit is on the default branch. abcd implement record shows what the run did and captures its transcripts into the history store.

The drain works issues through the loop (drainLoop). abcd drain hands each open issue that needs no decision to the implement loop, one lane at a time, and hands every other issue back to a person by kind; abcd build <iss-N> starts an issue-keyed lane whose brief is the issue and its remedy. The drain stops at the end of its working window and at --max.

A stale tag no longer lapses a deferral (anchorStale). A checkout that has not fetched the newest release tag no longer reads a record deferred past that tag as lapsed: the record is handed back as "anchor stale", naming the tag and git fetch --tags, with no remote call. The drain's rule is no longer read through a linked decision store.

Integration follow-ups. A handed-back run names the way out: every later step's refusal and build next's exclusion now name the run's directory under the local run tier as the thing to remove once the intent is replanned (fl1). abcd implement record --transcript now carries a transcript's scan gap on the run record, as history capture does, when gitleaks is armed and not installed. reclassify --by adr-N resolves the ADR through the record-id seam, so an id two files claim refuses there too, and the start check's two-file case now asserts that refusal. stale_edge follows supersession chains through the build's own blocked check, so a chain settled by an accepted decision or a discipline says "settled by ...: drop the edge" (rulings CF1, CF2). Drafts itd-22 and itd-33 drop their settled edges on itd-2, so record-lint shows only the three cycle warnings. The reading windows are recalibrated at the tip. Six decision-log entries appear twice because two histories held them in opposite order; an appended entry says so. The reflect command (itd-24) is not in this change: its review returned FIX FIRST.

Resolves: iss-129
Resolves: iss-2608291814575788
Resolves: iss-2609020113012227
Resolves: iss-2609300025372991
Resolves: iss-2609300032219282
Resolves: iss-2609300109005165
Resolves: iss-2609300841407812
Resolves: iss-2609300841466575
Resolves: iss-2609300903497125
Resolves: iss-2609300905174086
Resolves: iss-2609300905225841
Resolves: iss-2609300916451435
Resolves: iss-2609300916459279
Resolves: iss-2609300916465620
Resolves: iss-2609300929557796
Resolves: iss-2609301000153822
Resolves: iss-2609301002043276
Resolves: iss-2609301046433372
Resolves: iss-2609301128211767
Resolves: iss-2609301128253254
Resolves: iss-2609301303434847
Resolves: iss-2609301307566557

Assisted-by: Claude:claude-opus-5-5

itd-4, itd-6, itd-50, itd-65 and itd-66 cite spec ids of the retired
predecessor store without the specs charter's qualifier, and each id is
at or below spc-70, so it resolves in the live store to a spec on
another subject. Each site was read against the live spec it collides
with:

- itd-6: spc-2, spc-4 and spc-5 are the earlier Python lineage's RP MCP
  specs (live: lifecycle automation, the transcript clock, folder
  classification); lines that already said "earlier Python lineage" in
  words now also carry the parenthetical.
- itd-50: spc-52 is the audit-loop spec (live: dangling supersedes).
- itd-65 and itd-66: spc-64 is the gitleaks and pii.py secret/PII gate
  (live: the read-block eval) and spc-27 the oracle (live: the
  surface-coverage registry).
- itd-4: spc-20 to spc-23 are the ledger specs of the superseded record
  system (live: banlist, fresh install, stale-binary warning, tier
  placement).

Two shipped acceptance criteria gain the qualifier (itd-4's drift
criterion and itd-65's fail-closed criterion). Each names a precedent
or an owner, not what is promised, so the qualifier restores the
authored reading and changes no promise: a wording clarification, with
no Audit Notes line.

Refs: iss-2609290448510918
Assisted-by: Claude:claude-opus-5-5
…ecs stand

itd-36's Implementing specs section said the frontmatter spec_id records
spc-38 as the primary delivering spec; the frontmatter records
spc-2609211905174684, and live spc-38 and spc-39 are itd-136's record
explorer and itd-137's relationship chart. itd-4's said its spc-20 to
spc-23 do not exist in the native spec store; the live store holds all
four as other specs. Both sections now name the live spec_id, say the
native store reuses each number, and qualify every predecessor id, the
shape itd-4's own spc-6 catch-up paragraph already had. Neither section
is an acceptance criterion.

The finding is captured in this change and resolved in the next.

Refs: iss-2609300025372991, iss-2609290448510918
Assisted-by: Claude:claude-opus-5-5
…e specs

The fix landed in 542737b: both Implementing specs sections name the
live spec_id and qualify every predecessor-store id.

Resolves: iss-2609300025372991
Assisted-by: Claude:claude-opus-5-5
…intents cite

itd-17, itd-20, itd-27, itd-29, itd-47 and itd-49 sit in superseded/
and describe pre-rebuild work in the predecessor store's terms: the
flow-next checkpoints, the Python oracle and Codex leg, the Ralph quota
work and the grill skill's first spec. Every spc-N they cite at or
below spc-70 was read against the live spec it collides with, and none
names the live one (live spc-3 is the lifeboat coverage experiment,
spc-6 issue capture, spc-12 the disembark grounding, spc-41 the banner),
so each carries the specs charter's qualifier. Where one id repeats
within one line of prose, the first mention carries it.

Left as written: itd-27's reclassification_history reason (a dated
reason keeps the words it was written with) and itd-47's Implementing
specs section, whose false spec_id claim is corrected in its own change.

Refs: iss-2609290448510918
Assisted-by: Claude:claude-opus-5-5
Superseded itd-47's Implementing specs section said its frontmatter
spec_id records spc-27 as the primary delivering spec. The frontmatter
holds spec_id: null, and spc-27 and spc-32 are predecessor-store ids
that the live store reuses for the surface-coverage registry and abcd
update. The section now says the ids are history, that no native spec
delivers the intent (adr-22 supersedes it), and qualifies each id. This
is the same defect as iss-2609300025372991 in a third intent.

The finding is captured in this change and resolved in the next.

Refs: iss-2609300032219282, iss-2609300025372991, iss-2609290448510918
Assisted-by: Claude:claude-opus-5-5
… not hold

The fix landed in 8f5b726: the Implementing specs section of superseded
itd-47 keeps its predecessor-store ids as qualified history and says no
native spec delivers it.

Resolves: iss-2609300032219282
Assisted-by: Claude:claude-opus-5-5
iss-2609290448510918 gains a dated progress section and a remedy line.
The census over the four intent folders counts 186 sites; 94 were the
predecessor store's and carry the qualifier, 72 cite live specs and 15
are data. The five sites in itd-82 and itd-130 were read but not edited,
because other branches carry those intents, so the record stays open
until they are confirmed at the merged tip.

Refs: iss-2609290448510918
Assisted-by: Claude:claude-opus-5-5
Both are used only inside the changelog package, so they leave the
exported surface and the reach-audit baseline. DeriveNext stays
baselined: launch/semver.go cites it by its qualified name, and launch
is being edited elsewhere.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
glossary.RenderIndex and RenderLayout are reached only by the package's
own README anti-drift tests; grounds.ParseToken and
identity.EffectiveCommitter only by their own packages. All four leave
the exported surface and the reach-audit baseline; the fence-writer
reason in mdrecord follows the rename.

glossary.Scan stays baselined (site cites it by qualified name, and site
is being edited elsewhere), as does identity.LoadPin (the ahoy tests
call it across packages).

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
Render (now renderMapping, the table the brief's anti-drift test pins),
Tiers (now allTiers), VerifyManifest and RecordManifestSHA256 are
reached only from inside the lifeboat package. They leave the exported
surface and the reach-audit baseline. ManifestSHA256 stays baselined:
a cli test calls it across packages.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
cite.NewHTTPChecker (now newShippedHTTPChecker, beside the existing
newHTTPChecker it wraps), cite.ParseReceipt and
machineload.ParseLoadavgSysctl are reached only from inside their own
packages. They leave the exported surface and the reach-audit baseline.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
BuildCitation, CountSourceTokens, CoverageIndexPath, DetectLicence,
NormaliseSourceText, QueryPages, RenderCitedMatches, RenderNoMatches,
ResolveDistilledPages, SourceContentHash and ValidateDistilledPage are
reached only from inside the memory package, and none is named by
writer.go. They leave the exported surface and the reach-audit
baseline.

The ten names writer.go calls (Dir, IsMemoryPageName, LoadRegistry,
ParsePageFilename, RenderContradictions, RenderIndex, SerializeRegistry,
SourceClasses, SourceHashes, SourcesIndexPath), MergeIngest (writer.go
names it in a comment) and writer.go's own two stay baselined: that file
is reserved to another lane.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
Only the external mode tests read the question marker through
QuestionOpen; no production path calls it. It becomes
QuestionOpenForTest, the audit's convention for a test seam, and leaves
the reach-audit baseline. SetAt stays baselined: cli tests call it
across packages.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
Admits, AssemblingPositions, DefinitionPath, DeriveCandidateRun,
EncodeBundle, EncodeManifest, ExclusionsFor, Kinds (now allKinds),
LoadDefinitions, ManifestHash, PresetFor, PresetWindow, Render (now
renderCharter, the charter table), Scans (now allScans) and WideningRuns
are reached only from inside the reading package, its tests included.
They leave the exported surface and the reach-audit baseline; no
rendered output changes, so the include-table digest holds.

AssemblerVersion, DecodeManifest and LoadDefinition stay baselined: cli
tests call them across packages.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
ParseColor, ContrastRGB, Contrast, FormatRatio, Render (now renderRow)
and Validate (now validateSettings) are reached only from inside the
statusline package. They leave the exported surface and the
reach-audit baseline. LoadFrom stays baselined: an ahoy test calls it
across packages.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
Pin (now summaryPin) and Read (now readEntries) are reached only from
inside the reviews package; the board reaches them through Staleness.
They leave the exported surface and the reach-audit baseline, and the
reviews-charter scripts' comments that cite them by path follow the
rename.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
…nctions

readingitem.LocateDisposition and LocateSurprise, scribe.DecodeManifest
and Exclusions, source.Load (now loadCorpus), and spec.RenderSteps and
SortByNumber are reached only from inside their own packages. They
leave the exported surface and the reach-audit baseline.

scribe.AllowList stays baselined (a lint test calls it across
packages), as does spec.Validate (spec/store.go calls it and lint cites
it by qualified name).

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
The record stays open. Its progress section names the 55 names this
lane paid (54 unexported, 1 renamed to a declared test seam, none
deleted), the 28 names left in its packages and why, and the one
candidate for a later wiring lane; the deferral reason carries the new
count of 140.

Refs: iss-2609252211487887
Assisted-by: Claude:claude-opus-5-5
…ss exec

Found while consolidating the flock sites: a re-exec test whose
WithFileLock holder starts a child and is killed finds the lock still held
for its whole budget while the child lives.

Refs: iss-2609300109005165
Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
openLockFd opened the lock file with a raw syscall.Open and no O_CLOEXEC,
so every child a holder started inherited the descriptor. flock holds until
every descriptor on the open file description closes, so a child that
outlived its parent kept the lock: the next writer waited out its whole
budget and got contention from a process that no longer existed.

The new re-exec tests take the lock in a child process: another process is
excluded for its budget and then granted the lock, and a holder killed
with or without a still-running grandchild frees it. The grandchild case
failed before this change (contention after 2s) and passes after it.

Refs: iss-2609300109005165

Assisted-by: Claude:claude-opus-5-5
Three record stores (decide, intent, spec) lock their own directory
descriptor so no lock artefact sits in the committed tree, and each ran a
hand-rolled flock loop to do it, because WithFileLock takes a lock file
and cannot lock a directory. WithDirLock is that primitive beside it, on
the same contract: the shared acquireFlock poll, ErrLockContention past the
caller's budget naming it, fn's error unwrapped. The directory is opened
O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC and never created: a symlink or a
non-directory is ErrLockPathUnsafe (told apart by Lstat, since the errno
differs between kernels), and an absent directory is the open's own
not-exist error, which spec's store-must-exist mode depends on.

The re-exec tests now run both primitives: exclusion between two
processes, release when the holder dies (with and without a grandchild),
and the bounded wait with its error.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
The ADR store's mint lock ran its own flock loop on the store directory.
It now takes fsutil.WithDirLock on the same directory, so an older binary
flocking that directory by hand and this one still exclude each other.
The contention refusal keeps its words ("decide: could not acquire mint
lock within <budget>") and fn's error passes through unchanged; the wait
polls on fsutil's backoff (5ms doubling to 100ms) instead of a flat 10ms,
inside the same 5s budget.

A new test holds the store with a raw flock, as an older binary does, and
pins the refusal text and the grant after release; it passed on the old
loop before the move and passes after it.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
The spec store's lock ran its own flock loop on specs/. It now takes
fsutil.WithDirLock on the same directory, so an older binary flocking
specs/ by hand and this one still exclude each other. An absent store in
storeMustExist mode is still errStoreAbsent (the open's ENOENT, which the
primitive returns unwrapped), the busy refusal is still ErrStoreLockBusy
"within <budget>", and fn's error passes through unchanged. The wait polls
on fsutil's backoff instead of a flat 10ms, inside the same budget.

A new test holds specs/ with a raw flock, as an older binary does, and
pins the refusal text and the grant after release; it passed on the old
loop before the move and passes after it.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
The intent store's mint lock ran its own flock loop on intents/. It now
takes fsutil.WithDirLock on the same directory, so an older binary
flocking intents/ by hand and this one still exclude each other. The busy
refusal is still errIntentLockBusy "within <budget>" and fn's error passes
through unchanged.

The busy seam the lock tests observe a blocked writer through
(onIntentMintLockBusy) is kept without a seam in fsutil: the lock takes one
attempt that does not wait, fires the seam on its refusal, then waits the
whole budget. The seam fires once per acquisition rather than once per
poll; its only user closes a channel once. The wait polls on fsutil's
backoff rather than a flat 10ms, and the pair acquisition's rest note says
so: 2 x fsutil.LockPollCeiling is now the bound for every waiter on all
three record-store locks, not only the ledger's.

A new test holds intents/ with a raw flock, as an older binary does, and
pins the refusal text and the grant after release; it passed on the old
loop before the move and passes after it.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
The memory store's lock opened .abcd/memory/.lock and flocked it by hand,
non-blocking. It now takes fsutil.WithFileLock on the same path with no
wait, so an older binary flocking that file by hand and this one still
exclude each other, and a held lock still fails closed at once as a bare
*StoreLockHeldError. The path pre-check keeps its words; a descriptor the
primitive refuses (a symlink or non-regular file judged on the fd) maps to
the same *UnsafeStorePathError, and fn's error passes through unchanged.

Two race-only branches change shape: a lock file unlinked between the open
and the lock was refused ("zero links") and is now reopened by the
primitive's inode revalidation, and the fd-level type refusal reads as the
path pre-check does.

lockModeIsRegular goes with the hand-rolled check; its unit test becomes a
behavioural one (a FIFO at the lock path is refused), and fsutil gains the
same pin on the descriptor check that now carries iss-2608261133210491's
S_IFMT mask. A new test holds the lock file with a raw flock, as an older
binary does; it passed on the old code before the move and passes after.

Refs: iss-129
Refs: iss-2608261133210491

Assisted-by: Claude:claude-opus-5-5
The per-repo records lock opened records/.lock and flocked it blocking,
with no timeout, handing back a release func. It now takes
fsutil.WithFileLock on the same path, so an older binary flocking that
file by hand and this one still exclude each other, and Capture and
Migrate run their locked work in a closure (captureLocked,
migrateLocked, moved unchanged).

Behaviour change: the wait is bounded. A capture or migration behind a
holder that never lets go used to hang, and with it the session-end hook
that runs a capture; past repoLockTimeout (2 minutes, since the holder
redacts a whole transcript of up to 64 MiB under the lock) it now fails
with fsutil.ErrLockContention, "history: acquire lock <path>: ...", and
writes nothing. A lock path the primitive refuses (a symlink, or not a
regular file on the descriptor) keeps its *StorePathError and words.

TestCaptureGivesUpOnAHeldRecordsLockWithinItsBudget holds the lock with a
raw flock and watched Capture still waiting after 10s against a 300ms
budget before the change; after it, Capture gives up within the budget
and captures once the lock is released.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
With the five hand-rolled flock sites moved onto fsutil.WithFileLock and
fsutil.WithDirLock, a test parses every non-test Go file in the module and
fails on any Flock or FcntlFlock selector outside internal/fsutil, naming
each file:line, so a sixth loop cannot appear. Test files stay free to
flock directly: that is how they stand in for another process or an older
binary holding the lock. Before the moves it listed nine lines across the
five sites (decide, history, intent, memory, spec); it passes now.

Refs: iss-129

Assisted-by: Claude:claude-opus-5-5
Resolves: iss-2609300109005165

Assisted-by: Claude:claude-opus-5-5
…rimitives

Impact fix rather than internal: history's records lock used to wait with
no end and now gives up after repoLockTimeout (2 minutes) with
fsutil.ErrLockContention; every other site keeps its refusal, its budget
and its lock path.

Resolves: iss-129

Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
Lifts the skip integ24b2 left on TestStartBlockedRowRefusesADecisionIdTwoFilesClaim (follow-up ai1). With adrIdUnique landed, recordid.LookupOne refuses an id two decision files claim with *recordid.AmbiguousIDError, so the start check carries that refusal out instead of settling the edge on whichever copy sorts first. The test asserts the typed refusal naming adr-37 and both files, and that no settled row comes back.

Assisted-by: Claude:claude-opus-5-5
Follow-up ta1 (review-ttyAdr, review-adrIdUnique). reclassify's resolveSuccessor listed the decision store root itself and wrote into the first file whose name carried the id, so an ADR id two files claim was settled first-wins on the write path while every reader refuses it. It now resolves the id through recordid.LookupOne, which refuses an ambiguous id naming each claimant; nothing is written. TestReclassifySupersededByAnADRIdTwoFilesClaimRefuses was watched fail (the reclassify succeeded) before the change.

Assisted-by: Claude:claude-opus-5-5
Semantic conflict (le1): cfSettled on main changed followBlocker to (repoRoot, corpus, id) (blockerEnd, error), which lintEdges' intent.SupersessionChainOf called with the old shape, so the build broke. SupersessionChainOf now takes (repoRoot string, links []ChainLink, id string) and returns (chain, endID, endBucket, problem string, err error), calling followBlocker(repoRoot, corpus, id); lint.SetSupersessionChain, stale_edge's call (which propagates err) and the test stub follow it. A chain that ends at an accepted decision (CF1) or a discipline (CF2) is settled, so stale_edge says 'is settled by adr-N (accepted)/itd-N (disciplines): drop the edge' instead of inviting a repoint; TestStaleEdgeSaysASettledChainIsSettled was watched fail on a scratch copy with the old wording.

Assisted-by: Claude:claude-opus-5-5
Follow-up rd1. Draft itd-22's blocked_by itd-2 and draft itd-33's builds_on itd-2 named in-session subagent dispatch, superseded by itd-2609201916056194, which does not carry that dispatch contract; the dispatch is standing practice the conventions router states. Read under ruling CF2 of 2026-09-30 (decision logged by orchestrator abcd-b3), both edges are settled and dropped, each with a linkage note under the record's Audit Notes. record-lint's stale_edge rule now reports nothing; the three edge_cycle warnings remain.

Assisted-by: Claude:claude-opus-5-5
Conflict: internal/reachaudit/testdata/core-unreached.txt. Kept both sides' deletions (main's three: guard.Defaults, intent.WithMintLock, oracle.BundledDenylist; the branch's 55); neither side added a name. 137 names remain and TestEveryExportedCoreFunctionIsReachedOrBaselined passes.

Assisted-by: Claude:claude-opus-5-5
…ap ids

Follow-up cr1 (review-citeReach). The qualifier sweep labelled itd-29's two predecessor spec ids '(predecessor store)', but the record itself says they are speculative substrate from the legacy roadmap and were never in a store; the three citations now say 'legacy roadmap'.

Assisted-by: Claude:claude-opus-5-5
Follow-up rp1 (review-rootPin). supersededRoot's hardening, an executable path the lookup cannot read says nothing and walks nothing from the working directory, had only the reviewer's scratch probe. TestVersionSaysNothingWhenTheExecutableIsUnknown commits it: with the working directory inside a differently named plugin root, an erroring lookup yields no note, whether it returns no path or a relative one beside its error. The relative case was watched fail on a scratch copy with executablePath's error check removed; the empty case resolves to '.' and walks nothing either way, so it pins rather than discriminates.

Assisted-by: Claude:claude-opus-5-5
Carries fidelityOnce.

Assisted-by: Claude:claude-opus-5-5
Conflict: internal/surface/cli/history.go, two new functions at one spot; kept both, gitleaksAug's scanGapLines and loopLanding's captureTranscriptSource. The capture verb calls the extracted helper and still renders the scan gap.

Assisted-by: Claude:claude-opus-5-5
Conflicts:
- internal/core/decide/decide.go: main (9804500) and the branch (675519d) fixed the same stated-ADR delimiter search with different helpers; main's renderKeys/renderCloseAndTitle shape is kept in all four hunks, and nothing else names the branch's renderHead.
- internal/core/implement/loop/land.go: kept loopLanding's records commit (hooks on, the implementer's model named, never Assisted-by: None) and the branch's issue-lane description of what landed.
Semantic conflicts:
- loop.go StatusPeers (main, CC1) called the old peersCheck(r, session, snap); the branch generalised it to (id, bucket, session, snap), so it now passes r.IntentID and r.Bucket.
- issue_test.go: the issue lane's receipt reported a placeholder model loopLanding's trailer rule refuses; it now reports claude-test-5, and the test asserts the records commit names it.

Assisted-by: Claude:claude-opus-5-5
…now needs

Semantic conflict between gitleaksAug (978a00c) and remedyRequired on main: the three augmenter capture tests filed an issue with no remedy, which main's capture refuses, so each failed before reaching the scan it tests. The shared fixture now names a remedy. The merge itself was clean; the failure surfaced in the capture package's run after drainLoop.

Assisted-by: Claude:claude-opus-5-5
Conflict: commands/drain.md, the --json field paragraph. Kept the fix round's wording (a deferral past anchor stays live until the newer tag is fetched, which supersedes 76f3320's 'has lapsed') and drainLoop's new title field in dispositions.

Assisted-by: Claude:claude-opus-5-5
Follow-up fl1 (review-fixLoop item 4). A handed-back run stays in progress by construction until itd-50's drafts/ move lands terminal liveness, and no verb clears it; yet every later step's refusal named no way past it, and build next excluded the intent with 'resume it with abcd implement step', a step that refuses. Both now name the run's directory, .abcd/.work.local/run/<run-id>, as the thing to remove once the intent is replanned, and the pick's exclusion says the run handed the intent back rather than inviting a resume. commands/implement.md and commands/build.md say the same. TestALaneThatExhaustsItsFixRoundsIsHandedBack was watched fail on both assertions before the change.

Refs: iss-2609301303434847

Assisted-by: Claude:claude-opus-5-5
… out

Resolves: iss-2609301303434847

Assisted-by: Claude:claude-opus-5-5
Follow-up cr1 (review-citeReach). iss-2609252211487887's deferral reason counted the 140 names drainReach's sort left on its own branch; merged beside main's and the other lanes' removals, the baseline holds 136, and TestEveryExportedCoreFunctionIsReachedOrBaselined passes on it.

Refs: iss-2609252211487887

Assisted-by: Claude:claude-opus-5-5
Integration interaction between gitleaksAug and loopLanding. implement record --transcript stores each transcript through the history capture, but the record's transcript entry dropped the capture's scan gap, so a transcript stored with the native scanner alone, in a repository that armed gitleaks where gitleaks is not installed, was not disclosed there while history capture names it. The entry now carries scan_gap (home-redacted, omitted when empty), and the text record renders it with the same scanGapLines history capture uses. commands/implement.md and the implement surface chapter say so. TestImplementRecordRendersATranscriptsScanGap was watched fail with the field present and unwired.

Refs: iss-2609301307566557

Assisted-by: Claude:claude-opus-5-5
…pts' scan gap

Resolves: iss-2609301307566557

Assisted-by: Claude:claude-opus-5-5
Measured on a clean clone of cb46dbe (the 24b-3 merges and the follow-ups): widening 1,463,871 estimated tokens, window 1,460,000 -> 1,480,000; detection 1,472,907, 1,470,000 -> 1,490,000; entailment 418,715, 420,000 -> 430,000 (0.31% headroom, under the 1% rule).

Refs: iss-2609251455354719

Assisted-by: Claude:claude-opus-5-5
…n gap

f41a1a7's sentence spelled the history verb in code form above the generated appendix, which TestSurfaceChapterProseStatesNoShape refuses; the prose now names the verb in words.

Refs: iss-2609301307566557

Assisted-by: Claude:claude-opus-5-5
Joining main with the implement-loop lanes, which held six itd-111 entries in the opposite order around the BU1/BT1 entry, leaves those six repeated after BU1: keeping main's copy alone would drop the lanes' order (DA002), and repeating BU1 instead exceeds the merge base's bound (DA003). The appended entry names the six and says the first copy is the record.

Assisted-by: Claude:claude-opus-5-5
@REPPL
REPPL enabled auto-merge September 30, 2026 13:51
@REPPL
REPPL added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 4065dc7 Sep 30, 2026
14 checks passed
@REPPL
REPPL deleted the integ/land-24b3 branch September 30, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant