forge-orkes 0.30.0 → 0.32.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/bin/create-forge.js +94 -118
- package/package.json +1 -1
- package/template/.claude/skills/chief-of-staff/SKILL.md +238 -0
- package/template/.claude/skills/discussing/SKILL.md +2 -0
- package/template/.claude/skills/forge/SKILL.md +31 -4
- package/template/.claude/skills/planning/SKILL.md +9 -0
- package/template/.claude/skills/upgrading/SKILL.md +21 -198
- package/template/.forge/FORGE.md +57 -23
- package/template/.forge/migrations/0.10.0-per-milestone-requirements.md +10 -6
- package/template/.forge/migrations/0.17.0-cross-layer-contracts.md +7 -7
- package/template/.forge/migrations/0.19.0-worktree-safe-state.md +9 -4
- package/template/.forge/migrations/0.20.0-nested-phase-layout.md +8 -4
- package/template/.forge/migrations/0.22.0-backlog-compaction.md +12 -8
- package/template/.forge/migrations/0.28.0-worktree-root.md +5 -5
- package/template/.forge/migrations/0.29.0-shared-state-taxonomy.md +7 -2
- package/template/.forge/migrations/0.32.0-chief-streams.md +159 -0
- package/template/.forge/releases.yml +85 -15
- package/template/.forge/templates/context.md +4 -0
- package/template/.forge/templates/migration-guide.md +111 -0
- package/template/.forge/templates/streams/active.yml +34 -0
- package/template/.forge/templates/streams/brief.md +47 -0
- package/template/.forge/templates/streams/stream.yml +56 -0
- package/template/.forge/templates/streams/work-package.yml +70 -0
package/bin/create-forge.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
6
7
|
|
|
7
8
|
const templateDir = path.join(__dirname, '..', 'template');
|
|
8
9
|
const targetDir = process.cwd();
|
|
@@ -349,144 +350,117 @@ async function install() {
|
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
353
|
+
* Extract the first fenced ```bash block under a guide's `## Detection` heading.
|
|
354
|
+
* Returns the script string, or null if the guide has no such block (e.g. a
|
|
355
|
+
* non-conformant guide). State-machine parse — no markdown dependency.
|
|
354
356
|
*/
|
|
355
|
-
function
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
legacy.push(path.join('.forge', entry.name));
|
|
365
|
-
} else if (/^requirements-m\d+\.yml$/.test(entry.name)) {
|
|
366
|
-
legacy.push(path.join('.forge', entry.name));
|
|
357
|
+
function extractDetectionBlock(guideContent) {
|
|
358
|
+
const lines = guideContent.split('\n');
|
|
359
|
+
let inDetection = false;
|
|
360
|
+
let inCode = false;
|
|
361
|
+
const code = [];
|
|
362
|
+
for (const line of lines) {
|
|
363
|
+
if (!inDetection) {
|
|
364
|
+
if (/^##\s+Detection\b/.test(line)) inDetection = true;
|
|
365
|
+
continue;
|
|
367
366
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if (!phase.isDirectory()) continue;
|
|
374
|
-
const reqPath = path.join(phasesDir, phase.name, 'requirements.yml');
|
|
375
|
-
if (fs.existsSync(reqPath)) {
|
|
376
|
-
legacy.push(path.join('.forge', 'phases', phase.name, 'requirements.yml'));
|
|
377
|
-
}
|
|
367
|
+
// Stop scanning if we hit the next top-level (## ...) heading before a block.
|
|
368
|
+
if (!inCode && /^##\s/.test(line) && !/^##\s+Detection\b/.test(line)) break;
|
|
369
|
+
if (!inCode) {
|
|
370
|
+
if (/^```bash\s*$/.test(line)) inCode = true;
|
|
371
|
+
continue;
|
|
378
372
|
}
|
|
373
|
+
if (/^```/.test(line)) break; // closing fence
|
|
374
|
+
code.push(line);
|
|
379
375
|
}
|
|
380
|
-
|
|
381
|
-
return legacy;
|
|
376
|
+
return code.length ? code.join('\n') : null;
|
|
382
377
|
}
|
|
383
378
|
|
|
384
379
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
380
|
+
* Data-driven post-upgrade migration check. Loops over the migration guides
|
|
381
|
+
* synced into the project this run (.forge/migrations/{v}-*.md), runs each
|
|
382
|
+
* guide's `## Detection` block, and on a `MIGRATE` stdout hit prints a warning
|
|
383
|
+
* with the exact guide pointer. There are NO per-version code branches: a new
|
|
384
|
+
* guide that ships with a conformant Detection block is auto-covered.
|
|
385
|
+
*
|
|
386
|
+
* Range: installed < v <= source. `installedVersion` is the project's
|
|
387
|
+
* settings.json forge.version captured BEFORE the version stamp; `source` is
|
|
388
|
+
* pkgVersion (this package's package.json) — never the template settings.json
|
|
389
|
+
* literal, which is a placeholder the installer stamps at write time. Non-
|
|
390
|
+
* interactive: the bin only warns and points at the guide; it never applies.
|
|
390
391
|
*/
|
|
391
|
-
function
|
|
392
|
-
const
|
|
393
|
-
if (!fs.existsSync(
|
|
394
|
-
|
|
395
|
-
let content;
|
|
396
|
-
try {
|
|
397
|
-
content = fs.readFileSync(projectYml, 'utf-8');
|
|
398
|
-
} catch {
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Already declared a layers: key (any value, including []) → migrated.
|
|
403
|
-
if (/^layers:/m.test(content)) return false;
|
|
404
|
-
// A seeded contract index implies layers were declared at init → migrated.
|
|
405
|
-
if (fs.existsSync(path.join(targetDir, '.forge', 'contracts', 'index.yml'))) return false;
|
|
392
|
+
function runPostUpgradeMigrationChecks(installedVersion) {
|
|
393
|
+
const migrationsDir = path.join(targetDir, '.forge', 'migrations');
|
|
394
|
+
if (!fs.existsSync(migrationsDir)) return;
|
|
406
395
|
|
|
407
|
-
|
|
408
|
-
|
|
396
|
+
const installed =
|
|
397
|
+
installedVersion && installedVersion !== 'unknown' ? installedVersion : '0.0.0';
|
|
398
|
+
const source = pkgVersion;
|
|
409
399
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
* registry (regenerated by rollup) and moves desire_paths to append-only files.
|
|
413
|
-
* A legacy index.yml still carries desire_paths:/metrics:/embedded narrative, or
|
|
414
|
-
* is large. Returns true if index.yml exists and looks legacy; false if missing
|
|
415
|
-
* (not yet initialized) or already a slim registry.
|
|
416
|
-
*/
|
|
417
|
-
function detectLegacyStateIndex() {
|
|
418
|
-
const indexYml = path.join(targetDir, '.forge', 'state', 'index.yml');
|
|
419
|
-
if (!fs.existsSync(indexYml)) return false;
|
|
420
|
-
|
|
421
|
-
let content;
|
|
400
|
+
// Select in-range guides: installed < v <= source. Parse {v} from the filename.
|
|
401
|
+
let guides;
|
|
422
402
|
try {
|
|
423
|
-
|
|
403
|
+
guides = fs
|
|
404
|
+
.readdirSync(migrationsDir)
|
|
405
|
+
.map((name) => {
|
|
406
|
+
const m = name.match(/^(\d+\.\d+\.\d+)-.*\.md$/);
|
|
407
|
+
return m ? { name, version: m[1] } : null;
|
|
408
|
+
})
|
|
409
|
+
.filter(Boolean)
|
|
410
|
+
.filter(
|
|
411
|
+
(g) =>
|
|
412
|
+
compareVersions(installed, g.version) < 0 &&
|
|
413
|
+
compareVersions(g.version, source) <= 0
|
|
414
|
+
)
|
|
415
|
+
.sort((a, b) => compareVersions(a.version, b.version));
|
|
424
416
|
} catch {
|
|
425
|
-
return
|
|
417
|
+
return;
|
|
426
418
|
}
|
|
427
419
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
420
|
+
for (const guide of guides) {
|
|
421
|
+
const guidePath = path.join(migrationsDir, guide.name);
|
|
422
|
+
let script;
|
|
423
|
+
try {
|
|
424
|
+
script = extractDetectionBlock(fs.readFileSync(guidePath, 'utf-8'));
|
|
425
|
+
} catch {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (!script) continue; // no runnable Detection block → treat as no-op
|
|
429
|
+
|
|
430
|
+
let stdout = '';
|
|
431
|
+
try {
|
|
432
|
+
stdout = execSync(script, {
|
|
433
|
+
cwd: targetDir,
|
|
434
|
+
shell: '/bin/bash',
|
|
435
|
+
encoding: 'utf-8',
|
|
436
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
437
|
+
});
|
|
438
|
+
} catch {
|
|
439
|
+
// A non-zero exit or detection error is a no-op, not a failure — keep going.
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (!/MIGRATE/.test(stdout)) continue; // no-op
|
|
432
444
|
|
|
433
|
-
|
|
434
|
-
|
|
445
|
+
const reasonLine = stdout
|
|
446
|
+
.split('\n')
|
|
447
|
+
.find((l) => l.includes('MIGRATE'));
|
|
448
|
+
const reason = reasonLine
|
|
449
|
+
? reasonLine.replace(/^.*MIGRATE\s*[—-]?\s*/, '').trim()
|
|
450
|
+
: '';
|
|
435
451
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
function runPostUpgradeMigrationChecks() {
|
|
443
|
-
const legacyReqs = detectLegacyRequirementsLayout();
|
|
444
|
-
if (legacyReqs.length > 0) {
|
|
445
|
-
console.log(' ⚠ Pre-0.10.0 requirements layout detected');
|
|
446
|
-
console.log(' ─────────────────────────────────────────');
|
|
447
|
-
console.log(' Found:');
|
|
448
|
-
for (const p of legacyReqs) {
|
|
449
|
-
console.log(` ${p}`);
|
|
450
|
-
}
|
|
451
|
-
console.log();
|
|
452
|
-
console.log(' Forge 0.10.0+ uses per-milestone files at .forge/requirements/m{N}.yml.');
|
|
453
|
-
console.log(' Migration guide: .forge/migrations/0.10.0-per-milestone-requirements.md');
|
|
452
|
+
console.log(` ⚠ ${guide.version} migration available${reason ? ` — ${reason}` : ''}`);
|
|
453
|
+
console.log(' ─────────────────────────────────────────────────────────────');
|
|
454
|
+
console.log(` Guide: .forge/migrations/${guide.name}`);
|
|
455
|
+
console.log(
|
|
456
|
+
` (canonical: https://github.com/Attuned-Media/forge/blob/main/docs/migrations/${guide.name})`
|
|
457
|
+
);
|
|
454
458
|
console.log();
|
|
455
|
-
console.log('
|
|
459
|
+
console.log(' Migrations are never applied automatically. In Claude Code:');
|
|
456
460
|
console.log(' /forge then hand the guide to quick-tasking, or invoke Skill(quick-tasking)');
|
|
457
461
|
console.log(' with the guide path as the task definition.');
|
|
458
462
|
console.log();
|
|
459
463
|
}
|
|
460
|
-
|
|
461
|
-
if (detectMissingLayersConfig()) {
|
|
462
|
-
console.log(' ⚠ Cross-layer contracts: project.yml has no `layers:` field (Forge 0.17.0)');
|
|
463
|
-
console.log(' ──────────────────────────────────────────────────────────────────────');
|
|
464
|
-
console.log(' Forge 0.17.0 added cross-layer contract detection (planning Step 6.1),');
|
|
465
|
-
console.log(' which reads `layers:` from .forge/project.yml. Your project.yml predates');
|
|
466
|
-
console.log(' this field, so planning falls back to a coarse top-level-directory heuristic.');
|
|
467
|
-
console.log();
|
|
468
|
-
console.log(' Migration guide: .forge/migrations/0.17.0-cross-layer-contracts.md');
|
|
469
|
-
console.log();
|
|
470
|
-
console.log(' Quick fix: add a `layers:` list to .forge/project.yml — or `layers: []`');
|
|
471
|
-
console.log(' for a single-layer project to silence this notice. In Claude Code:');
|
|
472
|
-
console.log(' /forge then hand the guide to quick-tasking.');
|
|
473
|
-
console.log();
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (detectLegacyStateIndex()) {
|
|
477
|
-
console.log(' ⚠ Worktree-safe state: legacy state/index.yml detected (Forge 0.19.0)');
|
|
478
|
-
console.log(' ────────────────────────────────────────────────────────────────────');
|
|
479
|
-
console.log(' Forge 0.19.0 makes index.yml a derived registry (regenerated by rollup),');
|
|
480
|
-
console.log(' moves desire_paths to append-only files, and commits .forge/ state at every');
|
|
481
|
-
console.log(' phase handoff. Your index.yml predates this — it still carries desire_paths/');
|
|
482
|
-
console.log(' metrics or embedded narrative, which conflicts across git worktrees.');
|
|
483
|
-
console.log();
|
|
484
|
-
console.log(' Migration guide: .forge/migrations/0.19.0-worktree-safe-state.md');
|
|
485
|
-
console.log();
|
|
486
|
-
console.log(' This migration edits user-owned state, so it is guided — never automatic.');
|
|
487
|
-
console.log(' In Claude Code: /forge then hand the guide to quick-tasking.');
|
|
488
|
-
console.log();
|
|
489
|
-
}
|
|
490
464
|
}
|
|
491
465
|
|
|
492
466
|
async function upgrade() {
|
|
@@ -656,7 +630,9 @@ async function upgrade() {
|
|
|
656
630
|
|
|
657
631
|
console.log(` Upgraded to v${pkgVersion}\n`);
|
|
658
632
|
|
|
659
|
-
|
|
633
|
+
// Pass the version captured BEFORE upgradeSettings() stamped the new one, so the
|
|
634
|
+
// migration range is (installed, source] against the freshly-synced guides.
|
|
635
|
+
runPostUpgradeMigrationChecks(installedVersion);
|
|
660
636
|
}
|
|
661
637
|
|
|
662
638
|
// --- Entry point ---
|
package/package.json
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chief-of-staff
|
|
3
|
+
description: "Coordinate multiple active Forge streams. Use for explicit stream intents, mid-flow pivots, delegated work, cross-stream conflict checks, and merge ordering."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Chief Of Staff
|
|
7
|
+
|
|
8
|
+
Project-level traffic control for concurrent Forge work. The Chief owns the
|
|
9
|
+
project stream registry, stream lifecycle, conflict checks, context switching,
|
|
10
|
+
delegation, merge order, and closeout.
|
|
11
|
+
|
|
12
|
+
Use this skill when the user explicitly asks about streams, Chief of Staff, or
|
|
13
|
+
parallel application work. Do not route ordinary single-feature work here by
|
|
14
|
+
default. Inside each stream, continue to use Forge's Quick, Standard, and Full
|
|
15
|
+
tiers for actual execution.
|
|
16
|
+
|
|
17
|
+
## Operating Model
|
|
18
|
+
|
|
19
|
+
Forge has three coordination layers:
|
|
20
|
+
|
|
21
|
+
| Layer | Owner | Purpose | Default context |
|
|
22
|
+
|-------|-------|---------|-----------------|
|
|
23
|
+
| Project | Project Chief | Active streams, conflicts, pivots, merge order | `.forge/streams/active.yml` |
|
|
24
|
+
| Stream | Stream Chief or lead session | One coherent line of application work | `.forge/streams/{stream}.yml` + `brief.md` |
|
|
25
|
+
| Work Package | Worker session | Delegated bounded task | `.forge/streams/{stream}/packages/{id}.yml` |
|
|
26
|
+
|
|
27
|
+
Workers never coordinate laterally. A worker reports to its stream chief or the
|
|
28
|
+
Project Chief. Scope expansion, shared-surface edits, and cross-stream conflict
|
|
29
|
+
questions return to the Chief before work continues.
|
|
30
|
+
|
|
31
|
+
## Stream States
|
|
32
|
+
|
|
33
|
+
- `draft` - described but not ready for active work
|
|
34
|
+
- `active` - currently being driven in this session
|
|
35
|
+
- `paused` - safe to resume later; context snapshot is current
|
|
36
|
+
- `delegated` - worker session or external session is carrying the stream
|
|
37
|
+
- `blocked` - cannot proceed until a dependency, decision, or conflict clears
|
|
38
|
+
- `ready` - implementation is ready for merge or verification
|
|
39
|
+
- `merged` - stream work has landed in the integration branch
|
|
40
|
+
- `closed` - final outcome recorded; no active coordination remains
|
|
41
|
+
|
|
42
|
+
## Context Loading
|
|
43
|
+
|
|
44
|
+
1. Read `.forge/streams/active.yml` first.
|
|
45
|
+
2. If missing, create it from `.forge/templates/streams/active.yml`.
|
|
46
|
+
3. Load only the selected stream's `.forge/streams/{stream}.yml`.
|
|
47
|
+
4. Load the selected stream's `brief.md` when the user needs status, handoff,
|
|
48
|
+
conflict context, or resume context.
|
|
49
|
+
5. Load work packages only when delegating, checking worker status, or closing
|
|
50
|
+
a stream.
|
|
51
|
+
|
|
52
|
+
Keep summaries in the registry short. Deep history belongs in stream briefs,
|
|
53
|
+
packages, plans, verification reports, commits, and archived artifacts.
|
|
54
|
+
|
|
55
|
+
## Supported Intents
|
|
56
|
+
|
|
57
|
+
| Intent | Examples | Action |
|
|
58
|
+
|--------|----------|--------|
|
|
59
|
+
| Show | "show streams", "active streams", "what is running?" | Summarize registry without loading all deep history |
|
|
60
|
+
| Start | "start stream X", "new stream for billing" | Snapshot current stream, create stream files, register stream |
|
|
61
|
+
| Pause | "pause stream X" | Update brief, mark paused, keep next action explicit |
|
|
62
|
+
| Resume | "resume stream X" | Load stream context, set active, surface blockers and ownership |
|
|
63
|
+
| Delegate | "delegate stream X", "farm this out" | Create or update a work package and handoff prompt |
|
|
64
|
+
| Adopt M10 | "adopt existing M10 worktrees", "migrate active orchestrating sessions" | Wrap live M10 worktrees as streams without teardown |
|
|
65
|
+
| Sync | "sync streams" | Refresh registry summaries from active stream briefs |
|
|
66
|
+
| Detect Conflicts | "detect conflicts", "check stream conflicts" | Compare ownership and dependencies |
|
|
67
|
+
| Merge Safe | "merge safe?", "what can land?" | Identify ready streams with no blocking conflicts |
|
|
68
|
+
| Close | "close stream X" | Record final status, archive/mark closed, clear active pointer if needed |
|
|
69
|
+
|
|
70
|
+
## Start Stream Flow
|
|
71
|
+
|
|
72
|
+
1. Read the registry.
|
|
73
|
+
2. If another stream is active, snapshot its current state:
|
|
74
|
+
- update `.forge/streams/{stream}/brief.md`
|
|
75
|
+
- set `next_action`
|
|
76
|
+
- mark it `paused`, `delegated`, or leave `active` only if work continues
|
|
77
|
+
3. Create a stable stream id from the goal, such as `billing-webhooks`.
|
|
78
|
+
4. Create:
|
|
79
|
+
- `.forge/streams/{id}.yml` from `.forge/templates/streams/stream.yml`
|
|
80
|
+
- `.forge/streams/{id}/brief.md` from `.forge/templates/streams/brief.md`
|
|
81
|
+
- `.forge/streams/{id}/packages/`
|
|
82
|
+
5. Add a compact entry to `.forge/streams/active.yml`.
|
|
83
|
+
6. Declare ownership:
|
|
84
|
+
- `owned_surfaces` for files or areas the stream may edit
|
|
85
|
+
- `shared_surfaces` for contracts, APIs, schemas, or docs touched with care
|
|
86
|
+
- `read_only_surfaces` for context only
|
|
87
|
+
- `forbidden_surfaces` for explicit exclusions
|
|
88
|
+
7. If ownership is uncertain, start read-only and ask for a narrower boundary.
|
|
89
|
+
8. Run conflict detection before making edits.
|
|
90
|
+
|
|
91
|
+
## Adopt Existing M10 Worktrees Flow
|
|
92
|
+
|
|
93
|
+
Use this when a repo already has `orchestrating` / M10 worktrees running and the
|
|
94
|
+
user asks to move to Chief/Streams. Do not teardown, merge, rebase, or remove
|
|
95
|
+
worktrees as part of adoption. Adoption only creates stream metadata around
|
|
96
|
+
existing M10 state.
|
|
97
|
+
|
|
98
|
+
1. Read or create `.forge/streams/active.yml`.
|
|
99
|
+
2. Discover live M10 worktrees from both sources:
|
|
100
|
+
- `git worktree list --porcelain` branches matching `refs/heads/forge/m-*`
|
|
101
|
+
- `.forge/state/milestone-*.yml` with
|
|
102
|
+
`lifecycle.worktree_mode: active` or `degraded`
|
|
103
|
+
3. For each discovered worktree, read its milestone state when available:
|
|
104
|
+
- milestone id and name
|
|
105
|
+
- `current.status`, `current.phase`, `current.phase_name`
|
|
106
|
+
- `lifecycle.worktree_path`
|
|
107
|
+
- `lifecycle.worktree_branch`
|
|
108
|
+
- `lifecycle.worktree_anchor`
|
|
109
|
+
- `lifecycle.session_id`
|
|
110
|
+
- `lifecycle.claim_session_id`
|
|
111
|
+
- blockers or refused/degraded reason
|
|
112
|
+
4. Create a stable stream id. Prefer a slug from milestone id + name, for
|
|
113
|
+
example `m16-chief-streams`. If only the branch is known, derive from the
|
|
114
|
+
branch suffix.
|
|
115
|
+
5. For each adopted stream, create if missing:
|
|
116
|
+
- `.forge/streams/{id}.yml`
|
|
117
|
+
- `.forge/streams/{id}/brief.md`
|
|
118
|
+
- `.forge/streams/{id}/packages/`
|
|
119
|
+
6. Populate the stream file:
|
|
120
|
+
- `stream.id`, `goal`, `status`, `tier`
|
|
121
|
+
- `runtime.branch` from `lifecycle.worktree_branch`
|
|
122
|
+
- `runtime.worktree` from `lifecycle.worktree_path`
|
|
123
|
+
- `runtime.worker_sessions` with the M10 session / claim ids
|
|
124
|
+
- `ownership.owned_surfaces`, `shared_surfaces`, and `read_only_surfaces`
|
|
125
|
+
as empty lists if unknown
|
|
126
|
+
- `blockers` from milestone blockers or degraded/refused notes
|
|
127
|
+
- `context.milestone` pointing at `.forge/state/milestone-{id}.yml`
|
|
128
|
+
7. Populate the stream brief with:
|
|
129
|
+
- current milestone/phase/status
|
|
130
|
+
- worktree path and branch
|
|
131
|
+
- known blockers
|
|
132
|
+
- unknown ownership surfaces marked as needing declaration
|
|
133
|
+
- next action from milestone state or "declare ownership and sync"
|
|
134
|
+
8. Add or update the compact entry in `.forge/streams/active.yml`.
|
|
135
|
+
9. Run conflict detection. Missing ownership is a conflict until clarified, but
|
|
136
|
+
it does not block adoption.
|
|
137
|
+
10. Ask before changing `.forge/project.yml`:
|
|
138
|
+
- If the user confirms Chief should be the front door, set
|
|
139
|
+
`orchestration.auto: false`.
|
|
140
|
+
- If not confirmed, leave legacy M10 auto-routing unchanged.
|
|
141
|
+
|
|
142
|
+
Adoption is idempotent. If a stream already references the same worktree branch
|
|
143
|
+
or path, update its summary and brief instead of creating a duplicate stream.
|
|
144
|
+
|
|
145
|
+
## Pause And Resume Flow
|
|
146
|
+
|
|
147
|
+
Pause:
|
|
148
|
+
1. Update the stream brief with current state, decisions, blockers, and next
|
|
149
|
+
action.
|
|
150
|
+
2. Set registry status to `paused`, unless work was handed to another session
|
|
151
|
+
and should be `delegated`.
|
|
152
|
+
3. Clear `current_stream` only if no stream remains active in this session.
|
|
153
|
+
|
|
154
|
+
Resume:
|
|
155
|
+
1. Load registry, stream file, and brief.
|
|
156
|
+
2. Surface status, blockers, owned surfaces, shared surfaces, and next action.
|
|
157
|
+
3. Set the stream to `active`.
|
|
158
|
+
4. Run conflict detection before continuing.
|
|
159
|
+
|
|
160
|
+
## Delegate Flow
|
|
161
|
+
|
|
162
|
+
1. Confirm the stream has a clear goal and ownership boundary.
|
|
163
|
+
2. Copy `.forge/templates/streams/work-package.yml` to
|
|
164
|
+
`.forge/streams/{stream}/packages/{id}.yml`.
|
|
165
|
+
3. Include goal, owned/read-only/forbidden surfaces, deliverables, verification
|
|
166
|
+
commands, stop conditions, and reporting target.
|
|
167
|
+
4. Mark the stream `delegated` if the worker owns the next action.
|
|
168
|
+
5. Tell the worker to report back to the Chief, not to other workers.
|
|
169
|
+
6. If the worker finds conflicts, missing contracts, blocked verification, or
|
|
170
|
+
scope expansion, they stop and report back before editing outside package
|
|
171
|
+
scope.
|
|
172
|
+
|
|
173
|
+
If the work-package template is not present yet, create the smallest package
|
|
174
|
+
shape that preserves goal, surfaces, deliverables, stop conditions, and next
|
|
175
|
+
reporting step. Phase 25 upgrades this to the full schema.
|
|
176
|
+
|
|
177
|
+
Delegation prompt must name the package path and the reporting target:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
Work from `.forge/streams/{stream}/packages/{id}.yml`.
|
|
181
|
+
Edit only `scope.owns`.
|
|
182
|
+
Read `scope.may_read` for context.
|
|
183
|
+
Do not touch `scope.must_not_touch`.
|
|
184
|
+
Report conflicts, missing contracts, blocked verification, or scope expansion
|
|
185
|
+
back to the Chief. Do not coordinate directly with other workers.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Detect Conflicts
|
|
189
|
+
|
|
190
|
+
Compare all `active`, `delegated`, and `ready` streams:
|
|
191
|
+
|
|
192
|
+
- overlapping `owned_surfaces` means serialize, split, or block one stream
|
|
193
|
+
- `owned_surfaces` overlapping another stream's `shared_surfaces` requires a
|
|
194
|
+
reservation, contract, or explicit merge order
|
|
195
|
+
- overlapping `shared_surfaces` requires a named contract owner
|
|
196
|
+
- overlapping `read_only_surfaces` is safe
|
|
197
|
+
- missing ownership is a conflict until clarified
|
|
198
|
+
|
|
199
|
+
Record conflicts in both the registry and the affected stream files. Do not let
|
|
200
|
+
workers resolve cross-stream conflicts directly.
|
|
201
|
+
|
|
202
|
+
## Merge Safe Flow
|
|
203
|
+
|
|
204
|
+
Use "merge safe" to identify streams that can land:
|
|
205
|
+
|
|
206
|
+
1. Candidate stream status must be `ready`.
|
|
207
|
+
2. No blockers may be open.
|
|
208
|
+
3. Owned surfaces must not overlap another active or ready stream.
|
|
209
|
+
4. Shared surfaces must have a contract owner and documented validation.
|
|
210
|
+
5. Verification commands or manual validation must be listed.
|
|
211
|
+
6. Merge order must be explicit when more than one stream is ready.
|
|
212
|
+
|
|
213
|
+
If any condition fails, mark the stream blocked or keep it ready with a named
|
|
214
|
+
reason it cannot merge yet.
|
|
215
|
+
|
|
216
|
+
## Close Flow
|
|
217
|
+
|
|
218
|
+
1. Confirm final status: merged, intentionally abandoned, superseded, or split.
|
|
219
|
+
2. Record final outcome in the stream brief.
|
|
220
|
+
3. Update registry status to `closed`.
|
|
221
|
+
4. Clear `current_stream` if this was the active stream.
|
|
222
|
+
5. Keep closed stream history available until a later archive/compaction pass.
|
|
223
|
+
|
|
224
|
+
## Output Shape
|
|
225
|
+
|
|
226
|
+
Keep Chief responses short and operational:
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
Streams:
|
|
230
|
+
- billing-webhooks — active — owns api/billing, shared db/schema — next: verify webhook retries
|
|
231
|
+
- settings-ui — paused — owns app/settings — next: resume form validation
|
|
232
|
+
|
|
233
|
+
Conflicts:
|
|
234
|
+
- billing-webhooks and reporting-export both touch db/schema. Contract owner needed before either merges.
|
|
235
|
+
|
|
236
|
+
Next:
|
|
237
|
+
- Continue billing-webhooks, or start a new stream with read-only ownership until boundaries are declared.
|
|
238
|
+
```
|
|
@@ -45,10 +45,12 @@ Never accumulate decisions in working memory. Never batch writes to convergence.
|
|
|
45
45
|
`context.md` is **shared mutable** state per FORGE.md State Ownership — writes follow two structural rules:
|
|
46
46
|
- **Scope to your milestone's block.** Different milestone blocks → git merges cleanly across worktrees.
|
|
47
47
|
- **Append-only within a block.** When superseding an older decision, *strike through* (`~~old decision~~`) and append the new line below — never rewrite in place. Same-line concurrent edits become impossible by construction; reading the history of a decision becomes possible by inspection.
|
|
48
|
+
- **Keep active context active.** Write current/open decisions to `.forge/context.md`. Do not rehydrate old milestone blocks from `.forge/context-archive.md` unless the user is amending that historical decision.
|
|
48
49
|
|
|
49
50
|
**On first decision of the session:**
|
|
50
51
|
- Check if `.forge/context.md` exists. If not → create it from `.forge/templates/context.md`
|
|
51
52
|
- Add a milestone heading inside `## Locked Decisions`: `### M{id} — {name} (drafting)` (use "M?" if no milestone yet)
|
|
53
|
+
- If the needed historical decision is archived, cite the archive in the new active decision instead of copying the whole block back.
|
|
52
54
|
- **Cross-tree liveness check (informational).** If editing an existing block for milestone `{id}`, run `git worktree list --porcelain | awk '/^branch / && $2 ~ /refs\/heads\/forge\/m-'"$id"'(-|$)/ {print}'`. Any hit means m{id} is **live in another worktree** — surface one line: *"m{id} is live in worktree at `{path}`. Edits land cleanly on main but won't be visible to that worktree until it rebases or pulls."* Proceed with the write — main is often the canonical author of `context.md`, this is a heads-up, not a block.
|
|
53
55
|
|
|
54
56
|
**On each confirmed decision:**
|
|
@@ -163,7 +163,12 @@ No `project.yml` + Standard/Full → **Step 2A**. State exists → **Step 2B**.
|
|
|
163
163
|
|
|
164
164
|
## Step 1.5: Lifecycle Operations
|
|
165
165
|
|
|
166
|
-
Before tier detection, check if user request matches lifecycle patterns:
|
|
166
|
+
Before tier detection, check if user request matches stream or lifecycle patterns:
|
|
167
|
+
- "show streams" / "active streams" / "what streams are active" / "stream status" → **Show Streams**
|
|
168
|
+
- "start stream {name}" / "new stream {name}" / "create stream {name}" / "pivot to {work}" → **Start Stream**
|
|
169
|
+
- "pause stream {id}" / "resume stream {id}" / "delegate stream {id}" / "sync streams" → **Manage Streams**
|
|
170
|
+
- "detect conflicts" / "check stream conflicts" / "merge safe" / "close stream {id}" → **Manage Streams**
|
|
171
|
+
- "Chief of Staff" / "Project Chief" / "use streams" / "parallel workstreams" → **Chief**
|
|
167
172
|
- "defer milestone {id}" / "defer {id}" / "pause milestone {id}" → **Defer**
|
|
168
173
|
- "resume milestone {id}" / "undefer {id}" / "reactivate milestone {id}" → **Resume**
|
|
169
174
|
- "delete milestone {id}" / "archive milestone {id}" / "remove milestone {id}" → **Delete** (see below)
|
|
@@ -171,6 +176,22 @@ Before tier detection, check if user request matches lifecycle patterns:
|
|
|
171
176
|
|
|
172
177
|
No match → fall through to Step 2B (tier detection).
|
|
173
178
|
|
|
179
|
+
### Chief / Stream Operations
|
|
180
|
+
|
|
181
|
+
1. Invoke `Skill(chief-of-staff)` directly.
|
|
182
|
+
2. Do not enter tier detection. The Chief is project-level traffic control, not
|
|
183
|
+
a replacement for Forge's Quick, Standard, or Full tiers.
|
|
184
|
+
3. Use Chief only for explicit stream intents, concurrent application work,
|
|
185
|
+
mid-flow pivots, delegated work, conflict detection, merge ordering, or
|
|
186
|
+
closing streams.
|
|
187
|
+
4. Ordinary single-stream feature work still follows normal tier detection.
|
|
188
|
+
Once inside a stream, the stream uses Quick/Standard/Full for execution.
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
- "show streams" → `Skill(chief-of-staff)` summarizes `.forge/streams/active.yml`
|
|
192
|
+
- "start stream settings cleanup" → `Skill(chief-of-staff)` snapshots current stream, creates a new stream, and checks conflicts
|
|
193
|
+
- "merge safe?" → `Skill(chief-of-staff)` lists ready streams that can land without blocking conflicts
|
|
194
|
+
|
|
174
195
|
### Show Deferred
|
|
175
196
|
|
|
176
197
|
1. Invoke `Skill(deferred)` directly.
|
|
@@ -262,13 +283,17 @@ Tier + state → invoke via `Skill` tool. All phases use `Skill()`.
|
|
|
262
283
|
|
|
263
284
|
**CRITICAL: NEVER `EnterPlanMode`.** "Planning" = `Skill(planning)`. Native plan mode writes wrong format, bypasses gates + state.
|
|
264
285
|
|
|
265
|
-
**Experimental — M10 orchestration
|
|
286
|
+
**Experimental — M10 orchestration backend.** Chief/Streams is the preferred
|
|
287
|
+
user-facing orchestration model. M10 remains an optional backend for streams
|
|
288
|
+
that need worktree isolation, merge-queue discipline, or experimental file
|
|
289
|
+
claims. Route through `Skill(orchestrating)` first when **all** hold:
|
|
290
|
+
- **Selected:** Project Chief chooses M10 for a stream, user explicitly asks for multi-agent/worktree mode, or legacy `orchestration.auto` is enabled.
|
|
266
291
|
- **Installed:** `.claude/skills/orchestrating/` present + `forge-orchestrator` in `.mcp.json` + `.claude/hooks/forge-claim-check.sh` present.
|
|
267
|
-
- **Not opted out:** `orchestration.auto` in `project.yml` ≠ `false`
|
|
292
|
+
- **Not opted out:** `orchestration.auto` in `project.yml` ≠ `false` for legacy auto-routing; explicit Project Chief selection can still use M10 when installed.
|
|
268
293
|
- **Tier is Standard or Full.** Quick never auto-orchestrates (a worktree per typo is the wrong trade).
|
|
269
294
|
- **Not already in a session:** active milestone has no `lifecycle.worktree_mode: active|degraded` (don't re-bootstrap inside a live session).
|
|
270
295
|
|
|
271
|
-
`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
|
|
296
|
+
`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 backend control; set `orchestration.auto: false` in `project.yml` to opt out of legacy auto-routing.
|
|
272
297
|
|
|
273
298
|
### Auto-Routing (Always Deterministic)
|
|
274
299
|
|
|
@@ -307,6 +332,7 @@ Where `{source}` = `skills.{name}` | `models.default` | `parent session`. Suppre
|
|
|
307
332
|
| discussing | sonnet | Conversation |
|
|
308
333
|
| testing | sonnet | Code gen (author) + audit judgment (analyst) — matches executing/reviewing. M9: author-mode refuses e2e without `e2e:true` + `validated:true`. |
|
|
309
334
|
| deferred | haiku | Read + format only |
|
|
335
|
+
| chief-of-staff | sonnet | Cross-stream coordination and conflict judgment |
|
|
310
336
|
|
|
311
337
|
| `current.status` | Route To |
|
|
312
338
|
|-------------------|----------|
|
|
@@ -322,6 +348,7 @@ Where `{source}` = `skills.{name}` | `models.default` | `parent session`. Suppre
|
|
|
322
348
|
| `deferred` | Milestone frozen. *"Resume milestone {id}" to reactivate.* |
|
|
323
349
|
| `quick-tasking` | `Skill(quick-tasking)` |
|
|
324
350
|
| (keyword: `deferred` / "show deferred") | `Skill(deferred)` — read-only aggregator |
|
|
351
|
+
| (keyword: `show streams` / `start stream` / `merge safe` / `Chief of Staff`) | `Skill(chief-of-staff)` — project stream coordination |
|
|
325
352
|
|
|
326
353
|
### Status Advancement Check
|
|
327
354
|
|
|
@@ -32,6 +32,11 @@ If you find yourself writing a plan that only touches `src/models/`, `src/db/`,
|
|
|
32
32
|
Read `.forge/context.md` **Needs Resolution**. If unchecked `- [ ]` items:
|
|
33
33
|
*"{N} items need input: [list]. For each: lock, defer, fix, or drop?"*
|
|
34
34
|
|
|
35
|
+
Load `.forge/context.md` as the active decision set. Consult
|
|
36
|
+
`.forge/context-archive.md` only when the current milestone, stream, or
|
|
37
|
+
requirement cites an older milestone decision (for example M9 e2e policy or M14
|
|
38
|
+
backlog lifecycle). Do not load archived decision blocks wholesale.
|
|
39
|
+
|
|
35
40
|
| Choice | Action |
|
|
36
41
|
|--------|--------|
|
|
37
42
|
| Lock | Record in Locked Decisions |
|
|
@@ -61,6 +66,10 @@ If missing, create `.forge/context.md` from template:
|
|
|
61
66
|
|
|
62
67
|
Must complete BEFORE plans. Plans reference context.md.
|
|
63
68
|
|
|
69
|
+
If a plan depends on an archived decision, cite the archive section in the plan
|
|
70
|
+
or active context summary; do not copy the historical block back into
|
|
71
|
+
`.forge/context.md`.
|
|
72
|
+
|
|
64
73
|
## Step 4: Structure Requirements
|
|
65
74
|
|
|
66
75
|
Resolve current milestone ID from `.forge/state/index.yml` (active milestone) or invocation context. Target file: `.forge/requirements/m{N}.yml`.
|