Skip to content

feat(plan): brief the feat, not the method, and discover its context once - #89

Merged
protonspy merged 2 commits into
mainfrom
feat/plan-brief-context-pass
Jul 28, 2026
Merged

feat(plan): brief the feat, not the method, and discover its context once#89
protonspy merged 2 commits into
mainfrom
feat/plan-brief-context-pass

Conversation

@protonspy

@protonspy protonspy commented Jul 28, 2026

Copy link
Copy Markdown
Owner

What

A feat's brief was 94% development process — mission steps, approval authority, forbidden actions, verdict protocol, feat-exit checklist — all of it already in the plan-session CLAUDE.md the runner writes into every worktree (tree.go: writeEntryDoc) and in the plan-dev skill. The copies had drifted: the brief named feat-exit checks and sub-agents the entry doc did not, and its authority block spent 1.1 KB arguing against STOP rules that the worktree's CLAUDE.md no longer contains.

The brief now carries the feat and what governs it. The process stays in the one place that already loads every turn.

What replaces the removed prose is a per-feat context pass: before a feat is dispatched, a cheap model reads its worktree once and records what the feat touches, which decisions and stack rows govern it, what is already there and what is not — the discovery the orchestrator would otherwise redo on every attempt, at orchestrator prices.

Why a schema and not a prompt

Asked in prose for "a prompt to develop this feat", both a cheap and a strong model wrote the process instead of the context — and the stronger one wrote it worse, faithfully reproducing the repository's INTERACTIVE contract: create a branch, have a human approve each phase, open a PR. "Have a human approve" deadlocks a session that has no human, and would cost a whole attempt.

A closed schema with no free-text field cannot carry any of it. The failure is removed by construction rather than forbidden by wording.

Changes

  • FeatBrief drops the methodology and renders the feat's stored pack. It still reads only from disk, so the brief stays byte-deterministic (R7.3).
  • enrich.go: bounded schema, deterministic prompt, VerifyPack (every path must exist, every adr:/stack: must resolve through the same helpers the validators use, declared is corrected from the feat row), cache keyed to the feat row.
  • The pass runs after the worktree is prepared, so a feat with dependencies is described against the tree that actually has them merged in.
  • Enrichment never fails a dispatch: any failure is logged and the brief renders without it — which is also what --enrich-model none gets.
  • The plan-session CLAUDE.md absorbs what only the brief carried: spec-author delegation, the cycle-skill fallback, graph analyze --strict, the reviewers at feat exit, the Tier-3 evidence command, recording decisions in stack/ADR.
  • plan brief --refresh re-runs the pass, so a human can review and correct the pack before the run that uses it.

Evidence

Verified end-to-end against a real 13-feat plan in another workspace (plan brief --refresh, real claude -p spawn, exit 0, zero claims dropped by the verifier). The pass found two governing ADRs the plan row never declared, the wiki log.md the feat would have forgotten, and this pitfall — both dates confirmed by hand:

the Sources: line in frontend-app.md says code read 2026-07-12, predating ADR 0043 (2026-07-25) — every code_ref it cites is suspect, not just the ones already confirmed gone

Brief size on that feat: ~10.8 KB (7.5 KB methodology) → 8.2 KB, of which 3.6 KB is verified feat-specific context.

Known limitation

VerifyPack checks touches[].path and governors[].id — the fields that are citations. exists, missing and traps are free text and are not mechanically checkable; one trap in the real run carried an imprecise claim about which component boundaries ship a MANIFEST.md. Tightening this would mean requiring an evidence path per item, which changes the pack format — deliberately left for a follow-up.

Test plan

  • make check green (gofmt + vet + go test -race), internal/plan at 85.2% coverage.
  • New enrich_test.go: verification drops (nonexistent path, .. escape, unresolvable ADR, undecided stack row, bad flow), declared correction in both directions, per-feat caching and re-enrichment on an edited row, every failure mode leaving the dispatch intact, envelope parsing, schema bounds.
  • The guarantees the deleted brief tests protected did not disappear — they moved to TestPlanSessionEntryCarriesTheLoopContract, which fails if the plan-session CLAUDE.md loses any instruction of the loop.
  • TestBriefCarriesNoProcess is the new boundary: it fails if process reappears in the brief.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Atx6C8EMdJxUBKsijj7wpN

