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.
Files changed (72) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +26 -22
  3. package/RELEASE.md +38 -35
  4. package/SKILL.md +46 -48
  5. package/agents/god-deploy-engineer.md +2 -2
  6. package/agents/god-designer.md +3 -2
  7. package/agents/god-greenfieldifier.md +2 -4
  8. package/agents/god-launch-strategist.md +4 -5
  9. package/agents/god-observability-engineer.md +5 -5
  10. package/agents/god-reconciler.md +10 -4
  11. package/agents/god-retrospective.md +1 -1
  12. package/agents/god-updater.md +5 -5
  13. package/bin/install.js +6 -1
  14. package/fixtures/gate/build-pass/.godpowers/state.json +33 -0
  15. package/lib/README.md +2 -0
  16. package/lib/artifact-map.js +15 -3
  17. package/lib/cli-dispatch.js +39 -1
  18. package/lib/context-writer.js +4 -4
  19. package/lib/gate.js +107 -9
  20. package/lib/installer-args.js +24 -0
  21. package/lib/pillars.js +2 -4
  22. package/lib/recipes.js +16 -0
  23. package/lib/router.js +1 -5
  24. package/lib/source-sync.js +1 -1
  25. package/lib/state-advance.js +244 -0
  26. package/lib/state-lock.js +8 -4
  27. package/lib/state-views.js +460 -0
  28. package/lib/state.js +52 -3
  29. package/package.json +1 -1
  30. package/routing/god-audit.yaml +1 -1
  31. package/routing/god-build.yaml +1 -1
  32. package/routing/god-context.yaml +1 -1
  33. package/routing/god-deploy.yaml +3 -1
  34. package/routing/god-design.yaml +2 -2
  35. package/routing/god-launch.yaml +4 -1
  36. package/routing/god-migrate.yaml +0 -1
  37. package/routing/god-mode.yaml +1 -1
  38. package/routing/god-observe.yaml +4 -1
  39. package/routing/god-prd.yaml +1 -1
  40. package/routing/god-reconcile.yaml +2 -5
  41. package/routing/god-sync.yaml +1 -1
  42. package/routing/recipes/returning-after-break.yaml +1 -1
  43. package/schema/state.v1.json +68 -1
  44. package/skills/god-arch.md +1 -1
  45. package/skills/god-build.md +6 -4
  46. package/skills/god-deploy.md +16 -14
  47. package/skills/god-design.md +3 -3
  48. package/skills/god-fast.md +2 -2
  49. package/skills/god-feature.md +1 -1
  50. package/skills/god-harden.md +3 -3
  51. package/skills/god-hotfix.md +1 -1
  52. package/skills/god-init.md +14 -10
  53. package/skills/god-launch.md +14 -12
  54. package/skills/god-lifecycle.md +2 -1
  55. package/skills/god-mode.md +5 -4
  56. package/skills/god-observe.md +15 -13
  57. package/skills/god-pause-work.md +2 -2
  58. package/skills/god-prd.md +5 -4
  59. package/skills/god-quick.md +1 -1
  60. package/skills/god-repo.md +1 -1
  61. package/skills/god-resume-work.md +5 -4
  62. package/skills/god-roadmap-update.md +1 -1
  63. package/skills/god-roadmap.md +1 -1
  64. package/skills/god-rollback.md +1 -1
  65. package/skills/god-skip.md +2 -2
  66. package/skills/god-stack.md +1 -1
  67. package/skills/god-standards.md +1 -1
  68. package/skills/god-status.md +2 -2
  69. package/skills/god-story.md +1 -1
  70. package/skills/god-sync.md +2 -2
  71. package/workflows/bluefield-arc.yaml +2 -4
  72. package/workflows/brownfield-arc.yaml +2 -4
@@ -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 deploy/STATE.md
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 observe/STATE.md
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 launch/STATE.md
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/STATE.md: added STRIPE_WEBHOOK_SECRET env var
264
- - observe/STATE.md: added SLO for /api/stripe-webhook (99.5%)
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. |
@@ -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/design/STATE.md', required: true, lint: true }
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/build/STATE.md', required: true, lint: true }
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
  };
@@ -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
  };
@@ -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` (machine) and `.godpowers/PROGRESS.md` (human)');
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 all writes to');
92
- lines.push('`state.json`, `PROGRESS.md`, `intent.yaml`, and `events.jsonl`. Skills like');
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 live tier table.');
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 checkBuildEvidence(projectRoot, result) {
172
- const relPath = '.godpowers/build/STATE.md';
173
- const file = relToAbs(projectRoot, relPath);
174
- if (!fs.existsSync(file)) return;
175
- const text = fs.readFileSync(file, 'utf8');
176
- const failedCommands = extractFailedCommands(text);
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 = extractPassedCommands(text);
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
- `Build state records ${passedCommands.length} passed verification command(s).`
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
- if (tier === 'build') checkBuildEvidence(projectRoot, result);
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
  }
@@ -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: /(^|\/)deploy\/STATE\.md$/i, pillars: ['deploy'] },
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/deploy/STATE.md',
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
- if (!s) return false;
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
 
@@ -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/build/STATE.md', 'Build state'));
134
+ lines.push(summarizeArtifact(projectRoot, '.godpowers/state.json', 'Godpowers state'));
135
135
  lines.push('');
136
136
  lines.push('## Return Path');
137
137
  lines.push('');