godpowers 2.6.0 → 2.7.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 +22 -0
- package/README.md +26 -22
- package/RELEASE.md +38 -35
- package/SKILL.md +46 -48
- package/agents/god-deploy-engineer.md +2 -2
- package/agents/god-designer.md +3 -2
- package/agents/god-greenfieldifier.md +2 -4
- package/agents/god-launch-strategist.md +4 -5
- package/agents/god-observability-engineer.md +5 -5
- package/agents/god-reconciler.md +10 -4
- package/agents/god-retrospective.md +1 -1
- package/agents/god-updater.md +5 -5
- package/bin/install.js +6 -1
- package/fixtures/gate/build-pass/.godpowers/state.json +33 -0
- package/lib/README.md +2 -0
- package/lib/artifact-map.js +15 -3
- package/lib/cli-dispatch.js +39 -1
- package/lib/context-writer.js +4 -4
- package/lib/gate.js +107 -9
- package/lib/installer-args.js +24 -0
- package/lib/pillars.js +2 -4
- package/lib/recipes.js +16 -0
- package/lib/router.js +1 -5
- package/lib/source-sync.js +1 -1
- package/lib/state-advance.js +244 -0
- package/lib/state-lock.js +8 -4
- package/lib/state-views.js +460 -0
- package/lib/state.js +52 -3
- package/package.json +1 -1
- package/routing/god-audit.yaml +1 -1
- package/routing/god-build.yaml +1 -1
- package/routing/god-context.yaml +1 -1
- package/routing/god-deploy.yaml +3 -1
- package/routing/god-design.yaml +2 -2
- package/routing/god-launch.yaml +4 -1
- package/routing/god-migrate.yaml +0 -1
- package/routing/god-mode.yaml +1 -1
- package/routing/god-observe.yaml +4 -1
- package/routing/god-prd.yaml +1 -1
- package/routing/god-reconcile.yaml +2 -5
- package/routing/god-sync.yaml +1 -1
- package/routing/recipes/returning-after-break.yaml +1 -1
- package/schema/state.v1.json +68 -1
- package/skills/god-arch.md +1 -1
- package/skills/god-build.md +6 -4
- package/skills/god-deploy.md +16 -14
- package/skills/god-design.md +3 -3
- package/skills/god-fast.md +2 -2
- package/skills/god-feature.md +1 -1
- package/skills/god-harden.md +3 -3
- package/skills/god-hotfix.md +1 -1
- package/skills/god-init.md +14 -10
- package/skills/god-launch.md +14 -12
- package/skills/god-lifecycle.md +2 -1
- package/skills/god-mode.md +5 -4
- package/skills/god-observe.md +15 -13
- package/skills/god-pause-work.md +2 -2
- package/skills/god-prd.md +5 -4
- package/skills/god-quick.md +1 -1
- package/skills/god-repo.md +1 -1
- package/skills/god-resume-work.md +5 -4
- package/skills/god-roadmap-update.md +1 -1
- package/skills/god-roadmap.md +1 -1
- package/skills/god-rollback.md +1 -1
- package/skills/god-skip.md +2 -2
- package/skills/god-stack.md +1 -1
- package/skills/god-standards.md +1 -1
- package/skills/god-status.md +2 -2
- package/skills/god-story.md +1 -1
- package/skills/god-sync.md +2 -2
- package/workflows/bluefield-arc.yaml +2 -4
- package/workflows/brownfield-arc.yaml +2 -4
package/agents/god-updater.md
CHANGED
|
@@ -54,13 +54,13 @@ After feature work, every artifact that was impacted needs to reflect reality.
|
|
|
54
54
|
- Validate have-nots S-01 through S-05
|
|
55
55
|
|
|
56
56
|
### DEPLOY update (if reconciler said "needs-extension")
|
|
57
|
-
- Update
|
|
57
|
+
- Update `.godpowers/state.json` deploy evidence
|
|
58
58
|
- Document new env vars
|
|
59
59
|
- Update CI/CD config notes
|
|
60
60
|
- Have-nots D-01 through D-08
|
|
61
61
|
|
|
62
62
|
### OBSERVE update (if reconciler said "needs-slo" or "needs-alert")
|
|
63
|
-
- Update
|
|
63
|
+
- Update `.godpowers/state.json` observe evidence
|
|
64
64
|
- Define new SLO with error budget policy
|
|
65
65
|
- Define new alert with runbook reference
|
|
66
66
|
- Have-nots OB-01 through OB-08
|
|
@@ -71,7 +71,7 @@ After feature work, every artifact that was impacted needs to reflect reality.
|
|
|
71
71
|
- Have-nots H-01 through H-11
|
|
72
72
|
|
|
73
73
|
### LAUNCH update (if reconciler said "copy-update" or "new-launch")
|
|
74
|
-
- Update
|
|
74
|
+
- Update `.godpowers/state.json` launch evidence
|
|
75
75
|
- Update landing copy if user-visible
|
|
76
76
|
- Substitution-test new copy
|
|
77
77
|
|
|
@@ -260,8 +260,8 @@ Updated:
|
|
|
260
260
|
- arch/ARCH.md: added ADR-007 (auth refactor)
|
|
261
261
|
- arch/adr/007-auth-refactor.md: created
|
|
262
262
|
- roadmap/ROADMAP.md: Milestone 2 marked complete
|
|
263
|
-
- deploy
|
|
264
|
-
- observe
|
|
263
|
+
- state.json tier-3.deploy: added STRIPE_WEBHOOK_SECRET env var
|
|
264
|
+
- state.json tier-3.observe: added SLO for /api/stripe-webhook (99.5%)
|
|
265
265
|
- backlog/BACKLOG.md: resolved entry "Stripe webhook handling"
|
|
266
266
|
- todos/TODOS.md: marked "wire stripe events" as done
|
|
267
267
|
- threads/auth-migration.md: appended progress note
|
package/bin/install.js
CHANGED
|
@@ -51,6 +51,7 @@ function showHelp() {
|
|
|
51
51
|
log('Commands:');
|
|
52
52
|
log(' status Show the Godpowers Dashboard for a project');
|
|
53
53
|
log(' next Show the dashboard and recommended next command');
|
|
54
|
+
log(' state advance Update one tracked Godpowers state step');
|
|
54
55
|
log(' gate Check a tier artifact gate');
|
|
55
56
|
log(' mcp-info Show read-only MCP companion setup instructions');
|
|
56
57
|
log(' quick-proof Show a runnable proof from the shipped fixture');
|
|
@@ -65,7 +66,9 @@ function showHelp() {
|
|
|
65
66
|
}
|
|
66
67
|
log('');
|
|
67
68
|
log('Options:');
|
|
68
|
-
log(' --project=<path> Project root for status, next, proof, or automation commands');
|
|
69
|
+
log(' --project=<path> Project root for status, next, state, proof, or automation commands');
|
|
70
|
+
log(' --step=<name> Step for state advance, such as prd or tier-1.prd');
|
|
71
|
+
log(' --status=<status> Status for state advance');
|
|
69
72
|
log(' --tier=<name> Tier for gate: prd, design, arch, roadmap, stack, repo, build, or harden');
|
|
70
73
|
log(' --json Emit JSON for status, next, proof, or automation commands');
|
|
71
74
|
log(' --brief Render compact output for status, next, or proof');
|
|
@@ -100,6 +103,7 @@ function showHelp() {
|
|
|
100
103
|
log('Examples:');
|
|
101
104
|
log(' npx godpowers status --project=.');
|
|
102
105
|
log(' npx godpowers next --project=.');
|
|
106
|
+
log(' npx godpowers state advance --step=prd --status=done --project=.');
|
|
103
107
|
log(' npx godpowers gate --tier=prd --project=.');
|
|
104
108
|
log(' npx godpowers mcp-info --project=.');
|
|
105
109
|
log(' npx godpowers quick-proof --project=.');
|
|
@@ -216,6 +220,7 @@ module.exports = {
|
|
|
216
220
|
runMcpInfoCommand: cliDispatch.runMcpInfoCommand,
|
|
217
221
|
runExtensionScaffoldCommand: cliDispatch.runExtensionScaffoldCommand,
|
|
218
222
|
runGateCommand: cliDispatch.runGateCommand,
|
|
223
|
+
runStateCommand: cliDispatch.runStateCommand,
|
|
219
224
|
applyDefaultRuntimeSelection,
|
|
220
225
|
runInstall,
|
|
221
226
|
runUninstall,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://godpowers.dev/schema/state.v1.json",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"project": {
|
|
5
|
+
"name": "build-pass",
|
|
6
|
+
"started": "2026-06-10T00:00:00.000Z"
|
|
7
|
+
},
|
|
8
|
+
"tiers": {
|
|
9
|
+
"tier-2": {
|
|
10
|
+
"build": {
|
|
11
|
+
"status": "done",
|
|
12
|
+
"updated": "2026-06-10T18:08:00.000Z",
|
|
13
|
+
"artifact": "build/STATE.md",
|
|
14
|
+
"verification": {
|
|
15
|
+
"commands": [
|
|
16
|
+
{
|
|
17
|
+
"command": "npm test",
|
|
18
|
+
"status": "pass",
|
|
19
|
+
"exitCode": 0,
|
|
20
|
+
"ranAt": "2026-06-10T18:07:00.000Z"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"command": "npm run lint",
|
|
24
|
+
"status": "pass",
|
|
25
|
+
"exitCode": 0,
|
|
26
|
+
"ranAt": "2026-06-10T18:07:30.000Z"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/lib/README.md
CHANGED
|
@@ -9,6 +9,8 @@ package-level integrations.
|
|
|
9
9
|
| Module | Purpose |
|
|
10
10
|
|--------|---------|
|
|
11
11
|
| `state.js` | Read, initialize, validate, and write `.godpowers/state.json`. |
|
|
12
|
+
| `state-views.js` | Generate managed markdown views such as `.godpowers/PROGRESS.md` and Godpowers-owned per-tier `STATE.md` files from `state.json`. |
|
|
13
|
+
| `state-advance.js` | Mutate one tracked state step through locking and generated state view refresh. |
|
|
12
14
|
| `state-lock.js` | Coordinate state writes with a lock file. |
|
|
13
15
|
| `intent.js` | Read and validate `intent.yaml` from project roots or `.godpowers/`. |
|
|
14
16
|
| `frontmatter.js` | Parse shared markdown YAML frontmatter for skills, agents, Pillars, checkpoints, and design specs. |
|
package/lib/artifact-map.js
CHANGED
|
@@ -9,7 +9,7 @@ const TIER_ARTIFACTS = {
|
|
|
9
9
|
design: [
|
|
10
10
|
{ path: 'DESIGN.md', required: true, lint: true },
|
|
11
11
|
{ path: 'PRODUCT.md', required: false, lint: true },
|
|
12
|
-
{ path: '.godpowers/
|
|
12
|
+
{ path: '.godpowers/state.json', required: true, lint: false }
|
|
13
13
|
],
|
|
14
14
|
arch: [
|
|
15
15
|
{ path: '.godpowers/arch/ARCH.md', required: true, lint: true }
|
|
@@ -24,13 +24,18 @@ const TIER_ARTIFACTS = {
|
|
|
24
24
|
{ path: '.godpowers/repo/AUDIT.md', required: true, lint: true }
|
|
25
25
|
],
|
|
26
26
|
build: [
|
|
27
|
-
{ path: '.godpowers/
|
|
27
|
+
{ path: '.godpowers/state.json', required: true, lint: false }
|
|
28
28
|
],
|
|
29
29
|
harden: [
|
|
30
30
|
{ path: '.godpowers/harden/FINDINGS.md', required: true, lint: true }
|
|
31
31
|
]
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
const TIER_STATE_STEPS = {
|
|
35
|
+
design: { tierKey: 'tier-1', subStepKey: 'design' },
|
|
36
|
+
build: { tierKey: 'tier-2', subStepKey: 'build' }
|
|
37
|
+
};
|
|
38
|
+
|
|
34
39
|
function normalizeTier(tier) {
|
|
35
40
|
if (!tier) return null;
|
|
36
41
|
return String(tier).replace(/^\/?god-/, '').toLowerCase();
|
|
@@ -51,10 +56,17 @@ function requiredArtifactsForTier(tier) {
|
|
|
51
56
|
return artifacts ? artifacts.filter((artifact) => artifact.required) : null;
|
|
52
57
|
}
|
|
53
58
|
|
|
59
|
+
function stateStepForTier(tier) {
|
|
60
|
+
const key = normalizeTier(tier);
|
|
61
|
+
if (!key || !TIER_STATE_STEPS[key]) return null;
|
|
62
|
+
return { ...TIER_STATE_STEPS[key] };
|
|
63
|
+
}
|
|
64
|
+
|
|
54
65
|
module.exports = {
|
|
55
66
|
TIER_ARTIFACTS,
|
|
56
67
|
normalizeTier,
|
|
57
68
|
tiers,
|
|
58
69
|
artifactsForTier,
|
|
59
|
-
requiredArtifactsForTier
|
|
70
|
+
requiredArtifactsForTier,
|
|
71
|
+
stateStepForTier
|
|
60
72
|
};
|
package/lib/cli-dispatch.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const gate = require('./gate');
|
|
6
6
|
const identity = require('./package-identity');
|
|
7
|
+
const stateAdvance = require('./state-advance');
|
|
7
8
|
|
|
8
9
|
const VERSION = identity.PACKAGE_VERSION;
|
|
9
10
|
|
|
@@ -140,9 +141,45 @@ function runGateCommand(opts) {
|
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
function runStateCommand(opts) {
|
|
145
|
+
if (opts.stateAction !== 'advance') {
|
|
146
|
+
const result = {
|
|
147
|
+
command: 'state',
|
|
148
|
+
verdict: 'fail',
|
|
149
|
+
project: opts.project,
|
|
150
|
+
step: opts.step || null,
|
|
151
|
+
status: opts.status || null,
|
|
152
|
+
previousStatus: null,
|
|
153
|
+
updated: null,
|
|
154
|
+
warnings: [],
|
|
155
|
+
checks: [{ id: 'state-action-required', status: 'fail', artifact: '.godpowers/state.json', reason: 'state requires subcommand advance' }],
|
|
156
|
+
findings: [{ id: 'state-action-required', severity: 'error', artifact: '.godpowers/state.json', reason: 'state requires subcommand advance' }],
|
|
157
|
+
summary: { updated: false, state: '.godpowers/state.json', views: ['.godpowers/PROGRESS.md'] }
|
|
158
|
+
};
|
|
159
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
160
|
+
else console.log(stateAdvance.render(result));
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const result = stateAdvance.advance(opts.project, {
|
|
166
|
+
step: opts.step,
|
|
167
|
+
status: opts.status
|
|
168
|
+
});
|
|
169
|
+
if (opts.json) {
|
|
170
|
+
console.log(JSON.stringify(result, null, 2));
|
|
171
|
+
} else {
|
|
172
|
+
console.log(stateAdvance.render(result));
|
|
173
|
+
}
|
|
174
|
+
if (stateAdvance.exitCode(result) !== 0) {
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
143
179
|
const COMMAND_RUNNERS = {
|
|
144
180
|
status: runDashboardCommand,
|
|
145
181
|
next: runDashboardCommand,
|
|
182
|
+
state: runStateCommand,
|
|
146
183
|
'quick-proof': runQuickProofCommand,
|
|
147
184
|
'mcp-info': runMcpInfoCommand,
|
|
148
185
|
'automation-status': runAutomationCommand,
|
|
@@ -170,5 +207,6 @@ module.exports = {
|
|
|
170
207
|
runQuickProofCommand,
|
|
171
208
|
runMcpInfoCommand,
|
|
172
209
|
runExtensionScaffoldCommand,
|
|
173
|
-
runGateCommand
|
|
210
|
+
runGateCommand,
|
|
211
|
+
runStateCommand
|
|
174
212
|
};
|
package/lib/context-writer.js
CHANGED
|
@@ -83,13 +83,13 @@ function buildCanonicalContent(state, opts = {}) {
|
|
|
83
83
|
const scale = (state && (state.scale || (state.project && state.project.scale))) || opts.scale || 'unknown';
|
|
84
84
|
lines.push(`- Project: ${projectName}`);
|
|
85
85
|
lines.push(`- Mode: ${mode} Scale: ${scale}`);
|
|
86
|
-
lines.push('- State: `.godpowers/state.json`
|
|
86
|
+
lines.push('- State: `.godpowers/state.json` is authority; `.godpowers/PROGRESS.md` is generated for humans');
|
|
87
87
|
lines.push('');
|
|
88
88
|
|
|
89
89
|
lines.push('### Quarterback rule');
|
|
90
90
|
lines.push('');
|
|
91
|
-
lines.push('There is exactly one orchestrator: `god-orchestrator`. It owns
|
|
92
|
-
lines.push('`state.json`, `
|
|
91
|
+
lines.push('There is exactly one orchestrator: `god-orchestrator`. It owns writes to');
|
|
92
|
+
lines.push('`state.json`, `intent.yaml`, and `events.jsonl`; `PROGRESS.md` is regenerated from state. Skills like');
|
|
93
93
|
lines.push('`/god`, `/god-next`, `/god-status` read state without writing.');
|
|
94
94
|
lines.push('');
|
|
95
95
|
|
|
@@ -148,7 +148,7 @@ function buildCanonicalContent(state, opts = {}) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
lines.push('See `.godpowers/PROGRESS.md` for the
|
|
151
|
+
lines.push('See `.godpowers/state.json` for authority and `.godpowers/PROGRESS.md` for the generated tier table.');
|
|
152
152
|
return lines.join('\n');
|
|
153
153
|
}
|
|
154
154
|
|
package/lib/gate.js
CHANGED
|
@@ -11,6 +11,7 @@ const path = require('path');
|
|
|
11
11
|
const artifactMap = require('./artifact-map');
|
|
12
12
|
const linter = require('./artifact-linter');
|
|
13
13
|
const router = require('./router');
|
|
14
|
+
const stateStore = require('./state');
|
|
14
15
|
|
|
15
16
|
function relToAbs(projectRoot, relPath) {
|
|
16
17
|
return path.join(projectRoot, relPath);
|
|
@@ -168,12 +169,108 @@ function extractCommandStatuses(text) {
|
|
|
168
169
|
return entries;
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
function commandName(entry) {
|
|
173
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
174
|
+
const value = entry.command || entry.cmd || entry.name;
|
|
175
|
+
return value ? String(value).trim() : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function normalizeVerificationStatus(entry) {
|
|
179
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
180
|
+
const raw = entry.status || entry.result || entry.verdict;
|
|
181
|
+
if (raw) {
|
|
182
|
+
const text = String(raw).trim().toLowerCase();
|
|
183
|
+
if (/^(pass|passed|green|success|succeeded|ok)$/.test(text)) return 'pass';
|
|
184
|
+
if (/^(fail|failed|red|error)$/.test(text)) return 'fail';
|
|
185
|
+
}
|
|
186
|
+
if (Number.isInteger(entry.exitCode)) return entry.exitCode === 0 ? 'pass' : 'fail';
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function stateVerificationCommands(subStep) {
|
|
191
|
+
if (!subStep || typeof subStep !== 'object') return [];
|
|
192
|
+
const verification = subStep.verification && typeof subStep.verification === 'object'
|
|
193
|
+
? subStep.verification
|
|
194
|
+
: {};
|
|
195
|
+
const commands = verification.commands ||
|
|
196
|
+
subStep.verificationCommands ||
|
|
197
|
+
subStep['verification-commands'] ||
|
|
198
|
+
[];
|
|
199
|
+
return Array.isArray(commands) ? commands : [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function commandsWithStatus(subStep, wantedStatus) {
|
|
203
|
+
const commands = [];
|
|
204
|
+
for (const entry of stateVerificationCommands(subStep)) {
|
|
205
|
+
const name = commandName(entry);
|
|
206
|
+
if (!name) continue;
|
|
207
|
+
if (normalizeVerificationStatus(entry) !== wantedStatus) continue;
|
|
208
|
+
if (!commands.includes(name)) commands.push(name);
|
|
209
|
+
}
|
|
210
|
+
return commands;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function checkStateStepEvidence(projectRoot, tier, result) {
|
|
214
|
+
const stepRef = artifactMap.stateStepForTier(tier);
|
|
215
|
+
if (!stepRef) return null;
|
|
216
|
+
|
|
217
|
+
const relPath = '.godpowers/state.json';
|
|
218
|
+
const currentState = stateStore.read(projectRoot);
|
|
219
|
+
if (!currentState) {
|
|
220
|
+
const finding = makeFinding(
|
|
221
|
+
`state:${tier}:missing`,
|
|
222
|
+
'error',
|
|
223
|
+
relPath,
|
|
224
|
+
`${tier} gate requires structured state evidence in state.json.`
|
|
225
|
+
);
|
|
226
|
+
result.findings.push(finding);
|
|
227
|
+
addFindingSummary(result.summary, finding.severity);
|
|
228
|
+
result.checks.push(makeCheck(`state:${tier}:status`, 'fail', relPath, finding.reason));
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const tierState = currentState.tiers && currentState.tiers[stepRef.tierKey];
|
|
233
|
+
const subStep = tierState && tierState[stepRef.subStepKey];
|
|
234
|
+
if (!subStep) {
|
|
235
|
+
const finding = makeFinding(
|
|
236
|
+
`state:${tier}:step-missing`,
|
|
237
|
+
'error',
|
|
238
|
+
relPath,
|
|
239
|
+
`state.json does not record ${stepRef.tierKey}.${stepRef.subStepKey}.`
|
|
240
|
+
);
|
|
241
|
+
result.findings.push(finding);
|
|
242
|
+
addFindingSummary(result.summary, finding.severity);
|
|
243
|
+
result.checks.push(makeCheck(`state:${tier}:status`, 'fail', relPath, finding.reason));
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const status = subStep.status || 'pending';
|
|
248
|
+
const complete = stateStore.isCompleteStatus(status);
|
|
249
|
+
if (!complete) {
|
|
250
|
+
const finding = makeFinding(
|
|
251
|
+
`state:${tier}:incomplete`,
|
|
252
|
+
'error',
|
|
253
|
+
relPath,
|
|
254
|
+
`${stepRef.tierKey}.${stepRef.subStepKey} status is ${status}, expected a complete status.`
|
|
255
|
+
);
|
|
256
|
+
result.findings.push(finding);
|
|
257
|
+
addFindingSummary(result.summary, finding.severity);
|
|
258
|
+
}
|
|
259
|
+
result.checks.push(makeCheck(
|
|
260
|
+
`state:${tier}:status`,
|
|
261
|
+
complete ? 'pass' : 'fail',
|
|
262
|
+
relPath,
|
|
263
|
+
complete
|
|
264
|
+
? `${stepRef.tierKey}.${stepRef.subStepKey} records complete status ${status}.`
|
|
265
|
+
: `${stepRef.tierKey}.${stepRef.subStepKey} must be complete before this gate passes.`
|
|
266
|
+
));
|
|
267
|
+
return subStep;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function checkBuildEvidence(result, buildStep) {
|
|
271
|
+
const relPath = '.godpowers/state.json';
|
|
272
|
+
if (!buildStep) return;
|
|
273
|
+
const failedCommands = commandsWithStatus(buildStep, 'fail');
|
|
177
274
|
if (failedCommands.length > 0) {
|
|
178
275
|
const finding = makeFinding(
|
|
179
276
|
'build-verification-failed-command',
|
|
@@ -192,7 +289,7 @@ function checkBuildEvidence(projectRoot, result) {
|
|
|
192
289
|
result.summary.buildVerificationFailedCommands = failedCommands;
|
|
193
290
|
return;
|
|
194
291
|
}
|
|
195
|
-
const passedCommands =
|
|
292
|
+
const passedCommands = commandsWithStatus(buildStep, 'pass');
|
|
196
293
|
if (passedCommands.length === 0) {
|
|
197
294
|
const finding = makeFinding(
|
|
198
295
|
'build-verification-evidence',
|
|
@@ -214,7 +311,7 @@ function checkBuildEvidence(projectRoot, result) {
|
|
|
214
311
|
'build-verification-evidence',
|
|
215
312
|
'pass',
|
|
216
313
|
relPath,
|
|
217
|
-
`
|
|
314
|
+
`state.json records ${passedCommands.length} passed build verification command(s).`
|
|
218
315
|
));
|
|
219
316
|
result.summary.buildVerificationCommands = passedCommands;
|
|
220
317
|
}
|
|
@@ -280,7 +377,8 @@ function check(opts = {}) {
|
|
|
280
377
|
}
|
|
281
378
|
|
|
282
379
|
checkArtifacts(projectRoot, tier, artifacts, opts, result);
|
|
283
|
-
|
|
380
|
+
const stateStep = checkStateStepEvidence(projectRoot, tier, result);
|
|
381
|
+
if (tier === 'build') checkBuildEvidence(result, stateStep);
|
|
284
382
|
if (tier === 'harden') checkHardenCriticals(projectRoot, result);
|
|
285
383
|
return finalize(result);
|
|
286
384
|
}
|
package/lib/installer-args.js
CHANGED
|
@@ -4,6 +4,7 @@ const { RUNTIMES } = require('./installer-runtimes');
|
|
|
4
4
|
const COMMANDS = new Set([
|
|
5
5
|
'status',
|
|
6
6
|
'next',
|
|
7
|
+
'state',
|
|
7
8
|
'quick-proof',
|
|
8
9
|
'mcp-info',
|
|
9
10
|
'automation-status',
|
|
@@ -20,6 +21,9 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
20
21
|
project: cwd,
|
|
21
22
|
json: false,
|
|
22
23
|
brief: false,
|
|
24
|
+
stateAction: null,
|
|
25
|
+
step: null,
|
|
26
|
+
status: null,
|
|
23
27
|
extensionName: null,
|
|
24
28
|
extensionOutput: cwd,
|
|
25
29
|
extensionSkill: null,
|
|
@@ -41,6 +45,10 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
41
45
|
opts.command = arg;
|
|
42
46
|
continue;
|
|
43
47
|
}
|
|
48
|
+
if (opts.command === 'state' && !opts.stateAction && !arg.startsWith('-')) {
|
|
49
|
+
opts.stateAction = arg;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
44
52
|
|
|
45
53
|
switch (arg) {
|
|
46
54
|
case '--json':
|
|
@@ -55,6 +63,18 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
55
63
|
i++;
|
|
56
64
|
}
|
|
57
65
|
break;
|
|
66
|
+
case '--step':
|
|
67
|
+
if (args[i + 1]) {
|
|
68
|
+
opts.step = args[i + 1];
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case '--status':
|
|
73
|
+
if (args[i + 1]) {
|
|
74
|
+
opts.status = args[i + 1];
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
58
78
|
case '--project':
|
|
59
79
|
if (args[i + 1]) {
|
|
60
80
|
opts.project = path.resolve(args[i + 1]);
|
|
@@ -104,6 +124,10 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
104
124
|
opts.extensionWorkflow = arg.slice('--workflow='.length);
|
|
105
125
|
} else if (arg.startsWith('--tier=')) {
|
|
106
126
|
opts.tier = arg.slice('--tier='.length);
|
|
127
|
+
} else if (arg.startsWith('--step=')) {
|
|
128
|
+
opts.step = arg.slice('--step='.length);
|
|
129
|
+
} else if (arg.startsWith('--status=')) {
|
|
130
|
+
opts.status = arg.slice('--status='.length);
|
|
107
131
|
} else if (arg.startsWith('--profile=')) {
|
|
108
132
|
opts.profile = arg.slice('--profile='.length);
|
|
109
133
|
} else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
package/lib/pillars.js
CHANGED
|
@@ -96,8 +96,7 @@ const ARTIFACT_PILLAR_MAP = [
|
|
|
96
96
|
{ pattern: /(^|\/)stack\/DECISION\.md$/i, pillars: ['stack'] },
|
|
97
97
|
{ pattern: /(^|\/)roadmap\/ROADMAP\.md$/i, pillars: ['context', 'quality'] },
|
|
98
98
|
{ pattern: /(^|\/)build\/PLAN\.md$/i, pillars: ['quality', 'repo'] },
|
|
99
|
-
{ pattern:
|
|
100
|
-
{ pattern: /(^|\/)observe\/STATE\.md$/i, pillars: ['observe'] },
|
|
99
|
+
{ pattern: /^\.godpowers\/state\.json$/i, pillars: ['context', 'deploy', 'observe'] },
|
|
101
100
|
{ pattern: /(^|\/)harden\/FINDINGS\.md$/i, pillars: ['security', 'auth'] },
|
|
102
101
|
{ pattern: /(^|\/)design\/DESIGN\.md$/i, pillars: ['ui'] },
|
|
103
102
|
{ pattern: /(^|\/)design\/PRODUCT\.md$/i, pillars: ['context', 'ui'] }
|
|
@@ -109,8 +108,7 @@ const GODPOWERS_ARTIFACTS = [
|
|
|
109
108
|
'.godpowers/stack/DECISION.md',
|
|
110
109
|
'.godpowers/roadmap/ROADMAP.md',
|
|
111
110
|
'.godpowers/build/PLAN.md',
|
|
112
|
-
'.godpowers/
|
|
113
|
-
'.godpowers/observe/STATE.md',
|
|
111
|
+
'.godpowers/state.json',
|
|
114
112
|
'.godpowers/harden/FINDINGS.md',
|
|
115
113
|
'.godpowers/design/DESIGN.md',
|
|
116
114
|
'.godpowers/design/PRODUCT.md'
|
package/lib/recipes.js
CHANGED
|
@@ -112,6 +112,14 @@ function evaluateStateCondition(condition, projectRoot) {
|
|
|
112
112
|
if (cond.startsWith('file:')) {
|
|
113
113
|
return fs.existsSync(path.join(projectRoot, cond.slice(5).trim()));
|
|
114
114
|
}
|
|
115
|
+
if (cond.startsWith('state:')) {
|
|
116
|
+
const m = cond.slice(6).trim().match(/^([\w.-]+)\s*==\s*(.+)$/);
|
|
117
|
+
if (!m) return true;
|
|
118
|
+
const [, dottedPath, expected] = m;
|
|
119
|
+
const s = state.read(projectRoot);
|
|
120
|
+
const actual = state.valueAtPath(s, dottedPath);
|
|
121
|
+
return actual === expected.trim() || actual === parseValue(expected.trim());
|
|
122
|
+
}
|
|
115
123
|
if (cond.startsWith('lifecycle-phase ==')) {
|
|
116
124
|
const expected = cond.split('==')[1].trim();
|
|
117
125
|
const s = state.read(projectRoot);
|
|
@@ -131,6 +139,14 @@ function evaluateStateCondition(condition, projectRoot) {
|
|
|
131
139
|
return true;
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
function parseValue(value) {
|
|
143
|
+
if (value === 'true') return true;
|
|
144
|
+
if (value === 'false') return false;
|
|
145
|
+
if (value === 'null') return null;
|
|
146
|
+
if (/^\d+$/.test(value)) return parseInt(value, 10);
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
|
|
134
150
|
/**
|
|
135
151
|
* Suggest top recipes based on current project state alone (no intent text).
|
|
136
152
|
*/
|
package/lib/router.js
CHANGED
|
@@ -119,11 +119,7 @@ function evaluateCheck(check, projectRoot) {
|
|
|
119
119
|
if (!match) return false;
|
|
120
120
|
const [, dottedPath, expected] = match;
|
|
121
121
|
const s = state.read(projectRoot);
|
|
122
|
-
|
|
123
|
-
const actual = dottedPath.split('.').reduce((acc, k) => {
|
|
124
|
-
if (!acc || k === '__proto__' || k === 'constructor' || k === 'prototype') return undefined;
|
|
125
|
-
return acc[k];
|
|
126
|
-
}, s.tiers || s);
|
|
122
|
+
const actual = state.valueAtPath(s, dottedPath);
|
|
127
123
|
return actual === expected || actual === parseValue(expected);
|
|
128
124
|
}
|
|
129
125
|
|
package/lib/source-sync.js
CHANGED
|
@@ -131,7 +131,7 @@ function progressLines(projectRoot) {
|
|
|
131
131
|
lines.push(summarizeArtifact(projectRoot, '.godpowers/arch/ARCH.md', 'Architecture'));
|
|
132
132
|
lines.push(summarizeArtifact(projectRoot, '.godpowers/roadmap/ROADMAP.md', 'Roadmap'));
|
|
133
133
|
lines.push(summarizeArtifact(projectRoot, '.godpowers/stack/DECISION.md', 'Stack'));
|
|
134
|
-
lines.push(summarizeArtifact(projectRoot, '.godpowers/
|
|
134
|
+
lines.push(summarizeArtifact(projectRoot, '.godpowers/state.json', 'Godpowers state'));
|
|
135
135
|
lines.push('');
|
|
136
136
|
lines.push('## Return Path');
|
|
137
137
|
lines.push('');
|