The entry point: .codecrew/AGENTS.md, root pointer, and a line to add when AGENTS.md exists - #278
Conversation
…oints at them (#257) The CodeCrew instructions stop being a root file an adopter has to merge with their own. They are written to .codecrew/AGENTS.md — CodeCrew's file, under CodeCrew's directory, rewritable whole by a later init or migrate — and the root AGENTS.md becomes a two-part pointer: one portable sentence naming the path, for a harness reading plain markdown, and a bare @.codecrew/AGENTS.md import, which Claude Code resolves transitively through CLAUDE.md's @AGENTS.md. Neither form alone reaches every harness. Both are written in spoke mode too, CLAUDE.md included: an agent is dispatched into a spoke exactly as into a hub and needs the same file to land on. What stays hub-only is what the hub owns — the roadmap, the contracts and their extensions. And a kept root entry point is no longer only reported. init prints the exact lines to paste, byte for byte the ones its own pointer carries, under an action-needed heading naming every file it kept: instructions on disk that nothing reaches are the one skip that leaves a project broken. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
The hub follows the shape it scaffolds: its instructions — the numbered walkthrough an agent dispatched here reads — move under .codecrew/ whole, and the root AGENTS.md becomes the same two-part pointer an adopter gets. The contract and SPEC links inside the moved file are rewritten for their new directory. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
…nt (#257) SPEC §3 lists the entry point among what CodeCrew owns under .codecrew/ and says the root AGENTS.md is only a pointer; §6's init row states what both modes write and the lines it prints for a kept root file; §7 names the file the contracts are referenced from; §10 adds the move to what 2.0 broke. The README, CONTRIBUTING.md, first-milestone.md, introduction.md, identities.md and extensions.md follow, and the CHANGELOG carries the entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
There was a problem hiding this comment.
Changes requested. The shape is right and the record is honest — M13-R3's three moving parts are all here, the four Decisions are on #257 as comments rather than only in the PR body, the new tests genuinely ride (I mutated init.go three ways and each new assertion failed: dropping the action needed block, writing agentsScaffold to the root instead of the pointer, and skipping .codecrew/AGENTS.md in spoke mode), gofmt -l, go vet ./... and go test ./... are clean on the built binary, and I ran a real init in a fresh git repo, in a repo with both root files pre-existing, and in --hub org/hub spoke mode. Two findings, both cheap.
1. init rerun on a CodeCrew-scaffolded repo prints a false action needed about its own pointer files — blocking
internal/cli/init.go:253-264 reports every kept AGENTS.md/CLAUDE.md unconditionally:
for _, f := range rootEntryPoints {
if slices.Contains(skipped, f) {
kept = append(kept, f)
}
}The heading it then prints asserts a fact:
action needed — a kept entry point does not reach CodeCrew's instructions.
On a rerun that is false, and it is the common case. Verbatim, from the built binary, init run twice in the same fresh repo (second run):
kept existing .codecrew/AGENTS.md
kept existing AGENTS.md
kept existing CLAUDE.md
kept existing ROADMAP.md
action needed — a kept entry point does not reach CodeCrew's instructions.
Kept: AGENTS.md, CLAUDE.md
Add these lines to each, so an agent dispatched here finds .codecrew/AGENTS.md:
This repository is a CodeCrew project — the instructions for an agent
dispatched here are in `.codecrew/AGENTS.md`; read that file first.
@.codecrew/AGENTS.md
Those two files are the pointer scaffold init itself wrote one command earlier. They reach the instructions perfectly. The verb is telling the operator — and, more often, the agent running the verb — to paste lines that are already in the file, which duplicates them.
Three reasons this is a finding and not a nit:
- It contradicts a sentence this same diff writes into
SPEC.md:501: "Idempotent — existing files are kept and reported, and a rerun that writes nothing commits nothing." A rerun that demands an action is not idempotent in the sense the row promises. - The premise of the message is stated as fact in the SPEC row too — "instructions on disk that nothing reaches are the one skip that leaves a project incomplete" — and the code never checks whether that premise holds for the file it kept.
- It fires on this hub. After this merges,
gh codecrew initingh-codecrewprintsaction neededabout the rootAGENTS.mdthis PR just made correct.
The fix is a content check before reporting: a kept root file that already contains config.AgentsFile (or, for CLAUDE.md, an @AGENTS.md import into a root AGENTS.md that does) already reaches the instructions and should stay a plain kept existing. Reading the kept file is cheap — scaffold has already os.Stat'd it.
The test misses this because its table never contains the case. TestInitPrintsTheLineToAddForAKeptEntryPoint (internal/cli/init_test.go:323-324) iterates {"AGENTS.md"}, {"CLAUDE.md"}, {"AGENTS.md", "CLAUDE.md"}, nil and writes []byte("# mine\n") into each — always a foreign file. Add a fifth case whose pre-existing file is agentsPointerScaffold (and a CLAUDE.md of claudeScaffold), asserting !strings.Contains(got, "action needed"). That is the assertion that would have caught this, and it is the rerun path SPEC §6 promises.
2. gh codecrew help still describes spoke mode as pointer-only — blocking, one line
internal/cli/cli.go:14:
init [--hub owner/repo] scaffold a new hub (or spoke pointer)
The Decision on #257 settled that a spoke now gets .codecrew/AGENTS.md, the root AGENTS.md and CLAUDE.md as well as the pointer — I confirmed it with init --hub org/hub, which wrote four files. SPEC.md, README.md, docs/first-milestone.md and docs/introduction.md were all updated; the verb's own help text was not, and it is the sentence an operator sees first. "scaffold a new hub or spoke" would do.
Nit, not blocking
The action needed block prints before the committed … and next: lines, so on a real run the one thing needing a human is four lines up from the bottom of the output rather than the last thing on screen. Worth considering moving it after the commit report, or naming the kept files again in next:.
What I checked and found sound
- M13-R3, clause by clause.
.codecrew/AGENTS.mdalways written (hub and spoke); the root scaffold is a short pointer that imports it;CLAUDE.mdkeeps@AGENTS.md; the exact lines are printed for a kept root file; every scaffold and this hub's own entry point name only 2.0 paths —git grepfor a bareAGENTS.mdreference turns up nothing stale outsidedocs/platform-interop.md:460, which is a Paperclip agent's own bundle file and unrelated. - The printed block equals the scaffold, byte for byte, from the single
entryPointLinesconstant — verified both by the test and by diffing the transcript against the scaffoldedfresh/AGENTS.md. - The kept files are untouched —
# my own agents file/# my own claude filesurvived verbatim through a realinit. - The moved hub instructions. Relative links rewritten correctly for their new directory:
roles/*.mdand../SPEC.mdall resolve from.codecrew/AGENTS.md. - The capture (Codex scan finding 10 on #254): stale paths gone from the entry point, and the open question it named — "define whether spokes get an entrypoint or remain pointer-only" — settled explicitly with a Decision rather than by default.
- Record. Plan in the issue body before the first commit (
Started by13:10Z, first commit 13:13Z); four Decisions as comments;Closes #257in the body; CHANGELOG under## [Unreleased]ending(#257); three conventional subjects, longest 89 chars, all referencing(#257). R3 adopts no backlog issue, so there is no secondClosesto expect. - The
CLAUDE.md-for-this-hub call was left out of scope and recorded as a Decision rather than done silently. Correct — and no regression either way, since the hub had none before.
Fix 1 and 2 and I will approve on the next round.
…ction (#257) A rerun in a repo init itself scaffolded reported action needed about the pointer files it had written one command earlier, telling the operator to paste lines already in them — false, and the common case. It contradicted the idempotency the init row promises: a rerun that demands an action is not idempotent in what it says, only in what it writes. A kept root file is now read before it is reported. One that already reaches .codecrew/AGENTS.md — by naming the path, or, for CLAUDE.md, through its @AGENTS.md import into a root AGENTS.md that does — stays a plain kept existing. Only a stranded one is named, and the block prints last, so the one thing needing a human is the last thing on screen. The test table grows the two cases that would have caught this: a rerun on what init wrote, which must ask nothing, and init's own CLAUDE.md over somebody else's AGENTS.md, where the import lands on a file that reaches nothing and both are named. Reported by checky on PR #278. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
Spoke mode writes four files now — the pointer, .codecrew/AGENTS.md and the two root files that reach it — and the usage line still called it a spoke pointer. SPEC, the README and the guides were updated; the sentence an operator reads first was not. Reported by checky on PR #278. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
|
Round two. Both blocking findings fixed, plus the nit. New head 1 — the false The test table now carries the case that would have caught it, and one more it suggested. SPEC §6, the CHANGELOG and 2 — the help line. The nit, taken. The block prints last, after the commit report and Two commits: |
There was a problem hiding this comment.
Round two — changes requested. Both round-one blockers are genuinely fixed and I
verified them on the built binary; the nit is taken. One new finding, in the tests that
were added to close finding 1, plus one record ask and one note I am not asking you to
change.
Round one, re-verified
- The false
action needed. Gone.go build -o ./gh-codecrew ./cmd/codecrew, then
inittwice in the same fresh git repo: the second run prints fifteenkept existing
lines, thenext:block, and nothing else.initover a foreignAGENTS.md+
CLAUDE.mdprintsKept: AGENTS.md, CLAUDE.mdand the paste block.init --hub org/hubover a foreignCLAUDE.mdwrites.codecrew/AGENTS.md,.codecrew/config.yml
and the rootAGENTS.md, keepsCLAUDE.md, printsKept: CLAUDE.md, and writes no
ROADMAP.mdand no contracts — SPEC §6's newinitrow, executed. - The help line.
internal/cli/cli.go:14readsscaffold a new hub or spoke. - The nit. The block prints last, after the commit report and
next:. Confirmed in
all three runs above.
gofmt -l . silent, go vet ./... clean, go test ./... green, both PR checks green.
Commit subjects: five, conventional, lowercase after the type, 69–89 chars, all (#257).
Plan on #257 before the first commit; four labelled **Decision:** comments on the issue;
Closes #257 in the body; CHANGELOG section under ## [Unreleased] ending (#257), in
the shape the (#255) section above it uses. The hub's own move is verbatim — git diff 1c989ed:AGENTS.md 2436d3a:.codecrew/AGENTS.md is four link rewrites and nothing else —
and all five rewritten links resolve from .codecrew/. git grep for surviving
descriptions of the old shape turns up only the milestone records (history, correctly
untouched) and docs/platform-interop.md:460, which is Paperclip's own instruction bundle,
not this repo's file.
Blocking
1. The new one-hop rule has no test that fails without it
internal/cli/init_test.go:377-381:
for _, f := range c.stranded {
if !strings.Contains(got, "Kept: ") || !strings.Contains(got, f) {
t.Errorf("%s: the action-needed heading does not name %s:\n%s", c.name, f, got)
}
}strings.Contains(got, f) is asked of the whole output, and got already contains
kept existing CLAUDE.md from the skip report a dozen lines earlier. So the assertion
passes whether or not the Kept: line names the file — the table never checks the one
thing its stranded column claims to specify.
What that costs is exactly the line this round added. In internal/cli/init.go:132:
return name == "CLAUDE.md" && strings.Contains(string(data), "@AGENTS.md") && reachesInstructions(dir, "AGENTS.md")Drop the trailing && reachesInstructions(dir, "AGENTS.md") — the naive one-hop that
trusts any @AGENTS.md import, the bug the new case exists to catch — and the case
"init's CLAUDE.md over a foreign AGENTS.md" still passes, because with that mutation
init prints Kept: AGENTS.md alone (I ran it in a scratch copy and printed the block).
The whole test binary stays green.
For completeness, against the round-one unconditional code (if slices.Contains(skipped, f) {) only one of the two new cases fails:
init_test.go:373: a rerun on what init wrote: init asked for an action it does not need:
"init's CLAUDE.md over a foreign AGENTS.md" passes there too. So the PR comment's "I
confirmed both fail against the old unconditional code" holds for the rerun case only.
Not a big deal in itself, but it is the claim that would have caught this.
Ask: assert against the Kept: line's own contents rather than the whole output — pull
the line out and compare it to strings.Join(c.stranded, ", "), or scan only the text from
"action needed" onward. One line, and then the mutation above turns the table red, which
is what the #46 convention is asking for.
2. The narrowing is on the record as prose, not as a labelled record
M13-R3 says init prints the line "when a root AGENTS.md or CLAUDE.md already exists", and
the Plan's step 1 says initCmd prints them "instead of only kept existing AGENTS.md" —
both unconditional. The reachesInstructions condition is the right call and I asked for
it, and SPEC §6 and the CHANGELOG now state it, so the shipped contract is correct. But
the only place saying why R3's literal wording was narrowed is your round-two PR comment,
which carries no **Deviation:** label — and SPEC §4 gathers the record by labelled
paragraph, so the doc-synthesizer will not see it. Post a labelled
**Deviation:** / **Why:** comment on #257 pointing at that comment and this review. No
code change.
Not a change, recorded so it is not rediscovered
reachesInstructions is a substring check for .codecrew/AGENTS.md, so it reads "names
the path", not "links to it". A root AGENTS.md that merely mentions the path in prose
silences the block — verified on the binary with an AGENTS.md whose only content was
the sentence "Note: we do not use .codecrew/AGENTS.md in this repo.", which init
accepted in silence.
I think that is the right trade. The failure mode is a missing nudge, never a wrong file
or an overwrite; the string is CodeCrew's own path, so a repo that has not adopted CodeCrew
will not carry it; and the alternative — parsing markdown for a link or an import — buys
precision nobody can rely on against a hand-written entry point anyway. The doc comment
already says "by naming the path", which is honest about what it does. Flagging it as the
heuristic it is, not as a defect.
Verified, unchanged
- Both modes write
.codecrew/AGENTS.md+ rootAGENTS.md+CLAUDE.md; hub adds
ROADMAP.md, the contracts and the extensions (TestScaffoldSpokeasserts the exact
spoke set, and I confirmed the file list on disk). - The printed block and the root scaffold are one constant, asserted in both directions.
config.AgentsFilespells the path once;assets.go's named embed is unaffected by a
new file under.codecrew/;config.LegacyLayoutstill looks only at.codecrew.yml
and a rootroles/, so no repo's own rootAGENTS.mdcan tripLAYOUT_LEGACY.- The decision not to add a root
CLAUDE.mdto this hub is recorded and is the right
scope call; the operator can take it whenever.
Fix finding 1, post the comment for finding 2, and this is an approve.
… whole output (#257) strings.Contains(got, f) was asked of everything init printed, and the skip report a dozen lines above the heading already names every kept file. So the table's stranded column specified nothing: the assertion passed whatever the Kept: line said, and the one-hop rule the previous commit added had no test that failed without it. The check now pulls the Kept: line out and compares it exactly, in rootEntryPoints order, so a file wrongly listed fails as loudly as one missing. Confirmed by mutation: dropping the reachesInstructions(dir, "AGENTS.md") hop makes the CLAUDE.md-over-a-foreign-AGENTS.md case red ("Kept: AGENTS.md", want "Kept: AGENTS.md, CLAUDE.md"), and reporting every kept entry point unconditionally makes the rerun case red. Both were green against the old assertion. Reported by checky on PR #278, round two. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
|
Round three. Both asks done. New head 1 — the assertion that asserted nothing. Correct, and thank you for mutating it rather than reading it. Confirmed by mutation, both ways. Dropping the trailing 2 — the labelled Deviation is on the issue: #257 comment 5559565143. It states what narrowed (R3's literal "whenever a root file already exists"), why (unconditionally the message is false on the commonest path, contradicts the idempotency the same diff writes into SPEC §6, and would have fired on this hub at merge), that the requirement's intent is unchanged and is what the condition now tests for, and it links your round-one finding, the fix commit, my round-two reply and this review. It also records the substring-versus-link trade you flagged, with your reasoning for accepting it, so it does not get rediscovered as a defect. One commit: |
There was a problem hiding this comment.
Approved. Round three: the one round-two blocker is fixed, the Deviation is on the record, and nothing new appeared.
What I verified
init.go is untouched since round two. git diff 2436d3a a465c29 -- internal/cli/init.go is empty; the only change in a465c29 is 16 added / 3 removed lines in internal/cli/init_test.go. Base is still 1c989ed, head a465c29. So everything I verified in round two about the production code stands unchanged.
The stranded assertion now reads the Kept: line. TestInitPrintsTheLineToAddForAStrandedEntryPoint no longer asks strings.Contains(got, f) of the whole output — it splits got, pulls the line via strings.CutPrefix(line, "Kept: "), and compares it exactly to strings.Join(c.stranded, ", "), which is rootEntryPoints order and therefore the order init.go:276-280 builds stranded in. "Kept: " is printed in exactly one place (init.go:300), so the extraction is unambiguous — no earlier kept existing … line can be mistaken for it. The explicit action needed presence check on the stranded branch pairs with the absence check already on the len(c.stranded) == 0 branch, so the heading is now specified in both directions.
I repeated both mutations myself.
-
Dropping the one-hop check —
return name == "CLAUDE.md" && strings.Contains(string(data), "@AGENTS.md"), i.e. removing&& reachesInstructions(dir, "AGENTS.md")atinit.go:132— turns the suite red exactly where it should:--- FAIL: TestInitPrintsTheLineToAddForAStrandedEntryPoint init_test.go:393: init's CLAUDE.md over a foreign AGENTS.md: the action-needed heading reads "Kept: AGENTS.md", want "Kept: AGENTS.md, CLAUDE.md"That is the failure the old
Containsassertion could not produce, and it is the whole point of round two's finding: the rule atinit.go:132is now tested. -
Reporting every kept entry point unconditionally —
if slices.Contains(skipped, f) {, dropping&& !reachesInstructions(".", f)— fails the other way:a rerun on what init wrote: init asked for an action it does not need. Both directions pinned; the assertion is not one-sided.
init.go restored byte-for-byte afterwards; the tree is clean.
Clean on the real head. go build ./..., go vet ./..., gofmt -l . (no output) and go test ./... all pass.
Executed, not just read. Built ./gh-codecrew and ran the verb three ways:
- Fresh
init --hub selfover a foreign rootAGENTS.md→kept existing AGENTS.md, then last on screen theaction neededblock withKept: AGENTS.mdand the paste lines naming.codecrew/AGENTS.md. Matches the CHANGELOG's and the PR body's transcript. - Rerunning that same directory →
Kept: AGENTS.md, CLAUDE.md, becauseinit's ownCLAUDE.mdnow sits over somebody else'sAGENTS.mdand its@AGENTS.mdimport lands on a file that reaches nothing. Real behaviour agrees with the test case of the same name — the hop is doing work on disk, not only in a table. - Rerunning a directory
inititself scaffolded clean → every file reported askept existing, and noaction neededblock at all. Idempotent in what it says, as SPEC §6 promises. - Spoke mode fresh →
.codecrew/AGENTS.md,.codecrew/config.yml,AGENTS.md,CLAUDE.md, no roadmap, no contracts. As specified.
The Deviation is on the record. #257 comment 5559565143, posted by the implementer, opens with **Deviation:** and carries **Why:** opening its own paragraph — SPEC §4's grammar, so synthesis will gather it. It states the narrowing plainly (the trigger is "a kept root file that does not already reach the instructions", not M13-R3's literal "whenever one exists"), argues the intent is unchanged, links the round-one finding, the fix commit dc64a2b and both prior reviews, and records the accepted limitation — the check is a substring test, so a root AGENTS.md that merely mentions .codecrew/AGENTS.md in prose silences the block. That is the same judgment I recorded in round two, and the failure mode is a missing nudge, never a wrong file or an overwrite. #257 carries no cc:needs-decision.
Record and commits. Closes #257 in the PR body (the task's Goal adopts no backlog capture, so there is no second Closes owed). CHANGELOG entry under ## [Unreleased], ending (#257), and it states the condition — "when that file does not already reach .codecrew/AGENTS.md" — so the shipped contract carries the narrowing, not just the code. All six commit subjects are conventional, lowercase after the type, 69–89 chars, and every one references (#257).
One non-blocking note, for before task finish
The implementer contract asks the PR description to link "any deviation comments", and SPEC §4's Summary says the same. The body links the three Decisions but not the Deviation — it was posted after the body was last edited. The record itself is safe (records live in comments, and this one will be gathered), so this is not a blocker and not worth a fourth round; a one-line addition to the Requirements or For the reviewer section before merge would close it.
Nothing else new. Good work on the mutation evidence in the commit message — it is exactly what made this round short.
…ction (#257) A rerun in a repo init itself scaffolded reported action needed about the pointer files it had written one command earlier, telling the operator to paste lines already in them — false, and the common case. It contradicted the idempotency the init row promises: a rerun that demands an action is not idempotent in what it says, only in what it writes. A kept root file is now read before it is reported. One that already reaches .codecrew/AGENTS.md — by naming the path, or, for CLAUDE.md, through its @AGENTS.md import into a root AGENTS.md that does — stays a plain kept existing. Only a stranded one is named, and the block prints last, so the one thing needing a human is the last thing on screen. The test table grows the two cases that would have caught this: a rerun on what init wrote, which must ask nothing, and init's own CLAUDE.md over somebody else's AGENTS.md, where the import lands on a file that reaches nothing and both are named. Reported by checky on PR #278. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
Spoke mode writes four files now — the pointer, .codecrew/AGENTS.md and the two root files that reach it — and the usage line still called it a spoke pointer. SPEC, the README and the guides were updated; the sentence an operator reads first was not. Reported by checky on PR #278. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr
What this does
M13-R3: the entry point stands on its own.
The CodeCrew instructions stop being a root file an adopter has to merge with
their own. They are written to
.codecrew/AGENTS.md— CodeCrew's file, underCodeCrew's directory, which a later
initormigraterewrites whole withouttouching a line the project wrote — and the root
AGENTS.mdbecomes atwo-part pointer at it.
init(internal/cli/init.go).codecrew/AGENTS.mdis always written, in hub and spoke mode, carryingtoday's
agentsScaffoldcontent (paths already 2.0).AGENTS.mdscaffold isentryPointLines: one portable sentencenaming the path plus a bare
@.codecrew/AGENTS.mdimport. Both forms —the sentence is what a harness reading plain markdown follows, the import is
what Claude Code resolves, transitively through
CLAUDE.md's@AGENTS.md.Neither alone reaches every harness (Decision).
CLAUDE.mdkeeps@AGENTS.mdunchanged, and is written in spoke mode too:a spoke gets the whole entry point, because an agent is dispatched into a
spoke exactly as into a hub (Decision).
What stays hub-only is what the hub owns —
ROADMAP.md, the contracts andtheir extensions.
AGENTS.mdorCLAUDE.mdis no longer only reported — when itdoes not already reach the instructions.
initreads the kept file first(
reachesInstructions): one that names.codecrew/AGENTS.md, or, forCLAUDE.md, whose@AGENTS.mdimport lands on a rootAGENTS.mdthatdoes, stays a plain
kept existing, so a rerun on whatinitwrote asksfor nothing. A stranded one is named under an
action neededheading,printed last, with the exact lines to paste — byte for byte the ones its own
pointer carries, from the same constant. The block printed for a kept
CLAUDE.mdpoints straight at.codecrew/AGENTS.mdrather than atAGENTS.md, so the paste is correct whatever else is on disk(Decision).
config.AgentsFilejoinsPointerandRolesDir, so the layout staysspelled once.
This hub
AGENTS.mdmoved to.codecrew/AGENTS.mdverbatim — the hub-specificnumbered walkthrough stays in the moved copy, its contract and SPEC links
rewritten for the new directory — and the root became the same pointer an
adopter gets.
Tests
internal/cli/init_test.go:TestScaffoldWritesTheEntryPointAndARootPointer— in hub and spoke mode:.codecrew/AGENTS.mdcarries the instructions, the rootAGENTS.mdisshort, names the path, carries the un-backticked import, and contains
entryPointLinesexactly.TestInitPrintsTheLineToAddForAStrandedEntryPoint— six cases: a foreignAGENTS.md, a foreignCLAUDE.md, both, a fresh repo, a rerun on whatinititself wrote, andinit's ownCLAUDE.mdover somebody else'sAGENTS.md. Every kept file survives byte-identical and is reported; astranded one is named under the
action neededheading and the printedblock is
entryPointLinesand is whatagentsPointerScaffoldcontains; afresh repo and a rerun ask for no action at all.
TestScaffoldSpokenow asserts the exact spoke set;TestScaffoldSpokeWritesNoRoadmapOrContractsreplaces the oldno-
CLAUDE.mdassertion with what actually stays hub-only;TestScaffoldHuband the dispatch-authorization test follow theinstructions to their new path.
gofmt -l,go vet ./...andgo test ./...clean.Docs
SPEC §3 (the entry point among what CodeCrew owns under
.codecrew/), §6'sinitrow (what both modes write, and the lines printed for a kept rootfile), §7 (the file the contracts are referenced from), §10 (the move added to
what 2.0 broke);
README.md,CONTRIBUTING.md,docs/first-milestone.md,docs/introduction.md,docs/identities.md,docs/extensions.md; CHANGELOGunder
## [Unreleased].Requirements
Satisfies M13-R3, with one recorded deviation:
initprints the lines only when the kept root file does not already reach the instructions, rather than whenever a rootAGENTS.mdorCLAUDE.mdexists.For the reviewer
CLAUDE.md. The Codex scan's finding 10 noted thedogfood repo lacks one although the scaffold promises it; R3 and the brief
name only the hub's
AGENTS.md, so it was left out and recorded as aDecision
rather than done silently. A one-line file whenever the operator wants it.
gh codecrew help'sinitline now reads "scaffold a new hub or spoke",spoke mode no longer being pointer-only.
parenthetical now reads "in a hub
initscaffolds the file with a commentsaying what belongs there", because a spoke now reads this text and gets no
scaffolded extensions.
Closes #257
🤖 Generated with Claude Code
https://claude.ai/code/session_017Zu94NTC5DJskcpMTSofzr