gsdd-cli 0.27.0 → 0.28.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 +53 -22
- package/agents/DISTILLATION.md +2 -2
- package/agents/README.md +4 -4
- package/agents/approach-explorer.md +4 -4
- package/agents/executor.md +14 -14
- package/agents/integration-checker.md +2 -2
- package/agents/researcher.md +2 -2
- package/agents/roadmapper.md +6 -6
- package/agents/synthesizer.md +18 -18
- package/agents/verifier.md +2 -2
- package/bin/adapters/agents.mjs +3 -3
- package/bin/adapters/claude.mjs +16 -14
- package/bin/adapters/opencode.mjs +15 -12
- package/bin/gsdd.mjs +16 -13
- package/bin/lib/{models.mjs → config.mjs} +23 -16
- package/bin/lib/control-map.mjs +17 -488
- package/bin/lib/health-truth.mjs +8 -13
- package/bin/lib/health.mjs +25 -39
- package/bin/lib/init-flow.mjs +44 -38
- package/bin/lib/init-prompts.mjs +3 -3
- package/bin/lib/init-runtime.mjs +11 -30
- package/bin/lib/lifecycle-preflight.mjs +97 -410
- package/bin/lib/lifecycle-state.mjs +2 -1
- package/bin/lib/next.mjs +243 -20
- package/bin/lib/phase.mjs +10 -315
- package/bin/lib/rendering.mjs +64 -44
- package/bin/lib/runtime-freshness.mjs +18 -15
- package/bin/lib/state-dir.mjs +45 -0
- package/bin/lib/templates.mjs +59 -22
- package/bin/lib/work-context.mjs +12 -1
- package/bin/lib/workflows.mjs +0 -1
- package/bin/lib/workspace-root.mjs +11 -6
- package/distilled/DESIGN.md +58 -2
- package/distilled/EVIDENCE-INDEX.md +15 -2
- package/distilled/README.md +23 -33
- package/distilled/SKILL.md +9 -10
- package/distilled/templates/agents.block.md +5 -5
- package/distilled/templates/approach.md +3 -3
- package/distilled/templates/auth-matrix.md +2 -2
- package/distilled/templates/brownfield-change/CHANGE.md +1 -1
- package/distilled/templates/delegates/approach-explorer.md +5 -5
- package/distilled/templates/delegates/mapper-arch.md +3 -3
- package/distilled/templates/delegates/mapper-concerns.md +4 -4
- package/distilled/templates/delegates/mapper-quality.md +3 -3
- package/distilled/templates/delegates/mapper-tech.md +3 -3
- package/distilled/templates/delegates/plan-checker.md +5 -5
- package/distilled/templates/delegates/researcher-architecture.md +3 -3
- package/distilled/templates/delegates/researcher-features.md +3 -3
- package/distilled/templates/delegates/researcher-pitfalls.md +3 -3
- package/distilled/templates/delegates/researcher-stack.md +3 -3
- package/distilled/templates/delegates/researcher-synthesizer.md +7 -7
- package/distilled/templates/research/architecture.md +1 -1
- package/distilled/templates/research/pitfalls.md +1 -1
- package/distilled/templates/research/stack.md +1 -1
- package/distilled/templates/roadmap.md +4 -4
- package/distilled/templates/spec.md +2 -2
- package/distilled/workflows/audit-milestone.md +22 -22
- package/distilled/workflows/complete-milestone.md +35 -35
- package/distilled/workflows/execute.md +29 -29
- package/distilled/workflows/map-codebase.md +30 -30
- package/distilled/workflows/new-milestone.md +18 -18
- package/distilled/workflows/new-project.md +45 -45
- package/distilled/workflows/pause.md +15 -15
- package/distilled/workflows/plan.md +63 -63
- package/distilled/workflows/progress.md +40 -39
- package/distilled/workflows/quick.md +43 -43
- package/distilled/workflows/resume.md +23 -22
- package/distilled/workflows/verify-work.md +7 -7
- package/distilled/workflows/verify.md +20 -20
- package/docs/BROWNFIELD-PROOF.md +1 -1
- package/docs/RUNTIME-SUPPORT.md +13 -13
- package/docs/USER-GUIDE.md +17 -20
- package/docs/claude/context-monitor.md +1 -1
- package/docs/proof/consumer-node-cli/README.md +1 -1
- package/package.json +3 -3
- package/bin/lib/closeout-report.mjs +0 -318
- package/bin/lib/evidence-contract.mjs +0 -325
- package/bin/lib/provenance.mjs +0 -390
- package/bin/lib/session-fingerprint.mjs +0 -223
- package/bin/lib/ui-proof.mjs +0 -1007
- package/distilled/workflows/plan-milestone-gaps.md +0 -204
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
getRuntimeModelOverride,
|
|
29
29
|
loadProjectModelConfig,
|
|
30
30
|
resolveRuntimeAgentModel,
|
|
31
|
-
} from './
|
|
31
|
+
} from './config.mjs';
|
|
32
|
+
import { resolveStateDir } from './state-dir.mjs';
|
|
32
33
|
|
|
33
34
|
const __filename = fileURLToPath(import.meta.url);
|
|
34
35
|
const __dirname = dirname(__filename);
|
|
@@ -68,7 +69,7 @@ function compareGeneratedFile({ cwd, runtime, relativePath, expectedContent, rep
|
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
function buildClaudeEntries({ cwd, workflows }) {
|
|
72
|
+
function buildClaudeEntries({ cwd, workflows, stateDirName = '.work' }) {
|
|
72
73
|
const checkerModelAlias = resolveRuntimeAgentModel({
|
|
73
74
|
cwd,
|
|
74
75
|
runtime: 'claude',
|
|
@@ -85,8 +86,8 @@ function buildClaudeEntries({ cwd, workflows }) {
|
|
|
85
86
|
const entries = workflows.map((workflow) => ({
|
|
86
87
|
relativePath: `.claude/skills/${workflow.name}/SKILL.md`,
|
|
87
88
|
expectedContent: workflow.name === 'gsdd-plan'
|
|
88
|
-
? renderClaudePlanSkill()
|
|
89
|
-
: renderSkillContent(workflow),
|
|
89
|
+
? renderClaudePlanSkill({ stateDirName })
|
|
90
|
+
: renderSkillContent(workflow, { stateDirName }),
|
|
90
91
|
}));
|
|
91
92
|
|
|
92
93
|
entries.push(
|
|
@@ -107,7 +108,7 @@ function buildClaudeEntries({ cwd, workflows }) {
|
|
|
107
108
|
return entries;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
function buildOpenCodeEntries({ cwd, workflows }) {
|
|
111
|
+
function buildOpenCodeEntries({ cwd, workflows, stateDirName = '.work' }) {
|
|
111
112
|
const config = loadProjectModelConfig(cwd);
|
|
112
113
|
const checkerModelId = getRuntimeModelOverride(config, 'opencode', 'plan-checker');
|
|
113
114
|
const explorerModelId = getRuntimeModelOverride(config, 'opencode', 'approach-explorer');
|
|
@@ -115,8 +116,8 @@ function buildOpenCodeEntries({ cwd, workflows }) {
|
|
|
115
116
|
const entries = workflows.map((workflow) => ({
|
|
116
117
|
relativePath: `.opencode/commands/${workflow.name}.md`,
|
|
117
118
|
expectedContent: workflow.name === 'gsdd-plan'
|
|
118
|
-
? renderOpenCodePlanCommand()
|
|
119
|
-
: renderOpenCodeCommandContent(workflow),
|
|
119
|
+
? renderOpenCodePlanCommand({ stateDirName })
|
|
120
|
+
: renderOpenCodeCommandContent(workflow, { stateDirName }),
|
|
120
121
|
}));
|
|
121
122
|
|
|
122
123
|
entries.push(
|
|
@@ -150,31 +151,33 @@ function buildCodexEntries({ cwd }) {
|
|
|
150
151
|
];
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
function buildWorkspaceHelperEntries() {
|
|
154
|
+
function buildWorkspaceHelperEntries(stateDirName) {
|
|
154
155
|
return buildPlanningCliHelperEntries({
|
|
155
156
|
packageName: PACKAGE_JSON.name,
|
|
156
157
|
packageVersion: PACKAGE_JSON.version,
|
|
158
|
+
stateDirName,
|
|
157
159
|
}).map((entry) => ({
|
|
158
|
-
relativePath:
|
|
160
|
+
relativePath: `${stateDirName}/${entry.relativePath}`,
|
|
159
161
|
expectedContent: entry.content,
|
|
160
162
|
}));
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
export function collectExpectedRuntimeSurfaceGroups({ cwd = process.cwd(), workflows }) {
|
|
166
|
+
const stateDirName = resolveStateDir(cwd).name;
|
|
164
167
|
return [
|
|
165
168
|
{
|
|
166
169
|
runtime: 'workspace-helper',
|
|
167
170
|
label: 'workspace workflow helper',
|
|
168
|
-
root:
|
|
171
|
+
root: `${stateDirName}/bin`,
|
|
169
172
|
repairCommand: 'npx -y gsdd-cli update',
|
|
170
|
-
entries: buildWorkspaceHelperEntries(),
|
|
173
|
+
entries: buildWorkspaceHelperEntries(stateDirName),
|
|
171
174
|
},
|
|
172
175
|
{
|
|
173
176
|
runtime: 'portable',
|
|
174
177
|
label: 'portable skills',
|
|
175
178
|
root: '.agents/skills',
|
|
176
179
|
repairCommand: 'npx -y gsdd-cli update',
|
|
177
|
-
entries: buildPortableSkillEntries(workflows).map((entry) => ({
|
|
180
|
+
entries: buildPortableSkillEntries(workflows, { stateDirName }).map((entry) => ({
|
|
178
181
|
relativePath: entry.relativePath,
|
|
179
182
|
expectedContent: entry.content,
|
|
180
183
|
})),
|
|
@@ -184,14 +187,14 @@ export function collectExpectedRuntimeSurfaceGroups({ cwd = process.cwd(), workf
|
|
|
184
187
|
label: 'Claude Code native surfaces',
|
|
185
188
|
root: '.claude',
|
|
186
189
|
repairCommand: 'npx -y gsdd-cli update --tools claude',
|
|
187
|
-
entries: buildClaudeEntries({ cwd, workflows }),
|
|
190
|
+
entries: buildClaudeEntries({ cwd, workflows, stateDirName }),
|
|
188
191
|
},
|
|
189
192
|
{
|
|
190
193
|
runtime: 'opencode',
|
|
191
194
|
label: 'OpenCode native surfaces',
|
|
192
195
|
root: '.opencode',
|
|
193
196
|
repairCommand: 'npx -y gsdd-cli update --tools opencode',
|
|
194
|
-
entries: buildOpenCodeEntries({ cwd, workflows }),
|
|
197
|
+
entries: buildOpenCodeEntries({ cwd, workflows, stateDirName }),
|
|
195
198
|
},
|
|
196
199
|
{
|
|
197
200
|
runtime: 'codex',
|
|
@@ -206,7 +209,7 @@ export function collectExpectedRuntimeSurfaceGroups({ cwd = process.cwd(), workf
|
|
|
206
209
|
export function evaluateRuntimeFreshness({ cwd = process.cwd(), workflows = [] }) {
|
|
207
210
|
const groups = collectExpectedRuntimeSurfaceGroups({ cwd, workflows }).map((group) => {
|
|
208
211
|
const installed = group.runtime === 'workspace-helper'
|
|
209
|
-
? existsSync(
|
|
212
|
+
? existsSync(resolveStateDir(cwd).dir)
|
|
210
213
|
: existsSync(join(cwd, group.root));
|
|
211
214
|
const comparisons = installed
|
|
212
215
|
? group.entries.map((entry) => compareGeneratedFile({
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// state-dir.mjs — Single source of truth for the Workspine state directory.
|
|
2
|
+
//
|
|
3
|
+
// Workspine keeps everything in ONE folder: .work/. Older projects used
|
|
4
|
+
// .planning/. This module is the only place that decides which folder the tool
|
|
5
|
+
// reads and writes for a given repo root. No other module may hardcode the name.
|
|
6
|
+
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
export const STATE_DIR_NAME = '.work';
|
|
11
|
+
export const LEGACY_STATE_DIR_NAME = '.planning';
|
|
12
|
+
|
|
13
|
+
export const MIGRATION_NOTICE =
|
|
14
|
+
'Note: reading legacy .planning/ — new Workspine projects use .work/. Your .planning/ still works; move its files under .work/ when convenient.';
|
|
15
|
+
|
|
16
|
+
// True when a repo root already holds Workspine state in EITHER the current
|
|
17
|
+
// (.work) or legacy (.planning) location. Used for workspace-root discovery.
|
|
18
|
+
export function hasStateMarker(root) {
|
|
19
|
+
return (
|
|
20
|
+
existsSync(join(root, STATE_DIR_NAME, 'config.json')) ||
|
|
21
|
+
existsSync(join(root, STATE_DIR_NAME)) ||
|
|
22
|
+
existsSync(join(root, LEGACY_STATE_DIR_NAME, 'config.json')) ||
|
|
23
|
+
existsSync(join(root, LEGACY_STATE_DIR_NAME))
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Decide which state directory to use for a repo root.
|
|
28
|
+
// Precedence (config.json is the "really initialized" marker):
|
|
29
|
+
// 1. .work/config.json -> .work (migration done / new project)
|
|
30
|
+
// 2. .planning/config.json -> .planning (legacy; dual-read + notice)
|
|
31
|
+
// 3. .work/ dir -> .work
|
|
32
|
+
// 4. .planning/ dir -> .planning (legacy; dual-read + notice)
|
|
33
|
+
// 5. neither -> .work (brand-new repo default)
|
|
34
|
+
export function resolveStateDir(root) {
|
|
35
|
+
const workDir = join(root, STATE_DIR_NAME);
|
|
36
|
+
const legacyDir = join(root, LEGACY_STATE_DIR_NAME);
|
|
37
|
+
const work = { dir: workDir, name: STATE_DIR_NAME, legacy: false, migrationNotice: null };
|
|
38
|
+
const legacy = { dir: legacyDir, name: LEGACY_STATE_DIR_NAME, legacy: true, migrationNotice: MIGRATION_NOTICE };
|
|
39
|
+
|
|
40
|
+
if (existsSync(join(workDir, 'config.json'))) return work;
|
|
41
|
+
if (existsSync(join(legacyDir, 'config.json'))) return legacy;
|
|
42
|
+
if (existsSync(workDir)) return work;
|
|
43
|
+
if (existsSync(legacyDir)) return legacy;
|
|
44
|
+
return work;
|
|
45
|
+
}
|
package/bin/lib/templates.mjs
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
// templates.mjs - Project template and role installation/refresh helpers
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { existsSync, mkdirSync, readdirSync, cpSync, unlinkSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
5
|
+
import { basename, join } from 'path';
|
|
5
6
|
import { fileHash, readManifest } from './manifest.mjs';
|
|
7
|
+
import { localizeStateDirReferences } from './rendering.mjs';
|
|
6
8
|
|
|
7
|
-
export function installProjectTemplates({ planningDir, distilledDir, agentsDir }) {
|
|
9
|
+
export function installProjectTemplates({ planningDir, distilledDir, agentsDir, stateDirName = '.work' }) {
|
|
8
10
|
const localTemplatesDir = join(planningDir, 'templates');
|
|
9
11
|
const globalTemplatesDir = join(distilledDir, 'templates');
|
|
12
|
+
const stateName = basename(planningDir);
|
|
10
13
|
|
|
11
14
|
if (!existsSync(localTemplatesDir)) {
|
|
12
15
|
if (existsSync(globalTemplatesDir)) {
|
|
13
|
-
|
|
14
|
-
console.log(
|
|
16
|
+
copyTemplateTree(globalTemplatesDir, localTemplatesDir, { stateDirName });
|
|
17
|
+
console.log(` - copied templates to ${stateName}/templates/`);
|
|
15
18
|
// Warn-only by design: init should not fail on missing templates because
|
|
16
19
|
// the user may still proceed and fix later. The hard gate lives in
|
|
17
20
|
// `gsdd health` (E6/E7/E8) which reports these as errors. This is the
|
|
@@ -33,7 +36,7 @@ export function installProjectTemplates({ planningDir, distilledDir, agentsDir }
|
|
|
33
36
|
console.log(' - WARN: missing distilled/templates/; cannot copy templates');
|
|
34
37
|
}
|
|
35
38
|
} else {
|
|
36
|
-
console.log(
|
|
39
|
+
console.log(` - ${stateName}/templates/ already exists`);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
const localRolesDir = join(localTemplatesDir, 'roles');
|
|
@@ -41,18 +44,18 @@ export function installProjectTemplates({ planningDir, distilledDir, agentsDir }
|
|
|
41
44
|
if (existsSync(agentsDir)) {
|
|
42
45
|
mkdirSync(localRolesDir, { recursive: true });
|
|
43
46
|
for (const file of listRoleFiles(agentsDir)) {
|
|
44
|
-
|
|
47
|
+
copyTemplateFile(join(agentsDir, file), join(localRolesDir, file), { stateDirName });
|
|
45
48
|
}
|
|
46
|
-
console.log(
|
|
49
|
+
console.log(` - copied role contracts to ${stateName}/templates/roles/`);
|
|
47
50
|
} else {
|
|
48
51
|
console.log(' - WARN: missing agents/; cannot copy role contracts');
|
|
49
52
|
}
|
|
50
53
|
} else {
|
|
51
|
-
console.log(
|
|
54
|
+
console.log(` - ${stateName}/templates/roles/ already exists`);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
export function refreshTemplates({ planningDir, distilledDir, agentsDir, isDry = false }) {
|
|
58
|
+
export function refreshTemplates({ planningDir, distilledDir, agentsDir, isDry = false, stateDirName = '.work' }) {
|
|
56
59
|
const existingManifest = readManifest(planningDir);
|
|
57
60
|
const globalTemplatesDir = join(distilledDir, 'templates');
|
|
58
61
|
const localTemplatesDir = join(planningDir, 'templates');
|
|
@@ -65,11 +68,45 @@ export function refreshTemplates({ planningDir, distilledDir, agentsDir, isDry =
|
|
|
65
68
|
];
|
|
66
69
|
|
|
67
70
|
for (const category of categories) {
|
|
68
|
-
refreshCategory(category, existingManifest, isDry);
|
|
71
|
+
refreshCategory(category, existingManifest, isDry, { stateDirName });
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingManifest, isDry);
|
|
72
|
-
refreshRoles(agentsDir, join(localTemplatesDir, 'roles'), existingManifest, isDry);
|
|
74
|
+
refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingManifest, isDry, { stateDirName });
|
|
75
|
+
refreshRoles(agentsDir, join(localTemplatesDir, 'roles'), existingManifest, isDry, { stateDirName });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function contentHash(content) {
|
|
79
|
+
return createHash('sha256').update(content).digest('hex');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function localizedTemplateContent(srcPath, { stateDirName = '.work' } = {}) {
|
|
83
|
+
return localizeStateDirReferences(readFileSync(srcPath, 'utf-8'), { stateDirName });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function sourceTemplateHash(srcPath, { stateDirName = '.work' } = {}) {
|
|
87
|
+
if (!srcPath.endsWith('.md')) return fileHash(srcPath);
|
|
88
|
+
return contentHash(localizedTemplateContent(srcPath, { stateDirName }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function copyTemplateFile(srcPath, destPath, { stateDirName = '.work' } = {}) {
|
|
92
|
+
if (!srcPath.endsWith('.md')) {
|
|
93
|
+
cpSync(srcPath, destPath);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
writeFileSync(destPath, localizedTemplateContent(srcPath, { stateDirName }));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function copyTemplateTree(srcDir, destDir, { stateDirName = '.work' } = {}) {
|
|
100
|
+
mkdirSync(destDir, { recursive: true });
|
|
101
|
+
for (const entry of readdirSync(srcDir)) {
|
|
102
|
+
const srcPath = join(srcDir, entry);
|
|
103
|
+
const destPath = join(destDir, entry);
|
|
104
|
+
if (statSync(srcPath).isDirectory()) {
|
|
105
|
+
copyTemplateTree(srcPath, destPath, { stateDirName });
|
|
106
|
+
} else {
|
|
107
|
+
copyTemplateFile(srcPath, destPath, { stateDirName });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
73
110
|
}
|
|
74
111
|
|
|
75
112
|
function listRoleFiles(agentsDir) {
|
|
@@ -78,7 +115,7 @@ function listRoleFiles(agentsDir) {
|
|
|
78
115
|
);
|
|
79
116
|
}
|
|
80
117
|
|
|
81
|
-
function refreshCategory({ name, src, dest, manifestKey }, existingManifest, isDry) {
|
|
118
|
+
function refreshCategory({ name, src, dest, manifestKey }, existingManifest, isDry, { stateDirName = '.work' } = {}) {
|
|
82
119
|
if (!existsSync(src)) return;
|
|
83
120
|
if (!existsSync(dest) && !isDry) {
|
|
84
121
|
mkdirSync(dest, { recursive: true });
|
|
@@ -91,7 +128,7 @@ function refreshCategory({ name, src, dest, manifestKey }, existingManifest, isD
|
|
|
91
128
|
for (const file of sourceFiles) {
|
|
92
129
|
const srcPath = join(src, file);
|
|
93
130
|
const destPath = join(dest, file);
|
|
94
|
-
const srcHash =
|
|
131
|
+
const srcHash = sourceTemplateHash(srcPath, { stateDirName });
|
|
95
132
|
|
|
96
133
|
if (existsSync(destPath)) {
|
|
97
134
|
const destHash = fileHash(destPath);
|
|
@@ -106,7 +143,7 @@ function refreshCategory({ name, src, dest, manifestKey }, existingManifest, isD
|
|
|
106
143
|
if (isDry) {
|
|
107
144
|
console.log(` - would refresh ${name}/${file}`);
|
|
108
145
|
} else {
|
|
109
|
-
|
|
146
|
+
copyTemplateFile(srcPath, destPath, { stateDirName });
|
|
110
147
|
console.log(` - refreshed ${name}/${file}`);
|
|
111
148
|
}
|
|
112
149
|
}
|
|
@@ -126,7 +163,7 @@ function refreshCategory({ name, src, dest, manifestKey }, existingManifest, isD
|
|
|
126
163
|
}
|
|
127
164
|
}
|
|
128
165
|
|
|
129
|
-
function refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingManifest, isDry) {
|
|
166
|
+
function refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingManifest, isDry, { stateDirName = '.work' } = {}) {
|
|
130
167
|
if (!existsSync(globalTemplatesDir)) return;
|
|
131
168
|
|
|
132
169
|
const manifestHashes = existingManifest?.templates?.root || null;
|
|
@@ -135,7 +172,7 @@ function refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingMan
|
|
|
135
172
|
for (const file of sourceFiles) {
|
|
136
173
|
const srcPath = join(globalTemplatesDir, file);
|
|
137
174
|
const destPath = join(localTemplatesDir, file);
|
|
138
|
-
const srcHash =
|
|
175
|
+
const srcHash = sourceTemplateHash(srcPath, { stateDirName });
|
|
139
176
|
|
|
140
177
|
if (existsSync(destPath)) {
|
|
141
178
|
const destHash = fileHash(destPath);
|
|
@@ -150,7 +187,7 @@ function refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingMan
|
|
|
150
187
|
if (isDry) {
|
|
151
188
|
console.log(` - would refresh templates/${file}`);
|
|
152
189
|
} else {
|
|
153
|
-
|
|
190
|
+
copyTemplateFile(srcPath, destPath, { stateDirName });
|
|
154
191
|
console.log(` - refreshed templates/${file}`);
|
|
155
192
|
}
|
|
156
193
|
}
|
|
@@ -174,7 +211,7 @@ function refreshRootTemplates(globalTemplatesDir, localTemplatesDir, existingMan
|
|
|
174
211
|
}
|
|
175
212
|
}
|
|
176
213
|
|
|
177
|
-
function refreshRoles(agentsDir, localRolesDir, existingManifest, isDry) {
|
|
214
|
+
function refreshRoles(agentsDir, localRolesDir, existingManifest, isDry, { stateDirName = '.work' } = {}) {
|
|
178
215
|
if (!existsSync(agentsDir)) return;
|
|
179
216
|
if (!existsSync(localRolesDir) && !isDry) {
|
|
180
217
|
mkdirSync(localRolesDir, { recursive: true });
|
|
@@ -189,7 +226,7 @@ function refreshRoles(agentsDir, localRolesDir, existingManifest, isDry) {
|
|
|
189
226
|
for (const file of sourceFiles) {
|
|
190
227
|
const srcPath = join(agentsDir, file);
|
|
191
228
|
const destPath = join(localRolesDir, file);
|
|
192
|
-
const srcHash =
|
|
229
|
+
const srcHash = sourceTemplateHash(srcPath, { stateDirName });
|
|
193
230
|
|
|
194
231
|
if (existsSync(destPath)) {
|
|
195
232
|
const destHash = fileHash(destPath);
|
|
@@ -204,7 +241,7 @@ function refreshRoles(agentsDir, localRolesDir, existingManifest, isDry) {
|
|
|
204
241
|
if (isDry) {
|
|
205
242
|
console.log(` - would refresh roles/${file}`);
|
|
206
243
|
} else {
|
|
207
|
-
|
|
244
|
+
copyTemplateFile(srcPath, destPath, { stateDirName });
|
|
208
245
|
console.log(` - refreshed roles/${file}`);
|
|
209
246
|
}
|
|
210
247
|
}
|
package/bin/lib/work-context.mjs
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
writeSync,
|
|
13
13
|
} from 'fs';
|
|
14
14
|
import { basename, dirname, join, relative, resolve } from 'path';
|
|
15
|
+
import { evaluateLifecycleState } from './lifecycle-state.mjs';
|
|
16
|
+
import { resolveStateDir } from './state-dir.mjs';
|
|
15
17
|
|
|
16
18
|
export const WORK_DIR_NAME = '.work';
|
|
17
19
|
|
|
@@ -657,14 +659,22 @@ export function inspectWorkContext(cwd = process.cwd()) {
|
|
|
657
659
|
const questions = readOpenQuestions(paths.workDir);
|
|
658
660
|
const evidence = readJsonIfExists(paths.evidenceManifest);
|
|
659
661
|
const graph = readGraphEvents(paths.workDir);
|
|
660
|
-
const planningDir =
|
|
662
|
+
const { dir: planningDir, name: stateDirName, migrationNotice } = resolveStateDir(paths.root);
|
|
663
|
+
const lifecycle = evaluateLifecycleState({ planningDir });
|
|
661
664
|
const planning = {
|
|
662
665
|
exists: existsSync(planningDir),
|
|
663
666
|
has_spec: existsSync(join(planningDir, 'SPEC.md')),
|
|
664
667
|
has_roadmap: existsSync(join(planningDir, 'ROADMAP.md')),
|
|
665
668
|
has_milestones: existsSync(join(planningDir, 'MILESTONES.md')),
|
|
666
669
|
has_config: existsSync(join(planningDir, 'config.json')),
|
|
670
|
+
has_brownfield_change: lifecycle.brownfieldChange.exists,
|
|
671
|
+
non_phase_state: lifecycle.nonPhaseState,
|
|
672
|
+
brownfield_change: lifecycle.brownfieldChange,
|
|
673
|
+
current_phase: lifecycle.currentPhase?.number || null,
|
|
674
|
+
next_phase: lifecycle.nextPhase?.number || null,
|
|
675
|
+
counts: lifecycle.counts,
|
|
667
676
|
phases: scanPhaseEvidence(planningDir),
|
|
677
|
+
state_dir_name: stateDirName,
|
|
668
678
|
};
|
|
669
679
|
return {
|
|
670
680
|
paths,
|
|
@@ -676,6 +686,7 @@ export function inspectWorkContext(cwd = process.cwd()) {
|
|
|
676
686
|
evidence,
|
|
677
687
|
graph,
|
|
678
688
|
planning,
|
|
689
|
+
migration_notice: migrationNotice,
|
|
679
690
|
milestone: inspectWorkMilestone(paths.workDir),
|
|
680
691
|
focus_exists: existsSync(paths.focus),
|
|
681
692
|
handoff_exists: existsSync(paths.handoff),
|
package/bin/lib/workflows.mjs
CHANGED
|
@@ -17,7 +17,6 @@ export const WORKFLOWS = [
|
|
|
17
17
|
defineWorkflow({ name: 'gsdd-audit-milestone', workflow: 'audit-milestone.md', description: 'Audit a completed milestone - cross-phase integration, requirements coverage, E2E flows' }),
|
|
18
18
|
defineWorkflow({ name: 'gsdd-complete-milestone', workflow: 'complete-milestone.md', description: 'Complete milestone - archive, evolve spec, collapse roadmap' }),
|
|
19
19
|
defineWorkflow({ name: 'gsdd-new-milestone', workflow: 'new-milestone.md', description: 'New milestone - gather goals, define requirements, create roadmap phases' }),
|
|
20
|
-
defineWorkflow({ name: 'gsdd-plan-milestone-gaps', workflow: 'plan-milestone-gaps.md', description: 'Plan gap closure phases from audit results' }),
|
|
21
20
|
defineWorkflow({ name: 'gsdd-quick', workflow: 'quick.md', description: 'Quick task - plan and execute a sub-hour task outside the phase cycle' }),
|
|
22
21
|
defineWorkflow({ name: 'gsdd-pause', workflow: 'pause.md', description: 'Pause work - save session context for seamless resumption' }),
|
|
23
22
|
defineWorkflow({ name: 'gsdd-resume', workflow: 'resume.md', description: 'Resume work - restore context and route to next action' }),
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { dirname, join, resolve } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
+
import { hasStateMarker, resolveStateDir } from './state-dir.mjs';
|
|
4
5
|
|
|
5
6
|
function normalizePath(value, cwd) {
|
|
6
7
|
return resolve(cwd, String(value));
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
function hasPlanningMarker(root) {
|
|
10
|
-
return
|
|
11
|
+
return hasStateMarker(root);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function consumeWorkspaceRootArg(rawArgs = []) {
|
|
@@ -71,7 +72,7 @@ export function resolveWorkspaceContext(rawArgs = [], { cwd = process.cwd(), env
|
|
|
71
72
|
invalid: true,
|
|
72
73
|
error: 'Usage: --workspace-root <path>',
|
|
73
74
|
workspaceRoot: resolve(cwd),
|
|
74
|
-
planningDir:
|
|
75
|
+
planningDir: resolveStateDir(resolve(cwd)).dir,
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -81,9 +82,9 @@ export function resolveWorkspaceContext(rawArgs = [], { cwd = process.cwd(), env
|
|
|
81
82
|
return {
|
|
82
83
|
args,
|
|
83
84
|
invalid: true,
|
|
84
|
-
error: `Workspace root does not contain .planning/: ${workspaceRootArg}`,
|
|
85
|
+
error: `Workspace root does not contain .work/ or .planning/: ${workspaceRootArg}`,
|
|
85
86
|
workspaceRoot: explicitRoot,
|
|
86
|
-
planningDir:
|
|
87
|
+
planningDir: resolveStateDir(explicitRoot).dir,
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
90
|
}
|
|
@@ -105,7 +106,9 @@ export function resolveWorkspaceContext(rawArgs = [], { cwd = process.cwd(), env
|
|
|
105
106
|
args,
|
|
106
107
|
invalid: false,
|
|
107
108
|
workspaceRoot: candidate,
|
|
108
|
-
planningDir:
|
|
109
|
+
planningDir: resolveStateDir(candidate).dir,
|
|
110
|
+
stateDirName: resolveStateDir(candidate).name,
|
|
111
|
+
migrationNotice: resolveStateDir(candidate).migrationNotice,
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
114
|
}
|
|
@@ -115,7 +118,9 @@ export function resolveWorkspaceContext(rawArgs = [], { cwd = process.cwd(), env
|
|
|
115
118
|
args,
|
|
116
119
|
invalid: false,
|
|
117
120
|
workspaceRoot: fallbackRoot,
|
|
118
|
-
planningDir:
|
|
121
|
+
planningDir: resolveStateDir(fallbackRoot).dir,
|
|
122
|
+
stateDirName: resolveStateDir(fallbackRoot).name,
|
|
123
|
+
migrationNotice: resolveStateDir(fallbackRoot).migrationNotice,
|
|
119
124
|
};
|
|
120
125
|
}
|
|
121
126
|
|
package/distilled/DESIGN.md
CHANGED
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
61. [Deliberate Subagent Contract](#d61---deliberate-subagent-contract)
|
|
75
75
|
62. [Repo-Native UI Proof Contract](#d62---repo-native-ui-proof-contract)
|
|
76
76
|
63. [Computed-First Control Map](#d63---computed-first-control-map)
|
|
77
|
+
64. [Work-Native Continuity Authority](#d64---work-native-continuity-authority)
|
|
77
78
|
|
|
78
79
|
---
|
|
79
80
|
|
|
@@ -2507,7 +2508,7 @@ Sub-gap (b) was closed by D28's `<persistence>` mandate and guarded by G30. Sub-
|
|
|
2507
2508
|
|
|
2508
2509
|
## D56 - Executable Brownfield Routing And Widen-Only Escalation
|
|
2509
2510
|
|
|
2510
|
-
**Decision (2026-04-21):** Brownfield routing should treat `CHANGE.md` as the default operational anchor and allow a surviving `phase` or `quick` checkpoint to outrank it only when one strict-match rule proves the checkpoint is still the active execution surface. From concrete brownfield state, `/gsdd-new-project` remains available only as an intentional widen path into full milestone setup, not as an accidental fallback recommendation.
|
|
2511
|
+
**Decision (2026-04-21; revised 2026-06-30):** Brownfield routing should treat `CHANGE.md` as the default operational anchor and allow a surviving `phase` or `quick` checkpoint to outrank it only when one strict-match rule proves the checkpoint is still the active execution surface. From concrete brownfield state, `/gsdd-new-project` remains available only as an intentional widen path into full milestone setup, not as an accidental fallback recommendation. Active bounded brownfield planning uses the existing `gsdd-plan` workflow with explicit `brownfield-change` authority instead of being forced through unrelated ROADMAP phase membership.
|
|
2511
2512
|
|
|
2512
2513
|
**Context:**
|
|
2513
2514
|
- D54 defined the bounded `brownfield-change/` contract and D55 established its continuity floor, but the routing matrix above those artifacts was still inconsistent: `progress` could still route back through stale checkpoint residue, `resume` could still make a non-matching checkpoint primary, and `quick` / `map-codebase` / `new-project` still risked implying that full lifecycle initialization was the normal fallback from already-concrete bounded work.
|
|
@@ -2524,12 +2525,17 @@ Sub-gap (b) was closed by D28's `<persistence>` mandate and guarded by G30. Sub-
|
|
|
2524
2525
|
- `quick` uses it when the bounded change is still undefined or when the user intentionally widens scope
|
|
2525
2526
|
- `map-codebase` frames it as full lifecycle setup only when the user wants to widen beyond bounded brownfield work
|
|
2526
2527
|
- `new-project` itself recognizes existing `CHANGE.md` continuity and treats invocation from that state as deliberate promotion, not mistaken routing
|
|
2528
|
+
- Keep `gsdd-plan` as the bounded brownfield planning entrypoint when an active `.planning/brownfield-change/CHANGE.md` exists:
|
|
2529
|
+
- `gsdd next --json` reports `authority: brownfield_change` and routes to `gsdd-plan`
|
|
2530
|
+
- `gsdd-plan` classifies the target before phase preflight
|
|
2531
|
+
- `lifecycle-preflight plan brownfield-change` authorizes the change-folder lane without requiring the active ROADMAP phase to match a PBI or consumer ticket
|
|
2527
2532
|
- Lock the contract with helper, guard, and generated-surface scenario coverage.
|
|
2528
2533
|
|
|
2529
2534
|
**Why this fits the codebase:**
|
|
2530
2535
|
- It extends the existing provenance helper and generated-surface workflow model instead of inventing another routing registry or hidden state store.
|
|
2531
2536
|
- It preserves the D55 one-anchor split: checkpoints stay visible and resume-owned, but they do not silently demote the active brownfield contract unless the repo can prove the checkpoint is still current.
|
|
2532
2537
|
- It keeps the brownfield lane bounded by removing accidental fallback escalation while still preserving an intentional route into milestone planning.
|
|
2538
|
+
- It fixes the consumer failure mode where PBI/ticket-sized brownfield work was blocked by an unrelated active ROADMAP phase while still avoiding a new workflow or auto-runner surface.
|
|
2533
2539
|
- It matches the repo's anti-drift posture: ambiguous or weak routing proof fails closed in favor of the current operational anchor.
|
|
2534
2540
|
|
|
2535
2541
|
**Evidence:**
|
|
@@ -2549,7 +2555,7 @@ Sub-gap (b) was closed by D28's `<persistence>` mandate and guarded by G30. Sub-
|
|
|
2549
2555
|
- `get-shit-done/workflows/progress.md`, `get-shit-done/workflows/resume.md`, `get-shit-done/workflows/quick.md`, `get-shit-done/workflows/new-project.md`
|
|
2550
2556
|
|
|
2551
2557
|
**Consequences:**
|
|
2552
|
-
- Brownfield next-step routing now has one deterministic precedence rule instead of five workflow-local interpretations.
|
|
2558
|
+
- Brownfield next-step routing now has one deterministic precedence rule instead of five workflow-local interpretations, and active bounded brownfield planning has one explicit preflight authority.
|
|
2553
2559
|
- A stale or unrelated `phase` / `quick` checkpoint can remain reviewable without automatically hijacking the current brownfield lane.
|
|
2554
2560
|
- `/gsdd-new-project` remains part of the system, but as deliberate promotion from concrete brownfield work rather than as a generic fallback.
|
|
2555
2561
|
- Phase 43 can now focus on bounded growth and milestone handoff instead of first repairing invalid routing recommendations.
|
|
@@ -2919,6 +2925,56 @@ Posture compatibility is part of that closeout contract: `repo_closeout` and `ru
|
|
|
2919
2925
|
- `closeout-report` is a compact replay/report helper, not `progress`, `verify`, milestone audit, release automation, cleanup, or a dashboard. The source CLI path includes full health diagnostics; the generated helper reports health availability as a typed warning if the full health builder is not present in that helper runtime.
|
|
2920
2926
|
- Future health hardening can consume the same helper output for stricter reporting, but must avoid turning local annotations into product truth.
|
|
2921
2927
|
|
|
2928
|
+
## D64 - Work-Native Continuity Authority
|
|
2929
|
+
|
|
2930
|
+
**Decision (2026-06-29; revised 2026-06-30):** `.work` becomes the canonical continuity surface for `gsdd next` and future work-native state, while `.planning` remains readable legacy lifecycle input during migration and for existing workflows that still own their write paths. The authority used by routing, preflight, verification, and auto-gate packets must be explicit in machine-readable output. Repo policy, not Workspine itself, decides whether `.work` is committed or local-only; in the Workspine framework repo, `.work` is local dogfood/runtime state and should not be tracked. During migration, `gsdd next` may also route existing `.planning` brownfield-change authority into `gsdd-plan` without treating `.planning` ROADMAP phases as the only valid plan target.
|
|
2931
|
+
|
|
2932
|
+
**Context:**
|
|
2933
|
+
- PR #116 merged the first `gsdd next` / `.work` continuity slice, but the framework repo still had split truth: `.work/milestone` described a locally implemented continuity milestone while `.planning` still described v2.0.0 parallel orchestration and P65/P66.
|
|
2934
|
+
- Old v2.0.0 parallel orchestration is too distant to migrate wholesale. Its useful ideas are partial extraction candidates: write ownership and closeout truth gates move into work-native auto-gate safety now; the PR #113 registry material remains parked for later extraction or rescue.
|
|
2935
|
+
- OpenSpec's change/archive model and LeanSpec's compact spec guidance both reinforce that long-lived state needs clear ownership and small, reviewable truth surfaces rather than scattered status prose.
|
|
2936
|
+
- The cited OpenAI and Anthropic agent-orchestration guidance favors manager-owned orchestration, explicit handoffs, guardrails, evaluator loops, and bounded tool authority rather than an unbounded agent loop.
|
|
2937
|
+
- MCP and agent-tooling security guidance reinforce that tool/resource outputs and local memory surfaces are untrusted inputs unless their provenance, privacy posture, and authority boundary are explicit.
|
|
2938
|
+
|
|
2939
|
+
**Decision:**
|
|
2940
|
+
- Treat `.work` as the source of truth for `gsdd next` continuity routing, focus packets, work-native milestone state, decisions, questions, evidence pointers, dogfood findings, and bounded auto-gate state.
|
|
2941
|
+
- Keep `.planning` readable as legacy lifecycle input. Existing `gsdd-plan`, `gsdd-execute`, `gsdd-verify`, audit, complete-milestone, phase-status, and helper write paths continue to work against `.planning` until each surface is deliberately bridged or migrated.
|
|
2942
|
+
- Require routing and preflight output to report authority explicitly, using scoped values such as `work_milestone`, `planning`, or `blocked/conflict`; `.work` authority must not silently mask repo truth, PR truth, or unrelated `.planning` blockers.
|
|
2943
|
+
- Include `brownfield_change` as an explicit scoped authority for existing bounded-change artifacts. This is a compatibility bridge, not a new lifecycle root: it reuses the current `gsdd-plan` workflow and the brownfield-change folder contract.
|
|
2944
|
+
- Preserve repo/control-map truth as the highest authority for branch, PR, worktree, dirty-state, and delivery claims. `.work` can carry intent and continuity; it cannot convert local prose into integrated repo truth.
|
|
2945
|
+
- Define execute-until-gate as task-bounded automation, not session-bounded autonomy. Auto mode may run typed, reviewed, `type=auto` tasks and bounded verification/repair cycles only until a human gate, verification gap, repeated blocker, authority conflict, trust boundary, or scope expansion stops it.
|
|
2946
|
+
- Keep milestone completion user-owned. `gsdd next` may route to completion approval, but it must not mark a milestone complete autonomously.
|
|
2947
|
+
- Treat `.work` tracking as repo policy. Consumer projects may commit `.work` if that fits their privacy and collaboration model. The Workspine framework repo keeps `.work` local-only because it is dogfooding its own runtime state while developing the framework.
|
|
2948
|
+
- Reframe the old v2.0.0 parallel-orchestration milestone as legacy input rather than the active next milestone. Preserve write-set ownership and closeout truth gates in the new work-native authority milestone; park PR #113/P65 registry material as extract-later.
|
|
2949
|
+
|
|
2950
|
+
**Leverage:**
|
|
2951
|
+
- Lost: the old clean "v2.0.0 parallel PR orchestration next" story and the convenience of treating `.planning` as the only lifecycle state root.
|
|
2952
|
+
- Kept: repo-native files, plain markdown workflow contracts, existing `.planning` compatibility, computed-first control-map authority, plan/execute/verify separation, and human-owned completion.
|
|
2953
|
+
- Gained: a clear continuity authority for `gsdd next`, explicit migration semantics, safer execute-until-gate foundations, repo-policy-driven tracking, a brownfield compatibility bridge for bounded consumer changes, and a typed authority boundary that prevents future auto mode from running on stale or contradictory state.
|
|
2954
|
+
|
|
2955
|
+
**Evidence:**
|
|
2956
|
+
- `bin/lib/next.mjs`, `bin/lib/work-context.mjs`, `bin/lib/lifecycle-preflight.mjs`
|
|
2957
|
+
- `tests/gsdd.next.test.cjs` and `tests/phase.test.cjs`
|
|
2958
|
+
- `README.md` for the public `.work` tracking policy
|
|
2959
|
+
- PR #116, merged 2026-06-29 with merge commit `b91a138c42f2ab3ff7376317031208c7a716decd`: `https://github.com/PatrickSys/workspine/pull/116`
|
|
2960
|
+
- PR #113, still open as the parked registry extraction candidate: `https://github.com/PatrickSys/workspine/pull/113`
|
|
2961
|
+
- GSD comparison sources: `agents/_archive/gsd-roadmapper.md`, `agents/_archive/gsd-planner.md`, `agents/_archive/gsd-executor.md`, and `agents/_archive/gsd-verifier.md`. These preserve lifecycle rigor around requirements, plans, state, execution, and verification, but they do not define a separate work-native `next` authority root; GSDD preserves the rigor while moving agent-facing continuity into `.work`.
|
|
2962
|
+
- OpenSpec docs: `https://openspec.dev/`
|
|
2963
|
+
- LeanSpec docs: `https://www.lean-spec.dev/docs/guide/first-principles`
|
|
2964
|
+
- OpenAI Agents SDK orchestration docs: `https://developers.openai.com/api/docs/guides/agents/orchestration`
|
|
2965
|
+
- OpenAI guardrails and approvals docs: `https://developers.openai.com/api/docs/guides/agents/guardrails-approvals`
|
|
2966
|
+
- OpenAI agent evals docs: `https://developers.openai.com/api/docs/guides/agent-evals`
|
|
2967
|
+
- Anthropic agent engineering guidance: `https://www.anthropic.com/engineering/building-effective-agents`
|
|
2968
|
+
- Anthropic long-running harness guidance: `https://www.anthropic.com/engineering/harness-design-long-running-apps`
|
|
2969
|
+
- Model Context Protocol security guidance: `https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices`
|
|
2970
|
+
- GitHub Copilot repository instructions docs: `https://docs.github.com/en/copilot/concepts/prompting/response-customization`
|
|
2971
|
+
|
|
2972
|
+
**Consequences:**
|
|
2973
|
+
- Future `gsdd next` and auto-gate work should start by reconciling `.work`, `.planning`, repo/control-map, PR truth, and active brownfield-change authority into one conservative next action or one explicit blocker.
|
|
2974
|
+
- Future milestones must not say "P65 shipped, start P66" unless repo, PR, `.work`, and legacy `.planning` truth agree.
|
|
2975
|
+
- Future auto mode must expose typed gates, loop guards, evidence requirements, stop reasons, and authority source in JSON. It must never rely on prose such as "continue autonomously" as execution permission.
|
|
2976
|
+
- Future framework work should not add tracked `.work` runtime state by default. Durable product changes belong in source/design/workflow/test files; `.work` dogfood state remains local unless explicitly promoted.
|
|
2977
|
+
|
|
2922
2978
|
---
|
|
2923
2979
|
|
|
2924
2980
|
## Maintenance
|
|
@@ -430,10 +430,11 @@
|
|
|
430
430
|
## D56 — Executable Brownfield Routing And Widen-Only Escalation
|
|
431
431
|
- `.planning/SPEC.md` (`BROWNFIELD-03`, Current State, key decision row)
|
|
432
432
|
- `.planning/phases/42-executable-brownfield-routing/42-PLAN.md`
|
|
433
|
-
- `bin/lib/provenance.mjs`
|
|
433
|
+
- `bin/lib/provenance.mjs`, `bin/lib/lifecycle-state.mjs`, `bin/lib/next.mjs`, `bin/lib/work-context.mjs`, `bin/lib/lifecycle-preflight.mjs`
|
|
434
434
|
- `distilled/workflows/progress.md`, `distilled/workflows/resume.md`, `distilled/workflows/quick.md`, `distilled/workflows/map-codebase.md`, `distilled/workflows/new-project.md`
|
|
435
|
+
- `distilled/workflows/plan.md`
|
|
435
436
|
- `.internal-research/TODO.md`, `.internal-research/gaps.md`
|
|
436
|
-
- `tests/phase.test.cjs`, `tests/gsdd.guards.test.cjs`, `tests/gsdd.scenarios.test.cjs`
|
|
437
|
+
- `tests/phase.test.cjs`, `tests/gsdd.next.test.cjs`, `tests/gsdd.init.test.cjs`, `tests/gsdd.guards.test.cjs`, `tests/gsdd.scenarios.test.cjs`
|
|
437
438
|
- `get-shit-done/workflows/progress.md`, `get-shit-done/workflows/resume.md`, `get-shit-done/workflows/quick.md`, `get-shit-done/workflows/new-project.md`
|
|
438
439
|
|
|
439
440
|
## D57 — Bounded Brownfield Growth And Context-Preserving Milestone Handoff
|
|
@@ -515,6 +516,18 @@
|
|
|
515
516
|
- OpenSpec comparison: OpenSpec change archive flow does not own long-running multi-worktree local-state reconciliation as a portable harness surface
|
|
516
517
|
- Harness sources: https://www.anthropic.com/engineering/harness-design-long-running-apps, https://code.claude.com/docs/en/worktrees, https://developers.openai.com/codex/cloud, https://developers.openai.com/api/docs/guides/agents/orchestration, https://developers.openai.com/api/docs/guides/agents/guardrails-approvals, https://developers.openai.com/api/docs/guides/agent-evals, https://docs.github.com/en/copilot/concepts/agents/cloud-agent/about-cloud-agent, https://agent-browser.dev/sessions, https://developer.chrome.com/docs/devtools/agents
|
|
517
518
|
|
|
519
|
+
## D64 — Work-Native Continuity Authority
|
|
520
|
+
- `bin/lib/next.mjs`, `bin/lib/work-context.mjs`, `bin/lib/lifecycle-preflight.mjs`, `bin/lib/rendering.mjs`
|
|
521
|
+
- `distilled/workflows/plan.md`
|
|
522
|
+
- `tests/gsdd.next.test.cjs`, `tests/phase.test.cjs`, `tests/gsdd.init.test.cjs`, `tests/gsdd.guards.test.cjs`, `tests/gsdd.scenarios.test.cjs`
|
|
523
|
+
- `README.md` for the public `.work` tracking policy
|
|
524
|
+
- PR #116 merged 2026-06-29, merge commit `b91a138c42f2ab3ff7376317031208c7a716decd`: https://github.com/PatrickSys/workspine/pull/116
|
|
525
|
+
- PR #113 remains open as parked registry extraction input: https://github.com/PatrickSys/workspine/pull/113
|
|
526
|
+
- GSD comparison sources: `agents/_archive/gsd-roadmapper.md`, `agents/_archive/gsd-planner.md`, `agents/_archive/gsd-executor.md`, `agents/_archive/gsd-verifier.md`; upstream GSD lifecycle rigor does not define a separate work-native `next` authority root, so GSDD keeps the lifecycle rigor while moving agent-facing continuity into `.work`
|
|
527
|
+
- Spec framework sources: https://openspec.dev/, https://www.lean-spec.dev/docs/guide/first-principles
|
|
528
|
+
- Orchestrator sources: https://developers.openai.com/api/docs/guides/agents/orchestration, https://www.anthropic.com/engineering/building-effective-agents
|
|
529
|
+
- Industry guidance sources: https://developers.openai.com/api/docs/guides/agents/guardrails-approvals, https://developers.openai.com/api/docs/guides/agent-evals, https://www.anthropic.com/engineering/harness-design-long-running-apps, https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices, https://docs.github.com/en/copilot/concepts/prompting/response-customization
|
|
530
|
+
|
|
518
531
|
---
|
|
519
532
|
|
|
520
533
|
## Maintenance
|