@ulysses-ai/create-workspace 0.13.0-beta.2 → 0.14.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +15 -3
  2. package/package.json +1 -1
  3. package/template/.claude/hooks/_bash-output-advisory.test.mjs +88 -0
  4. package/template/.claude/hooks/bash-output-advisory.mjs +77 -0
  5. package/template/.claude/hooks/version-freshness-check.mjs +30 -0
  6. package/template/.claude/lib/freshness.mjs +75 -0
  7. package/template/.claude/lib/freshness.test.mjs +175 -0
  8. package/template/.claude/lib/registry-check.mjs +106 -0
  9. package/template/.claude/lib/registry-check.test.mjs +130 -0
  10. package/template/.claude/rules/memory-guidance.md +47 -0
  11. package/template/.claude/rules/task-list-mirroring.md +69 -0
  12. package/template/.claude/rules/token-economics.md.skip +23 -8
  13. package/template/.claude/rules/workspace-structure.md +2 -0
  14. package/template/.claude/scripts/build-shared-context-index.mjs +212 -0
  15. package/template/.claude/scripts/build-shared-context-index.test.mjs +318 -0
  16. package/template/.claude/scripts/migrate-claude-md-freshness-include.mjs +30 -0
  17. package/template/.claude/scripts/migrate-claude-md-freshness-include.test.mjs +54 -0
  18. package/template/.claude/scripts/sync-tasks.mjs +234 -0
  19. package/template/.claude/scripts/sync-tasks.test.mjs +350 -0
  20. package/template/.claude/settings.json +20 -9
  21. package/template/.claude/skills/braindump/SKILL.md +15 -0
  22. package/template/.claude/skills/build-docs-site/SKILL.md +1 -1
  23. package/template/.claude/skills/build-docs-site/checklists/pitfalls.md +4 -4
  24. package/template/.claude/skills/complete-work/SKILL.md +47 -55
  25. package/template/.claude/skills/handoff/SKILL.md +15 -0
  26. package/template/.claude/skills/maintenance/SKILL.md +49 -7
  27. package/template/.claude/skills/pause-work/SKILL.md +25 -4
  28. package/template/.claude/skills/release/SKILL.md +59 -43
  29. package/template/.claude/skills/start-work/SKILL.md +34 -2
  30. package/template/.claude/skills/workspace-update/SKILL.md +16 -0
  31. package/template/CLAUDE.md.tmpl +1 -0
  32. package/template/_gitignore +2 -3
  33. package/template/workspace.json.tmpl +1 -0
@@ -38,7 +38,18 @@ If conflicts arise in any repo, STOP and present them to the user. Do not auto-r
38
38
  Run `/braindump` to capture any final discussion/reasoning to the session tracker body.
39
39
  If the user declines or there's nothing to capture, skip.
40
40
 
41
- ### Step 4: Gather source material
41
+ ### Step 4: Flush task list to session.md
42
+
43
+ Before reading sources for synthesis, flush current `TodoWrite` state to `## Tasks` per the `task-list-mirroring` rule. This ensures the synthesis in Step 6 sees the final state:
44
+
45
+ ```bash
46
+ cd work-sessions/{session-name}/workspace
47
+ echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
48
+ ```
49
+
50
+ Mark `Complete work` as `in_progress` in the JSON before flushing — the rest of this skill IS the act of completing.
51
+
52
+ ### Step 5: Gather source material
42
53
 
43
54
  Formally read ALL sources before synthesizing — do not write release notes from memory alone:
44
55
 
@@ -62,20 +73,24 @@ Formally read ALL sources before synthesizing — do not write release notes fro
62
73
  git log origin/{repo-branch}..HEAD --oneline
63
74
  ```
64
75
 
65
- ### Step 5: Synthesize release notes
76
+ ### Step 6: Synthesize release notes
77
+
78
+ Branch notes are written to the **workspace** repo, not the project repo. They are an internal retrospection artifact consumed by `/release` at release time; the project repo only ever receives a `CHANGELOG.md` entry. This separation keeps dogfood content out of public project repos between feature merge and the next release cut.
66
79
 
67
80
  For each repo in the tracker's `repos:` list that has commits beyond the base branch:
68
81
 
69
82
  ```bash
70
83
  cd work-sessions/{session-name}/workspace/repos/{repo}
71
84
  COMMIT_ID=$(git rev-parse --short HEAD)
72
- mkdir -p release-notes/unreleased
85
+ cd ../.. # back to the workspace worktree
86
+ mkdir -p release-notes/unreleased/{repo-name}
73
87
  ```
74
88
 
75
- **File 1: `release-notes/unreleased/branch-release-notes-{COMMIT_ID}.md`**
89
+ **File 1: `release-notes/unreleased/{repo-name}/branch-release-notes-{COMMIT_ID}.md`** (relative to the workspace worktree)
76
90
  ```markdown
77
91
  ---
78
92
  branch: {branch}
93
+ repo: {repo-name}
79
94
  type: {feature|fix|chore}
80
95
  author: {user}
81
96
  date: {YYYY-MM-DD}