Summary by CodeRabbit

  • New Features
    • Added optional per-feature context enrichment for plan run, including --enrich-model (defaulting to sonnet, with none to disable) and cached workspace-validated references.
    • Added plan brief --refresh plus --enrich-model to regenerate feature context.
    • Updated the plan run session loop guidance (technology choice recording, stronger quality/security-review dispatch, and added csdd graph analyze --strict).
  • Documentation
    • Expanded plan-mode docs to describe the context pass, caching location, and refresh/enrichment commands.
  • Bug Fixes
    • Updated plan brief to treat ADRs as citation-only (no inline ADR body content).
  • Refactor
    • Streamlined feature briefs to focus on objectives, governing references, discovered context, and quality gates.

…once

A feat's brief was 94% development process — the mission steps, the approval
authority, the forbidden actions, the verdict protocol and the feat-exit
checklist — all of it already in the plan-session CLAUDE.md the runner writes
into every worktree and in the plan-dev skill. The copies had begun to disagree
about the feat-exit checks, and the authority block argued against STOP rules
that the worktree's CLAUDE.md no longer contains.

The brief now carries the feat and what governs it; the process stays in the
one place that already loads every turn. What replaces the removed prose is a
per-feat context pass that reads the worktree once, on a cheap model, and
records what the orchestrator would otherwise rediscover per attempt.

The pass answers into a closed schema rather than prose, and that is the
load-bearing part: asked in prose for "a prompt for this feat", both a cheap
and a strong model wrote the process instead of the context — and the stronger
one wrote it worse, faithfully reproducing the INTERACTIVE contract (create a
branch, have a human approve each phase, open a PR). "Have a human approve"
deadlocks a session that has no human. A schema with no free-text field cannot
carry any of it.

- FeatBrief drops the methodology and renders the feat's stored context pack;
  it still reads only from disk, so the brief stays byte-deterministic (R7.3)
- enrich.go: bounded schema, deterministic prompt, VerifyPack (every path must
  exist, every adr:/stack: must resolve through the same helpers the validators
  use, `declared` is corrected from the feat row), cache keyed to the feat row
- the pass runs after the worktree is prepared, so a feat with dependencies is
  described against the tree that actually has them merged in
- enrichment never fails a dispatch: any failure is logged and the brief renders
  without it, which is also what --enrich-model none gets
- the plan-session CLAUDE.md absorbs what only the brief carried: spec-author
  delegation, the cycle-skill fallback, graph analyze --strict, the reviewers at
  feat exit, the Tier-3 evidence command, recording decisions in stack/ADR
- plan brief --refresh re-runs the pass, so a human can review and correct the
  pack before the run that will use it

On a real 13-feat plan the brief goes from ~10.8 KB (7.5 KB of it methodology)
to 8.2 KB, of which 3.6 KB is verified, feat-specific context.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Atx6C8EMdJxUBKsijj7wpN
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 411446f7-35ac-4b92-97dd-56e3c17ccd7a

📥 Commits

Reviewing files that changed from the base of the PR and between 7e73431 and fece1d8.

📒 Files selected for processing (1)
  • internal/plan/enrich.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/plan/enrich.go

📝 Walkthrough

Walkthrough

Changes

Plan enrichment and briefing

