forge-orkes 0.21.0 → 0.24.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/package.json +1 -1
- package/template/.claude/skills/architecting/SKILL.md +15 -0
- package/template/.claude/skills/debugging/SKILL.md +18 -5
- package/template/.claude/skills/discussing/SKILL.md +1 -0
- package/template/.claude/skills/executing/SKILL.md +10 -0
- package/template/.claude/skills/forge/SKILL.md +7 -1
- package/template/.claude/skills/planning/SKILL.md +4 -3
- package/template/.claude/skills/quick-tasking/SKILL.md +3 -2
- package/template/.claude/skills/reviewing/SKILL.md +15 -0
- package/template/.claude/skills/upgrading/SKILL.md +39 -0
- package/template/.forge/FORGE.md +16 -1
- package/template/.forge/migrations/0.22.0-backlog-compaction.md +186 -0
- package/template/.forge/releases.yml +29 -0
- package/template/.forge/templates/refactor-backlog.yml +9 -4
package/package.json
CHANGED
|
@@ -68,8 +68,23 @@ Why this won.
|
|
|
68
68
|
- {Positive consequence}
|
|
69
69
|
- {Negative consequence}
|
|
70
70
|
- {Future decisions this enables or blocks}
|
|
71
|
+
|
|
72
|
+
## Platform & Goal Validation
|
|
73
|
+
*Required for foundational/irreversible mechanisms (runtime loading, storage engine, auth model, cross-platform primitives). Omit for low-stakes single-feature decisions.*
|
|
74
|
+
- **Platform reality:** Can the target platform(s) actually do this? Name the hard constraint that could forbid it (e.g. iOS code-signing forbids `dlopen` of non-bundled native code). An unvalidated "yes" here is the assumption most likely to become a dead-end years later.
|
|
75
|
+
- **Goal fit:** Does this mechanism actually enable the goal it exists for, *on that platform*? (A loading mechanism for "third-party extensibility" that the platform forbids does not serve that goal there.)
|
|
76
|
+
- **Carrying cost:** Count the platform-specific workarounds this mechanism requires. A high or growing count is a signal the mechanism fights the platform — record the number so it stays visible.
|
|
71
77
|
```
|
|
72
78
|
|
|
79
|
+
### Framing the Option Space (before you fill in Alternatives)
|
|
80
|
+
|
|
81
|
+
Enumerate options that intervene at **different layers**, not variants of one idea. For a load-cost problem, *when* you load, *whether* you load at all, *who holds the lock*, and *what it costs* are different layers; three load-*timing* options are one layer in three costumes. Always include:
|
|
82
|
+
|
|
83
|
+
- the **constraint-breaker** — "eliminate the need / change the constraint / question the assumption" (e.g. static-link the dependency instead of runtime-loading it → the whole bug class cannot exist);
|
|
84
|
+
- the **null option** — "do nothing".
|
|
85
|
+
|
|
86
|
+
Name which assumption each option challenges. *Then* converge in Rationale. A confident-looking list of same-layer variants is the failure this guards against — breadth of the enumerator is the real safeguard, so pair this with a second perspective (or `discussing`) when the option space is wide.
|
|
87
|
+
|
|
73
88
|
## Constitutional Gate Check
|
|
74
89
|
|
|
75
90
|
Before finalizing any decision, check against `.forge/constitution.md`:
|
|
@@ -29,7 +29,18 @@ Invoked by `orchestrating` skill when `forge_queue_commit` returns `status: conf
|
|
|
29
29
|
4. **Test**: Run it. Capture evidence.
|
|
30
30
|
5. **Eliminate**: Support or refute? Record result.
|
|
31
31
|
6. **Iterate**: Next hypothesis or refine current one
|
|
32
|
-
7. **Conclude**:
|
|
32
|
+
7. **Conclude**: Confirmation Gate passes (below) → root cause found. Fix it.
|
|
33
|
+
|
|
34
|
+
## Confirmation Gate
|
|
35
|
+
|
|
36
|
+
A root cause is a **commit point** — the moment a hypothesis becomes a finding that drives a fix. Run this before you write "root cause", "confirmed", "the cause", or "dominant cause" in the debug file, a commit message, or a report. Until all four hold, the claim stays labelled `hypothesis`:
|
|
37
|
+
|
|
38
|
+
1. **Mechanism observed, not inferred.** The causal mechanism is directly measured — not assumed from correlation or co-occurrence. ("Event X co-occurs with the bug" is correlation, not mechanism.)
|
|
39
|
+
2. **Alternatives explicitly refuted.** Each competing hypothesis is named and killed with evidence — not dismissed as "less likely". State the evidence that kills each one.
|
|
40
|
+
3. **Blind-spot check (highest-value rule).** For *every absence of signal* you rely on — "0 errors", "detector silent", "watchdog never fired" — first prove the instrument **can** detect the failure mode in question. A deadline-miss detector that measures intra-callback work is structurally blind to the callback never being called; a heartbeat-keyed watchdog is blind to a hang that keeps the heartbeat fresh. **A null result from a blind instrument is not evidence.**
|
|
41
|
+
4. **Falsification attempted.** A test that *could* have refuted the hypothesis was run and failed to refute it. State what that test was. If you cannot name one, you have a hypothesis, not a cause.
|
|
42
|
+
|
|
43
|
+
When the gate cannot pass yet, write the claim as `hypothesis` everywhere — never `cause` / `confirmed` / `dominant`. Promote it only when 1–4 are satisfied.
|
|
33
44
|
|
|
34
45
|
## Persistent Debug File
|
|
35
46
|
|
|
@@ -123,6 +134,7 @@ Add logging before changing behavior. See what's happening before guessing.
|
|
|
123
134
|
- **Anchoring**: Fixating on the first theory
|
|
124
135
|
- **Availability bias**: Blaming the last thing changed, even if unrelated
|
|
125
136
|
- **Sunk cost**: Continuing a dead-end investigation because of time spent
|
|
137
|
+
- **Instrument blindness**: Trusting a null result ("0 errors", "detector silent") from a tool that structurally cannot observe the failure mode in question. See the Confirmation Gate blind-spot check.
|
|
126
138
|
|
|
127
139
|
After 3 failed hypotheses, step back and re-read symptoms from scratch.
|
|
128
140
|
|
|
@@ -137,7 +149,8 @@ After 5 hypotheses tested without root cause:
|
|
|
137
149
|
|
|
138
150
|
When root cause found:
|
|
139
151
|
1. Fix the issue
|
|
140
|
-
2.
|
|
141
|
-
3.
|
|
142
|
-
4.
|
|
143
|
-
5.
|
|
152
|
+
2. **Confirm the fix addresses the mechanism, not just the symptom.** Re-run the falsification test / repro and verify the *mechanism* identified in the Confirmation Gate is gone — not merely that the symptom stopped appearing. "I stopped seeing it" is itself a premature commit: a symptom can vanish for an unrelated reason.
|
|
153
|
+
3. Add a regression test for this specific bug
|
|
154
|
+
4. Commit: `fix({scope}): {description of root cause and fix}`
|
|
155
|
+
5. Move debug file to `.forge/debug/resolved/`
|
|
156
|
+
6. Update `.forge/state/milestone-{id}.yml` — remove from blockers
|
|
@@ -267,6 +267,7 @@ Re-plan? Route to `planning` with summary.
|
|
|
267
267
|
3. **Reference user's words.** "Friday" factors into every rec.
|
|
268
268
|
4. **Stop when clear.** Don't manufacture uncertainty.
|
|
269
269
|
5. **Now vs later.** Flag deferrable vs blocking.
|
|
270
|
+
6. **Widen before narrowing.** Before converging on an approach, put options that intervene at *different layers* on the table — including the constraint-breaker ("change or eliminate the constraint") and "do nothing" — not three variants of one idea. Name the assumption each challenges, then pick. (Foundational/structural decisions formalize this in `architecting`'s option-space framing + Platform & Goal Validation.)
|
|
270
271
|
|
|
271
272
|
## Anti-Patterns
|
|
272
273
|
|
|
@@ -105,6 +105,16 @@ feat(auth-01): implement JWT-based login
|
|
|
105
105
|
- Include integration test for login flow
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## Version Bump Protocol
|
|
109
|
+
|
|
110
|
+
When a task bumps the project version (`package.json` etc.) or adds a CHANGELOG entry:
|
|
111
|
+
|
|
112
|
+
1. **Read the reserved number from `.forge/releases.yml`** — write ONLY the version this milestone reserved (Version Reservation Protocol, FORGE.md). Never invent a number at delivery.
|
|
113
|
+
2. **No reservation? Reserve now** (planning predated the registry): `git pull`, take `max(version) + bump-level`, append one `{milestone, version, bump, reserved_at, summary}` entry, **commit + push the reservation BEFORE editing the version file** so a parallel session sees it taken.
|
|
114
|
+
3. **Then** write the version + CHANGELOG entry in the normal atomic commit.
|
|
115
|
+
|
|
116
|
+
This is what stops two parallel milestone sessions from grabbing the same number or clobbering each other's version edit — the registry, not the file, is the source of truth for "what number is mine."
|
|
117
|
+
|
|
108
118
|
## Multi-Agent Claim Convention [Experimental — M10]
|
|
109
119
|
|
|
110
120
|
**Trigger:** active milestone state has `lifecycle.worktree_mode: active` (set by `orchestrating` skill). If absent or any other value → skip this section entirely; single-agent behavior unchanged.
|
|
@@ -205,7 +205,13 @@ Tier + state → invoke via `Skill` tool. All phases use `Skill()`.
|
|
|
205
205
|
|
|
206
206
|
**CRITICAL: NEVER `EnterPlanMode`.** "Planning" = `Skill(planning)`. Native plan mode writes wrong format, bypasses gates + state.
|
|
207
207
|
|
|
208
|
-
**Experimental
|
|
208
|
+
**Experimental — M10 orchestration (auto-routes when installed).** Installing M10 *is* the consent: `forge` then auto-routes Standard/Full work through `orchestrating` **before** `executing` — no separate invocation to remember. Route through `Skill(orchestrating)` first when **all** hold:
|
|
209
|
+
- **Installed:** `.claude/skills/orchestrating/` present + `forge-orchestrator` in `.mcp.json` + `.claude/hooks/forge-claim-check.sh` present.
|
|
210
|
+
- **Not opted out:** `orchestration.auto` in `project.yml` ≠ `false` (absent = on; install = consent per ADR-001).
|
|
211
|
+
- **Tier is Standard or Full.** Quick never auto-orchestrates (a worktree per typo is the wrong trade).
|
|
212
|
+
- **Not already in a session:** active milestone has no `lifecycle.worktree_mode: active|degraded` (don't re-bootstrap inside a live session).
|
|
213
|
+
|
|
214
|
+
`orchestrating` bootstrap stays the safety net — incompatible repo → it refuses, writes `worktree_mode: refused`, and `forge` continues single-agent. Absent install → standard routing. Manual `Skill(orchestrating)` still works for explicit control; set `orchestration.auto: false` in `project.yml` to opt back out.
|
|
209
215
|
|
|
210
216
|
### Auto-Routing (Always Deterministic)
|
|
211
217
|
|
|
@@ -381,6 +381,7 @@ Done when approved.
|
|
|
381
381
|
## Handoff
|
|
382
382
|
|
|
383
383
|
1. **Persist** -- plans `.forge/phases/`, reqs `.forge/requirements/m{N}.yml`, roadmap `.forge/roadmap.yml`, context `.forge/context.md`
|
|
384
|
-
2. **
|
|
385
|
-
3. **State
|
|
386
|
-
4.
|
|
384
|
+
2. **Reserve version (if delivery bumps the version)** -- if any plan in this milestone will bump `package.json`/the project version, append ONE entry to `.forge/releases.yml` (Version Reservation Protocol, FORGE.md): pull first, set `version` = highest reserved `max()` bumped by this change's semver level, append `{milestone, version, bump, reserved_at, summary}`. **Never edit prior entries.** No version bump in scope → skip. Surface the reserved number to the user.
|
|
385
|
+
3. **State** -- `current.status` = `executing` in `.forge/state/milestone-{id}.yml`
|
|
386
|
+
4. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after planning — m{N} {phase-name}"` (scoped; never `git add .`). The `releases.yml` reservation rides in this commit — **push it** so a parallel session sees the number taken before they reserve.
|
|
387
|
+
5. *"Plan written and synced. `/clear` then `/forge` to continue."*
|
|
@@ -70,8 +70,9 @@ Invoked via forge routing (mid-workflow or refactor-backlog item with milestone
|
|
|
70
70
|
|
|
71
71
|
1. Read `.forge/state/milestone-{id}.yml` for current position
|
|
72
72
|
2. Follow standard workflow (above)
|
|
73
|
-
3.
|
|
74
|
-
4.
|
|
73
|
+
3. **If this task worked a refactor-backlog item:** in `.forge/refactor-backlog.yml` set that item's `status: done`, write `completed` (today, ISO) + `completed_by` (commit sha / one-line), THEN run the same **compaction-on-write** as `reviewing` Step 7 → "Status vocab + compaction-on-write" (normalize statuses → archive terminal items to `.forge/refactor-backlog-archive.yml` → leave only actionable items). Compaction is idempotent. (See reviewing Step 7 for the canonical rule — not repeated here.) This closes the loop: a worked item leaves the working backlog instead of lingering as `pending`.
|
|
74
|
+
4. After commit: update `milestone-{id}.yml` — advance the `current` cursor if the task moved position and set `current.last_updated`; log deviations if any Rule 1-3 applied. Do **not** write a progress percent (derived on read by `forge`) and do **not** write `index.yml` (derived). Then **state-sync commit**: `git add .forge/` && `git commit -m "chore(forge): sync state after quick-task — m{N}"` (scoped; never `git add .`).
|
|
75
|
+
5. Report: fix description, files changed, current position
|
|
75
76
|
|
|
76
77
|
### Without Milestone
|
|
77
78
|
|
|
@@ -333,6 +333,21 @@ Missing? Create from `.forge/templates/refactor-backlog.yml`.
|
|
|
333
333
|
|
|
334
334
|
**When deferring an individual refactor item** (status flipped to `deferred`, as opposed to milestone-wide defer which goes through the forge skill): write `deferred_at` (ISO date today) and `deferred_reason` (one-line) siblings to `status: deferred`. Pre-existing items without these fields parse fine — lazy migration. These feed the `deferred` aggregator skill.
|
|
335
335
|
|
|
336
|
+
### Status vocab + compaction-on-write
|
|
337
|
+
|
|
338
|
+
**Canonical status vocab — the ONLY allowed values:** `pending | in_progress | done | dismissed | deferred`. Actionable = `pending|in_progress|deferred`. Terminal = `done|dismissed`.
|
|
339
|
+
|
|
340
|
+
**Normalization map** (apply before any write): `completed`,`complete`,`closed` → `done`; `wont_fix`,`stale` → `dismissed`. Any other non-canonical value (`null`, free text, `promoted_to_m{N}`, …) → leave in place, surface for human triage in the review output (`"Backlog has {N} item(s) with unrecognized status — triage"`). Never silently drop.
|
|
341
|
+
|
|
342
|
+
**Compaction-on-write** — run EVERY time you write the backlog, after appending new items:
|
|
343
|
+
1. Normalize statuses via the map above.
|
|
344
|
+
2. Move every terminal (`done|dismissed`) item — full record, intact — by **append** to `.forge/refactor-backlog-archive.yml` (create with a one-line header if absent: `# Forge Refactor Backlog — Archive (resolved/dismissed items). Append-only.`).
|
|
345
|
+
3. Remove those items from `.forge/refactor-backlog.yml` so the working file holds only actionable + un-triaged items.
|
|
346
|
+
|
|
347
|
+
Idempotent: a backlog with no terminal items and all-canonical statuses is left byte-unchanged. No item is ever lost — terminal items relocate, they are not deleted.
|
|
348
|
+
|
|
349
|
+
**Size-gate check (advisory).** After compaction, if the working file still exceeds the FORGE.md size gate (150 KB), emit: `"Refactor backlog over size gate ({size}) even after compaction — {N} actionable items. Consider triaging stale pending items or promoting a cluster to Standard tier. Advisory — does not block."` (A busy project with hundreds of unworked pending items legitimately trips this; the signal is "triage the actionable backlog", not "archiving failed".)
|
|
350
|
+
|
|
336
351
|
### Route
|
|
337
352
|
|
|
338
353
|
**HEALTHY/WARNINGS (accepted):** set `current.status: complete` in `milestone-{id}.yml`, then regenerate `index.yml` via the `forge` **Rollup** (do not hand-edit index). *"Milestone [{name}] complete. {N} backlog items."* Beads: `bd complete`.
|
|
@@ -227,6 +227,45 @@ Migrate now? (yes/no/show guide)
|
|
|
227
227
|
|
|
228
228
|
`upgrading` never moves phase dirs directly — migration runs via `quick-tasking`.
|
|
229
229
|
|
|
230
|
+
### Pre-0.22.0 unbounded refactor-backlog
|
|
231
|
+
|
|
232
|
+
Run from project root (backlog present only):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
F=.forge/refactor-backlog.yml
|
|
236
|
+
[ -f "$F" ] || exit 0
|
|
237
|
+
kb=$(( $(wc -c < "$F") / 1024 ))
|
|
238
|
+
terminal=$(grep -cE 'status:[[:space:]]*(done|dismissed|completed|complete|closed|wont_fix|stale)' "$F")
|
|
239
|
+
noncanon=$(grep -oE 'status:[[:space:]]*[A-Za-z_]+' "$F" \
|
|
240
|
+
| grep -vE 'status:[[:space:]]*(pending|in_progress|done|dismissed|deferred)$' | wc -l)
|
|
241
|
+
# bloat if: kb > 150 OR terminal > 0 OR noncanon > 0
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Surface the prompt when `kb > 150` OR `terminal > 0` OR `noncanon > 0`, printing the three detected numbers:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
Refactor-backlog compaction available (Forge 0.22.0)
|
|
248
|
+
────────────────────────────────────────────────────
|
|
249
|
+
Pre-0.22.0 the refactor backlog was append-only — resolved items were never
|
|
250
|
+
pruned and statuses drifted (completed/complete/closed/wont_fix/stale + junk).
|
|
251
|
+
0.22.0 archives terminal items to .forge/refactor-backlog-archive.yml and
|
|
252
|
+
enforces the canonical vocab (pending|in_progress|done|dismissed|deferred).
|
|
253
|
+
|
|
254
|
+
Detected: {kb} KB · {terminal} terminal item(s) · {noncanon} non-canonical status(es).
|
|
255
|
+
A migration guide is available at: .forge/migrations/0.22.0-backlog-compaction.md
|
|
256
|
+
(canonical: https://github.com/Attuned-Media/forge/blob/main/docs/migrations/0.22.0-backlog-compaction.md)
|
|
257
|
+
|
|
258
|
+
Migrate now? (yes/no/show guide)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- **yes** → invoke `quick-tasking` skill, hand it the migration guide as the task definition (snapshot item count, normalize statuses, split actionable/terminal/triage, archive terminal items to `.forge/refactor-backlog-archive.yml`, rewrite the working file, verify item-count conservation before committing).
|
|
262
|
+
- **show guide** → read and display the file, then re-ask
|
|
263
|
+
- **no** → note in upgrade report. The backlog keeps growing and statuses stay drifted until migrated; reviewing/quick-tasking will compact on their next write regardless, but a bloated file lingers until then. Recommend running before the next review cycle.
|
|
264
|
+
|
|
265
|
+
No-op guarantee: detection exits clean (no prompt) when the backlog is absent, under the gate, has zero terminal items, and all statuses are canonical. (Pending-item count is **not** a trigger — a busy-but-clean backlog is left alone.)
|
|
266
|
+
|
|
267
|
+
`upgrading` never edits the backlog directly — migration runs via `quick-tasking`.
|
|
268
|
+
|
|
230
269
|
### Future migrations
|
|
231
270
|
|
|
232
271
|
Add new detection blocks here for each Forge version that changes file layout. Pattern:
|
package/template/.forge/FORGE.md
CHANGED
|
@@ -17,6 +17,7 @@ All phases: **invoke via `Skill` tool**, never native behavior. `planning` → `
|
|
|
17
17
|
5. **Verify goals, not tasks.** "Does it work?" > "Did we finish checklist?"
|
|
18
18
|
6. **Never forget.** `.forge/state/` persists across sessions. Milestones concurrent. Beads-compatible.
|
|
19
19
|
7. **Pave desire paths.** Repeated deviations/corrections = signal → evolve framework.
|
|
20
|
+
8. **Disconfirm before commit.** Before locking a diagnosis, a solution, or a foundational choice, run the cheap pass that could break it. A conclusion reached without an attempt to refute it is a hypothesis, not a finding. Enforced at the three commit points: `debugging` (Confirmation Gate), `discussing`/`architecting` (option-space framing), `architecting` (Platform & Goal Validation).
|
|
20
21
|
|
|
21
22
|
## Workflow Tiers
|
|
22
23
|
|
|
@@ -72,6 +73,7 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
|
|
|
72
73
|
| `requirements/m{N}.yml` | 50 KB/file | Prevents scope creep |
|
|
73
74
|
| `plan.md` | 30 KB | Keeps executor context <50% |
|
|
74
75
|
| `constitution.md` | 10 KB | Gates must be scannable |
|
|
76
|
+
| `refactor-backlog.yml` | 150 KB | Working backlog stays scannable; terminal items archive to `refactor-backlog-archive.yml` (advisory — flags, never blocks) |
|
|
75
77
|
|
|
76
78
|
### Fresh Agent Pattern
|
|
77
79
|
Task touches 20+ files or complex subsystem → spawn fresh executor with isolated context. Prevents context rot.
|
|
@@ -135,7 +137,9 @@ State lives in `.forge/`:
|
|
|
135
137
|
- `context.md` — Locked decisions + deferred ideas (discuss phase)
|
|
136
138
|
- `research/milestone-{id}.md` — Research findings snapshot (dated, immutable)
|
|
137
139
|
- `phases/milestone-{id}/{phase}-{name}/plan-{NN}.md` — Task plans with must_haves frontmatter (`{id}`=milestone, `{phase}`=phase# preserved verbatim, `{name}`=kebab, `{NN}`=seq)
|
|
138
|
-
- `refactor-backlog.yml` — Refactoring catalog, worked via quick-tasking
|
|
140
|
+
- `refactor-backlog.yml` — Refactoring catalog (actionable items only), worked via quick-tasking. Canonical status vocab: `pending | in_progress | done | dismissed | deferred`. Terminal items auto-archive on the next reviewing/quick-tasking write (compaction-on-write).
|
|
141
|
+
- `refactor-backlog-archive.yml` — Append-only terminal-item archive (`done`/`dismissed`); keeps the audit trail out of the working backlog.
|
|
142
|
+
- `releases.yml` — Append-only version-reservation registry (cross-session coordination point; see Version Reservation Protocol)
|
|
139
143
|
- `archive/milestone-{id}/` — Archived milestone artifacts (archive-delete)
|
|
140
144
|
|
|
141
145
|
**Milestones** group phases into concurrent streams. Own state file — no conflicts across sessions. Can be deferred (frozen in place) or archive-deleted.
|
|
@@ -161,6 +165,17 @@ State-sync commits are separate from per-task code commits (which stay atomic du
|
|
|
161
165
|
- `index.yml` is **regenerated by rollup** (read every `milestone-*.yml` → rewrite the registry) by the main/orchestrator session — on `forge` resume and at `orchestrating` teardown. The rollup is deterministic + idempotent, so it **is** the reconcile step — never a hand-merge.
|
|
162
166
|
- Same-milestone parallel work is out of scope, guarded by the M10 claim layer.
|
|
163
167
|
|
|
168
|
+
### Version Reservation Protocol
|
|
169
|
+
|
|
170
|
+
The project version + CHANGELOG slot are **shared resources** — when two milestones run in parallel sessions, each independently bumping `package.json`/version and adding a CHANGELOG entry collides. `.forge/releases.yml` removes the number from per-milestone scope.
|
|
171
|
+
|
|
172
|
+
- **Reserve early, at planning.** When a milestone's delivery will bump the version, the `planning` skill appends ONE entry to `.forge/releases.yml`: `{milestone, version, bump, reserved_at, summary}`. The reserved version = highest `version:` in the file bumped by the change's semver level (capability → minor; fix/doc → patch — see version-bump criteria below).
|
|
173
|
+
- **Append-only — never edit prior entries.** Distinct lines = no cross-session conflict. Commit + push the reservation BEFORE editing the version file, so a parallel session pulls and sees the number taken.
|
|
174
|
+
- **Delivery never invents a number.** The `executing`/delivery step writes only the version reserved in `releases.yml`, then its CHANGELOG entry. If no reservation exists (milestone planned before this file), it reserves at delivery: pull, take `max() + bump`, append, push, then write.
|
|
175
|
+
- **Race resolution.** Two near-simultaneous reservations: the second push rebases onto the first (append-only → clean), re-reads `max()`, takes the next number.
|
|
176
|
+
|
|
177
|
+
**Version-bump criteria (0.x projects):** patch (`0.x.y`) = bug fix, doc tweak, sync-only, refinement. Minor (`0.x.0`) = new skill/agent/routing-row/auto-trigger/state-artifact/capability. Major (`x.0.0`) = post-1.0 breaking change. Default to minor when in doubt — a capability addition shipped as a patch drifts the version away from truth.
|
|
178
|
+
|
|
164
179
|
## Deviation Rules
|
|
165
180
|
|
|
166
181
|
**Full definitions:** `.claude/agents/executor.md`.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Migration Guide: Refactor-Backlog Compaction (Forge 0.22.0)
|
|
2
|
+
|
|
3
|
+
Applies to projects whose `.forge/refactor-backlog.yml` has grown unbounded
|
|
4
|
+
(terminal items never pruned) and/or has drifted off the canonical status
|
|
5
|
+
vocabulary.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Before 0.22.0 the refactor backlog was **append-only** — the `reviewing` skill
|
|
10
|
+
added items (`Next ID = max + 1`) but nothing ever removed resolved ones. Over a
|
|
11
|
+
project's life, `done`/`dismissed` items accumulate forever, and the `status`
|
|
12
|
+
field drifts into synonyms (`completed`, `complete`, `closed`) and junk
|
|
13
|
+
(`null`, free text). One real project reached **9,256 lines / 439 items**, of
|
|
14
|
+
which only ~220 were actionable — the rest was resolved-item sediment.
|
|
15
|
+
|
|
16
|
+
Per Article XI (context is sacred), the working backlog must stay scannable. A
|
|
17
|
+
multi-thousand-line YAML file is a context tax on every `reviewing`/`deferred`
|
|
18
|
+
read and is impossible to triage by hand.
|
|
19
|
+
|
|
20
|
+
0.22.0 keeps the working file **actionable-only**: terminal items move to an
|
|
21
|
+
append-only archive, and statuses are normalized to a single vocabulary.
|
|
22
|
+
|
|
23
|
+
## What changes
|
|
24
|
+
|
|
25
|
+
- Terminal items (`done` | `dismissed`) move — full record intact — to
|
|
26
|
+
`.forge/refactor-backlog-archive.yml` (append-only).
|
|
27
|
+
- Statuses normalize to the canonical set:
|
|
28
|
+
`pending | in_progress | done | dismissed | deferred`.
|
|
29
|
+
- The working `.forge/refactor-backlog.yml` keeps only **actionable**
|
|
30
|
+
(`pending | in_progress | deferred`) and any **un-triaged** items.
|
|
31
|
+
|
|
32
|
+
## What does NOT change
|
|
33
|
+
|
|
34
|
+
- **No item is deleted.** Terminal items are relocated to the archive, not
|
|
35
|
+
dropped. The full record (`completed`, `completed_by`, `dismissed_reason`,
|
|
36
|
+
dates) is preserved — the audit trail just lives out of the working file.
|
|
37
|
+
- **IDs are stable.** `R042` stays `R042` in whichever file it lands in.
|
|
38
|
+
- **Actionable items are content-stable** apart from status normalization
|
|
39
|
+
(a legacy `completed` only ever appears on a terminal item, so actionable
|
|
40
|
+
items are typically byte-unchanged).
|
|
41
|
+
- **`deferred` items stay in the working file** with their `deferred_at` /
|
|
42
|
+
`deferred_reason` — they are actionable (awaiting a revisit), not terminal.
|
|
43
|
+
|
|
44
|
+
## Status normalization map
|
|
45
|
+
|
|
46
|
+
Apply before splitting. Fold each legacy value to its canonical equivalent:
|
|
47
|
+
|
|
48
|
+
| Legacy value(s) | Canonical |
|
|
49
|
+
|-----------------|-----------|
|
|
50
|
+
| `completed`, `complete`, `closed` | `done` |
|
|
51
|
+
| `wont_fix`, `stale` | `dismissed` |
|
|
52
|
+
| `pending`, `in_progress`, `done`, `dismissed`, `deferred` | (unchanged) |
|
|
53
|
+
| anything else (`null`, `RegistrationStatus`, free text, `promoted_to_m{N}`) | **TRIAGE** |
|
|
54
|
+
|
|
55
|
+
A `TRIAGE` value is **never** auto-resolved. Leave the item in the working file
|
|
56
|
+
and surface it for a human decision: *"{N} item(s) have an unrecognized status —
|
|
57
|
+
triage."* Silently dropping or guessing is forbidden.
|
|
58
|
+
|
|
59
|
+
## Detection
|
|
60
|
+
|
|
61
|
+
Bloat/drift exists if the file is over the 150 KB size gate, OR any terminal
|
|
62
|
+
item is present, OR any non-canonical status appears:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
F=.forge/refactor-backlog.yml
|
|
66
|
+
[ -f "$F" ] || { echo "no backlog — no-op"; exit 0; }
|
|
67
|
+
kb=$(( $(wc -c < "$F") / 1024 ))
|
|
68
|
+
terminal=$(grep -cE 'status:[[:space:]]*(done|dismissed|completed|complete|closed|wont_fix|stale)' "$F")
|
|
69
|
+
noncanon=$(grep -oE 'status:[[:space:]]*[A-Za-z_]+' "$F" \
|
|
70
|
+
| grep -vE 'status:[[:space:]]*(pending|in_progress|done|dismissed|deferred)$' | wc -l)
|
|
71
|
+
echo "size=${kb}KB terminal=$terminal noncanon=$noncanon"
|
|
72
|
+
# bloat if: kb > 150 OR terminal > 0 OR noncanon > 0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
A clean backlog (under the gate, zero terminal, all-canonical) prints
|
|
76
|
+
`size=… terminal=0 noncanon=0` → **no-op**. The detector keys off bloat that
|
|
77
|
+
compaction actually fixes, **not** the count of pending items — a busy-but-clean
|
|
78
|
+
backlog is left alone.
|
|
79
|
+
|
|
80
|
+
## ⚠️ Environment gotcha (read before scripting)
|
|
81
|
+
|
|
82
|
+
The Bash tool runs **zsh**, where `$BASH_REMATCH` / `=~` capture groups **do not
|
|
83
|
+
populate** — a regex-capture loop written for bash silently produces empty
|
|
84
|
+
captures and can corrupt output. Two rules:
|
|
85
|
+
|
|
86
|
+
1. **Prefer a single-pass YAML-aware split over per-line regex.** This migration
|
|
87
|
+
relocates list items between two YAML files — operate on whole item blocks,
|
|
88
|
+
not field-by-field string surgery.
|
|
89
|
+
2. If you must use `=~` capture, **wrap the loop in `bash -c '…'`** so it runs
|
|
90
|
+
under bash, not zsh.
|
|
91
|
+
|
|
92
|
+
And the cardinal rule from prior migrations: **verify item count before == after
|
|
93
|
+
+ archived.** If the totals don't conserve, STOP and recover from git.
|
|
94
|
+
|
|
95
|
+
## Procedure
|
|
96
|
+
|
|
97
|
+
### 1. Secure current state first
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git add .forge/refactor-backlog.yml
|
|
101
|
+
git commit -m "chore(forge): secure backlog before 0.22.0 compaction"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2. Snapshot the original item count
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
N_BEFORE=$(grep -cE '^[[:space:]]*-?[[:space:]]*id:' .forge/refactor-backlog.yml)
|
|
108
|
+
echo "items before: $N_BEFORE"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 3. Normalize statuses
|
|
112
|
+
|
|
113
|
+
Fold synonyms per the map above (`completed`/`complete`/`closed` → `done`;
|
|
114
|
+
`wont_fix`/`stale` → `dismissed`). Record any value that is still non-canonical
|
|
115
|
+
after normalization as the **TRIAGE** set — do not change it.
|
|
116
|
+
|
|
117
|
+
### 4. Split into three buckets
|
|
118
|
+
|
|
119
|
+
Walk the item list once and bucket each item by its (normalized) status:
|
|
120
|
+
|
|
121
|
+
- **actionable** → `pending | in_progress | deferred`
|
|
122
|
+
- **terminal** → `done | dismissed`
|
|
123
|
+
- **triage** → anything else (unrecognized)
|
|
124
|
+
|
|
125
|
+
Operate on whole item blocks (an item starts at `- id:` and runs until the next
|
|
126
|
+
`- id:` or EOF). Preserve every field verbatim.
|
|
127
|
+
|
|
128
|
+
### 5. Write the archive
|
|
129
|
+
|
|
130
|
+
Append the **terminal** bucket (full records) to
|
|
131
|
+
`.forge/refactor-backlog-archive.yml`. Create it if absent with a header:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
# Forge Refactor Backlog — Archive (resolved/dismissed items). Append-only.
|
|
135
|
+
# Items relocated here by compaction; full record preserved for audit trail.
|
|
136
|
+
items:
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 6. Rewrite the working file
|
|
140
|
+
|
|
141
|
+
Rewrite `.forge/refactor-backlog.yml` with the **actionable + triage** buckets
|
|
142
|
+
only (triage stays visible until a human resolves it). Keep the template header.
|
|
143
|
+
|
|
144
|
+
### 7. Verify conservation — the hard gate
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
A=$(grep -cE '^[[:space:]]*-?[[:space:]]*id:' .forge/refactor-backlog.yml) # actionable + triage
|
|
148
|
+
B=$(grep -cE '^[[:space:]]*-?[[:space:]]*id:' .forge/refactor-backlog-archive.yml) # archived (subtract its template example if any)
|
|
149
|
+
echo "working=$A archived=$B sum=$((A+B)) before=$N_BEFORE"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`working + archived` **must equal** `N_BEFORE`. If it does not, **STOP** — do not
|
|
153
|
+
commit; `git checkout .forge/refactor-backlog.yml` and investigate (a malformed
|
|
154
|
+
item block failed to parse).
|
|
155
|
+
|
|
156
|
+
### 8. Commit
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git add .forge/refactor-backlog.yml .forge/refactor-backlog-archive.yml
|
|
160
|
+
git commit -m "chore(forge): compact refactor backlog — archive terminal items (0.22.0)"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## After migration
|
|
164
|
+
|
|
165
|
+
- The working file may still exceed the 150 KB gate if the project has hundreds
|
|
166
|
+
of genuinely-unworked `pending` items. That is a legitimate **triage** signal
|
|
167
|
+
("work or dismiss stale items / promote a cluster to Standard tier"), **not** a
|
|
168
|
+
failed migration. Compaction archives resolved items; it does not invent triage
|
|
169
|
+
decisions about open ones.
|
|
170
|
+
- From here on, `reviewing` Step 7 and `quick-tasking` compact on every write —
|
|
171
|
+
the backlog stays bounded without re-running this migration.
|
|
172
|
+
|
|
173
|
+
## Rollback
|
|
174
|
+
|
|
175
|
+
The whole operation is one commit over two files. `git revert <sha>` (or
|
|
176
|
+
`git checkout <pre-migration-sha> -- .forge/refactor-backlog.yml` and delete the
|
|
177
|
+
archive) fully reverses it — nothing was destroyed.
|
|
178
|
+
|
|
179
|
+
## Notes
|
|
180
|
+
|
|
181
|
+
- Idempotent: re-running on an already-compacted backlog archives nothing and
|
|
182
|
+
leaves the working file byte-unchanged.
|
|
183
|
+
- The `upgrading` skill (Step 7) detects bloat/drift after an upgrade and offers
|
|
184
|
+
to run this migration via `quick-tasking`. It never edits the backlog directly.
|
|
185
|
+
- Canonical copy of this guide:
|
|
186
|
+
https://github.com/Attuned-Media/forge/blob/main/docs/migrations/0.22.0-backlog-compaction.md
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Forge Release Registry — version reservations across concurrent milestones
|
|
2
|
+
#
|
|
3
|
+
# PURPOSE: remove the version number + CHANGELOG slot from per-milestone scope so
|
|
4
|
+
# two milestones running in parallel sessions never collide on your project's
|
|
5
|
+
# version or pick the same number. This is the coordination point both sessions
|
|
6
|
+
# read and append to.
|
|
7
|
+
#
|
|
8
|
+
# COMMITTED + APPEND-ONLY. Each milestone appends exactly ONE entry when it
|
|
9
|
+
# reserves its version (at planning, or at delivery if planning predates this
|
|
10
|
+
# file). Appending — never editing prior entries — is what keeps two sessions
|
|
11
|
+
# from conflicting: distinct lines, no contention. Commit and push the
|
|
12
|
+
# reservation so a parallel session pulls and sees numbers already taken.
|
|
13
|
+
#
|
|
14
|
+
# RULE: the next free version = highest `version:` in this file, bumped by the
|
|
15
|
+
# change's semver level (capability = minor; fix/doc = patch). Delivery NEVER
|
|
16
|
+
# writes a project version that is not reserved here first.
|
|
17
|
+
#
|
|
18
|
+
# RACE NOTE: reserve EARLY (at planning), commit + push the reservation BEFORE
|
|
19
|
+
# editing the version. If two sessions reserve near-simultaneously, the second
|
|
20
|
+
# push rebases onto the first (append-only → no conflict) and re-reads max() to
|
|
21
|
+
# take the next number.
|
|
22
|
+
|
|
23
|
+
releases: []
|
|
24
|
+
# Example entry shape (delete this comment once you add a real one):
|
|
25
|
+
# - milestone: 1
|
|
26
|
+
# version: "0.2.0"
|
|
27
|
+
# bump: minor
|
|
28
|
+
# reserved_at: "YYYY-MM-DD"
|
|
29
|
+
# summary: "one-line description of the milestone"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# Forge Refactor Backlog
|
|
2
|
-
# Auto-managed by the
|
|
3
|
-
#
|
|
1
|
+
# Forge Refactor Backlog — actionable items only.
|
|
2
|
+
# Auto-managed by the reviewing skill. Items added after milestone audits;
|
|
3
|
+
# worked via quick-tasking (effort: quick) or Standard tier (effort: standard).
|
|
4
4
|
#
|
|
5
|
-
# Status vocab: pending | in_progress | done | dismissed | deferred
|
|
5
|
+
# Status vocab (the ONLY allowed values): pending | in_progress | done | dismissed | deferred
|
|
6
|
+
# Actionable = pending | in_progress | deferred Terminal = done | dismissed
|
|
7
|
+
# Compaction-on-write: every reviewing/quick-tasking write normalizes statuses and
|
|
8
|
+
# moves terminal items (full record) to .forge/refactor-backlog-archive.yml, so this
|
|
9
|
+
# file holds only actionable items. No item is deleted — terminal items relocate.
|
|
10
|
+
# Legacy synonyms are normalized: completed/complete/closed → done; wont_fix/stale → dismissed.
|
|
6
11
|
# Optional fields (lazy migration — only required on entries set to status: deferred going forward):
|
|
7
12
|
# deferred_at: ISO date when status flipped to deferred
|
|
8
13
|
# deferred_reason: one-line why; revisited when status changes
|