@@ -87,10 +102,11 @@ date: {YYYY-MM-DD}
87
102
  Written from scratch per coherent-revisions rule.}
88
103
  ```
89
104
 
90
- **File 2: `release-notes/unreleased/branch-release-questions-{COMMIT_ID}.md`**
105
+ **File 2: `release-notes/unreleased/{repo-name}/branch-release-questions-{COMMIT_ID}.md`**
91
106
  ```markdown
92
107
  ---
93
108
  branch: {branch}
109
+ repo: {repo-name}
94
110
  author: {user}
95
111
  date: {YYYY-MM-DD}
96
112
  ---
@@ -100,23 +116,22 @@ date: {YYYY-MM-DD}
100
116
  {Only genuinely open questions — not things resolved during implementation.}
101
117
  ```
102
118
 
103
- Commit per repo:
119
+ The `repo:` frontmatter field is what `/release` uses to know which project repo's `CHANGELOG.md` should consume each note. The directory name is the same as the field for redundancy.
120
+
121
+ After all repos are processed, commit once on the workspace branch:
104
122
  ```bash
123
+ cd work-sessions/{session-name}/workspace
105
124
  git add release-notes/unreleased/
106
125
  git commit -m "docs: add release notes for {branch}"
107
126
  ```
108
127
 
109
128
  If a repo has no commits beyond the base, skip release notes for it.
110
129
 
111
- ### Step 6: Consume session-scoped sources
112
-
113
- The entire `work-sessions/{session-name}/` folder is removed by the cleanup script in Step 11. Before that happens, make sure everything worth preserving has landed in release notes.
114
-
115
- No separate "consume spec and plan" commit is needed for the project repos — specs and plans now live in the session folder, not in the project worktrees. The project worktrees only carry source code changes.
130
+ ### Step 7: Remove session artifacts from the workspace branch
116
131
 
117
- ### Step 6c: Remove session artifacts from the workspace branch
132
+ The entire `work-sessions/{session-name}/` folder is removed by the cleanup script in Step 12. Before that happens, make sure everything worth preserving has landed in release notes (Step 6) — once Step 6 has run, the tracker, specs, and plans have served their purpose.
118
133
 
119
- Session content (tracker, specs, plans) lives at the top of the workspace worktree on the session branch. Its purpose was synthesis into release notes in Step 5 — that work is now done. Remove the files from the branch before the final push so main's top level stays free of session artifacts:
134
+ Session content lives at the top of the workspace worktree on the session branch. Remove these files from the branch before the final push so main's top level stays free of session artifacts:
120
135
 
121
136
  ```bash
122
137
  cd work-sessions/{session-name}/workspace
@@ -130,35 +145,11 @@ The `|| true` guards keep this idempotent — if a file is already gone (e.g., a
130
145
 
131
146
  This commit persists in the branch's history. On squash merge or rebase merge, branch history collapses to one clean commit on main with no session artifacts. On merge commits, branch history is reachable but the final tree on main shows no session content.
132
147
 
133
- ### Step 6b: Version bump (if applicable)
134
-
135
- For each repo in the tracker's `repos:`, check if the repo has versioning:
136
- ```bash
137
- cd work-sessions/{session-name}/workspace/repos/{repo}
138
- cat package.json 2>/dev/null | grep '"version"'
139
- ```
140
-
141
- If no `package.json` or no `version` field: skip this repo — no versioning to manage.
142
-
143
- If the repo has a version, determine the appropriate bump from the release notes just written:
144
-
145
- 1. Read the `type:` field from the branch release notes created in Step 5
146
- 2. Determine the bump:
147
- - `type: fix` or `type: chore` → **patch** (auto-bump, no confirmation needed)
148
- - `type: feature` → **minor** (present to user: "This session adds new functionality. Suggested bump: {current} → {next-minor}. Confirm or adjust?")
149
- - Breaking changes detected (schema changes, removed APIs, convention changes) → **major** (present to user: "This session includes breaking changes. Suggested bump: {current} → {next-major}. Confirm or adjust?")
150
-
151
- 3. Apply the bump:
152
- ```bash
153
- git add package.json
154
- git commit -m "chore: bump version to {new-version}"
155
- ```
156
-
157
- The user can override any suggestion. Accept their decision.
148
+ > **No version bump here.** Versions are bumped at release time by `/release`, which consumes accumulated unreleased branch notes into a single `CHANGELOG.md` entry per project repo. `/complete-work` only writes branch notes; it does not modify any project repo's `package.json`. This avoids version drift when multiple feature branches land between releases.
158
149
 
159
- ### Step 7: Detect remote type per repo
150
+ ### Step 8: Detect remote type per repo
160
151
 
161
- For each repo in the tracker's `repos:` plus the workspace repo, determine the remote type. This drives how Step 8 and Step 9 push and merge.
152
+ For each repo in the tracker's `repos:` plus the workspace repo, determine the remote type. This drives how Step 9 and Step 10 push and merge.
162
153
 
163
154
  ```bash
164
155
  cd work-sessions/{session-name}/workspace/repos/{repo}
@@ -167,14 +158,14 @@ git remote get-url origin 2>&1
167
158
 
168
159
  Classify the result:
169
160
 
170
- - **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step 8a, Step 9a).
171
- - **Local / bare remote** — URL is a filesystem path (starts with `/`, `./`, `file://`, or points at a `.git` bare mirror) → use the local merge flow (Step 8b, Step 9b).
172
- - **Other remote** (e.g., GitLab, Bitbucket, self-hosted) — no `gh` support → fall back to the local merge flow (Step 8b, Step 9b), and mention it in the final summary.
161
+ - **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step 9a, Step 10a).
162
+ - **Local / bare remote** — URL is a filesystem path (starts with `/`, `./`, `file://`, or points at a `.git` bare mirror) → use the local merge flow (Step 9b, Step 10b).
163
+ - **Other remote** (e.g., GitLab, Bitbucket, self-hosted) — no `gh` support → fall back to the local merge flow (Step 9b, Step 10b), and mention it in the final summary.
173
164
  - **No remote at all** — "No remote configured for {repo}. Want me to create one on GitHub, add an existing URL, or keep the session local (push/merge inside the local clone only)?" Act on the user's choice. Never silently skip push.