Layer / File(s) Summary
Context pack model and lifecycle
internal/plan/enrich.go, internal/plan/enrich_test.go
Adds structured enrichment packs, schema validation, parsing, workspace verification, atomic persistence, cache keys, and failure-tolerant reuse tests.
Runner enrichment execution
internal/plan/runner.go, internal/plan/runner_exec.go
Adds enrichment hooks and model configuration, runs enrichment in prepared feat worktrees, and invokes Claude with bounded JSON output and allowed tools.
Brief rendering and CLI refresh
internal/plan/brief.go, internal/cli/plan.go, internal/plan/*test.go, internal/cli/*test.go
Generates refs-only briefs with optional verified context sections, quality gates, and process pointers; adds --enrich-model and --refresh behavior and updates assertions.
Plan session process contract
internal/templater/templates/plan/CLAUDE.md.tmpl, internal/templater/templater_test.go, README.md
Documents technology decisions, delegation and review gates, strict graph validation, context enrichment controls, and the plan-session contract.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant PlanRunner
  participant FeatWorktree
  participant EnsurePack
  participant Claude
  participant FeatBrief
  CLI->>PlanRunner: run plan with enrich model
  PlanRunner->>FeatWorktree: prepare feat worktree
  PlanRunner->>EnsurePack: load or create context pack
  EnsurePack->>Claude: request constrained JSON enrichment
  Claude-->>EnsurePack: return context pack
  EnsurePack-->>PlanRunner: return verified or empty pack
  PlanRunner->>FeatBrief: generate plan and context brief
  FeatBrief-->>CLI: print feat brief
Loading

Possibly related PRs

  • protonspy/csdd#67: Updates related FeatBrief wording and done-gating behavior.
  • protonspy/csdd#71: Changes strict graph-analysis pass/fail behavior used by the updated plan-session checklist.
  • protonspy/csdd#87: Overlaps with refs-only brief generation and citation handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Clearly summarizes the main change: plan briefs now focus on the feat and add a one-time context discovery pass.
Docstring Coverage ✅ Passed Docstring coverage is 96.43% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/plan-brief-context-pass

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Atx6C8EMdJxUBKsijj7wpN

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cli/plan.go`:
- Around line 568-577: In the refresh flow around plan.EnrichHook, build and
validate the enrichment hook before calling plan.RemovePack. If the hook is nil,
return the existing --refresh model error without deleting the stored pack; only
invoke RemovePack after validation succeeds.

In `@internal/plan/enrich.go`:
- Around line 362-378: The stale-pack handling spans
internal/plan/enrich.go:362-378 and internal/plan/brief.go:114-122. In
EnsurePack, remove the stored pack via RemovePack when its key mismatches
PackKey(doc, feat) before attempting enrichment, so disabled or failed
enrichment leaves no stale file. In FeatBrief, validate a loaded pack’s Key
against PackKey(doc, feat) and ignore mismatches instead of rendering them.
- Around line 166-172: Handle the return value from fmt.Fprintf in PackKey so
errcheck no longer flags the hash-writing operation. Since the hash.Hash writer
cannot fail, explicitly discard the returned count and error while preserving
the existing input order and digest generation.

In `@internal/plan/runner.go`:
- Line 484: Update the EnsurePack call in the runner flow so its verification
root uses dir, matching the worktree passed to the enricher and allowing
worktree-only files to survive verification, while preserving opts.Root as the
storage root for the resulting pack.
- Around line 480-484: Move the EnsurePack enrichment call out of the
synchronous openDispatch/squad-filling path and execute it within the
per-dispatch goroutine so a slow claude call cannot block other feats. Preserve
the existing enrichment arguments and failure-handling behavior, while allowing
multiple dispatched feats to enrich concurrently up to the squad limit.

In `@internal/templater/templates/plan/CLAUDE.md.tmpl`:
- Around line 94-98: Update the completion criteria wording in the plan template
to say “all checks below” instead of “the three checks below,” preserving the
existing four checklist conditions including csdd graph analyze --strict and
spec validate.

In `@README.md`:
- Line 305: Update the README section around “What a session is handed” to match
the canonical plan-session contract in CLAUDE.md.tmpl: remove claims that
sessions own branches or open PRs, and state that process authority is
centralized in the plan-session CLAUDE.md rather than also living in plan-dev.
Preserve the distinction that sessions receive the feat and plan inputs while
following the worktree contract for development actions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 69326c60-5a0c-4ecd-9cb4-919445067eca

📥 Commits

Reviewing files that changed from the base of the PR and between 0919dff and 7e73431.

📒 Files selected for processing (13)
  • README.md
  • internal/cli/plan.go
  • internal/cli/plan_adr_test.go
  • internal/cli/plan_bridge_cli_test.go
  • internal/plan/adr_test.go
  • internal/plan/brief.go
  • internal/plan/brief_test.go
  • internal/plan/enrich.go
  • internal/plan/enrich_test.go
  • internal/plan/runner.go
  • internal/plan/runner_exec.go
  • internal/templater/templater_test.go
  • internal/templater/templates/plan/CLAUDE.md.tmpl
💤 Files with no reviewable changes (2)
  • internal/cli/plan_adr_test.go
  • internal/plan/adr_test.go

Comment thread internal/cli/plan.go
Comment on lines +568 to +577
if refresh {
if err := plan.RemovePack(r, doc.Slug, target.Slug); err != nil {
render.Err(err.Error())
return 1
}
hook := plan.EnrichHook(enrichModel)
if hook == nil {
render.Err("--refresh needs a model: pass --enrich-model")
return 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

--refresh deletes the stored pack before validating the model.

With --enrich-model none (or empty), RemovePack has already run when the nil-hook check fails, so the user loses the pack — including one they hand-edited, which the --refresh help text explicitly invites. Build and validate the hook first.

🔧 Proposed reorder
 	if refresh {
+		hook := plan.EnrichHook(enrichModel)
+		if hook == nil {
+			render.Err("--refresh needs a model: pass --enrich-model")
+			return 1
+		}
 		if err := plan.RemovePack(r, doc.Slug, target.Slug); err != nil {
 			render.Err(err.Error())
 			return 1
 		}
-		hook := plan.EnrichHook(enrichModel)
-		if hook == nil {
-			render.Err("--refresh needs a model: pass --enrich-model")
-			return 1
-		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if refresh {
if err := plan.RemovePack(r, doc.Slug, target.Slug); err != nil {
render.Err(err.Error())
return 1
}
hook := plan.EnrichHook(enrichModel)
if hook == nil {
render.Err("--refresh needs a model: pass --enrich-model")
return 1
}
if refresh {
hook := plan.EnrichHook(enrichModel)
if hook == nil {
render.Err("--refresh needs a model: pass --enrich-model")
return 1
}
if err := plan.RemovePack(r, doc.Slug, target.Slug); err != nil {
render.Err(err.Error())
return 1
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/plan.go` around lines 568 - 577, In the refresh flow around
plan.EnrichHook, build and validate the enrichment hook before calling
plan.RemovePack. If the hook is nil, return the existing --refresh model error
without deleting the stored pack; only invoke RemovePack after validation
succeeds.

Comment thread internal/plan/enrich.go
Comment thread internal/plan/enrich.go
Comment on lines +362 to +378
key := PackKey(doc, feat)
if p, err := LoadPack(root, doc.Slug, feat.Slug); err == nil && p != nil && p.Key == key {
return p
}
if enrich == nil {
return nil
}
out, err := enrich(EnrichRequest{
Feat: feat,
Prompt: enrichPrompt(doc, feat),
Schema: enrichSchema,
Dir: dir,
})
if err != nil {
logf(" · context enrichment for %s failed (%v); briefing without it", feat.Slug, err)
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stale context packs can be briefed as current. Nothing enforces EnrichPack.Key freshness at render time: EnsurePack returns nil for a stale pack but leaves the file on disk, and FeatBrief loads it regardless — so an edited feat row plus a disabled or failing enricher yields a brief carrying the previous row's discovery.

  • internal/plan/enrich.go#L362-L378: on a key mismatch, discard the stored pack (RemovePack) before attempting re-enrichment, so a failed or disabled pass leaves nothing stale behind.
  • internal/plan/brief.go#L114-L122: ignore a loaded pack whose Key does not equal PackKey(doc, feat) instead of rendering it unconditionally.
📍 Affects 2 files
  • internal/plan/enrich.go#L362-L378 (this comment)
  • internal/plan/brief.go#L114-L122
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/plan/enrich.go` around lines 362 - 378, The stale-pack handling
spans internal/plan/enrich.go:362-378 and internal/plan/brief.go:114-122. In
EnsurePack, remove the stored pack via RemovePack when its key mismatches
PackKey(doc, feat) before attempting enrichment, so disabled or failed
enrichment leaves no stale file. In FeatBrief, validate a loaded pack’s Key
against PackKey(doc, feat) and ignore mismatches instead of rendering them.

Comment thread internal/plan/runner.go
Comment on lines +480 to +484
// Discovery, once per feat rather than once per attempt: EnsurePack reuses the
// stored pack whenever the feat row that produced it has not changed. It cannot
// fail the dispatch — enrichment is an optimization over the deterministic brief,
// so every failure inside it is logged and the brief renders without it.
EnsurePack(opts.Root, doc, feat, dir, opts.Hooks.Enrich, logf)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Enrichment runs on the dispatch loop goroutine.

openDispatch is called from the squad-filling loop, so a claude enrichment call — bounded only by enrichIdle (3 min) — blocks every other feat from being dispatched, including with --squad-limit > 1. Consider running the pass inside the per-dispatch goroutine, or at least noting the serialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/plan/runner.go` around lines 480 - 484, Move the EnsurePack
enrichment call out of the synchronous openDispatch/squad-filling path and
execute it within the per-dispatch goroutine so a slow claude call cannot block
other feats. Preserve the existing enrichment arguments and failure-handling
behavior, while allowing multiple dispatched feats to enrich concurrently up to
the squad limit.

Comment thread internal/plan/runner.go
// stored pack whenever the feat row that produced it has not changed. It cannot
// fail the dispatch — enrichment is an optimization over the deterministic brief,
// so every failure inside it is logged and the brief renders without it.
EnsurePack(opts.Root, doc, feat, dir, opts.Hooks.Enrich, logf)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

VerifyPack checks paths against the shared root, not the worktree the enricher read.

EnsurePack receives opts.Root as the verification root but dir as the enrichment cwd. VerifyPack stats filepath.Join(root, path), so a file that only exists in the feat's worktree (created by a merged dependency — exactly the case the comment on Line 471 motivates) is dropped as "does not exist", while a path that exists only on the base branch survives. Verify against dir and keep the pack stored under opts.Root.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/plan/runner.go` at line 484, Update the EnsurePack call in the
runner flow so its verification root uses dir, matching the worktree passed to
the enricher and allowing worktree-only files to survive verification, while
preserving opts.Root as the storage root for the resulting pack.

Comment on lines +94 to +98
- `csdd graph analyze --strict` passes (spec ↔ task ↔ component traceability holds)
- every task box in `specs/<feat>/tasks.md` is `[x]`
- `specs/<feat>/test-report.json` is green with no open attentions

The loop checks these three on disk before accepting your verdict. It does not
The loop checks the last three on disk before accepting your verdict. It does not

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Update the stale “three checks” count.

After adding csdd graph analyze --strict, the checklist now has four conditions, but Lines 51-52 still say done requires “the three checks below.” Change that wording to “all checks below” so the session cannot interpret the new graph check or spec validate as optional.

Suggested wording
-4. **Verdict.** `done` only when the three checks below hold;
+4. **Verdict.** `done` only when all checks below hold;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/templater/templates/plan/CLAUDE.md.tmpl` around lines 94 - 98,
Update the completion criteria wording in the plan template to say “all checks
below” instead of “the three checks below,” preserving the existing four
checklist conditions including csdd graph analyze --strict and spec validate.

Comment thread README.md

The session's verdict declares one of three intents: `done` (the whole feat is delivered and the session's own checks — `csdd spec validate`, `csdd graph analyze --strict`, the plan's Quality Gates — pass), `continue` (honest partial work; the summary is the handoff to the next session on the same feat), or `blocked` (the feat turned out to need another feat of this plan that has not landed — it parks without spending an attempt, and the discovered edge is recorded in `.csdd/plan/<slug>/discovered-deps.json`, never written back into `plan.md`).

What a session is handed is **the feat, not the method**. The brief carries the feat row, its governing refs, the seeds, the Executor Notes and the plan's Quality Gates; the development process — the session's authority to approve its own phases, the cycle, what it delegates, the git it owns and what makes a `done` acceptable — lives in the plan-session `CLAUDE.md` written into each worktree and in the `plan-dev` skill, both of which the session reads every turn anyway.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align this section with the canonical plan-session contract.

The surrounding text still says the session owns branches and opens PRs, while internal/templater/templates/plan/CLAUDE.md.tmpl says it must not create or switch branches, push, or open a PR. It also says the process lives in both CLAUDE.md and plan-dev, conflicting with the stated centralization in CLAUDE.md. Update this section so users and autonomous sessions receive one consistent contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 305, Update the README section around “What a session is
handed” to match the canonical plan-session contract in CLAUDE.md.tmpl: remove
claims that sessions own branches or open PRs, and state that process authority
is centralized in the plan-session CLAUDE.md rather than also living in
plan-dev. Preserve the distinction that sessions receive the feat and plan
inputs while following the worktree contract for development actions.

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