godpowers 3.0.1 → 3.11.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/CHANGELOG.md +255 -2
- package/README.md +31 -14
- package/RELEASE.md +21 -33
- package/SKILL.md +71 -112
- package/bin/install.js +44 -0
- package/fixtures/gate/harden-pass/.godpowers/state.json +26 -0
- package/lib/README.md +1 -0
- package/lib/artifact-map.js +2 -1
- package/lib/cli-dispatch.js +449 -3
- package/lib/command-families.js +10 -2
- package/lib/evidence/.provenance.json +45 -0
- package/lib/evidence-import.js +147 -0
- package/lib/evidence.js +908 -0
- package/lib/gate.js +26 -15
- package/lib/install-profiles.js +4 -1
- package/lib/installer-args.js +241 -1
- package/lib/quarterback.js +183 -0
- package/lib/surface-profile.js +168 -0
- package/lib/work-report.js +137 -0
- package/package.json +2 -2
- package/references/orchestration/GOD-MODE-RUNBOOK.md +9 -14
- package/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +40 -82
- package/references/shared/DASHBOARD-CONTRACT.md +66 -29
- package/references/shared/README.md +1 -1
- package/routing/god-demo.yaml +35 -0
- package/routing/god-first-run.yaml +34 -0
- package/routing/god-surface.yaml +39 -0
- package/routing/recipes/try-safely.yaml +26 -0
- package/skills/god-agent-audit.md +5 -6
- package/skills/god-archaeology.md +5 -6
- package/skills/god-audit.md +6 -7
- package/skills/god-automation-setup.md +6 -7
- package/skills/god-automation-status.md +6 -7
- package/skills/god-context-scan.md +7 -8
- package/skills/god-demo.md +53 -0
- package/skills/god-design-impact.md +5 -6
- package/skills/god-discuss.md +5 -6
- package/skills/god-docs.md +5 -10
- package/skills/god-doctor.md +8 -9
- package/skills/god-dogfood.md +7 -10
- package/skills/god-explore.md +5 -7
- package/skills/god-first-run.md +64 -0
- package/skills/god-harden.md +5 -2
- package/skills/god-help.md +77 -51
- package/skills/god-hygiene.md +5 -6
- package/skills/god-lifecycle.md +11 -13
- package/skills/god-list-assumptions.md +7 -8
- package/skills/god-locate.md +7 -8
- package/skills/god-map-codebase.md +5 -6
- package/skills/god-migrate.md +6 -15
- package/skills/god-next.md +16 -17
- package/skills/god-preflight.md +7 -8
- package/skills/god-progress.md +7 -8
- package/skills/god-reconcile.md +5 -6
- package/skills/god-reconstruct.md +5 -7
- package/skills/god-refactor.md +6 -7
- package/skills/god-roadmap-check.md +6 -7
- package/skills/god-scan.md +5 -9
- package/skills/god-spike.md +5 -6
- package/skills/god-standards.md +5 -6
- package/skills/god-status.md +12 -10
- package/skills/god-surface.md +61 -0
- package/skills/god-sync.md +4 -8
- package/skills/god-tech-debt.md +5 -6
- package/skills/god-test-runtime.md +4 -8
- package/skills/god-version.md +1 -1
- package/skills/god.md +53 -52
package/lib/gate.js
CHANGED
|
@@ -12,6 +12,7 @@ const artifactMap = require('./artifact-map');
|
|
|
12
12
|
const linter = require('./artifact-linter');
|
|
13
13
|
const router = require('./router');
|
|
14
14
|
const stateStore = require('./state');
|
|
15
|
+
const evidence = require('./evidence');
|
|
15
16
|
|
|
16
17
|
function relToAbs(projectRoot, relPath) {
|
|
17
18
|
return path.join(projectRoot, relPath);
|
|
@@ -267,40 +268,47 @@ function checkStateStepEvidence(projectRoot, tier, result) {
|
|
|
267
268
|
return subStep;
|
|
268
269
|
}
|
|
269
270
|
|
|
270
|
-
|
|
271
|
+
// Executed-evidence requirement for executable-gated tiers. Generalized from
|
|
272
|
+
// the original build-only check: a substep whose key is in
|
|
273
|
+
// evidence.EXECUTED_REQUIRED_SUBSTEPS must record at least one passed
|
|
274
|
+
// verification command and zero failed ones. Finding ids and summary keys are
|
|
275
|
+
// tier-prefixed so the build tier keeps its existing `build-verification-*`
|
|
276
|
+
// contract while harden gains `harden-verification-*`.
|
|
277
|
+
function checkExecutedEvidence(result, step, tier) {
|
|
271
278
|
const relPath = '.godpowers/state.json';
|
|
272
|
-
if (!
|
|
273
|
-
const
|
|
279
|
+
if (!step) return;
|
|
280
|
+
const label = tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
281
|
+
const failedCommands = commandsWithStatus(step, 'fail');
|
|
274
282
|
if (failedCommands.length > 0) {
|
|
275
283
|
const finding = makeFinding(
|
|
276
|
-
|
|
284
|
+
`${tier}-verification-failed-command`,
|
|
277
285
|
'error',
|
|
278
286
|
relPath,
|
|
279
|
-
|
|
287
|
+
`${label} state records failed verification command(s): ${failedCommands.join(', ')}.`
|
|
280
288
|
);
|
|
281
289
|
result.findings.push(finding);
|
|
282
290
|
addFindingSummary(result.summary, finding.severity);
|
|
283
291
|
result.checks.push(makeCheck(
|
|
284
|
-
|
|
292
|
+
`${tier}-verification-failed-command`,
|
|
285
293
|
'fail',
|
|
286
294
|
relPath,
|
|
287
295
|
finding.reason
|
|
288
296
|
));
|
|
289
|
-
result.summary
|
|
297
|
+
result.summary[`${tier}VerificationFailedCommands`] = failedCommands;
|
|
290
298
|
return;
|
|
291
299
|
}
|
|
292
|
-
const passedCommands = commandsWithStatus(
|
|
300
|
+
const passedCommands = commandsWithStatus(step, 'pass');
|
|
293
301
|
if (passedCommands.length === 0) {
|
|
294
302
|
const finding = makeFinding(
|
|
295
|
-
|
|
303
|
+
`${tier}-verification-evidence`,
|
|
296
304
|
'error',
|
|
297
305
|
relPath,
|
|
298
|
-
|
|
306
|
+
`${label} state does not record exact project verification commands that passed.`
|
|
299
307
|
);
|
|
300
308
|
result.findings.push(finding);
|
|
301
309
|
addFindingSummary(result.summary, finding.severity);
|
|
302
310
|
result.checks.push(makeCheck(
|
|
303
|
-
|
|
311
|
+
`${tier}-verification-evidence`,
|
|
304
312
|
'fail',
|
|
305
313
|
relPath,
|
|
306
314
|
finding.reason
|
|
@@ -308,12 +316,12 @@ function checkBuildEvidence(result, buildStep) {
|
|
|
308
316
|
return;
|
|
309
317
|
}
|
|
310
318
|
result.checks.push(makeCheck(
|
|
311
|
-
|
|
319
|
+
`${tier}-verification-evidence`,
|
|
312
320
|
'pass',
|
|
313
321
|
relPath,
|
|
314
|
-
`state.json records ${passedCommands.length} passed
|
|
322
|
+
`state.json records ${passedCommands.length} passed ${tier} verification command(s).`
|
|
315
323
|
));
|
|
316
|
-
result.summary
|
|
324
|
+
result.summary[`${tier}VerificationCommands`] = passedCommands;
|
|
317
325
|
}
|
|
318
326
|
|
|
319
327
|
function checkHardenCriticals(projectRoot, result) {
|
|
@@ -378,7 +386,10 @@ function check(opts = {}) {
|
|
|
378
386
|
|
|
379
387
|
checkArtifacts(projectRoot, tier, artifacts, opts, result);
|
|
380
388
|
const stateStep = checkStateStepEvidence(projectRoot, tier, result);
|
|
381
|
-
|
|
389
|
+
const stepRef = artifactMap.stateStepForTier(tier);
|
|
390
|
+
if (stepRef && evidence.EXECUTED_REQUIRED_SUBSTEPS.has(stepRef.subStepKey)) {
|
|
391
|
+
checkExecutedEvidence(result, stateStep, tier);
|
|
392
|
+
}
|
|
382
393
|
if (tier === 'harden') checkHardenCriticals(projectRoot, result);
|
|
383
394
|
return finalize(result);
|
|
384
395
|
}
|
package/lib/install-profiles.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const COMMON = [
|
|
2
2
|
'god',
|
|
3
|
+
'god-first-run',
|
|
4
|
+
'god-demo',
|
|
3
5
|
'god-help',
|
|
6
|
+
'god-surface',
|
|
4
7
|
'god-version',
|
|
5
8
|
'god-status'
|
|
6
9
|
];
|
|
@@ -131,7 +134,7 @@ const PROFILE_SKILLS = {
|
|
|
131
134
|
};
|
|
132
135
|
|
|
133
136
|
const PROFILE_DESCRIPTIONS = {
|
|
134
|
-
core: 'front door, status, verbs, and autonomous compatibility',
|
|
137
|
+
core: 'first-run guidance, front door, status, verbs, surface control, and autonomous compatibility',
|
|
135
138
|
builder: 'core plus planning leaves, stories, and runtime verification',
|
|
136
139
|
maintainer: 'core plus hygiene, deps, docs, repair, automation, and extensions',
|
|
137
140
|
suite: 'core plus multi-repo suite and workstream coordination',
|
package/lib/installer-args.js
CHANGED
|
@@ -11,7 +11,18 @@ const COMMANDS = new Set([
|
|
|
11
11
|
'automation-setup',
|
|
12
12
|
'dogfood',
|
|
13
13
|
'extension-scaffold',
|
|
14
|
-
'
|
|
14
|
+
'surface',
|
|
15
|
+
'demo',
|
|
16
|
+
'gate',
|
|
17
|
+
'verify',
|
|
18
|
+
'can-close',
|
|
19
|
+
'route',
|
|
20
|
+
'report',
|
|
21
|
+
'reflect',
|
|
22
|
+
'memory',
|
|
23
|
+
'lesson',
|
|
24
|
+
'outcome',
|
|
25
|
+
'import-ledger'
|
|
15
26
|
]);
|
|
16
27
|
|
|
17
28
|
function parseArgs(argv, cwd = process.cwd()) {
|
|
@@ -21,6 +32,7 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
21
32
|
project: cwd,
|
|
22
33
|
json: false,
|
|
23
34
|
brief: false,
|
|
35
|
+
full: false,
|
|
24
36
|
stateAction: null,
|
|
25
37
|
step: null,
|
|
26
38
|
status: null,
|
|
@@ -30,6 +42,38 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
30
42
|
extensionAgent: null,
|
|
31
43
|
extensionWorkflow: null,
|
|
32
44
|
tier: null,
|
|
45
|
+
verifyCommand: null,
|
|
46
|
+
routePrompt: null,
|
|
47
|
+
substep: null,
|
|
48
|
+
claim: null,
|
|
49
|
+
timeout: null,
|
|
50
|
+
attest: false,
|
|
51
|
+
evidence: null,
|
|
52
|
+
since: null,
|
|
53
|
+
peek: false,
|
|
54
|
+
reflectAction: null,
|
|
55
|
+
outcome: null,
|
|
56
|
+
observation: null,
|
|
57
|
+
rootCause: null,
|
|
58
|
+
nextAction: null,
|
|
59
|
+
lesson: null,
|
|
60
|
+
memoryAction: null,
|
|
61
|
+
memoryKey: null,
|
|
62
|
+
memoryValue: null,
|
|
63
|
+
category: null,
|
|
64
|
+
lessonAction: null,
|
|
65
|
+
lessonText: null,
|
|
66
|
+
tags: null,
|
|
67
|
+
scope: null,
|
|
68
|
+
outcomeAction: null,
|
|
69
|
+
outcomeSlug: null,
|
|
70
|
+
outcomeGoal: null,
|
|
71
|
+
outcomeVerify: null,
|
|
72
|
+
budget: null,
|
|
73
|
+
reason: null,
|
|
74
|
+
importFrom: null,
|
|
75
|
+
apply: false,
|
|
76
|
+
dryRun: false,
|
|
33
77
|
runtimes: [],
|
|
34
78
|
global: false,
|
|
35
79
|
local: false,
|
|
@@ -49,6 +93,27 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
49
93
|
opts.stateAction = arg;
|
|
50
94
|
continue;
|
|
51
95
|
}
|
|
96
|
+
if (opts.command === 'verify' && opts.verifyCommand === null && !arg.startsWith('-')) {
|
|
97
|
+
opts.verifyCommand = arg;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (opts.command === 'route' && opts.routePrompt === null && !arg.startsWith('-')) {
|
|
101
|
+
opts.routePrompt = arg;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (opts.command === 'memory' && !arg.startsWith('-')) {
|
|
105
|
+
if (opts.memoryAction === null) { opts.memoryAction = arg; continue; }
|
|
106
|
+
if (opts.memoryKey === null) { opts.memoryKey = arg; continue; }
|
|
107
|
+
if (opts.memoryValue === null) { opts.memoryValue = arg; continue; }
|
|
108
|
+
}
|
|
109
|
+
if (opts.command === 'lesson' && !arg.startsWith('-')) {
|
|
110
|
+
if (opts.lessonAction === null) { opts.lessonAction = arg; continue; }
|
|
111
|
+
if (opts.lessonText === null) { opts.lessonText = arg; continue; }
|
|
112
|
+
}
|
|
113
|
+
if (opts.command === 'outcome' && !arg.startsWith('-')) {
|
|
114
|
+
if (opts.outcomeAction === null) { opts.outcomeAction = arg; continue; }
|
|
115
|
+
if (opts.outcomeSlug === null) { opts.outcomeSlug = arg; continue; }
|
|
116
|
+
}
|
|
52
117
|
|
|
53
118
|
switch (arg) {
|
|
54
119
|
case '--json':
|
|
@@ -57,6 +122,21 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
57
122
|
case '--brief':
|
|
58
123
|
opts.brief = true;
|
|
59
124
|
break;
|
|
125
|
+
case '--full':
|
|
126
|
+
opts.full = true;
|
|
127
|
+
break;
|
|
128
|
+
case '--apply':
|
|
129
|
+
opts.apply = true;
|
|
130
|
+
break;
|
|
131
|
+
case '--dry-run':
|
|
132
|
+
opts.dryRun = true;
|
|
133
|
+
break;
|
|
134
|
+
case '--runtime':
|
|
135
|
+
if (args[i + 1]) {
|
|
136
|
+
opts.runtimes.push(args[i + 1]);
|
|
137
|
+
i++;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
60
140
|
case '--tier':
|
|
61
141
|
if (args[i + 1]) {
|
|
62
142
|
opts.tier = args[i + 1];
|
|
@@ -75,6 +155,126 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
75
155
|
i++;
|
|
76
156
|
}
|
|
77
157
|
break;
|
|
158
|
+
case '--substep':
|
|
159
|
+
if (args[i + 1]) {
|
|
160
|
+
opts.substep = args[i + 1];
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
case '--claim':
|
|
165
|
+
if (args[i + 1]) {
|
|
166
|
+
opts.claim = args[i + 1];
|
|
167
|
+
i++;
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
case '--timeout':
|
|
171
|
+
if (args[i + 1]) {
|
|
172
|
+
opts.timeout = args[i + 1];
|
|
173
|
+
i++;
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case '--evidence':
|
|
177
|
+
if (args[i + 1]) {
|
|
178
|
+
opts.evidence = args[i + 1];
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case '--attest':
|
|
183
|
+
opts.attest = true;
|
|
184
|
+
break;
|
|
185
|
+
case '--peek':
|
|
186
|
+
opts.peek = true;
|
|
187
|
+
break;
|
|
188
|
+
case '--since':
|
|
189
|
+
if (args[i + 1]) {
|
|
190
|
+
opts.since = args[i + 1];
|
|
191
|
+
i++;
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case '--action':
|
|
195
|
+
if (args[i + 1]) {
|
|
196
|
+
opts.reflectAction = args[i + 1];
|
|
197
|
+
i++;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
case '--outcome':
|
|
201
|
+
if (args[i + 1]) {
|
|
202
|
+
opts.outcome = args[i + 1];
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
case '--observation':
|
|
207
|
+
if (args[i + 1]) {
|
|
208
|
+
opts.observation = args[i + 1];
|
|
209
|
+
i++;
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
case '--root-cause':
|
|
213
|
+
if (args[i + 1]) {
|
|
214
|
+
opts.rootCause = args[i + 1];
|
|
215
|
+
i++;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case '--next':
|
|
219
|
+
if (args[i + 1]) {
|
|
220
|
+
opts.nextAction = args[i + 1];
|
|
221
|
+
i++;
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
case '--lesson':
|
|
225
|
+
if (args[i + 1]) {
|
|
226
|
+
opts.lesson = args[i + 1];
|
|
227
|
+
i++;
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case '--category':
|
|
231
|
+
if (args[i + 1]) {
|
|
232
|
+
opts.category = args[i + 1];
|
|
233
|
+
i++;
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
case '--tags':
|
|
237
|
+
if (args[i + 1]) {
|
|
238
|
+
opts.tags = args[i + 1];
|
|
239
|
+
i++;
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
case '--scope':
|
|
243
|
+
if (args[i + 1]) {
|
|
244
|
+
opts.scope = args[i + 1];
|
|
245
|
+
i++;
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
case '--goal':
|
|
249
|
+
if (args[i + 1]) {
|
|
250
|
+
opts.outcomeGoal = args[i + 1];
|
|
251
|
+
i++;
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
case '--verify':
|
|
255
|
+
if (args[i + 1]) {
|
|
256
|
+
opts.outcomeVerify = args[i + 1];
|
|
257
|
+
i++;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
case '--budget':
|
|
261
|
+
if (args[i + 1]) {
|
|
262
|
+
opts.budget = args[i + 1];
|
|
263
|
+
i++;
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
case '--reason':
|
|
267
|
+
if (args[i + 1]) {
|
|
268
|
+
opts.reason = args[i + 1];
|
|
269
|
+
i++;
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
case '--from':
|
|
273
|
+
if (args[i + 1]) {
|
|
274
|
+
opts.importFrom = args[i + 1];
|
|
275
|
+
i++;
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
78
278
|
case '--project':
|
|
79
279
|
if (args[i + 1]) {
|
|
80
280
|
opts.project = path.resolve(args[i + 1]);
|
|
@@ -122,12 +322,52 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
122
322
|
opts.extensionAgent = arg.slice('--agent='.length);
|
|
123
323
|
} else if (arg.startsWith('--workflow=')) {
|
|
124
324
|
opts.extensionWorkflow = arg.slice('--workflow='.length);
|
|
325
|
+
} else if (arg.startsWith('--runtime=')) {
|
|
326
|
+
opts.runtimes.push(arg.slice('--runtime='.length));
|
|
125
327
|
} else if (arg.startsWith('--tier=')) {
|
|
126
328
|
opts.tier = arg.slice('--tier='.length);
|
|
127
329
|
} else if (arg.startsWith('--step=')) {
|
|
128
330
|
opts.step = arg.slice('--step='.length);
|
|
129
331
|
} else if (arg.startsWith('--status=')) {
|
|
130
332
|
opts.status = arg.slice('--status='.length);
|
|
333
|
+
} else if (arg.startsWith('--substep=')) {
|
|
334
|
+
opts.substep = arg.slice('--substep='.length);
|
|
335
|
+
} else if (arg.startsWith('--claim=')) {
|
|
336
|
+
opts.claim = arg.slice('--claim='.length);
|
|
337
|
+
} else if (arg.startsWith('--timeout=')) {
|
|
338
|
+
opts.timeout = arg.slice('--timeout='.length);
|
|
339
|
+
} else if (arg.startsWith('--evidence=')) {
|
|
340
|
+
opts.evidence = arg.slice('--evidence='.length);
|
|
341
|
+
} else if (arg.startsWith('--since=')) {
|
|
342
|
+
opts.since = arg.slice('--since='.length);
|
|
343
|
+
} else if (arg.startsWith('--action=')) {
|
|
344
|
+
opts.reflectAction = arg.slice('--action='.length);
|
|
345
|
+
} else if (arg.startsWith('--outcome=')) {
|
|
346
|
+
opts.outcome = arg.slice('--outcome='.length);
|
|
347
|
+
} else if (arg.startsWith('--observation=')) {
|
|
348
|
+
opts.observation = arg.slice('--observation='.length);
|
|
349
|
+
} else if (arg.startsWith('--root-cause=')) {
|
|
350
|
+
opts.rootCause = arg.slice('--root-cause='.length);
|
|
351
|
+
} else if (arg.startsWith('--next=')) {
|
|
352
|
+
opts.nextAction = arg.slice('--next='.length);
|
|
353
|
+
} else if (arg.startsWith('--lesson=')) {
|
|
354
|
+
opts.lesson = arg.slice('--lesson='.length);
|
|
355
|
+
} else if (arg.startsWith('--category=')) {
|
|
356
|
+
opts.category = arg.slice('--category='.length);
|
|
357
|
+
} else if (arg.startsWith('--tags=')) {
|
|
358
|
+
opts.tags = arg.slice('--tags='.length);
|
|
359
|
+
} else if (arg.startsWith('--scope=')) {
|
|
360
|
+
opts.scope = arg.slice('--scope='.length);
|
|
361
|
+
} else if (arg.startsWith('--goal=')) {
|
|
362
|
+
opts.outcomeGoal = arg.slice('--goal='.length);
|
|
363
|
+
} else if (arg.startsWith('--verify=')) {
|
|
364
|
+
opts.outcomeVerify = arg.slice('--verify='.length);
|
|
365
|
+
} else if (arg.startsWith('--budget=')) {
|
|
366
|
+
opts.budget = arg.slice('--budget='.length);
|
|
367
|
+
} else if (arg.startsWith('--reason=')) {
|
|
368
|
+
opts.reason = arg.slice('--reason='.length);
|
|
369
|
+
} else if (arg.startsWith('--from=')) {
|
|
370
|
+
opts.importFrom = arg.slice('--from='.length);
|
|
131
371
|
} else if (arg.startsWith('--profile=')) {
|
|
132
372
|
opts.profile = arg.slice('--profile='.length);
|
|
133
373
|
} else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quarterback: the entry-level router.
|
|
3
|
+
*
|
|
4
|
+
* A thin decision layer that composes the existing structural router
|
|
5
|
+
* (lib/router.js) and the fuzzy-intent playbook (lib/recipes.js) rather than
|
|
6
|
+
* duplicating them. It adds exactly two genes that Godpowers lacks at entry:
|
|
7
|
+
* - refuse-on-red: never start new work when the latest executed verdict is
|
|
8
|
+
* red or harden findings carry an unresolved Critical (the [10] route).
|
|
9
|
+
* - proportional ceremony: do not open an arc for a one-line fix (the [90]
|
|
10
|
+
* route).
|
|
11
|
+
* Everything else delegates to router.suggestNext() and recipes.matchIntent().
|
|
12
|
+
*
|
|
13
|
+
* Read-only: route() never mutates state. See docs/FUSION-ARCHITECTURE.md 4.3.
|
|
14
|
+
*
|
|
15
|
+
* @typedef {Object} Play
|
|
16
|
+
* @property {string} route One of recover, resume, recovery, brownfield,
|
|
17
|
+
* research, review, full, feature, trivial.
|
|
18
|
+
* @property {string} reason Why this route was chosen.
|
|
19
|
+
* @property {string|null} nextCommand The command (or null to answer inline).
|
|
20
|
+
* @property {string} ceremony none | light | focused | full | inherit.
|
|
21
|
+
* @property {string} verificationStrategy none | artifact+attested | executed-where-gated.
|
|
22
|
+
* @property {string} chatPolicy Always "stay in this chat as executor".
|
|
23
|
+
* @property {boolean} mutatesState Always false.
|
|
24
|
+
* @property {{ classification: string, latestVerdict: string, activeArc: string|null, openFindings: boolean }} evidence
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
|
|
30
|
+
const router = require('./router');
|
|
31
|
+
const recipes = require('./recipes');
|
|
32
|
+
const evidence = require('./evidence');
|
|
33
|
+
const stateStore = require('./state');
|
|
34
|
+
|
|
35
|
+
const CONTINUATION_KEYWORDS = ['continue', 'resume', 'next step', 'keep going', "what's next", 'whats next', 'pick up', 'carry on'];
|
|
36
|
+
const INCIDENT_KEYWORDS = ['incident', 'outage', 'hotfix', 'postmortem', 'post-mortem', 'rollback', 'regression', 'production is down', 'broke prod', 'sev1', 'sev 1'];
|
|
37
|
+
const BROWNFIELD_KEYWORDS = ['inherited', 'inherit', 'existing codebase', 'legacy', 'brownfield', 'took over', 'onboard onto', 'understand this repo', 'archaeology'];
|
|
38
|
+
const RESEARCH_KEYWORDS = ['spike', 'explore', 'research', 'prototype', 'proof of concept', 'proof-of-concept', 'poc', 'evaluate', 'not sure which', 'unsure', 'investigate'];
|
|
39
|
+
const REVIEW_KEYWORDS = ['audit', 'review', 'critique', 'find risks', 'find bugs', 'security review', 'assess', 'what could go wrong', 'red team'];
|
|
40
|
+
const FULL_KEYWORDS = ['idea to production', 'ship it all', 'end to end', 'end-to-end', 'full arc', 'god mode', 'build the whole', 'whole thing', 'from scratch to launch', 'take it to production'];
|
|
41
|
+
const TRIVIAL_KEYWORDS = ['typo', 'rename', 'one-line', 'one line', 'quick question', 'what is', 'how do i', 'how do you', 'tweak', 'small fix', 'change the wording', 'bump the'];
|
|
42
|
+
|
|
43
|
+
function hasAny(text, keywords) {
|
|
44
|
+
return keywords.some((kw) => text.includes(kw));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function classify(prompt) {
|
|
48
|
+
const text = String(prompt || '').toLowerCase().trim();
|
|
49
|
+
if (text === '') return 'continue';
|
|
50
|
+
if (hasAny(text, CONTINUATION_KEYWORDS)) return 'continue';
|
|
51
|
+
if (hasAny(text, INCIDENT_KEYWORDS)) return 'incident';
|
|
52
|
+
if (hasAny(text, FULL_KEYWORDS)) return 'full';
|
|
53
|
+
if (hasAny(text, BROWNFIELD_KEYWORDS)) return 'brownfield';
|
|
54
|
+
if (hasAny(text, RESEARCH_KEYWORDS)) return 'research';
|
|
55
|
+
if (hasAny(text, REVIEW_KEYWORDS)) return 'review';
|
|
56
|
+
if (hasAny(text, TRIVIAL_KEYWORDS)) return 'trivial';
|
|
57
|
+
return 'feature';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function latestVerdict(projectRoot) {
|
|
61
|
+
const records = evidence.history({ projectRoot });
|
|
62
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
63
|
+
const record = records[i];
|
|
64
|
+
if (record && record.kind === 'executed') {
|
|
65
|
+
return record.verified ? 'green' : 'red';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return 'none';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function openFindings(projectRoot) {
|
|
72
|
+
const findings = path.join(projectRoot, '.godpowers', 'harden', 'FINDINGS.md');
|
|
73
|
+
// Only treat findings as open when the file exists and carries a Critical or a
|
|
74
|
+
// blocked launch gate. router.hasNoCriticalFindings is fail-closed (false when
|
|
75
|
+
// the file is absent), so guard on existence to avoid false "red" on projects
|
|
76
|
+
// that have not run harden yet.
|
|
77
|
+
if (!fs.existsSync(findings)) return false;
|
|
78
|
+
return !router.hasNoCriticalFindings(projectRoot);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function activeArc(projectRoot) {
|
|
82
|
+
const state = stateStore.read(projectRoot);
|
|
83
|
+
if (!state) return null;
|
|
84
|
+
return state['active-arc'] || state.arc || state['lifecycle-phase'] || null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function recipeCommand(prompt, projectRoot) {
|
|
88
|
+
const matches = recipes.matchIntent(prompt, projectRoot);
|
|
89
|
+
if (!matches.length || matches[0].score < 10) return null;
|
|
90
|
+
const recipe = matches[0].recipe;
|
|
91
|
+
const name = recipe['default-sequence'] || 'default';
|
|
92
|
+
const steps = recipes.getSequence(recipe, name);
|
|
93
|
+
const first = steps[0] && steps[0].command;
|
|
94
|
+
return first ? String(first).split(/\s+/)[0] : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function play(route, reason, nextCommand, ceremony, verificationStrategy, ev) {
|
|
98
|
+
return {
|
|
99
|
+
route,
|
|
100
|
+
reason,
|
|
101
|
+
nextCommand,
|
|
102
|
+
ceremony,
|
|
103
|
+
verificationStrategy,
|
|
104
|
+
chatPolicy: 'stay in this chat as executor',
|
|
105
|
+
mutatesState: false,
|
|
106
|
+
evidence: ev
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Decide the entry play for a prompt. First match wins down the priority ladder.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} prompt Free-text user intent (may be empty).
|
|
114
|
+
* @param {{ projectRoot?: string }} [opts]
|
|
115
|
+
* @returns {Play}
|
|
116
|
+
*/
|
|
117
|
+
function route(prompt, opts = {}) {
|
|
118
|
+
const projectRoot = path.resolve(opts.projectRoot || process.cwd());
|
|
119
|
+
const ev = {
|
|
120
|
+
classification: classify(prompt),
|
|
121
|
+
latestVerdict: latestVerdict(projectRoot),
|
|
122
|
+
activeArc: activeArc(projectRoot),
|
|
123
|
+
openFindings: openFindings(projectRoot)
|
|
124
|
+
};
|
|
125
|
+
const next = router.suggestNext(projectRoot);
|
|
126
|
+
const initialized = stateStore.isInitialized(projectRoot);
|
|
127
|
+
|
|
128
|
+
// [10] recover: refuse-on-red. Never start new work on a red check.
|
|
129
|
+
if (ev.latestVerdict === 'red') {
|
|
130
|
+
return play('recover', 'Latest executed verification is red; debug and re-verify before new work.',
|
|
131
|
+
'/god-debug', 'focused', 'executed-where-gated', ev);
|
|
132
|
+
}
|
|
133
|
+
if (ev.openFindings) {
|
|
134
|
+
return play('recover', 'Harden findings carry an unresolved Critical or a blocked launch gate; resolve before new work.',
|
|
135
|
+
'/god-debug', 'focused', 'executed-where-gated', ev);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// [20] resume: an active arc with non-done substeps plus continuation intent.
|
|
139
|
+
const hasOpenArc = initialized && next && next.command && next.command !== '/god-init'
|
|
140
|
+
&& next.tier !== 'steady-state';
|
|
141
|
+
if (hasOpenArc && ev.classification === 'continue') {
|
|
142
|
+
return play('resume', `Active arc has open work: ${next.reason}.`,
|
|
143
|
+
next.command, 'inherit', 'executed-where-gated', ev);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// [30]-[90]: classification-based new work. Delegate to recipes/router.
|
|
147
|
+
switch (ev.classification) {
|
|
148
|
+
case 'incident':
|
|
149
|
+
return play('recovery', 'Incident, hotfix, or postmortem intent.',
|
|
150
|
+
recipeCommand(prompt, projectRoot) || '/god-hotfix', 'focused', 'executed-where-gated', ev);
|
|
151
|
+
case 'brownfield':
|
|
152
|
+
return play('brownfield', 'Inheriting or understanding existing code.',
|
|
153
|
+
recipeCommand(prompt, projectRoot) || '/god-archaeology', 'full', 'artifact+attested', ev);
|
|
154
|
+
case 'research':
|
|
155
|
+
return play('research', 'Uncertain technology; time-box a spike or exploration.',
|
|
156
|
+
recipeCommand(prompt, projectRoot) || '/god-spike', 'light', 'artifact+attested', ev);
|
|
157
|
+
case 'review':
|
|
158
|
+
return play('review', 'Find risks, critique, or audit; no new feature work.',
|
|
159
|
+
recipeCommand(prompt, projectRoot) || '/god-code-review', 'light', 'artifact+attested', ev);
|
|
160
|
+
case 'full':
|
|
161
|
+
return play('full', 'Idea-to-production request; run the full arc.',
|
|
162
|
+
'/god-mode', 'full', 'executed-where-gated', ev);
|
|
163
|
+
case 'trivial':
|
|
164
|
+
return play('trivial', 'Single reversible edit or question; do not open an arc.',
|
|
165
|
+
'/god-fast', 'none', 'none', ev);
|
|
166
|
+
case 'continue':
|
|
167
|
+
// Continuation intent but no open arc: point at the structural next step.
|
|
168
|
+
return play('resume', next && next.reason ? next.reason : 'Continue from current state.',
|
|
169
|
+
next ? next.command : '/god-init', initialized ? 'inherit' : 'full', 'executed-where-gated', ev);
|
|
170
|
+
default:
|
|
171
|
+
return play('feature', 'Ordinary multi-step feature.',
|
|
172
|
+
recipeCommand(prompt, projectRoot) || '/god-feature', 'full', 'executed-where-gated', ev);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
route,
|
|
178
|
+
classify,
|
|
179
|
+
// Internals exposed for tests.
|
|
180
|
+
_latestVerdict: latestVerdict,
|
|
181
|
+
_openFindings: openFindings,
|
|
182
|
+
_activeArc: activeArc
|
|
183
|
+
};
|