@ulysses-ai/create-workspace 0.17.0-beta.0 → 0.19.0-beta.0
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.
- package/README.md +3 -3
- package/lib/init.mjs +4 -1
- package/lib/payload.mjs +18 -1
- package/lib/payload.test.mjs +55 -0
- package/lib/scaffold.mjs +23 -6
- package/lib/scaffold.test.mjs +59 -0
- package/package.json +1 -1
- package/template/CLAUDE.md.tmpl +19 -2
- package/template/{.claude → _claude}/hooks/_utils.mjs +1 -1
- package/template/_claude/hooks/repo-write-detection.mjs +204 -0
- package/template/{.claude → _claude}/hooks/session-start.mjs +35 -1
- package/template/_claude/hooks/subagent-start.mjs +111 -0
- package/template/{.claude → _claude}/lib/session-frontmatter.mjs +28 -0
- package/template/{.claude → _claude}/rules/coherent-revisions.md +1 -1
- package/template/_claude/rules/forge-operations.md +57 -0
- package/template/_claude/rules/git-conventions.md +39 -0
- package/template/_claude/rules/goal-driven-work.md +24 -0
- package/template/_claude/rules/honest-pushback.md +56 -0
- package/template/_claude/rules/memory-guidance.md +66 -0
- package/template/{.claude → _claude}/rules/superpowers-workflow.md.skip +1 -1
- package/template/{.claude → _claude}/rules/task-list-mirroring.md +6 -0
- package/template/_claude/rules/work-item-tracking.md +48 -0
- package/template/_claude/rules/workspace-structure.md +79 -0
- package/template/{.claude → _claude}/scripts/build-workspace-context.mjs +86 -30
- package/template/_claude/scripts/chat-record.mjs +315 -0
- package/template/_claude/scripts/cleanup-work-session.mjs +436 -0
- package/template/_claude/scripts/context-footprint.mjs +391 -0
- package/template/{.claude → _claude}/scripts/forges/github.mjs +46 -0
- package/template/{.claude → _claude}/scripts/forges/gitlab.mjs +3 -2
- package/template/{.claude → _claude}/scripts/forges/interface.mjs +13 -0
- package/template/{.claude → _claude}/scripts/generate-claude-local.mjs +21 -2
- package/template/_claude/scripts/migrate-sessions.mjs +1571 -0
- package/template/{.claude → _claude}/scripts/migrate-to-workspace-context.mjs +7 -2
- package/template/_claude/scripts/task-pr.mjs +447 -0
- package/template/_claude/scripts/task-worktree.mjs +525 -0
- package/template/{.claude → _claude}/scripts/trackers/github-issues.mjs +11 -0
- package/template/{.claude → _claude}/scripts/trackers/interface.mjs +8 -0
- package/template/_claude/scripts/workspace-diagnostics.mjs +654 -0
- package/template/{.claude → _claude}/skills/braindump/SKILL.md +12 -4
- package/template/{.claude → _claude}/skills/build-docs-site/SKILL.md +5 -5
- package/template/{.claude → _claude}/skills/build-docs-site/templates/spec.md.tmpl +1 -1
- package/template/_claude/skills/complete-work/SKILL.md +452 -0
- package/template/_claude/skills/context-placement/SKILL.md +202 -0
- package/template/{.claude/rules/goal-driven-work.md → _claude/skills/goal-driven-work/SKILL.md} +46 -19
- package/template/{.claude → _claude}/skills/handoff/SKILL.md +12 -4
- package/template/{.claude → _claude}/skills/maintenance/SKILL.md +56 -17
- package/template/_claude/skills/migrate-sessions/SKILL.md +70 -0
- package/template/{.claude → _claude}/skills/pause-work/SKILL.md +9 -1
- package/template/_claude/skills/release/SKILL.md +91 -0
- package/template/{.claude → _claude}/skills/start-work/SKILL.md +89 -7
- package/template/{.claude → _claude}/skills/workspace-init/SKILL.md +3 -1
- package/template/{.claude → _claude}/skills/workspace-update/SKILL.md +4 -0
- package/template/_gitignore +9 -0
- package/template/workspace.json.tmpl +4 -3
- package/template/.claude/hooks/repo-write-detection.mjs +0 -107
- package/template/.claude/hooks/subagent-start.mjs +0 -44
- package/template/.claude/rules/forge-operations.md +0 -107
- package/template/.claude/rules/git-conventions.md +0 -34
- package/template/.claude/rules/honest-pushback.md +0 -56
- package/template/.claude/rules/memory-guidance.md +0 -109
- package/template/.claude/rules/work-item-tracking.md +0 -90
- package/template/.claude/rules/workspace-structure.md +0 -137
- package/template/.claude/scripts/cleanup-work-session.mjs +0 -247
- package/template/.claude/skills/complete-work/SKILL.md +0 -498
- package/template/.claude/skills/release/SKILL.md +0 -151
- /package/template/{.claude → _claude}/agents/aside-researcher.md +0 -0
- /package/template/{.claude → _claude}/agents/implementer.md +0 -0
- /package/template/{.claude → _claude}/agents/researcher.md +0 -0
- /package/template/{.claude → _claude}/agents/reviewer.md +0 -0
- /package/template/{.claude → _claude}/hooks/bash-output-advisory.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/post-compact.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/pre-compact.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/session-end.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/version-freshness-check.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/workspace-update-check.mjs +0 -0
- /package/template/{.claude → _claude}/lib/freshness.mjs +0 -0
- /package/template/{.claude → _claude}/lib/registry-check.mjs +0 -0
- /package/template/{.claude → _claude}/lib/require-node.mjs +0 -0
- /package/template/{.claude → _claude}/recipes/migrate-from-notion.md +0 -0
- /package/template/{.claude → _claude}/rules/agent-rules.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/cloud-infrastructure.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/config-review.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/documentation.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/local-dev-environment.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/product-integrity.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/scope-guard.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/token-economics.md.skip +0 -0
- /package/template/{.claude → _claude}/scripts/add-repo-to-session.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/capture-context.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/create-work-session.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-canonical-priority.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-claude-md-freshness-include.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-open-work.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-session-layout.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/sweep-references.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/sync-tasks.mjs +0 -0
- /package/template/{.claude → _claude}/settings.json +0 -0
- /package/template/{.claude → _claude}/skills/aside/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/framing.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/pitfalls.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/review.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/bulk-fill-migration.py +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/forbidden-word-grep.mjs +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/leak-grep.mjs +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/custom.css.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/docusaurus.config.ts.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Arrow.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Box.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/DiagramContainer.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Region.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/SectionTitle.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/tokens.ts +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/sidebars.ts.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/promote/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/setup-tracker/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/sync-work/SKILL.md +0 -0
- /package/template/{.mcp.json → _mcp.json} +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-placement
|
|
3
|
+
description: Use when deciding where a durable fact, instruction, or procedure belongs — a rule, canonical context, shared or personal context, auto-memory, or a skill — and when writing it once the destination is settled. Routes the decision, states the always-loaded cost before anything is written, and carries the frontmatter schema, generator invocations, and the canonical admission test.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Context Placement
|
|
7
|
+
|
|
8
|
+
Something is worth keeping. This skill decides where it goes, and makes the cost of that
|
|
9
|
+
choice visible before you write it.
|
|
10
|
+
|
|
11
|
+
The hard part is routing, not prose. Most placement mistakes are not badly written files —
|
|
12
|
+
they are correctly written files in a destination that charges every future session for
|
|
13
|
+
them. Two of this workspace's own bugs (gh:136, gh:138) were exactly that: rules that had
|
|
14
|
+
quietly absorbed reference material and worked examples until the always-loaded directory
|
|
15
|
+
cost more than the conversation.
|
|
16
|
+
|
|
17
|
+
## The destinations
|
|
18
|
+
|
|
19
|
+
Exactly one of these. The right-hand column is what every session pays, forever, for the
|
|
20
|
+
choice.
|
|
21
|
+
|
|
22
|
+
| destination | route here when | always-loaded cost |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| **nowhere** | it is already covered somewhere, or it is true only today | zero |
|
|
25
|
+
| `.claude/rules/{name}.md` with `paths:` | an instruction that applies only when specific files are touched | zero until a matching file is read |
|
|
26
|
+
| `.claude/skills/{name}/SKILL.md` | a procedure with steps, invoked on demand | the `description` line, ~200 B |
|
|
27
|
+
| auto-memory | a machine-local preference or correction; not shared, not reviewable | one `MEMORY.md` line, ~100 B |
|
|
28
|
+
| `workspace-context/team-member/{user}/` | one person's working context | one index line, ~120 B, that user only |
|
|
29
|
+
| `workspace-context/shared/` | team-visible reference to look up when relevant | one index line, ~120 B |
|
|
30
|
+
| `workspace-context/shared/locked/` | a team-wide fact or constraint that passes the canonical test | **the whole file, every session** |
|
|
31
|
+
| `.claude/rules/{name}.md` (no `paths:`) | an instruction that must shape every session, everywhere in the repo | **the whole file, every session** |
|
|
32
|
+
|
|
33
|
+
The table is ordered by cost, cheapest first. Work down it and stop at the first
|
|
34
|
+
destination that genuinely fits. The two bold rows are the only ones that tax every
|
|
35
|
+
session; reach them only after the cheaper ones have actually been ruled out, not skipped.
|
|
36
|
+
|
|
37
|
+
**`nowhere` is the most common correct answer.** Before anything else, check whether the
|
|
38
|
+
thing is already stated. A second copy in a second location is worse than no copy: they
|
|
39
|
+
drift, and the reader cannot tell which one is current.
|
|
40
|
+
|
|
41
|
+
## Step 1 — classify what you have
|
|
42
|
+
|
|
43
|
+
Three kinds. The kind determines which destinations are even eligible.
|
|
44
|
+
|
|
45
|
+
- **Instruction** — changes what Claude *does*. "Never force push." "Rebase before opening
|
|
46
|
+
a PR." Imperative, applies without being asked for. Eligible: rules (scoped or not).
|
|
47
|
+
- **Reference** — something Claude needs to *know* when a particular topic comes up. API
|
|
48
|
+
shapes, schemas, architecture facts, why an approach was rejected. Eligible: canonical,
|
|
49
|
+
shared, team-member, auto-memory.
|
|
50
|
+
- **Procedure** — a sequence of steps with a beginning and an end, run on request.
|
|
51
|
+
Eligible: a skill.
|
|
52
|
+
|
|
53
|
+
Most drift comes from putting reference or procedure content in a rule. A rule that
|
|
54
|
+
contains an API table or a worked example is carrying reference material at instruction
|
|
55
|
+
prices. Split it: the instruction stays in the rule, the detail moves to a skill or a
|
|
56
|
+
context file, and the rule points at it in one line.
|
|
57
|
+
|
|
58
|
+
## Step 2 — scope it
|
|
59
|
+
|
|
60
|
+
For an instruction, the only question that matters is *when must this be true?*
|
|
61
|
+
|
|
62
|
+
- Every session, every file → unconditional rule.
|
|
63
|
+
- Only when certain files are involved → rule with `paths:`.
|
|
64
|
+
|
|
65
|
+
`paths:` is a real Claude Code feature and is almost always the better answer for anything
|
|
66
|
+
domain-specific. A rule about migration scripts, or about a single repo's test conventions,
|
|
67
|
+
does not need to be in context while you are editing documentation.
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
---
|
|
71
|
+
paths:
|
|
72
|
+
- ".claude/scripts/**/*.mjs"
|
|
73
|
+
- "repos/*/template/_claude/scripts/**/*.mjs"
|
|
74
|
+
---
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Rules with `paths:` load only when Claude reads a matching file. Rules without it load at
|
|
78
|
+
launch, at the same priority as `CLAUDE.md`, in every session — and, for anything shipped
|
|
79
|
+
in the template, in every downstream workspace too.
|
|
80
|
+
|
|
81
|
+
For reference content, scope is about reach. Pick the narrowest that works: auto-memory
|
|
82
|
+
(this machine only) → `team-member/{user}/` (one person) → `shared/` (the team) →
|
|
83
|
+
`shared/locked/` (the team, always loaded).
|
|
84
|
+
|
|
85
|
+
## Step 3 — state the cost, then write
|
|
86
|
+
|
|
87
|
+
Before writing, run the footprint tool and report the projection to the user in one line.
|
|
88
|
+
This step is not optional. Making the cost visible at the decision point is the whole
|
|
89
|
+
reason this skill exists.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
node .claude/scripts/context-footprint.mjs --root .
|
|
93
|
+
node .claude/scripts/context-footprint.mjs --root . --add <bytes> --as <destination>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Valid `--as` values: `rule`, `rule-scoped`, `locked`, `shared`, `team-member`, `memory`,
|
|
97
|
+
`skill`, `nowhere`.
|
|
98
|
+
|
|
99
|
+
Say it plainly — "this adds 1.4 KB to every session, taking always-loaded context from
|
|
100
|
+
7.8% to 8.0% of the window" — and then write the file. If the projection looks
|
|
101
|
+
disproportionate to the value, go back to the table and take a cheaper row.
|
|
102
|
+
|
|
103
|
+
## The canonical test
|
|
104
|
+
|
|
105
|
+
Canonical content is loaded verbatim into every session prompt, so it frames how Claude
|
|
106
|
+
reads the rest of the conversation. The principle: canonical describes what *is* and what
|
|
107
|
+
*to do*, never what *to think*.
|
|
108
|
+
|
|
109
|
+
> If Claude read this for the first time during a session about an unrelated topic, would
|
|
110
|
+
> it (a) help frame the problem correctly, or (b) push Claude toward a particular answer to
|
|
111
|
+
> a question that hasn't been asked yet?
|
|
112
|
+
|
|
113
|
+
(a) is canonical. (b) is `shared/` at most, more often `team-member/{user}/`.
|
|
114
|
+
|
|
115
|
+
**Belongs in `shared/locked/`:**
|
|
116
|
+
|
|
117
|
+
- **Facts about the system** — architecture, supported targets, naming conventions, where
|
|
118
|
+
things live, what is published.
|
|
119
|
+
- **Hard constraints** — "must work on Windows and macOS," "PRs only, no direct push to
|
|
120
|
+
main." Constraints scope the solution space without prejudging the solution.
|
|
121
|
+
- **Process rules** — workflow discipline that applies regardless of task.
|
|
122
|
+
- **Settled-rejection guardrails** — "we evaluated X, rejected it because Y, do not propose
|
|
123
|
+
it again." Must include the *why*, so a genuine edge case can still be recognised.
|
|
124
|
+
- **Meta-principles for debiasing** — explicit reminders to widen evaluation.
|
|
125
|
+
|
|
126
|
+
**Does not:**
|
|
127
|
+
|
|
128
|
+
- **Opinions on open technical questions.** "Library X beats Y here" makes Claude start
|
|
129
|
+
from the conclusion instead of reasoning toward it. Write it as a constraint with its
|
|
130
|
+
reason, or leave it in `shared/`.
|
|
131
|
+
- **Conclusions Claude might be asked to question.** Locking a conclusion on a topic still
|
|
132
|
+
under design biases the discussion before it starts.
|
|
133
|
+
- **Personal preferences.** Those are `team-member/{user}/`.
|
|
134
|
+
- **Status snapshots that age fast.** `project-status.md` is the bounded exception; finer
|
|
135
|
+
detail lives in the tracker.
|
|
136
|
+
|
|
137
|
+
Pre-loaded conclusions do not read as opinions to Claude — they read as ground truth. A
|
|
138
|
+
reference doc Claude *finds* while researching is weighed against the question; a canonical
|
|
139
|
+
doc loaded before the question is asked frames what Claude considers at all.
|
|
140
|
+
|
|
141
|
+
## Writing a workspace-context file
|
|
142
|
+
|
|
143
|
+
Frontmatter fields — conventions, not all required on every file:
|
|
144
|
+
|
|
145
|
+
- `state` — `locked` (team truth, lives under `shared/locked/`) or `ephemeral`.
|
|
146
|
+
- `lifecycle` — for ephemeral files: `active` or `resolved`.
|
|
147
|
+
- `type` — `reference`, `braindump`, `handoff`, `research`, `design`, `index`, `canonical`,
|
|
148
|
+
`promoted`.
|
|
149
|
+
- `priority` — locked files only: `critical` (always in canonical) or `reference` (eligible
|
|
150
|
+
for trim or stub under budget pressure). Absent defaults to `critical`.
|
|
151
|
+
- `topic` — kebab-case slug matching the filename after any type prefix.
|
|
152
|
+
- `author` — required for `team-member/{user}/` files.
|
|
153
|
+
- `updated` — ISO date of last meaningful edit. `/maintenance` flags stale `active` files.
|
|
154
|
+
- `description` — one line, used verbatim by the generated indexes. Without it the index
|
|
155
|
+
falls back to the first sentence, then the filename slug. Adding one to a file with a
|
|
156
|
+
weak fallback is the cheapest possible index improvement.
|
|
157
|
+
- `confidence` — `high` | `medium` | `low`. Use on research, design, and exploration where
|
|
158
|
+
conclusions may still shift. Skip on locked files and on handoffs and braindumps.
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
---
|
|
162
|
+
state: ephemeral
|
|
163
|
+
lifecycle: active
|
|
164
|
+
type: research
|
|
165
|
+
topic: vector-search-evaluation
|
|
166
|
+
description: Evaluation of FAISS for workspace-context — concluded the NL index is sufficient at our scale.
|
|
167
|
+
author: alex
|
|
168
|
+
confidence: medium
|
|
169
|
+
updated: 2026-04-25
|
|
170
|
+
---
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Regenerating the indexes
|
|
174
|
+
|
|
175
|
+
One generator produces all three artifacts in a single pass:
|
|
176
|
+
|
|
177
|
+
- `workspace-context/index.md` — catalog of `shared/` (locked first), imported by `CLAUDE.md`.
|
|
178
|
+
- `workspace-context/canonical.md` — verbatim concatenation of `shared/locked/*.md`, also
|
|
179
|
+
imported by `CLAUDE.md`.
|
|
180
|
+
- `workspace-context/team-member/{user}/index.md` — per-user catalog, imported by each
|
|
181
|
+
user's gitignored `CLAUDE.local.md`.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
node .claude/scripts/build-workspace-context.mjs --check --root . # exits 1 if stale
|
|
185
|
+
node .claude/scripts/build-workspace-context.mjs --write --root . # regenerate
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Gitignored files (anything matching `local-only-*`) are excluded automatically, and
|
|
189
|
+
`workspace-context/.indexignore` adds path-prefix excludes for tracked files that should
|
|
190
|
+
not appear in the shared index.
|
|
191
|
+
|
|
192
|
+
The canonical byte budget is opt-in: `workspace.canonicalBudgetBytes` is off by default
|
|
193
|
+
(absent or `null`), and the whole always-loaded set is measured by
|
|
194
|
+
`workspace.alwaysLoadedBudgetBytes` instead. When a byte count is set and `canonical.md`
|
|
195
|
+
exceeds it, the builder honours per-file `priority` and section-level
|
|
196
|
+
`<!-- canonical:trim --> ... <!-- canonical:end-trim -->`
|
|
197
|
+
markers to fit: `priority: reference` files are trimmed, then stubbed; `priority: critical`
|
|
198
|
+
files are always included in full. `/maintenance` audits the budget when on and offers
|
|
199
|
+
triage when over.
|
|
200
|
+
|
|
201
|
+
Hand edits to `index.md`, `canonical.md`, or any per-user index are overwritten. Change the
|
|
202
|
+
source file or its `description:` instead.
|
package/template/{.claude/rules/goal-driven-work.md → _claude/skills/goal-driven-work/SKILL.md}
RENAMED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## When to reach for `/goal`
|
|
1
|
+
---
|
|
2
|
+
name: goal-driven-work
|
|
3
|
+
description: Use when setting up or running multi-phase autonomous work under Claude Code's built-in /goal command in a Ulysses workspace — drafting a goal-{topic}.md artifact, defining phases, dispatching phase agent teams, or completing a goal-driven session. Covers the frontmatter schema, the three phase types, gate conventions, the integration-branch model with per-phase sub-PRs, and model tiering.
|
|
4
|
+
---
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
# Goal-Driven Work
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
The convention layer over Claude Code's built-in `/goal` command. `/goal` is the autonomy
|
|
9
|
+
loop; this skill gives the main agent durable phase state and a consistent dispatch pattern
|
|
10
|
+
across turns and resumes.
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
Read `.claude/rules/goal-driven-work.md` first for whether `/goal` is the right shape for
|
|
13
|
+
the work at hand. This skill covers everything after that decision.
|
|
14
14
|
|
|
15
15
|
## File layout
|
|
16
16
|
|
|
17
|
-
- One `goal-{topic}.md` artifact at the top of the
|
|
17
|
+
- One `goal-{topic}.md` artifact per effort. Session model: at the top of the session worktree, alongside `session.md`. Task model: in the chat drawer at `workspace-scratchpad/chats/{chat}/`, alongside its phase outputs. One goal per worktree or task either way.
|
|
18
18
|
- The artifact's frontmatter holds machine state; its body holds the human-readable goal statement, per-phase intent, and a mandatory `## Start command` section (see "Kicking off the goal") with the literal `/goal "..."` invocation the user runs to start the loop.
|
|
19
19
|
- Phase output artifacts live as siblings. `research-*.md` and `crossref-*.md` are goal-native (produced by `parallel-research` and `crossref` phase types). `design-*.md` and `plan-*.md` are pre-existing session-artifact patterns that `type: skill` phases reuse when the wrapped skill is `superpowers:brainstorming` or `superpowers:writing-plans`; they are not goal-specific.
|
|
20
|
-
-
|
|
20
|
+
- Session model: the artifact is tracked on the session branch and lives there until `/complete-work` runs, which strips it before the final PR. Task model: the drawer is machine-local and untracked; `/complete-work` routes the artifact (promote into `workspace-context/` or discard) at completion, so while a task is active give the artifact's frontmatter a `workItem: {id}` line — the offer is scoped to the task that owns the artifact.
|
|
21
21
|
|
|
22
22
|
## Frontmatter schema
|
|
23
23
|
|
|
@@ -122,7 +122,7 @@ This guidance is workspace-side mitigation only. The underlying friction — the
|
|
|
122
122
|
|
|
123
123
|
## Integration branch and per-phase sub-PRs
|
|
124
124
|
|
|
125
|
-
While a `/goal`-driven
|
|
125
|
+
While a `/goal`-driven effort is running, its work branch (`feature/{session-name}` for a session, the task's branch for a task) acts as the goal's integration branch. Main is untouched until `/complete-work` opens the final PR for human review. This is the key autonomy boundary: phase agents can merge their own work, repeatedly, throughout the goal — but only into the integration branch, never into main. The integration-branch and sub-branch guidance in this section applies to task branches unchanged.
|
|
126
126
|
|
|
127
127
|
Two merge strategies, picked per phase:
|
|
128
128
|
|
|
@@ -197,11 +197,38 @@ The `/goal` evaluator runs after every turn against the conversation transcript.
|
|
|
197
197
|
- **Demonstrable from transcript.** The main agent's own output must be able to evidence completion. "The PR URL was reported in the transcript and `git status` showed clean" rather than "the work feels done."
|
|
198
198
|
- **Bounded.** Includes a turn budget as a backstop (e.g., "or stop after 60 turns") so the loop can't run away if something goes wrong.
|
|
199
199
|
- **Within the 4000-char limit.** Up to four kilobytes of condition text are accepted.
|
|
200
|
+
- **Reachable by the agent.** This is the one that gets written wrong. If any phase carries
|
|
201
|
+
`gate: review`, or the goal depends on a merge, a deploy, or anything else only the
|
|
202
|
+
operator can authorise, then a condition demanding those be *done* can never be satisfied
|
|
203
|
+
by the agent — and the evaluator will re-ping indefinitely against work that is correctly
|
|
204
|
+
waiting. Either the condition names the blocked state as terminal, or the goal cannot end
|
|
205
|
+
without the backstop.
|
|
206
|
+
|
|
207
|
+
**The failure this prevents, observed.** A goal was started with
|
|
208
|
+
|
|
209
|
+
> All 6 phases show status: complete. […] The seven PRs are merged or closed.
|
|
210
|
+
|
|
211
|
+
while three of its phases were `gate: review` and the PRs depended on two prerequisite PRs
|
|
212
|
+
merging to `main`. Everything the agent could do was done inside about forty turns; the
|
|
213
|
+
remaining eighty were spent re-reporting the same three blockers, because the condition had
|
|
214
|
+
no terminal state for "built, verified, awaiting authorisation."
|
|
215
|
+
|
|
216
|
+
Write the disjunction in from the start:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
Every phase shows status: complete, or status: awaiting-review with its artifact
|
|
220
|
+
written. […] Either <external step> is done, or it is still pending and that is
|
|
221
|
+
stated in the transcript. Or stop after <N> turns.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Editing the artifact afterwards does not rescue a running goal: the evaluator's text is
|
|
225
|
+
fixed from the original `/goal` invocation. The corrected condition only takes effect after
|
|
226
|
+
`/goal clear` and a re-run of the `## Start command`.
|
|
200
227
|
|
|
201
228
|
A reasonable template:
|
|
202
229
|
|
|
203
230
|
```
|
|
204
|
-
|
|
231
|
+
Every phase in goal-<topic>.md shows status: complete, or status: awaiting-review with its artifact written. Phase artifacts exist at: <list paths>. Either the /complete-work skill has opened the final PR with the URL in the transcript, or the goal is blocked on a named operator step and that is stated. Or stop after <N> turns.
|
|
205
232
|
```
|
|
206
233
|
|
|
207
234
|
Fill in `<topic>`, paths, and `<N>` per goal. Anchor on artifacts and committed state, not on feelings.
|
|
@@ -216,7 +243,7 @@ Every `goal-{topic}.md` body MUST include a `## Start command` section containin
|
|
|
216
243
|
## Start command
|
|
217
244
|
|
|
218
245
|
```
|
|
219
|
-
/goal "All phases in goal-<topic>.md show status: complete. Phase artifacts exist at: <paths>. The /complete-work skill has
|
|
246
|
+
/goal "All phases in goal-<topic>.md show status: complete. Phase artifacts exist at: <paths>. The /complete-work skill has opened the final PR; the PR URL appeared in the transcript. Or stop after <N> turns."
|
|
220
247
|
```
|
|
221
248
|
````
|
|
222
249
|
|
|
@@ -230,9 +257,9 @@ When the artifact is drafted and the user has reviewed it, the agent's hand-off
|
|
|
230
257
|
|
|
231
258
|
## Lifecycle integration
|
|
232
259
|
|
|
233
|
-
- `/goal` runs inside
|
|
234
|
-
-
|
|
235
|
-
- `session.md`'s `## Tasks` should mirror the phase list at coarse grain (one task per phase) so `TodoWrite` shows high-level progress
|
|
260
|
+
- `/goal` runs inside started work. It does NOT replace `/start-work`. Session model: the session is created the normal way and the goal artifact is drafted at the worktree top (including its `## Start command` block). Task model: the task is started the normal way and the artifact is drafted into the chat drawer. Either way the user runs the `## Start command` block's `/goal "..."` to kick off the loop.
|
|
261
|
+
- Session model: the goal artifact lives on the session branch and travels with `git push`, surviving across machines and `--resume`. Task model: the drawer is machine-local — push the task branch for the code, and `/promote` the artifact if it must travel.
|
|
262
|
+
- Session model: `session.md`'s `## Tasks` should mirror the phase list at coarse grain (one task per phase) so `TodoWrite` shows high-level progress; the main agent updates `## Tasks` at phase transitions via the helper specified by the `task-list-mirroring` rule, in addition to updating `goal-{topic}.md`. Task model: TodoWrite is the live mirror and there is no durable `## Tasks` — the goal artifact's own `phases:` list is the durable state.
|
|
236
263
|
- `/pause-work` works without special handling. The goal-evaluator state resets on resume per the Claude Code docs; phase state is durable in the artifact.
|
|
237
264
|
- `/complete-work` reads `goal-*.md`, `research-*.md`, and `crossref-*.md` for release-note synthesis and strips them from the branch before the final PR, alongside the existing `design-*.md` and `plan-*.md` handling. When a goal artifact is present, it also runs a pre-flight check that every declared sub-branch (from phases with `integration.strategy: sub-branch`) has been merged into the session branch. Unmerged sub-branches abort completion with a clear list to resolve.
|
|
238
265
|
|
|
@@ -396,7 +423,7 @@ on the session branch.
|
|
|
396
423
|
## Start command
|
|
397
424
|
|
|
398
425
|
```
|
|
399
|
-
/goal "All 5 phases in goal-evaluate-rate-limiting.md show status: complete. Phase artifacts exist at: research-rate-limiting-strategies.md, crossref-existing-infrastructure.md, design-rate-limiting.md, plan-rate-limiting.md, and the implementation commits land on the session branch (visible in git log). The /complete-work skill has
|
|
426
|
+
/goal "All 5 phases in goal-evaluate-rate-limiting.md show status: complete. Phase artifacts exist at: research-rate-limiting-strategies.md, crossref-existing-infrastructure.md, design-rate-limiting.md, plan-rate-limiting.md, and the implementation commits land on the session branch (visible in git log). The /complete-work skill has opened the final PR; the PR URL appeared in the transcript. Or stop after 60 turns."
|
|
400
427
|
```
|
|
401
428
|
|
|
402
429
|
This is the frontmatter `completion_condition` flattened to one line. Run it after reviewing the artifact; it flips the goal to `status: active`.
|
|
@@ -11,7 +11,7 @@ Save structured workstream state to workspace-context. Usable anytime, any numbe
|
|
|
11
11
|
- `/handoff {name}` — create or update a named handoff
|
|
12
12
|
- `/handoff` (no param) — analyze session and suggest name(s)
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Lifecycle-Aware Behavior
|
|
15
15
|
|
|
16
16
|
When called within an active work session (the active-session pointer at `.claude/.active-session.json` exists inside the current worktree):
|
|
17
17
|
|
|
@@ -26,9 +26,17 @@ When called within an active work session (the active-session pointer at `.claud
|
|
|
26
26
|
git commit -m "handoff: update {session-name} tracker"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
29
|
+
Under the task model — `workspace.sessionModel` is `"task"` in `workspace.json` AND the SessionStart hook injected a `Chat record:` line (`{chat}` is its name):
|
|
30
|
+
|
|
31
|
+
- Default behavior: write `handoff_{topic}.md` directly into that chat's drawer at `workspace-scratchpad/chats/{chat}/` — the drawer sits outside `workspace-context/`, so `capture-context.mjs` is not involved
|
|
32
|
+
- No commit for drawer writes: the drawer is gitignored and machine-local; `/complete-work` lists it and asks what to promote into `workspace-context/`
|
|
33
|
+
- While a task is active, add `workItem: {id}` to the file's frontmatter: `/complete-work` offers a drawer item only to the task that owns it, so the tag keeps this capture out of another task's promotion list
|
|
34
|
+
|
|
35
|
+
When called from the workspace root with no active session — every other case, including a `sessionModel: "session"` workspace (the `Chat record:` line is injected in every chat, so it alone does not select the drawer):
|
|
36
|
+
|
|
37
|
+
- Use `--local-only` so the captured file is gitignored (the root only allows local-only writes), landing in `team-member/{user}/`
|
|
38
|
+
- If the task model applies but the `Chat record:` line is absent, say the drawer destination is unavailable for that reason
|
|
39
|
+
- Suggest starting work (`/start-work`) first if the handoff is about actionable work
|
|
32
40
|
|
|
33
41
|
The flows below apply when NOT in an active work session, or when the user explicitly asks for a standalone handoff file.
|
|
34
42
|
|
|
@@ -44,6 +44,10 @@ For each workspace-context `.md` file and each `work-sessions/*/workspace/sessio
|
|
|
44
44
|
- Worktrees whose branch has already been merged? (cleanup candidates)
|
|
45
45
|
- Workspace repo on expected branch?
|
|
46
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.
|
|
47
|
+
- Task-model state (gh:146), three checks:
|
|
48
|
+
- **Unrecorded task worktrees** — list `repos/*/.claude/worktrees/*` and `.claude/worktrees/*`, read each candidate's branch (`git -C "{path}" rev-parse --abbrev-ref HEAD`), and keep only those on a task-prefixed branch (`feature/`, `bugfix/`, `chore/`) — Claude Code's own worktrees carry other branch names, so the prefix filter skips them without guessing a name convention. Cross-reference the chat records (`node .claude/scripts/chat-record.mjs --root . --list`): a task-prefixed worktree no record entry claims is *unrecorded* — it may be a legitimate no-tracker task (those are never recorded), so present it and ask before suggesting `node .claude/scripts/task-worktree.mjs --root . --remove --repo "{repo}" --branch "{branch}"`.
|
|
49
|
+
- **Stale record entries** — a record task entry whose worktree is gone (neither `repos/{repo}/.claude/worktrees/{slug}/` nor, for `repo: "."`, `.claude/worktrees/{slug}/` exists). Suggest `node .claude/scripts/chat-record.mjs --root . --remove-task --chat "{chat}" --work-item "{workItem}" --repo "{repo}"` (omit `--repo` when the entry has none).
|
|
50
|
+
- **Merged but never completed** — a recorded task branch that already merged. Judge merged-ness by the forge's merged PRs for that repo, matching on head branch — never `git branch --merged`, which a squash merge (never an ancestor) silently misses. `/complete-work` never ran. Suggest running `/complete-work` for that branch (detection from the chat record finds it).
|
|
47
51
|
|
|
48
52
|
### 5. Workspace-context auto-file integrity
|
|
49
53
|
|
|
@@ -55,9 +59,9 @@ node .claude/scripts/build-workspace-context.mjs --check --root .
|
|
|
55
59
|
|
|
56
60
|
The script reports per-artifact status as JSON and uses three exit codes to distinguish what's wrong:
|
|
57
61
|
|
|
58
|
-
- `0` — all artifacts current and the rendered canonical fits inside `workspace.canonicalBudgetBytes`.
|
|
62
|
+
- `0` — all artifacts current and, when a canonical budget is set, the rendered canonical fits inside `workspace.canonicalBudgetBytes`.
|
|
59
63
|
- `1` — at least one artifact is `missing` or `stale`. Run `--write` to regenerate. `missing` means the artifact does not exist yet; `stale` means it exists but no longer matches its sources (a file was added or deleted, a `description:` changed, a `shared/locked/` file was edited, an `.indexignore` rule was added).
|
|
60
|
-
- `2` — artifacts are current but canonical body bytes exceed the budget after the trim and stub stages have already run. Regeneration cannot fix this; the locked content itself needs triage. Stale wins over over-budget when both apply, so a `1` can hide an over-budget condition until you regen.
|
|
64
|
+
- `2` — artifacts are current but canonical body bytes exceed the budget after the trim and stub stages have already run. Only reachable when a budget is set. Regeneration cannot fix this; the locked content itself needs triage. Stale wins over over-budget when both apply, so a `1` can hide an over-budget condition until you regen.
|
|
61
65
|
|
|
62
66
|
The JSON payload always includes a `canonical` block summarizing the budget outcome:
|
|
63
67
|
|
|
@@ -79,11 +83,40 @@ The JSON payload always includes a `canonical` block summarizing the budget outc
|
|
|
79
83
|
|
|
80
84
|
`selectionStatus` walks `ok` → `trimmed` → `stubbed` → `over-budget` as the script gives up progressively more reference content trying to fit the budget. `trimmedFiles` lists reference files whose `<!-- canonical:trim --> ... <!-- canonical:end-trim -->` spans were dropped; `stubbedFiles` lists reference files whose entire body was replaced with a one-line breadcrumb. `overBy` is present only when `selectionStatus === 'over-budget'` and reports the bytes still over after stubbing.
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
The canonical budget is opt-in. `workspace.canonicalBudgetBytes` is off unless workspace.json sets it — absent or `null` means no budget. When off, `canonical.md` ships every locked file in full, the `canonical` block reports `"budget": null` with `selectionStatus: "ok"`, exit `2` cannot occur, and the audit reports one informational line in place of the budget OK/warning line:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
• Canonical budget: off (alwaysLoadedBudgetBytes covers the total)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
No warning accompanies it. To turn the budget back on, set a byte count in workspace.json (e.g. `"canonicalBudgetBytes": 40960`) and regenerate.
|
|
93
|
+
|
|
94
|
+
Audit mode reports the status verbatim. When a budget is set and `selectionStatus` is `over-budget`, audit emits the budget violation and recommends `/maintenance cleanup` to triage — regeneration will not resolve it. Cleanup mode runs `--write` when `missing` or `stale`, re-checks, and then enters the budget triage flow described in cleanup step 11 if the post-regen check still reports `over-budget`.
|
|
83
95
|
|
|
84
96
|
While the indexes are 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.
|
|
85
97
|
|
|
86
|
-
### 6.
|
|
98
|
+
### 6. Always-loaded context budget
|
|
99
|
+
|
|
100
|
+
Everything Claude reads at launch — CLAUDE.md, its @-imports, and the active rules — is measured against `workspace.alwaysLoadedBudgetBytes`:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
node .claude/scripts/context-footprint.mjs --root .
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Rules carrying `paths:` frontmatter are conditional (they load only when a matching file is touched); the script lists them in a separate conditional section and excludes them from the total. With no `alwaysLoadedBudgetBytes` in workspace.json there is no budget and this check passes trivially.
|
|
107
|
+
|
|
108
|
+
Within budget → an OK line: `✓ Always-loaded context: 43 KB / 64 KB`. Over budget → a Warning (the workspace still functions; this is drift, not breakage) naming the top contributors and the fixes:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
⚠ Always-loaded context exceeds budget: 78 KB / 64 KB. Top contributors:
|
|
112
|
+
.claude/rules/git-conventions.md (12 KB), CLAUDE.md (9 KB),
|
|
113
|
+
.claude/rules/workspace-structure.md (8 KB). Scope situational rules with
|
|
114
|
+
paths: frontmatter, or move reference content to shared/.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The script itself exits `1` when over budget; `/maintenance` reports that as the warning above, not as a failed run.
|
|
118
|
+
|
|
119
|
+
### 7. Template freshness
|
|
87
120
|
|
|
88
121
|
Compare the workspace's pinned template version against the latest published on npm.
|
|
89
122
|
|
|
@@ -108,7 +141,7 @@ Report one of:
|
|
|
108
141
|
|
|
109
142
|
Active recommendations. Flags problems and suggests fixes, but asks before acting.
|
|
110
143
|
|
|
111
|
-
###
|
|
144
|
+
### 8. Component age check
|
|
112
145
|
|
|
113
146
|
Scan the following file sets for a YAML frontmatter `updated:` field:
|
|
114
147
|
- `.claude/rules/*.md` (active rules only — `.md.skip` files are included too, since the rule content can still drift)
|
|
@@ -122,22 +155,25 @@ Files without an `updated:` field are skipped — the check is opt-in and activa
|
|
|
122
155
|
|
|
123
156
|
When stale candidates are found, surface them as warnings in the output format and link to `config-review.md.skip` (in `.claude/rules/`) as the opt-in rule that documents the review cadence and rationale.
|
|
124
157
|
|
|
125
|
-
###
|
|
158
|
+
### 9. Stale context
|
|
126
159
|
- Ephemeral files not updated in 7+ days — suggest resolve, update, or archive
|
|
127
160
|
- `work-sessions/{name}/` folders whose worktrees are gone — suggest cleanup
|
|
128
161
|
- Session trackers whose branches have been merged — suggest `/complete-work` post-flight cleanup
|
|
162
|
+
- Unrecorded task-prefixed worktrees (no chat-record entry claims them; may be no-tracker tasks) — ask, then suggest `task-worktree.mjs --remove`
|
|
163
|
+
- Chat-record task entries whose worktree is gone — suggest `chat-record.mjs --remove-task`
|
|
164
|
+
- Recorded task branches already merged (per the forge's merged PRs, not `git branch --merged`) — suggest `/complete-work`
|
|
129
165
|
- Braindumps that overlap significantly — suggest merging (e.g., "workspace-branching.md and persistent-work-sessions.md cover the same topic")
|
|
130
166
|
- Handoffs referencing deleted branches — suggest resolve or remove
|
|
131
167
|
|
|
132
|
-
###
|
|
168
|
+
### 10. Context reconciliation
|
|
133
169
|
- Read recent workspace-context writes (last session or last N files by updated date)
|
|
134
170
|
- For each, scan other workspace-context files for references that are now stale
|
|
135
171
|
- Surface: "{file} says X but {newer-file} now says Y. Update {file}?"
|
|
136
172
|
- This is the capture-time cross-check, run retroactively instead of inline
|
|
137
173
|
|
|
138
|
-
###
|
|
174
|
+
### 11. Canonical budget triage
|
|
139
175
|
|
|
140
|
-
This step runs only when the post-regen `--check` from step 9 still reports `selectionStatus: 'over-budget'`.
|
|
176
|
+
This step runs only when a canonical budget is set (`workspace.canonicalBudgetBytes` holds a number) and the post-regen `--check` from the cleanup regen pass (Flow step 9) still reports `selectionStatus: 'over-budget'`. With the budget off — absent or `null` in workspace.json — `--check` can never report over-budget, so this step is unreachable. Skip it too if the regular regen pass cleared the budget, or if `--check` was already `ok`, `trimmed`, or `stubbed` after that pass.
|
|
141
177
|
|
|
142
178
|
The rest of cleanup is suggestion-list-with-confirmation: surface a candidate, ask before applying, move on. Triage is the one meaningfully more interactive surface in `/maintenance`. It runs as a small REPL: present the budget state and a triage menu, take one action, re-run `--check`, present the menu again with the new state. No suggestion is auto-applied; every action is the user's choice.
|
|
143
179
|
|
|
@@ -182,7 +218,7 @@ For each chosen action:
|
|
|
182
218
|
|
|
183
219
|
Trim markers and demotions only matter for `priority: reference` files — `<!-- canonical:trim -->` spans on a `priority: critical` file are inert until the file is demoted. The triage flow never auto-decides which file to demote or which section to wrap; it surfaces the data, presents options, and waits.
|
|
184
220
|
|
|
185
|
-
###
|
|
221
|
+
### 12. Forge configuration
|
|
186
222
|
|
|
187
223
|
Read `workspace.json`. If `workspace.tracker?.type === 'github-issues'` and `workspace.forge` is unset, emit a notice (not an error):
|
|
188
224
|
|
|
@@ -194,8 +230,9 @@ Read `workspace.json`. If `workspace.tracker?.type === 'github-issues'` and `wor
|
|
|
194
230
|
|
|
195
231
|
This is migration guidance for workspaces created before the `forge` field landed — the field is back-compat with a sensible default, so the unset case is not a bug, just an opportunity to make the implicit explicit. If `workspace.forge.type` is set to a value with no adapter at `.claude/scripts/forges/{type}.mjs`, that IS an error and goes in the Issues section.
|
|
196
232
|
|
|
197
|
-
###
|
|
198
|
-
- Canonical budget — read from the same `--check` invocation as step 5.
|
|
233
|
+
### 13. Health metrics
|
|
234
|
+
- Canonical budget — read from the same `--check` invocation as step 5. When a budget is set, reported as `current / budget` bytes with the selection status (e.g., `full`, `2 reference files trimmed`); over-budget cases are deferred to the cleanup triage flow rather than re-reported here. When off, report the step 5 one-liner: `• Canonical budget: off (alwaysLoadedBudgetBytes covers the total)`.
|
|
235
|
+
- Always-loaded context — read from the same `context-footprint.mjs` invocation as audit step 6, reported the same way (`current / budget` bytes); over-budget is already surfaced as a warning there.
|
|
199
236
|
- Number of ephemeral files — flag if accumulating without resolution
|
|
200
237
|
- Session log stats (if `workspace-scratchpad/session-log.jsonl` exists):
|
|
201
238
|
- Sessions without capture
|
|
@@ -224,11 +261,12 @@ Cleanup suggestions (2):
|
|
|
224
261
|
⊕ migration-recipes.md still says "/sync handles dogfood" but
|
|
225
262
|
/sync was replaced by /sync-work — update?
|
|
226
263
|
|
|
227
|
-
OK (
|
|
264
|
+
OK (6):
|
|
228
265
|
✓ All CLAUDE.md skill references valid
|
|
229
266
|
✓ Workspace structure matches rule
|
|
230
267
|
✓ workspace.json repos all present
|
|
231
268
|
✓ Canonical: 17 KB / 40 KB (full)
|
|
269
|
+
✓ Always-loaded context: 43 KB / 64 KB
|
|
232
270
|
✓ Template is up to date (v0.14.0)
|
|
233
271
|
```
|
|
234
272
|
|
|
@@ -239,10 +277,11 @@ OK (5):
|
|
|
239
277
|
3. Read workspace.json — extract repo manifest
|
|
240
278
|
4. Check `.claude/rules/`, `.claude/skills/`, `.claude/agents/` against references
|
|
241
279
|
5. Check git state (worktrees, branches, remotes)
|
|
242
|
-
6. Run `node .claude/scripts/build-workspace-context.mjs --check --root .` — capture status. Exit `0` = clean and within budget, `1` = artifact missing or stale, `2` = artifacts current but canonical body over budget. The `canonical` block in the JSON output drives both the audit budget line and the cleanup triage decision.
|
|
243
|
-
7.
|
|
244
|
-
8.
|
|
245
|
-
9.
|
|
280
|
+
6. Run `node .claude/scripts/build-workspace-context.mjs --check --root .` — capture status. Exit `0` = clean (and within budget when one is set), `1` = artifact missing or stale, `2` = artifacts current but canonical body over budget — only possible with a budget set. The `canonical` block in the JSON output drives both the audit budget line and the cleanup triage decision; `"budget": null` means the canonical budget is off.
|
|
281
|
+
7. Run `node .claude/scripts/context-footprint.mjs --root .` — capture the total and the `BUDGET` line. Exit `0` = within budget or no budget set; exit `1` = over budget, reported as a warning with the top contributors (audit step 6).
|
|
282
|
+
8. Read session-log.jsonl if it exists
|
|
283
|
+
9. If cleanup mode: regenerate the workspace-context auto-files if stale (index.md, canonical.md, per-user team-member indexes); compare files pairwise for overlap; scan for stale cross-references. If post-regen `--check` reports `over-budget`, enter the canonical-budget triage flow described in cleanup step 11.
|
|
284
|
+
10. Compile and present findings grouped by severity
|
|
246
285
|
|
|
247
286
|
## Notes
|
|
248
287
|
- Audit mode is always read-only — never modifies files
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrate-sessions
|
|
3
|
+
description: Migrate this workspace from the session lifecycle to the task lifecycle — inventory old work sessions, decide each one with the operator, finish or archive them, and switch workspace.json to the task model. Runs only inside the current workspace; never deletes anything.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrate Sessions
|
|
7
|
+
|
|
8
|
+
Drain a workspace's accumulated session entries and switch new work to the task lifecycle. The script is `.claude/scripts/migrate-sessions.mjs`; this skill is the operator procedure around it.
|
|
9
|
+
|
|
10
|
+
**Scope rule, before anything else: this skill acts only on the workspace it is run in.** Never read, inventory, or act on any other workspace or directory — even if asked to "do them all." Each workspace runs its own migration from its own root, by its own operator, on its own schedule.
|
|
11
|
+
|
|
12
|
+
**Run from the launcher root only** — the workspace root itself, never a session folder or any other worktree. The script refuses a linked-worktree `--root` on its own (one exception, the Switch step below), and it refuses to back up or archive the session that hosts the current chat, so there is no way to drain the session you are sitting in from inside it.
|
|
13
|
+
|
|
14
|
+
## 1. Inventory
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node .claude/scripts/migrate-sessions.mjs --inventory
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Read-only. Present the stderr table plus each session's proposal with its reasons and warnings. Say plainly that the proposals are proposals — evidence and a starting point, not decisions. Pay particular attention to the per-remote state shown per worktree (`same`, `ahead +N`, `behind -N`, `diverged +N/-M`, `not-fetched`, `unknown`) and to `unbacked` warnings: they change what Finish and Archive mean for that session. Entries shown as `foreign` (symlinked) are never acted on — surface them for manual reconciliation.
|
|
21
|
+
|
|
22
|
+
## 2. Decide per session, with the operator — one at a time
|
|
23
|
+
|
|
24
|
+
For each session, lay out its evidence and ask the operator which way to go. Never infer the decision from the proposal. The options:
|
|
25
|
+
|
|
26
|
+
- **Finish** (typical for MERGEABLE) — resume the session with `/start-work`, then run `/complete-work`; its own merge confirmation applies there. But if the inventory shows a **diverged** remote for that session, say so *before* the operator chooses Finish: `/complete-work`'s plain push will be rejected, and pushing the rewritten history needs `--force-with-lease` — which you run only on the operator's explicit yes naming the branch. Never force silently.
|
|
27
|
+
- **Archive** (typical for ABANDONED, a broken shell, or a MERGEABLE the operator gives up on) — take it out of the active lifecycle without destroying anything. Two steps, each its own decision:
|
|
28
|
+
1. **Offer a backup.** Archiving keeps everything on this machine; a backup adds an off-machine copy of the session's commits, and it is what makes a later deletion safe. It creates `drain/{session}/…` tags and pushes them to the resolved remote(s) — tags may land in a public repository, so show the plan first: `node .claude/scripts/migrate-sessions.mjs --backup --session {name} --dry-run` (add `--remote <name>` to aim somewhere other than the resolved default). It lists, per tip, the tag, the remote, and which tips a remote branch or tag already holds exactly, with no side effects. On the operator's yes, run it without `--dry-run` and show the tags it created. Declining is fine — the archive still keeps everything locally. A repo with no remote at all is refused by backup; say so.
|
|
29
|
+
2. **Archive after an explicit yes naming the session:** `node .claude/scripts/migrate-sessions.mjs --archive --session {name}`. The whole session folder moves to `{sessions}/.archived/{name}--{timestamp}/` and git's worktree links are repaired to follow it — every commit, uncommitted edit, untracked or ignored file, and embedded repository comes along. If the move or the repair fails, the session is put back and the result says whether every link was verified. The session's branches stay checked out in the archived worktrees, so a new task cannot reuse those branch names until the archive is deleted. The archive refuses, touching nothing, when a directory in the folder cannot be read, when the folder holds a worktree of a repository outside this workspace, or when it holds a submodule checkout (its link cannot be repaired) — surface the reason; for a submodule the options are Finish or Keep. Relay any `warnings` (relative symlinks that pointed outside the session no longer resolve after the move).
|
|
30
|
+
- **Keep** (typical for ACTIVE, and the only sane answer for UNKNOWN) — leave it; it completes later under the session lifecycle.
|
|
31
|
+
|
|
32
|
+
Unbacked commits (present on no remote) are safe in an archive — they are only ever at risk when someone deletes one. Say so when you archive such a session, and offer the backup.
|
|
33
|
+
|
|
34
|
+
## 3. Switch — never write the launcher's tracked `workspace.json` directly
|
|
35
|
+
|
|
36
|
+
The switch procedure:
|
|
37
|
+
|
|
38
|
+
1. Create a workspace task worktree for the change:
|
|
39
|
+
```bash
|
|
40
|
+
node .claude/scripts/task-worktree.mjs --root . --create --repo . --branch chore/enable-task-model
|
|
41
|
+
```
|
|
42
|
+
2. Run the switch against that worktree (the one mode that accepts a linked-worktree root — it only edits `workspace.json`):
|
|
43
|
+
```bash
|
|
44
|
+
node .claude/scripts/migrate-sessions.mjs --enable-task-model --root .claude/worktrees/chore-enable-task-model
|
|
45
|
+
```
|
|
46
|
+
3. Commit there, open a PR through the workspace's normal flow, and pull the launcher after merge. The launcher root never commits to its default branch.
|
|
47
|
+
|
|
48
|
+
The switch output reports `remainingSessions: null` when run from the worktree — the real remaining-sessions list comes from a separate `--inventory` at the launcher root. Remaining sessions are fine either way: they keep resuming and completing under the session lifecycle after the switch.
|
|
49
|
+
|
|
50
|
+
## 4. Verify
|
|
51
|
+
|
|
52
|
+
Re-run `--inventory` at the launcher root and report what remains and why — kept sessions, anything the operator deferred, foreign entries, or an empty list.
|
|
53
|
+
|
|
54
|
+
## 5. Afterwards
|
|
55
|
+
|
|
56
|
+
The first new piece of work starts with `/start-work` under the task lifecycle.
|
|
57
|
+
|
|
58
|
+
## Deleting an archive — the operator's call, never this skill's
|
|
59
|
+
|
|
60
|
+
Archives are meant to be kept until the operator has looked at them. When the operator asks to delete one, first show them — for the whole archive, not just its top level — everything that deletion would destroy, per worktree (list them with `git -C {repo} worktree list --porcelain` in the workspace repo and each `repos/{name}`, filtered to paths under the archive):
|
|
61
|
+
|
|
62
|
+
- commits no remote holds: `git -C {worktree} log --oneline --branches --not --remotes` and whether a `drain/*` tag covers the tip;
|
|
63
|
+
- uncommitted, untracked and ignored files: `git -C {worktree} status --porcelain --ignored`;
|
|
64
|
+
- edits hidden from status: `git -C {worktree} ls-files -v` — any lowercase tag (assume-unchanged) or `S` (skip-worktree) whose file differs from the index;
|
|
65
|
+
- per-worktree refs that die with the worktree: `git -C {worktree} for-each-ref refs/worktree refs/bisect`;
|
|
66
|
+
- an operation in progress: `MERGE_HEAD`, `rebase-merge`, `rebase-apply` under `git -C {worktree} rev-parse --git-dir`;
|
|
67
|
+
- embedded repositories (any `.git` directory under the archive) and their unpushed branches;
|
|
68
|
+
- files in the archive outside the worktrees (e.g. beside `workspace/`).
|
|
69
|
+
|
|
70
|
+
Only on their explicit yes naming the archive: remove the worktrees deepest-first (`git -C {repo} worktree remove --force {path}`), delete each branch they confirm (`git -C {repo} branch -D {branch}`), then delete the archive folder. Nothing in this workspace does this automatically.
|
|
@@ -28,7 +28,15 @@ This is a coherent rewrite of the Progress section, not an append (coherent-revi
|
|
|
28
28
|
|
|
29
29
|
### Step 3: Update frontmatter status and post pause comment on tracker
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Set `status: paused` in the tracker's frontmatter. `.claude/lib/session-frontmatter.mjs` is
|
|
32
|
+
a library, not a CLI — running it with flags does nothing and exits 2:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node --input-type=module -e '
|
|
36
|
+
import { updateSessionFile } from "./.claude/lib/session-frontmatter.mjs";
|
|
37
|
+
updateSessionFile("session.md", { status: "paused" });
|
|
38
|
+
'
|
|
39
|
+
```
|
|
32
40
|
|
|
33
41
|
If the session tracker has a `workItem:` field AND `workspace.tracker` is configured, post a pause comment on the linked issue via the adapter:
|
|
34
42
|
|