174
165
 
175
- ### Step 8: Push all repos
166
+ ### Step 9: Push all repos
176
167
 
177
- #### Step 8a: GitHub remotes
168
+ #### Step 9a: GitHub remotes
178
169
 
179
170
  ```bash
180
171
  # Each project repo with a GitHub remote
@@ -188,7 +179,7 @@ git commit -m "chore: finalize context for {session-name}"
188
179
  git push -u origin {branch}
189
180
  ```
190
181
 
191
- #### Step 8b: Local/bare remotes
182
+ #### Step 9b: Local/bare remotes
192
183
 
193
184
  ```bash
194
185
  # Push the feature branch to the bare remote so it exists there
@@ -202,11 +193,11 @@ git commit -m "chore: finalize context for {session-name}"
202
193
  git push -u origin {branch}
203
194
  ```
204
195
 
205
- The push shape is the same as 8a — what differs is the merge mechanics in Step 9b.
196
+ The push shape is the same as 9a — what differs is the merge mechanics in Step 10b.
206
197
 
207
- ### Step 9: Merge and present unified summary
198
+ ### Step 10: Merge and present unified summary
208
199
 
209
- #### Step 9a: GitHub remotes — create PRs, unified summary, merge
200
+ #### Step 10a: GitHub remotes — create PRs, unified summary, merge
210
201
 
211
202
  Create one PR per project repo plus one workspace PR:
212
203
 
@@ -258,7 +249,7 @@ cd repos/{repo} && git pull origin {repo-branch}
258
249
  cd {main-workspace-root} && git pull origin main
259
250
  ```
260
251
 
261
- #### Step 9b: Local / bare / other remotes — local merge flow
252
+ #### Step 10b: Local / bare / other remotes — local merge flow
262
253
 
263
254
  No PRs are created — these remotes don't have a PR concept (or we don't have a client wired up for them). Present an adjusted summary:
264
255
 
@@ -304,7 +295,7 @@ For repos with no remote at all (user chose "keep local"): skip push entirely. T
304
295
  cd repos/{repo} && git merge --ff-only {branch}
305
296
  ```
306
297
 
307
- ### Step 10: Close the linked issue on the tracker
298
+ ### Step 11: Close the linked issue on the tracker
308
299
 
309
300
  If the session tracker has a `workItem:` field AND `workspace.tracker` is configured, close the linked issue via the adapter after all PRs have merged:
310
301
 
