feat: ground open-questions on a repo's in-code work markers (itd-95)#124
Merged
REPPL merged 9 commits intoJul 21, 2026
Merged
Conversation
The minted spc-10 stub carried no design record, so `abcd intent ready itd-95` failed its spec_body check and the intent could not be implemented. Write the spec body: the bounded WalkFiles primitive over root.FS(), the conventions-tier marker adapter, and a decided answer for every one of itd-95's open questions (marker set, tier, scan scope, caps, output shape, dedup, status ceiling, native-tier interaction). impact: additive on the intent records the change class on the intent itself rather than in CHANGELOG.md, so the entry is derived from the intent corpus instead of hand-maintained. Assisted-by: Claude:claude-opus-4-8
evidence/open-questions was grounded by exactly one adapter, the native one, so every repository without an .abcd/ record fell through to a blank even when its source was dense with TODO and FIXME. The mapping row already named those markers as a read and predicted a partial conventions status; nothing delivered it. Add SourceContext.WalkFiles, the bounded recursive walk the probe lacked, mirroring embark.go's fs.WalkDir over root.FS() rather than inventing a second traversal idiom. It is contained by os.Root, skips symlinks instead of following them (a probed foreign tree carries ordinary symlinks, unlike a packed lifeboat where one is a trust violation), skips VCS/dependency/generated trees, and reports truncation at its file cap. Add convOpenQuestionsSource on top of it: a marker scan citing path:line for TODO, FIXME, XXX, HACK and BUG. Its ceiling is StatusPartial by construction — a marker says something is unfinished, not what the question is — with volume moving the confidence. The scan is bounded on three dimensions, not two: the file cap and the per-file cap multiply to 195 GiB, so an aggregate read budget bounds the product the way maxEmbarkTotalBytes does on the embark side. Every bound that fires is named in the evidence, and a blank drawn from a scan that was cut short says the scan was cut short rather than generalising over files it never opened. Captured, not fixed: iss-110 (the skip set knows only Node and Go), iss-111 (bare-word matches prose about markers), iss-112 (fs.WalkDir's per-directory ReadDir is unbounded). Assisted-by: Claude:claude-opus-4-8
The minted spc-11 stub carried no design record, so `abcd intent ready itd-96` failed its spec_body check. Write the spec body, resolving every one of itd-96's open questions: a dedicated naming document is naming's primary source with the glossary as an explicitly-qualified fallback, the preference order for architecture docs, the package-layout scan on top of spc-10's WalkFiles, and the thresholds separating the confidence levels. Both new sections ceiling at partial, matching what the mapping row already predicts for the conventions tier, so the strength of the signal is carried by confidence rather than by inflating the status. That keeps mapping.go — the brief-to-lifeboat contract — unedited. impact: additive records the change class on the intent rather than in CHANGELOG.md. Assisted-by: Claude:claude-opus-4-8
constraints/naming and internals were each grounded by exactly one adapter, both native, so a repository with a NAMING.md and an ARCHITECTURE.md sitting in plain sight still reported both sections blank for want of an .abcd/ record. convNamingSource reads a dedicated naming document first and falls back to the glossary, cited with an explicit qualifier at lower confidence: a glossary defines terms rather than ruling on what may be renamed, so it is real but weaker evidence. The glossary section keeps its own adapter and its own row; naming is visibly a derived reading, never a duplicate. convInternalsSource combines architecture prose, measured with the README prose threshold, with a package-layout scan over WalkFiles. Both sections ceiling at partial, matching what the mapping row already predicts for this tier, so the strength of the signal lives in the confidence rather than in an inflated status. Also harden the walk itself, which the security review showed the tier gate now makes reachable from a source tree carrying no other conventional file. Its cap counted regular files only, so a tree of directories holding nothing regular never reached it; and every directory is resolved from the containment root one component at a time, so a chain of directories costs the square of its depth. A depth-1500 tree took over two minutes and now takes 0.4s. Directories are counted against the same cap and descent is capped at maxWalkDepth, both reported through the existing truncated flag. A layout scan cut short by a cap no longer reports as a complete one: the truncation note is no longer conditional on having found a package, and a blank drawn from a partial walk says the walk was partial. Captured, not fixed: iss-113 (the marker adapter's truncation wording names only the file cap), iss-114 (os.Root path re-resolution is the quadratic underneath the depth cap). Assisted-by: Claude:claude-opus-4-8
An independent security review of the itd-96 branch found the walk this spec landed can be made to run without terminating on a hostile tree, which is exactly the population disembark exists to serve: an abandoned repository nobody vouches for. Two dimensions were unbounded. The cap counted regular files only, so a tree of directories holding nothing regular never reached it. And every directory is opened by resolving its path from the containment root one component at a time, so a chain of directories costs the square of its depth to walk — a depth-1500 chain, trivial to create, did not finish in two minutes and now takes 0.4s. Directories are now counted against the same cap, and descent is capped at maxWalkDepth. The depth cap prunes the chain rather than the tree, so what sits above it is still walked. Both report through the existing truncated flag, so every loud-staging path already in place names them, and the marker adapter's truncation wording now names all three bounds instead of the file cap alone. Captured: iss-114 (os.Root path re-resolution is the quadratic underneath the depth cap; per-directory OpenRoot would make each open constant-time). Assisted-by: Claude:claude-opus-4-8
The bound-the-walk fix the security review forced belongs with the WalkFiles primitive, so it landed on the parent branch. Merging it back leaves this branch's diff as the itd-96 adapters alone.
The marker adapter's truncation evidence named only the file cap; it now names all three bounds the walk enforces, fixed on the itd-95 branch alongside the change that made the wording imprecise. Assisted-by: Claude:claude-opus-4-8
Two decisions from the itd-95/itd-96 work that a future session would otherwise re-litigate: that the mapping table's per-tier columns are a ceiling adapters honour rather than a loose prediction, and that a walk over a foreign tree must name which of entries, depth, and aggregate bytes bounds it — a per-item cap and a count cap multiply, and their product is not a bound. Assisted-by: Claude:claude-opus-4-8
feat: ground naming and internals from a repo's conventional docs (itd-96)
REPPL
added a commit
that referenced
this pull request
Jul 21, 2026
Both this programme and the derived-versioning programme minted spc-10 and spc-11 on the same day, from branches cut off the same base. abcd mints the next free id from the local tree, so concurrent branches always mint the same number and neither branch can see the collision. It surfaces only on merge, and it breaks the bidirectional intent-spec link in both directions at once: whichever spec wins the store lookup leaves the other intent's link dangling, so `abcd intent ready` failed for itd-95 and itd-67 simultaneously on the merged tree. The derived-versioning programme's ids are already on main, so this programme renumbers: spc-10 -> spc-12 (itd-95), spc-11 -> spc-13 (itd-96), with both intents' spec_id and every reference in the record updated to match. The commit messages and PR bodies of #124 and #126 still name the old ids; that history is accurate as of when it was written and is left alone. Captured iss-115: specs need the uniqueness lint issues already have, in record-lint so CI catches a collision on the PR rather than a human catching it at merge. Assisted-by: Claude:claude-opus-4-8
REPPL
added a commit
that referenced
this pull request
Jul 21, 2026
Same collision as the specs, one directory over: main captured its own iss-110 while this branch was open, and abcd mints the next free issue id from the local tree, so both branches took 110. The difference is that issues already have the uniqueness lint iss-74 added, so record-lint caught this at the push gate instead of letting a human find it at merge — which is exactly the guard iss-115 asks for on the spec side. Main's iss-110 is already merged, so this branch renumbers. Only the id and the filename change; the finding is untouched. The commit message and PR body of #124 still name iss-110. Assisted-by: Claude:claude-opus-4-8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Grounds
evidence/open-questionsat the conventions tier on the work markers a team left in its source, and lands the bounded recursive-walk primitive the probe was missing.Resolves ledger issue iss-99 (
.abcd/work/issues/open/) — this repository tracks issues in the record, not on GitHub, so there is no GitHub issue to auto-close.Why
evidence/open-questionshad exactly one adapter,nativeOpenQuestionsSource. On any repository without an.abcd/record — the common case for a rescue — the section fell straight through to a blank, even when the code was dense withTODOandFIXME. The mapping row already named those markers as a read and predicted a partial conventions status; nothing delivered it. adr-36 makes that a blank abcd can close with a better adapter, not a question for a human.What
SourceContext.WalkFiles(rel) (paths, truncated)— the bounded recursive walkSourceContextlacked (it exposed only a non-recursiveListDir). It mirrorsembark.go'sfs.WalkDir(root.FS(), …)rather than inventing a second traversal idiom: contained byos.Root, non-regular files skipped, VCS/dependency/generated trees skipped by name at any depth, capped atmaxWalkFiles, sorted repo-relative POSIX paths.Symlinks are skipped, not fatal — deliberately unlike embark, where a symlink inside a packed lifeboat is a trust violation. A probed foreign tree carries ordinary symlinks; refusing them would refuse most real repositories. The doc comment carries that reasoning.
convOpenQuestionsSource— a sibling ofconvGlossarySourcecitingpath:line (MARKER)forTODO,FIXME,XXX,HACK,BUG. Its ceiling isStatusPartialby construction: a marker says something is unfinished, not what the question is, so markers are a thread to pull rather than a framed set. Volume moves the confidence instead.mapping.gois unchanged — the row'spartialprediction is honoured as a ceiling.Bounds, and the honesty around them
The scan is bounded on three dimensions, not two.
maxWalkFiles×maxProbeReadBytesmultiply to ~195 GiB with nothing bounding the product — the security review measured a tree costing ~600 MB on disk extrapolating to hours of blocked CPU. An aggregate read budget now bounds it, the waymaxEmbarkTotalBytesalready does on the embark side, charged before the binary test so a tree of blobs spends it exactly as a tree of source does.Every bound that fires is named in the cited evidence, and a blank drawn from a scan that was cut short says so rather than generalising "no markers" over files it never opened. A blank is a first-class result (adr-35) only while it is trustworthy.
Review
Built detector-first, then reviewed by two independent agents (ruthless + security). Both returned FIX-FIRST; all three fix-first findings are dispositioned in the diff:
XXX-XXX-XXXredaction shape that its own comment claimed it rejected — a support phone number became an open question. Verified by execution, fixed by excluding the hyphen from the leading class, pinned byTestConvMarkerRePinsTheRecognisedSpellings.impact: additiveon the intent instead, so the entry derives from the intent corpus rather than being hand-maintained.Three "consider" findings were captured rather than fixed, since each is spec-conformant and a design revisit: iss-110 (the skip set knows only Node and Go, so a Python
.venvis cited as the team's own markers and can eat the walk cap), iss-111 (the bare-word alternative matches prose about markers — 42 such hits on this repository), iss-112 (fs.WalkDir's per-directoryReadDiris unbounded, so the file cap does not bound walk memory as its comment claims).Verification
Detectors watched fail before each change.
gofmt -l .empty,go build ./...,go vet ./...,go test ./...,go test -race ./internal/core/lifeboat/,abcd docs lint— all green.abcd intent ready itd-95passes all four checks.Read-only over the source is asserted byte-for-byte: hash every file in a fixture, run the full
Probe(), re-hash, assert the map is identical.Stacking
Base is
feat/itd-95-96-conventions-adapters(the planning commit). itd-96's two adapters stack on this branch and reuseWalkFiles.