@@ -328,9 +319,9 @@ if (ws.workspace?.tracker) {
328
319
 
329
320
  If `workItem:` is unset, skip the close — this was a blank session.
330
321
 
331
- If the close call fails (tracker unreachable, auth expired), report the error in the unified summary but do not block Step 11 cleanup. The issue can be closed manually via the GitHub UI; no data is at risk.
322
+ If the close call fails (tracker unreachable, auth expired), report the error in the unified summary but do not block Step 12 cleanup. The issue can be closed manually via the GitHub UI; no data is at risk.
332
323
 
333
- ### Step 11: Cleanup
324
+ ### Step 12: Cleanup
334
325
 
335
326
  Run the cleanup helper script from the main workspace root:
336
327
  ```bash
@@ -342,7 +333,7 @@ The script tears down in the **mandatory** order:
342
333
  2. Remove the workspace worktree from the workspace repo
343
334
  3. `git worktree prune` on each project repo (belt-and-suspenders for orphan records)
344
335
  4. Delete local branches in all repos
345
- 5. `rm -rf work-sessions/{session-name}/` — the tracker, specs, plans, and any local-only artifacts vanish. Their content was already archived into release notes in Step 5.
336
+ 5. `rm -rf work-sessions/{session-name}/` — the tracker, specs, plans, and any local-only artifacts vanish. Their content was already archived into release notes in Step 6.
346
337
 
347
338
  Workspace-first removal silently deletes the nested project worktrees' `.git` files and leaves orphan worktree records in the project repos. The script enforces the safe order.
348
339
 
@@ -362,8 +353,9 @@ Ask: "These changes weren't part of a formal work session. What do you want to d
362
353
  - **Revert** — undo the changes (with confirmation)
363
354
 
364
355
  ## Notes
365
- - Release notes live in the PROJECT repo worktrees — never the workspace
356
+ - Branch release notes live in the WORKSPACE repo at `release-notes/unreleased/{repo-name}/` — never in project repos. Project repos only ever see code commits and (at release time) `CHANGELOG.md` entries written by `/release`.
366
357
  - The session tracker's body is the primary source for release note synthesis — it captures the full session history alongside specs and plans
367
358
  - All repos get PRed and merged together — one approval for all
359
+ - Version bumps happen in `/release`, not `/complete-work` — this avoids version drift when multiple feature branches land between releases
368
360
  - The teardown order is mandatory: project worktrees first, then workspace worktree, then prune, then delete the session folder
369
361
  - Context consumption, cleanup, and auto-committing release notes are intentional workflow behavior — these bypass normal commit conventions by design
@@ -84,6 +84,21 @@ updated: {YYYY-MM-DD}
84
84
  - If no: ask for a single name that covers both
85
85
  4. Proceed with the named flow for each handoff
86
86
 
87
+ ## Include task snapshot
88
+
89
+ If an active session exists (detected via `.claude/.active-session.json`), include a `## Tasks at capture time` section in the handoff artifact with a snapshot of the current `TodoWrite` state:
90
+
91
+ ```markdown
92
+ ## Tasks at capture time
93
+
94
+ - [x] Start work
95
+ - [x] Reproduce on iOS Safari
96
+ - [ ] Identify race condition
97
+ - [ ] Complete work
98
+ ```
99
+
100
+ Use the same GFM checkbox format as `session.md`'s `## Tasks` section (just `content` and `status` per task — no `activeForm` field, no blockquote line) and render it inline in the handoff. Do NOT call `sync-tasks.mjs --write` — handoffs are snapshots, not the canonical store.
101
+
87
102
  ## Updating Existing Handoffs
88
103
 
89
104
  When updating an existing handoff, rewrite it as a fresh snapshot of current understanding (coherent-revisions rule). Don't append below the old content. The updated handoff should read as if written in one pass reflecting the current state.
@@ -30,6 +30,7 @@ For each shared-context `.md` file and each `work-sessions/*/workspace/session.m
30
30
  - `lifecycle: active` on a file not updated in 7+ days? (stale candidate)
31
31
  - `lifecycle: resolved` files that should have been processed by /complete-work?
32
32
  - Session tracker `status: active` but the workspace worktree at `work-sessions/{name}/workspace/` is missing? (orphaned)
33
+ - `confidence` field present? Must be one of `high`, `medium`, `low` if set.
33
34
 
34
35
  ### 3. Workspace structure
35
36
  - Actual directory layout matches what workspace-structure rule describes?
@@ -44,24 +45,63 @@ For each shared-context `.md` file and each `work-sessions/*/workspace/session.m
44
45
  - Workspace repo on expected branch?
45
46
  - Orphan worktree records in project repos — run `git -C repos/{repo} worktree list` for each repo and flag any `prunable` markers. These usually come from a workspace-first teardown (the unsafe order) leaving stale admin records behind. Suggest `git worktree prune` on the affected repo.
46
47
 
48
+ ### 5. Shared-context index integrity
49
+
50
+ `shared-context/index.md` is auto-generated from frontmatter. Run the check:
51
+
52
+ ```bash
53
+ node .claude/scripts/build-shared-context-index.mjs --check --root .
54
+ ```
55
+
56
+ Three failure modes (script exits 1 with a JSON status):
57
+
58
+ - `missing` — `shared-context/` exists but `index.md` does not. Run `--write` to create.
59
+ - `stale` — `index.md` exists but its entries no longer match the filesystem. Causes: a file was added or deleted, a `description:` was changed, a `.indexignore` rule was added. Run `--write` to regenerate.
60
+ - (no failure) — `current` with the entry count.
61
+
62
+ Audit mode reports the status. Cleanup mode runs `--write` if stale or missing, then re-checks.
63
+
64
+ While the index is being read, also flag entries with weak fallbacks: filename-slug-only descriptions (e.g., "project status" with no period) usually indicate the underlying file is missing a `description:` or has no usable opening sentence. Suggest adding `description:` to those source files — the index will pick it up on the next regeneration.
65
+
66
+ ### 6. Template freshness
67
+
68
+ Compare the workspace's pinned template version against the latest published on npm.
69
+
70
+ Always invoke `refreshIfStale` from the audit (regardless of `workspace.versionCheck.ambient` — the user explicitly ran `/maintenance`):
71
+
72
+ ```javascript
73
+ import { refreshIfStale } from './.claude/lib/freshness.mjs';
74
+ const result = await refreshIfStale({
75
+ workspaceRoot: process.cwd(),
76
+ ttlMs: 24 * 60 * 60 * 1000,
77
+ });
78
+ ```
79
+
80
+ Report one of:
81
+ - `outdated` → `✗ Template v{current} → v{latest} available. Run npx @ulysses-ai/create-workspace --upgrade.`
82
+ - `current` → `✓ Template is up to date (v{latest}).`
83
+ - `unknown` (with cache) → `⚠ Could not reach npm registry; last cached latest was v{latest} as of {checkedAt}.`
84
+ - `unknown` (no cache) → `⚠ Could not reach npm registry; no cached version on file. Try again when online.`
85
+ - `skipped: 'uninitialized'` → `⚠ Workspace not initialized; freshness check unavailable.`
86
+
47
87
  ## Cleanup
48
88
 
49
89
  Active recommendations. Flags problems and suggests fixes, but asks before acting.
50
90
 
51
- ### 5. Stale context
91
+ ### 7. Stale context
52
92
  - Ephemeral files not updated in 7+ days — suggest resolve, update, or archive
53
93
  - `work-sessions/{name}/` folders whose worktrees are gone — suggest cleanup
54
94
  - Session trackers whose branches have been merged — suggest `/complete-work` post-flight cleanup
55
95
  - Braindumps that overlap significantly — suggest merging (e.g., "workspace-branching.md and persistent-work-sessions.md cover the same topic")
56
96
  - Handoffs referencing deleted branches — suggest resolve or remove
57
97
 
58
- ### 6. Context reconciliation
98
+ ### 8. Context reconciliation
59
99
  - Read recent shared-context writes (last session or last N files by updated date)
60
100
  - For each, scan other shared-context files for references that are now stale
61
101
  - Surface: "{file} says X but {newer-file} now says Y. Update {file}?"
62
102
  - This is the capture-time cross-check, run retroactively instead of inline
63
103
 
64
- ### 7. Health metrics
104
+ ### 9. Health metrics
65
105
  - Size of `shared-context/locked/` relative to the active model's context window — flag if over 5% (yellow) or 15% (red). Absolute byte count is a weak proxy; contradictions, stale references, and duplicated coverage across files matter more than total size.
66
106
  - Number of ephemeral files — flag if accumulating without resolution
67
107
  - Session log stats (if `workspace-scratchpad/session-log.jsonl` exists):
@@ -90,11 +130,12 @@ Cleanup suggestions (2):
90
130
  ⊕ migration-recipes.md still says "/sync handles dogfood" but
91
131
  /sync was replaced by /sync-work — update?
92
132
 
93
- OK (4):
133
+ OK (5):
94
134
  ✓ All CLAUDE.md skill references valid
95
135
  ✓ Workspace structure matches rule
96
136
  ✓ workspace.json repos all present
97
137
  ✓ No frontmatter errors
138
+ ✓ Template is up to date (v0.14.0)
98
139
  ```
99
140
 
100
141
  ## Flow
@@ -104,9 +145,10 @@ OK (4):
104
145
  3. Read workspace.json — extract repo manifest
105
146
  4. Check `.claude/rules/`, `.claude/skills/`, `.claude/agents/` against references
106
147
  5. Check git state (worktrees, branches, remotes)
107
- 6. Read session-log.jsonl if it exists
108
- 7. If cleanup mode: compare files pairwise for overlap, scan for stale cross-references
109
- 8. Compile and present findings grouped by severity
148
+ 6. Run `node .claude/scripts/build-shared-context-index.mjs --check --root .` — capture status
149
+ 7. Read session-log.jsonl if it exists
150
+ 8. If cleanup mode: regenerate the shared-context index if stale; compare files pairwise for overlap; scan for stale cross-references
151
+ 9. Compile and present findings grouped by severity
110
152
 
111
153
  ## Notes
112
154
  - Audit mode is always read-only — never modifies files
@@ -54,7 +54,28 @@ If `workItem:` is unset, skip the comment — this is a blank session with no tr
54
54
 
55
55
  If the comment fails (tracker unreachable, auth expired), report the error but do not block the pause. The pause state lives locally in the session tracker regardless.
56
56
 
57
- ### Step 4: Commit and push workspace
57
+ ### Step 4: Flush task list to session.md
58
+
59
+ Before the commit picks it up, flush current `TodoWrite` state to `## Tasks` per the `task-list-mirroring` rule:
60
+
61
+ ```bash
62
+ cd work-sessions/{session-name}/workspace
63
+ echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
64
+ ```
65
+
66
+ The `<JSON-of-current-todos>` is the same shape Claude has been maintaining via `TodoWrite`:
67
+
68
+ ```json
69
+ {
70
+ "todos": [
71
+ { "content": "...", "activeForm": "...", "status": "pending|in_progress|completed" }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ The helper enforces the bookend invariant — pass whatever current state you have, including any missing or misplaced bookends, and the helper will normalize.
77
+
78
+ ### Step 5: Commit and push workspace
58
79
 
59
80
  ```bash
60
81
  # From the workspace worktree
@@ -64,7 +85,7 @@ git commit -m "handoff: pause {session-name}"
64
85
  git push -u origin {branch}
65
86
  ```
66
87
 
67
- ### Step 5: Push project repos
88
+ ### Step 6: Push project repos
68
89
 
69
90
  ```bash
70
91
  # For each repo in the tracker's repos:
@@ -72,7 +93,7 @@ cd work-sessions/{session-name}/workspace/repos/{repo}
72
93
  git push -u origin {branch}
73
94
  ```
74
95
 
75
- ### Step 6: Create draft PRs
96
+ ### Step 7: Create draft PRs
76
97
 
77
98
  ```bash
78
99
  # For each repo in the tracker's repos:
@@ -85,7 +106,7 @@ gh pr create --draft --title "context: {session-name} (paused)" --body "Workspac
85
106
 
86
107
  If PRs already exist, update them to draft status if needed.
87
108
 
88
- ### Step 7: Confirm
109
+ ### Step 8: Confirm
89
110
 
90
111
  "Session '{session-name}' paused. Resume anytime with /start-work."
91
112
 
@@ -1,14 +1,20 @@
1
1
  ---
2
2
  name: release
3
- description: Combine unreleased branch notes into a versioned release document. Targets project repos, not the workspace. Synthesizes ephemeral shared context into locked entries. Use at release time.
3
+ description: Prepend a new CHANGELOG.md entry per project repo by synthesizing unreleased branch notes. Deletes consumed branch notes and synthesizes workspace shared-context into locked entries. Use at release time.
4
4
  ---
5
5
 
6
6
  # Release
7
7
 
8
- Combine unreleased branch-release-notes into a versioned document per project repo. Synthesize ephemeral workspace context into locked team knowledge.
8
+ Synthesize unreleased branch notes (in the **workspace** repo) into a concise, user-facing entry at the top of each project repo's `CHANGELOG.md`. Delete the consumed branch notes from the workspace. Bump the project repo's `package.json` version. In parallel, promote resolved workspace shared-context into locked team knowledge.
9
+
10
+ ## Why this shape
11
+
12
+ Branch notes are detailed dogfood-retrospective artifacts that should not bloat public project repos. By keeping them in the workspace repo until release time, the project repo stays lean — it only ever sees code commits and `CHANGELOG.md` entries. A single `CHANGELOG.md` with one concise entry per version is what users of a published package actually want. Branch notes remain the input format for `/complete-work` (they capture per-session detail at the right moment), but they live in the workspace and are consumed-then-deleted by `/release`.
13
+
14
+ Versions are bumped here, not in `/complete-work`, because version semantics describe what shipped — accumulated changes since the last release — not the timing of any individual feature merge.
9
15
 
10
16
  ## Parameters
11
- - `/release {version}` — create release notes for a specific version
17
+ - `/release {version}` — create a release entry for a specific version
12
18
  - `/release` — ask for the version
13
19
 
14
20
  ## Flow
@@ -22,66 +28,65 @@ Check `workspace.json` for `releaseMode`:
22
28
  - **ask**: "Process all repos together or individually?"
23
29
 
24
30
  **Step 2: Read unreleased notes**
25
- For each target repo:
31
+ Branch notes live in the **workspace** repo, written there by `/complete-work`. For each target repo, list the workspace's unreleased subdirectory for that project:
26
32
  ```bash
27
- ls repos/{repo}/release-notes/unreleased/
33
+ ls release-notes/unreleased/{repo}/
28
34
  ```
29
35
  Read all `branch-release-notes-*.md` and `branch-release-questions-*.md` files.
30
36
 
31
- If no unreleased files exist: "No unreleased notes found for {repo}. Nothing to release."
37
+ If no unreleased files exist for a target repo: "No unreleased notes found for {repo}. Nothing to release."
38
+
39
+ The frontmatter `repo:` field on each branch-notes file confirms which project repo the notes belong to — match that to the directory name as a sanity check. Notes mismatched on `repo:` are a sign of manual file moves; surface to the user.
32
40
 
33
41
  **Step 3: Group and organize**
34
- Group notes by type using `type:` frontmatter (feature, fix, chore):
35
- - Features first, then fixes, then chores
36
- - Within each group, order chronologically by date
42
+ Group notes by `type:` frontmatter (feature, fix, chore). Within each group, order chronologically by date. This ordering drives bullet sequence in the synthesized entry.
37
43
 
38
44
  **Step 4: Handle questions**
39
45
  Present all open questions from `branch-release-questions-*.md` files:
40
46
  "These questions are still open from development. For each one:"
41
47
  - **Answer** — provide the answer, remove from questions
42
- - **Defer** — keep in a "Known Issues" section of the release notes
48
+ - **Defer** — keep as a "Known issues" sub-bullet in the CHANGELOG entry
43
49
  - **Discard** — no longer relevant
44
50
 
45
- **Step 5: Synthesize release document**
46
- Write `repos/{repo}/release-notes/v{version}.md`:
47
- ```markdown
48
- # v{version} Release Notes
49
-
50
- **Date:** {YYYY-MM-DD}
51
+ **Step 5: Synthesize the CHANGELOG entry**
51
52
 
52
- ## Features
53
- {Coherent narrative combining all feature branch-release-notes.
54
- Deduplicate related items. Credit authors.
55
- Write from scratch — don't concatenate. Coherent-revisions rule applies.}
53
+ Read the current `repos/{repo}/CHANGELOG.md` (if it exists) so the new entry matches the existing voice and structure. If no CHANGELOG exists, create one with a short header explaining that entries are written for package users, not contributors.
56
54
 
57
- ## Fixes
58
- {Same treatment for bugfix branches.}
55
+ Prepend a new section at the top of the changelog body (after the header, before any existing version entries). Write it user-facing — what shipped, not how it shipped:
59
56
 
60
- ## Maintenance
61
- {Same for chore branches.}
57
+ ```markdown
58
+ ## v{version} {YYYY-MM-DD}
62
59
 
63
- ## Known Issues
64
- {Deferred questions from Step 4, if any.}
60
+ - {Concise bullet per meaningful change. Features, fixes, and chores interleaved
61
+ by significance, not by category. Each bullet is one sentence or short paragraph
62
+ in plain user-facing language: "the CLI now supports X", "corrected Y behavior
63
+ on Z", not "we decided" or "the team merged." Deduplicate related items.
64
+ Write from scratch per the coherent-revisions rule.}
65
65
 
66
- ## Contributors
67
- {List of unique authors from all branch notes.}
66
+ ### Known issues
67
+ - {Deferred questions from Step 4, if any. Omit this subsection when empty.}
68
68
  ```
69
69
 
70
- **Step 6: Archive unreleased files**
70
+ The entry stays short. If a change needs more detail, reference the repo's docs or a dedicated design doc — do not inline session-level retrospection into the public changelog.
71
+
72
+ **Step 6: Delete consumed branch notes from the workspace**
71
73
  ```bash
72
- mkdir -p repos/{repo}/release-notes/archive/v{version}
73
- mv repos/{repo}/release-notes/unreleased/branch-release-* repos/{repo}/release-notes/archive/v{version}/
74
+ rm release-notes/unreleased/{repo}/branch-release-*
75
+ # If the directory is now empty, remove it too:
76
+ rmdir release-notes/unreleased/{repo} 2>/dev/null || true
74
77
  ```
78
+ The branch notes were an intermediate capture; their content is now in the CHANGELOG entry and their raw form in git history. They do not survive into the project repo.
75
79
 
76
- **Step 7: Commit release notes to project repo**
80
+ **Step 7: Commit the CHANGELOG entry to the project repo**
77
81
  ```bash
78
82
  cd repos/{repo}
79
- git add release-notes/
80
- git commit -m "docs: v{version} release notes"
83
+ git add CHANGELOG.md
84
+ git commit -m "docs: v{version} changelog entry"
81
85
  ```
86
+ This commit lands on the project repo's source clone (which stays on its default branch). The user pushes it when ready — `/release` does not push automatically.
82
87
 
83
- **Step 7b: Bump package.json version**
84
- If the project repo has a `package.json` with a `version` field (as in the scaffolder repo), update it to match the release version:
88
+ **Step 7b: Bump package.json version (project repo)**
89
+ If the project repo has a `package.json` with a `version` field, update it to match the release version:
85
90
  ```bash
86
91
  cd repos/{repo}
87
92
  # Update "version": "..." in package.json to the release version
@@ -90,9 +95,17 @@ git commit -m "chore: bump version to v{version}"
90
95
  ```
91
96
  Skip this step if the repo has no package.json or no version field.
92
97
 
98
+ **Step 7c: Commit the consumed-notes deletion in the workspace**
99
+ ```bash
100
+ # From the workspace root
101
+ git add release-notes/unreleased/
102
+ git commit -m "release: consume {repo} branch notes for v{version}"
103
+ ```
104
+ Workspace and project repos have separate commits — they are separate git histories.
105
+
93
106
  **Step 8: Consume project-scoped specs**
94
107
  Project-scoped specs and plans in `shared-context/{user}/` (ongoing) that are fully covered by this release:
95
- - Consume into the release notes (their content is now captured in the versioned doc)
108
+ - Consume into the CHANGELOG entry (their content is now captured there)
96
109
  - Remove the source files
97
110
  - If partially covered: rewrite the spec to reflect only what remains unimplemented
98
111
 
@@ -116,11 +129,14 @@ Process ephemeral shared-context entries:
116
129
  ```
117
130
 
118
131
  **Step 10: Report**
119
- "Release v{version} complete for {repo}. {N} branch notes combined. {M} context entries synthesized into {K} locked entries."
132
+ "Release v{version} complete for {repo}. {N} branch notes consumed into CHANGELOG.md. {M} context entries synthesized into {K} locked entries."
120
133
 
121
134
  ## Notes
122
- - Release notes live in the PROJECT repo — the workspace never gets versioned
123
- - Context synthesis happens in the WORKSPACE repo — both get separate commits
124
- - The archive directory preserves raw branch notes for audit
125
- - Per-repo is the default each repo has its own release cadence
126
- - The coherent-revisions rule applies: write the release narrative from scratch, don't concatenate branch notes
135
+
136
+ - Release entries live in `CHANGELOG.md` at the project repo root one file, one concise entry per version. No `release-notes/v*.md`, no `release-notes/archive/`.
137
+ - Branch notes live in the WORKSPACE at `release-notes/unreleased/{repo}/`. `/complete-work` writes them; `/release` consumes and deletes them. They never reach project repos.
138
+ - Versions are bumped here, not in `/complete-work`. This keeps the version semantics aligned with what actually shipped (accumulated changes since last release).
139
+ - The public repo stays lean. Detailed per-branch retrospection exists in workspace git history (the consumed-notes commit) but is not surfaced as standalone files in either repo.
140
+ - Context synthesis happens in the WORKSPACE repo — Step 7c (consumed-notes) and Step 9 (shared-context synthesis) are separate workspace commits.
141
+ - Per-repo is the default — each project repo has its own release cadence.
142
+ - The coherent-revisions rule applies: write the CHANGELOG entry from scratch, don't concatenate branch notes.
@@ -53,8 +53,14 @@ Begin or resume a persistent work session. Each session lives in its own `work-s
53
53
  - `names` is a list of all names the chat has had (users can rename). Append, never replace.
54
54
  - `ended` is set by the session-end hook when the chat closes.
55
55
  4. Update the tracker `status:` to `active` if it was `paused`
56
- 5. Run history reconstruction (see below)
57
- 6. Tell user: "Resuming {name}. Work from `work-sessions/{name}/workspace/`."
56
+ 5. Restore the task list from `## Tasks` per the `task-list-mirroring` rule:
57
+ ```bash
58
+ cd work-sessions/{name}/workspace
59
+ node .claude/scripts/sync-tasks.mjs --read session.md
60
+ ```
61
+ Pass the parsed `todos` array to `TodoWrite` so the live UI matches the durable state. If the section is missing (legacy session predating this feature), seed it first via `--write` with an empty `todos` array — the helper will insert the bookends.
62
+ 6. Run history reconstruction (see below)
63
+ 7. Tell user: "Resuming {name}. Work from `work-sessions/{name}/workspace/`."
58
64
 
59
65
  ### History Reconstruction
60
66
 
@@ -165,6 +171,32 @@ Register this chat in the tracker's `chatSessions` frontmatter. For new sessions
165
171
 
166
172
  The tracker already reflects the correct state — assignment happened in step 5 or 6 via `adapter.claim()`. Do not write to any local file mirror. There is no `open-work.md`.
167
173
 
174
+ ### Seed the task list
175
+
176
+ After session creation, seed the `## Tasks` section in the new tracker so `TodoWrite` has something to mirror. See the `task-list-mirroring` rule for the schema.
177
+
178
+ ```bash
179
+ # Build the seed from inside the worktree so the helper resolves workspace.json correctly.
180
+ cd work-sessions/{session-name}/workspace
181
+ echo '{"todos": []}' | node .claude/scripts/sync-tasks.mjs --write session.md
182
+ ```
183
+
184
+ The helper auto-inserts the `Start work` (completed) and `Complete work` (pending) bookends, and resolves the tracker title from `workItem:` if set.
185
+
186
+ Then call `TodoWrite` with the same seed so the live UI matches:
187
+
188
+ ```javascript
189
+ // Pseudocode — call the actual TodoWrite tool.
190
+ TodoWrite({
191
+ todos: [
192
+ { content: 'Start work', activeForm: 'Starting work', status: 'completed' },
193
+ { content: 'Complete work', activeForm: 'Completing work', status: 'pending' },
194
+ ]
195
+ });
196
+ ```
197
+
198
+ The auto-commit at the end of "Capture prior conversation context" picks up the new section — no separate commit needed.
199
+
168
200
  ### Capture prior conversation context
169
201
 
170
202
  If brainstorming, spec writing, or design discussion happened in this conversation before `/start-work` was called, that reasoning needs to be captured into the session tracker body. Otherwise it will be lost when the conversation ends and `/complete-work` will produce thin release notes.
@@ -75,6 +75,22 @@ Also handle these non-component files from the payload:
75
75
 
76
76
  Read `toVersion` from `.workspace-update/.manifest.json` and update `templateVersion` in `workspace.json` to match.
77
77
 
78
+ ### Step 4a: Run idempotent migrators
79
+
80
+ The payload may include migrator scripts at `.workspace-update/.claude/scripts/migrate-*.mjs` that bring older workspaces forward in shape. They are idempotent — safe to re-run on already-migrated workspaces. Run each one and surface its action in the upgrade summary.
81
+
82
+ ```bash
83
+ node .workspace-update/.claude/scripts/migrate-claude-md-freshness-include.mjs
84
+ ```
85
+
86
+ Output is JSON: `{"action":"appended"|"unchanged"|"skipped"}`.
87
+
88
+ - `appended` — the workspace's `CLAUDE.md` got the `@local-only-template-freshness.md` include line added at the end.
89
+ - `unchanged` — the line was already present.
90
+ - `skipped` — no `CLAUDE.md` exists at the workspace root (rare; surface to the user).
91
+
92
+ Add other migrators here as the template ships them.
93
+
78
94
  ### Step 5: Post-update verification
79
95
 
80
96
  Run `/maintenance audit` again to verify the update didn't introduce: