godpowers 3.13.1 → 3.14.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 +102 -0
- package/README.md +71 -77
- package/RELEASE.md +21 -21
- package/bin/install.js +32 -8
- package/lib/README.md +1 -0
- package/lib/checkpoint.js +5 -1
- package/lib/cli-dispatch.js +20 -5
- package/lib/command-families.js +8 -1
- package/lib/dashboard.js +5 -5
- package/lib/evidence.js +14 -5
- package/lib/have-nots-validator.js +5 -1
- package/lib/intent.js +9 -4
- package/lib/pillars.js +13 -0
- package/lib/planning-systems.js +1 -6
- package/lib/recipe-coverage-sync.js +3 -12
- package/lib/release-surface-sync.js +3 -12
- package/lib/repo-surface-sync.js +3 -31
- package/lib/requirements.js +4 -12
- package/lib/reverse-sync.js +9 -2
- package/lib/route-quality-sync.js +3 -21
- package/lib/source-sync.js +0 -4
- package/lib/state.js +6 -0
- package/lib/sync-check.js +56 -0
- package/lib/sync-fs.js +13 -1
- package/lib/work-report.js +2 -0
- package/package.json +3 -3
- package/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +10 -3
- package/routing/recipes/bug-no-urgency.yaml +4 -0
- package/routing/recipes/release-maintenance.yaml +6 -0
- package/routing/recipes/whats-done.yaml +3 -0
- package/skills/god-org-context.md +1 -1
- package/skills/god-reconcile.md +4 -2
- package/skills/god-smite.md +1 -1
- package/skills/god-sync.md +4 -1
package/lib/evidence.js
CHANGED
|
@@ -195,6 +195,10 @@ function appendJsonlAtomic(file, record) {
|
|
|
195
195
|
return file;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// Reads the whole ledger into memory. This is bounded and acceptable for a CLI:
|
|
199
|
+
// each record caps its stdout/stderr tails (TAIL_CHARS) so growth is slow, and
|
|
200
|
+
// every consumer reads it once per command, never in a loop. If a long-lived
|
|
201
|
+
// project's ledger ever grows large, add an opt-in prune/size cap here (PERF-002).
|
|
198
202
|
function readJsonl(file) {
|
|
199
203
|
let raw;
|
|
200
204
|
try {
|
|
@@ -542,11 +546,16 @@ function history(opts = {}) {
|
|
|
542
546
|
}
|
|
543
547
|
|
|
544
548
|
/**
|
|
545
|
-
* The
|
|
546
|
-
* a substep may close to done only when evidence bound to it since
|
|
547
|
-
* in-flight supports the close. This is a read-only predicate. It does
|
|
548
|
-
* mutate state and is NOT
|
|
549
|
-
* is
|
|
549
|
+
* The advisory close-freshness check, rebound from Mythify's completion rule
|
|
550
|
+
* (cmd_step): a substep may close to done only when evidence bound to it since
|
|
551
|
+
* it went in-flight supports the close. This is a read-only predicate. It does
|
|
552
|
+
* NOT mutate state and is NOT wired into gate.js or the close path; that wiring
|
|
553
|
+
* is a deliberate behavior change tracked as the rest of Phase 1. Until then it
|
|
554
|
+
* is advisory discipline the orchestrator runs (via `can-close`), NOT the
|
|
555
|
+
* mechanically enforced gate. The enforced gate is `gate.js`
|
|
556
|
+
* (`npx godpowers gate`), which checks recorded pass/fail evidence but not the
|
|
557
|
+
* since-in-flight freshness this predicate adds. Treat the two as distinct:
|
|
558
|
+
* `gate` is the mechanical boundary, `can-close` is the stricter advisory one.
|
|
550
559
|
*
|
|
551
560
|
* Tier-appropriate (docs/FUSION-ARCHITECTURE.md section 4.2):
|
|
552
561
|
* - Executable-gated substeps (build, deploy, harden) require the latest
|
|
@@ -53,9 +53,13 @@ const LABEL_TAGS = ['DECISION', 'HYPOTHESIS', 'OPEN QUESTION', 'OPEN-QUESTION'];
|
|
|
53
53
|
function findPositions(content, regex) {
|
|
54
54
|
const positions = [];
|
|
55
55
|
const lines = content.split('\n');
|
|
56
|
+
// Compile once and reuse across lines (PERF-001); reset lastIndex per line so
|
|
57
|
+
// matching is identical to a fresh per-line regex.
|
|
58
|
+
const flags = regex.flags.includes('g') ? regex.flags : regex.flags + 'g';
|
|
59
|
+
const localRegex = new RegExp(regex.source, flags);
|
|
56
60
|
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
localRegex.lastIndex = 0;
|
|
57
62
|
let match;
|
|
58
|
-
const localRegex = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g');
|
|
59
63
|
while ((match = localRegex.exec(lines[i])) !== null) {
|
|
60
64
|
positions.push({ line: i + 1, column: match.index + 1, matched: match[0] });
|
|
61
65
|
}
|
package/lib/intent.js
CHANGED
|
@@ -332,15 +332,20 @@ function splitInlineArray(text) {
|
|
|
332
332
|
return parts;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
// Far beyond any legitimate config nesting; caps recursion so a hostile,
|
|
336
|
+
// thousands-deep YAML file cannot overflow the stack (SEC-002).
|
|
337
|
+
const MAX_CLEAN_DEPTH = 200;
|
|
338
|
+
|
|
339
|
+
function cleanArrays(obj, depth = 0) {
|
|
340
|
+
if (depth > MAX_CLEAN_DEPTH) return obj;
|
|
341
|
+
if (Array.isArray(obj)) return obj.map((v) => cleanArrays(v, depth + 1));
|
|
337
342
|
if (obj && typeof obj === 'object') {
|
|
338
343
|
// Detect array container (legacy or new)
|
|
339
|
-
if (obj.__items__) return obj.__items__.map(cleanArrays);
|
|
344
|
+
if (obj.__items__) return obj.__items__.map((v) => cleanArrays(v, depth + 1));
|
|
340
345
|
if (obj.__pending_array__) return obj.__pending_array__;
|
|
341
346
|
const cleaned = {};
|
|
342
347
|
for (const [k, v] of Object.entries(obj)) {
|
|
343
|
-
cleaned[k] = cleanArrays(v);
|
|
348
|
+
cleaned[k] = cleanArrays(v, depth + 1);
|
|
344
349
|
}
|
|
345
350
|
return cleaned;
|
|
346
351
|
}
|
package/lib/pillars.js
CHANGED
|
@@ -114,6 +114,13 @@ const GODPOWERS_ARTIFACTS = [
|
|
|
114
114
|
'.godpowers/design/PRODUCT.md'
|
|
115
115
|
];
|
|
116
116
|
|
|
117
|
+
// ===========================================================================
|
|
118
|
+
// Pillar model: parse pillar files, detect installed pillars, compute the
|
|
119
|
+
// per-task load set, and construct/initialize pillar files. Shared with the
|
|
120
|
+
// artifact-sync workflow below (init/ensurePillar/pillarStub/detect are also
|
|
121
|
+
// part of the public API).
|
|
122
|
+
// ===========================================================================
|
|
123
|
+
|
|
117
124
|
function stripQuotes(value) {
|
|
118
125
|
return String(value).trim().replace(/^['"]|['"]$/g, '');
|
|
119
126
|
}
|
|
@@ -401,6 +408,12 @@ function writeFenced(filePath, begin, end, content) {
|
|
|
401
408
|
fs.writeFileSync(filePath, next);
|
|
402
409
|
}
|
|
403
410
|
|
|
411
|
+
// ===========================================================================
|
|
412
|
+
// Artifact-sync workflow: turn Godpowers artifacts (PRD/ARCH/...) into durable
|
|
413
|
+
// pillar signals and write them into the routed pillar files. Builds on the
|
|
414
|
+
// model above (init/ensurePillar/pillarStub/detect/buildProtocolContent).
|
|
415
|
+
// ===========================================================================
|
|
416
|
+
|
|
404
417
|
function artifactToPillars(artifactPath) {
|
|
405
418
|
const normalized = artifactPath.replace(/\\/g, '/');
|
|
406
419
|
const pillars = [];
|
package/lib/planning-systems.js
CHANGED
|
@@ -100,10 +100,6 @@ function ensureDir(filePath) {
|
|
|
100
100
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function sha(input) {
|
|
104
|
-
return crypto.createHash('sha256').update(input).digest('hex');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
103
|
function hashFiles(projectRoot, files) {
|
|
108
104
|
const h = crypto.createHash('sha256');
|
|
109
105
|
for (const file of files.map((f) => f.path).sort()) {
|
|
@@ -473,7 +469,6 @@ module.exports = {
|
|
|
473
469
|
_private: {
|
|
474
470
|
classifyFile,
|
|
475
471
|
extractSignals,
|
|
476
|
-
filesForKinds
|
|
477
|
-
sha
|
|
472
|
+
filesForKinds
|
|
478
473
|
}
|
|
479
474
|
};
|
|
@@ -10,6 +10,9 @@ const path = require('path');
|
|
|
10
10
|
|
|
11
11
|
const recipes = require('./recipes');
|
|
12
12
|
const { read, write } = require('./sync-fs');
|
|
13
|
+
const { makeAddCheck } = require('./sync-check');
|
|
14
|
+
|
|
15
|
+
const addCheck = makeAddCheck('recipe-coverage');
|
|
13
16
|
|
|
14
17
|
const LOG_PATH = '.godpowers/surface/RECIPE-COVERAGE-SYNC.md';
|
|
15
18
|
|
|
@@ -42,18 +45,6 @@ const REQUIRED_COVERAGE = [
|
|
|
42
45
|
];
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
46
|
-
checks.push({
|
|
47
|
-
area: 'recipe-coverage',
|
|
48
|
-
id,
|
|
49
|
-
status,
|
|
50
|
-
path: relPath,
|
|
51
|
-
message,
|
|
52
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
53
|
-
spawn: opts.spawn || null
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
48
|
function recipePath(projectRoot, name) {
|
|
58
49
|
const rel = `routing/recipes/${name}.yaml`;
|
|
59
50
|
return fs.existsSync(path.join(projectRoot, rel)) ? rel : 'routing/recipes/';
|
|
@@ -10,6 +10,9 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
12
|
const { read, write, readJson } = require('./sync-fs');
|
|
13
|
+
const { makeAddCheck } = require('./sync-check');
|
|
14
|
+
|
|
15
|
+
const addCheck = makeAddCheck('release-surface');
|
|
13
16
|
|
|
14
17
|
const LOG_PATH = '.godpowers/surface/RELEASE-SURFACE-SYNC.md';
|
|
15
18
|
|
|
@@ -46,18 +49,6 @@ function releaseGateText(projectRoot, pkg) {
|
|
|
46
49
|
].join('\n');
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
50
|
-
checks.push({
|
|
51
|
-
area: 'release-surface',
|
|
52
|
-
id,
|
|
53
|
-
status,
|
|
54
|
-
path: relPath,
|
|
55
|
-
message,
|
|
56
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
57
|
-
spawn: opts.spawn || null
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
52
|
function detect(projectRoot) {
|
|
62
53
|
const checks = [];
|
|
63
54
|
const pkg = readJson(projectRoot, 'package.json') || {};
|
package/lib/repo-surface-sync.js
CHANGED
|
@@ -12,6 +12,7 @@ const path = require('path');
|
|
|
12
12
|
|
|
13
13
|
const { parseSimpleYaml } = require('./intent');
|
|
14
14
|
const { read, write, exists, readJson } = require('./sync-fs');
|
|
15
|
+
const { addCheck, listFiles } = require('./sync-check');
|
|
15
16
|
const extensions = require('./extensions');
|
|
16
17
|
const repoDocSync = require('./repo-doc-sync');
|
|
17
18
|
const routeQualitySync = require('./route-quality-sync');
|
|
@@ -52,19 +53,6 @@ const REQUIRED_PACKAGE_CHECKS = [
|
|
|
52
53
|
'routing/god-export-otel.yaml'
|
|
53
54
|
];
|
|
54
55
|
|
|
55
|
-
function rel(projectRoot, absPath) {
|
|
56
|
-
return path.relative(projectRoot, absPath).split(path.sep).join('/');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function listFiles(projectRoot, relDir, pattern) {
|
|
60
|
-
const dir = path.join(projectRoot, relDir);
|
|
61
|
-
if (!fs.existsSync(dir)) return [];
|
|
62
|
-
return fs.readdirSync(dir)
|
|
63
|
-
.filter((name) => pattern.test(name))
|
|
64
|
-
.sort()
|
|
65
|
-
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
56
|
function releaseGateText(projectRoot, pkg) {
|
|
69
57
|
return [
|
|
70
58
|
JSON.stringify((pkg && pkg.scripts) || {}),
|
|
@@ -81,19 +69,6 @@ function commandForSkill(skillPath) {
|
|
|
81
69
|
return `/${path.basename(skillPath, '.md')}`;
|
|
82
70
|
}
|
|
83
71
|
|
|
84
|
-
function addCheck(checks, area, id, status, relPath, message, opts = {}) {
|
|
85
|
-
checks.push({
|
|
86
|
-
area,
|
|
87
|
-
id,
|
|
88
|
-
status,
|
|
89
|
-
path: relPath,
|
|
90
|
-
message,
|
|
91
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
92
|
-
safeFix: opts.safeFix === true,
|
|
93
|
-
spawn: opts.spawn || null
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
72
|
function routingChecks(projectRoot) {
|
|
98
73
|
const checks = [];
|
|
99
74
|
const skills = listFiles(projectRoot, 'skills', /^god.*\.md$/);
|
|
@@ -485,15 +460,12 @@ function releasePolicyChecks(projectRoot) {
|
|
|
485
460
|
checks,
|
|
486
461
|
'release',
|
|
487
462
|
'release-checklist-surface-sync',
|
|
488
|
-
read(projectRoot, 'docs/RELEASE-CHECKLIST.md').includes('repo-surface-sync'),
|
|
463
|
+
read(projectRoot, 'docs/RELEASE-CHECKLIST.md').includes('repo-surface-sync') ? 'fresh' : 'stale',
|
|
489
464
|
'docs/RELEASE-CHECKLIST.md',
|
|
490
465
|
'Release checklist references repo-surface-sync readiness.',
|
|
491
466
|
{ spawn: 'god-docs-writer' }
|
|
492
467
|
);
|
|
493
|
-
return checks
|
|
494
|
-
...check,
|
|
495
|
-
status: check.status === true ? 'fresh' : (check.status === false ? 'stale' : check.status)
|
|
496
|
-
}));
|
|
468
|
+
return checks;
|
|
497
469
|
}
|
|
498
470
|
|
|
499
471
|
function detect(projectRoot) {
|
package/lib/requirements.js
CHANGED
|
@@ -33,9 +33,11 @@ const linkage = require('./linkage');
|
|
|
33
33
|
const state = require('./state');
|
|
34
34
|
const atomic = require('./atomic-write');
|
|
35
35
|
const textUtil = require('./text-util');
|
|
36
|
+
const artifactMap = require('./artifact-map');
|
|
37
|
+
const { readTextOrNull: readText } = require('./sync-fs');
|
|
36
38
|
|
|
37
|
-
const PRD_PATH = '
|
|
38
|
-
const ROADMAP_PATH = '
|
|
39
|
+
const PRD_PATH = artifactMap.requiredArtifactsForTier('prd')[0].path;
|
|
40
|
+
const ROADMAP_PATH = artifactMap.requiredArtifactsForTier('roadmap')[0].path;
|
|
39
41
|
const LEDGER_PATH = '.godpowers/REQUIREMENTS.md';
|
|
40
42
|
|
|
41
43
|
const PRIORITIES = ['MUST', 'SHOULD', 'COULD'];
|
|
@@ -44,16 +46,6 @@ const REQ_ID_RE_G = /\bP-(MUST|SHOULD|COULD)-\d+\b/g;
|
|
|
44
46
|
const MILESTONE_ID_RE = /\bM-[\w-]+\b/;
|
|
45
47
|
const LABEL_RE = /\[(?:DECISION|HYPOTHESIS|OPEN QUESTION)\]/g;
|
|
46
48
|
|
|
47
|
-
function readText(projectRoot, relPath) {
|
|
48
|
-
const file = path.join(projectRoot, relPath);
|
|
49
|
-
if (!fs.existsSync(file)) return null;
|
|
50
|
-
try {
|
|
51
|
-
return fs.readFileSync(file, 'utf8');
|
|
52
|
-
} catch (e) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
49
|
function pad2(n) {
|
|
58
50
|
return String(n).padStart(2, '0');
|
|
59
51
|
}
|
package/lib/reverse-sync.js
CHANGED
|
@@ -318,23 +318,29 @@ function run(projectRoot, opts = {}) {
|
|
|
318
318
|
// write the file when the PRD actually declares requirements, to avoid
|
|
319
319
|
// littering pre-PRD projects with an empty ledger.
|
|
320
320
|
let requirementsSummary = null;
|
|
321
|
+
let requirementsError = null;
|
|
321
322
|
if (opts.runRequirements !== false) {
|
|
322
323
|
try {
|
|
323
324
|
const derived = requirements.derive(projectRoot);
|
|
324
325
|
if (derived.hasRequirements) {
|
|
325
|
-
requirements.writeLedger(projectRoot, derived);
|
|
326
326
|
const currentState = state.read(projectRoot);
|
|
327
327
|
requirementsSummary = requirements.summarizeForState(
|
|
328
328
|
derived,
|
|
329
329
|
currentState && currentState.deliverables
|
|
330
330
|
);
|
|
331
|
+
// Write state.json first, then the ledger, so a state-write failure
|
|
332
|
+
// cannot leave a REQUIREMENTS.md ledger that state.json never references.
|
|
331
333
|
if (currentState) {
|
|
332
334
|
currentState.deliverables = requirementsSummary;
|
|
333
335
|
state.write(projectRoot, currentState);
|
|
334
336
|
}
|
|
337
|
+
requirements.writeLedger(projectRoot, derived);
|
|
335
338
|
}
|
|
336
339
|
} catch (e) {
|
|
340
|
+
// ERR-001: surface the failure instead of swallowing it, so the caller can
|
|
341
|
+
// tell a genuine requirements-step error apart from "no requirements".
|
|
337
342
|
requirementsSummary = null;
|
|
343
|
+
requirementsError = e.message;
|
|
338
344
|
}
|
|
339
345
|
}
|
|
340
346
|
|
|
@@ -346,7 +352,8 @@ function run(projectRoot, opts = {}) {
|
|
|
346
352
|
footers,
|
|
347
353
|
sourceSyncResult,
|
|
348
354
|
reviewItems,
|
|
349
|
-
requirements: requirementsSummary
|
|
355
|
+
requirements: requirementsSummary,
|
|
356
|
+
requirementsError
|
|
350
357
|
};
|
|
351
358
|
}
|
|
352
359
|
|
|
@@ -11,6 +11,9 @@ const path = require('path');
|
|
|
11
11
|
|
|
12
12
|
const { parseSimpleYaml } = require('./intent');
|
|
13
13
|
const { read, write } = require('./sync-fs');
|
|
14
|
+
const { makeAddCheck, listFiles } = require('./sync-check');
|
|
15
|
+
|
|
16
|
+
const addCheck = makeAddCheck('route-quality');
|
|
14
17
|
|
|
15
18
|
const LOG_PATH = '.godpowers/surface/ROUTE-QUALITY-SYNC.md';
|
|
16
19
|
const CONTEXTUAL_NEXT_VALUES = new Set([
|
|
@@ -101,15 +104,6 @@ const TIER_GATE_COMMANDS = new Set([
|
|
|
101
104
|
'/god-harden'
|
|
102
105
|
]);
|
|
103
106
|
|
|
104
|
-
function listFiles(projectRoot, relDir, pattern) {
|
|
105
|
-
const dir = path.join(projectRoot, relDir);
|
|
106
|
-
if (!fs.existsSync(dir)) return [];
|
|
107
|
-
return fs.readdirSync(dir)
|
|
108
|
-
.filter((name) => pattern.test(name))
|
|
109
|
-
.sort()
|
|
110
|
-
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
107
|
function arr(value) {
|
|
114
108
|
return Array.isArray(value) ? value : [];
|
|
115
109
|
}
|
|
@@ -122,18 +116,6 @@ function parseRoute(projectRoot, routePath) {
|
|
|
122
116
|
}
|
|
123
117
|
}
|
|
124
118
|
|
|
125
|
-
function addCheck(checks, id, status, relPath, message, opts = {}) {
|
|
126
|
-
checks.push({
|
|
127
|
-
area: 'route-quality',
|
|
128
|
-
id,
|
|
129
|
-
status,
|
|
130
|
-
path: relPath,
|
|
131
|
-
message,
|
|
132
|
-
severity: opts.severity || (status === 'fresh' ? 'info' : 'warning'),
|
|
133
|
-
spawn: opts.spawn || null
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
119
|
function spawnTokens(route) {
|
|
138
120
|
const execution = route.execution || {};
|
|
139
121
|
return normalizeSpawnList([
|
package/lib/source-sync.js
CHANGED
|
@@ -30,10 +30,6 @@ const SYSTEM_TARGETS = {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
function rel(projectRoot, absPath) {
|
|
34
|
-
return path.relative(projectRoot, absPath).split(path.sep).join('/');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
33
|
function sha(input) {
|
|
38
34
|
return `sha256:${crypto.createHash('sha256').update(input).digest('hex')}`;
|
|
39
35
|
}
|
package/lib/state.js
CHANGED
|
@@ -365,6 +365,11 @@ function progressSummary(state) {
|
|
|
365
365
|
const steps = orderedSubSteps(state);
|
|
366
366
|
const total = steps.length;
|
|
367
367
|
const completed = steps.filter(step => isCompleteStatus(step.status)).length;
|
|
368
|
+
// JRN-002: skipped/not-required steps count toward `completed` (and therefore
|
|
369
|
+
// the percent), so a run that skipped tiers can show a high percent that
|
|
370
|
+
// overstates how much was actually built. Expose the skipped count so the
|
|
371
|
+
// dashboard can annotate the percent honestly instead of silently inflating.
|
|
372
|
+
const skipped = steps.filter(step => step.status === 'skipped' || step.status === 'not-required').length;
|
|
368
373
|
|
|
369
374
|
let currentIndex = steps.findIndex(step => isActiveStatus(step.status));
|
|
370
375
|
if (currentIndex < 0) {
|
|
@@ -377,6 +382,7 @@ function progressSummary(state) {
|
|
|
377
382
|
percent: total === 0 ? 0 : Math.round((completed / total) * 100),
|
|
378
383
|
completed,
|
|
379
384
|
total,
|
|
385
|
+
skipped,
|
|
380
386
|
remaining: Math.max(total - completed, 0),
|
|
381
387
|
currentStep: current ? current.ordinal : 0,
|
|
382
388
|
current,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared check-builder and file-lister for the lib/*-sync.js family (ARC-001).
|
|
3
|
+
*
|
|
4
|
+
* The aggregator (repo-surface-sync) passes a per-call `area` and may mark a
|
|
5
|
+
* check `safeFix`, so it uses the full `addCheck`. The single-area sync modules
|
|
6
|
+
* (recipe-coverage, release-surface, route-quality) bind their area once via
|
|
7
|
+
* `makeAddCheck(area)`; their records intentionally omit `safeFix` (none of
|
|
8
|
+
* their checks are auto-fixable), matching the original per-module builders.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
function severityFor(status, opts) {
|
|
15
|
+
return opts.severity || (status === 'fresh' ? 'info' : 'warning');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Full form: caller supplies `area`; records include the `safeFix` flag.
|
|
19
|
+
function addCheck(checks, area, id, status, relPath, message, opts = {}) {
|
|
20
|
+
checks.push({
|
|
21
|
+
area,
|
|
22
|
+
id,
|
|
23
|
+
status,
|
|
24
|
+
path: relPath,
|
|
25
|
+
message,
|
|
26
|
+
severity: severityFor(status, opts),
|
|
27
|
+
safeFix: opts.safeFix === true,
|
|
28
|
+
spawn: opts.spawn || null
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Area-bound form for single-area modules; records omit `safeFix`.
|
|
33
|
+
function makeAddCheck(area) {
|
|
34
|
+
return function (checks, id, status, relPath, message, opts = {}) {
|
|
35
|
+
checks.push({
|
|
36
|
+
area,
|
|
37
|
+
id,
|
|
38
|
+
status,
|
|
39
|
+
path: relPath,
|
|
40
|
+
message,
|
|
41
|
+
severity: severityFor(status, opts),
|
|
42
|
+
spawn: opts.spawn || null
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function listFiles(projectRoot, relDir, pattern) {
|
|
48
|
+
const dir = path.join(projectRoot, relDir);
|
|
49
|
+
if (!fs.existsSync(dir)) return [];
|
|
50
|
+
return fs.readdirSync(dir)
|
|
51
|
+
.filter((name) => pattern.test(name))
|
|
52
|
+
.sort()
|
|
53
|
+
.map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { addCheck, makeAddCheck, listFiles };
|
package/lib/sync-fs.js
CHANGED
|
@@ -34,4 +34,16 @@ function readJson(projectRoot, relPath) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// Like read(), but returns null (not '') when the file is missing or unreadable,
|
|
38
|
+
// for callers that distinguish "absent" from "empty".
|
|
39
|
+
function readTextOrNull(projectRoot, relPath) {
|
|
40
|
+
const file = path.join(projectRoot, relPath);
|
|
41
|
+
if (!fs.existsSync(file)) return null;
|
|
42
|
+
try {
|
|
43
|
+
return fs.readFileSync(file, 'utf8');
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { read, write, exists, readJson, readTextOrNull };
|
package/lib/work-report.js
CHANGED
|
@@ -108,6 +108,8 @@ function render(result) {
|
|
|
108
108
|
lines.push(result.since === 'all'
|
|
109
109
|
? 'No verification records yet.'
|
|
110
110
|
: 'Nothing new since the last report.');
|
|
111
|
+
// CNT-006: do not dead-end; name what populates the ledger.
|
|
112
|
+
lines.push('Run "npx godpowers verify <command>" or "reflect --action ..." to add records.');
|
|
111
113
|
return lines.join('\n');
|
|
112
114
|
}
|
|
113
115
|
lines.push(`Since: ${result.since}${result.peek ? ' (peek, cursor not advanced)' : ''}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godpowers",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "AI-powered development system: 120 slash commands and 40 specialist agents that take a project from raw idea to hardened production. Runs inside Claude Code, Codex, Cursor, Windsurf, Gemini, and 10+ other AI coding tools.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"godpowers": "./bin/install.js"
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"test:e2e": "node tests/integration/full-arc.test.js",
|
|
25
25
|
"test:mcp": "npm --workspace @godpowers/mcp test",
|
|
26
26
|
"coverage": "c8 --reporter=text --reporter=lcov node scripts/run-tests.js",
|
|
27
|
-
"coverage:lib": "c8 --include=lib/**/*.js --check-coverage --lines 90 --branches 75 --reporter=text node scripts/run-tests.js",
|
|
27
|
+
"coverage:lib": "c8 --include=lib/**/*.js --check-coverage --lines 90 --branches 75 --reporter=text --reporter=json-summary node scripts/run-tests.js",
|
|
28
28
|
"test:audit": "npm audit --omit=dev && git diff --check && npm run test:surface",
|
|
29
29
|
"pack:check": "node scripts/check-package-contents.js",
|
|
30
30
|
"pack:mcp:check": "npm --workspace @godpowers/mcp run pack:check",
|
|
31
|
-
"release:check": "npm run coverage:lib && npm run test:audit && npm run pack:check && npm run pack:mcp:check",
|
|
31
|
+
"release:check": "npm run coverage:lib && node scripts/check-per-file-coverage.js && npm run test:audit && npm run pack:check && npm run pack:mcp:check",
|
|
32
32
|
"lint": "node scripts/static-check.js"
|
|
33
33
|
},
|
|
34
34
|
"workspaces": [
|
|
@@ -406,6 +406,9 @@ maker that fixes is never the checker that grades.
|
|
|
406
406
|
command and records the iteration. Repeat until the outcome succeeds or the
|
|
407
407
|
budget is exhausted.
|
|
408
408
|
- Never mark a finding resolved while `can-close` for its substep is red.
|
|
409
|
+
(`can-close` is the advisory since-in-flight freshness check you run as
|
|
410
|
+
discipline; the mechanically enforced gate is `npx godpowers gate`. Both
|
|
411
|
+
must agree before you close.)
|
|
409
412
|
4. **Re-audit.** Re-run `god-debt-assessor` and confirm findings are resolved,
|
|
410
413
|
not relocated, and that no Strength regressed. The loop is done when no
|
|
411
414
|
Confirmed Critical or High remains (or the agreed bucket is empty).
|
|
@@ -535,9 +538,13 @@ requested or final sign-off begins.
|
|
|
535
538
|
6. Verify their output exists on disk
|
|
536
539
|
7. Run have-nots check on the artifact and run `standards.gate-command` when configured
|
|
537
540
|
8. For an executable-gated sub-step (build, deploy, harden), record executed
|
|
538
|
-
evidence with `npx godpowers verify "<cmd>" --substep <tier.substep
|
|
539
|
-
|
|
540
|
-
|
|
541
|
+
evidence with `npx godpowers verify "<cmd>" --substep <tier.substep>`, confirm
|
|
542
|
+
the enforced gate passes with `npx godpowers gate --tier=<tier> --project=.`,
|
|
543
|
+
and then run the advisory freshness check
|
|
544
|
+
`npx godpowers can-close --substep <tier.substep> --project=.` (it must exit
|
|
545
|
+
zero). The gate is the mechanical boundary; can-close is the stricter
|
|
546
|
+
since-in-flight discipline. Never advance the sub-step to done while either
|
|
547
|
+
is red.
|
|
541
548
|
9. If pass and can-close is green: advance the sub-step to done via
|
|
542
549
|
`npx godpowers state advance`, sync CHECKPOINT.md, run the proactive
|
|
543
550
|
auto-invoke sweep, print the "Step result" card, then move to next sub-step
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: god-org-context
|
|
3
3
|
description: |
|
|
4
|
-
|
|
4
|
+
Set up or read organization-level context (bluefield support): shared
|
|
5
5
|
standards, conventions, infrastructure, libraries. Constrains downstream
|
|
6
6
|
agents to respect org-wide decisions when building new code in an
|
|
7
7
|
established context.
|
package/skills/god-reconcile.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: god-reconcile
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Check before feature work (read-only): reconcile all impacted artifacts to
|
|
5
|
+
find conflicts before you build. This is the BEFORE half of the pair; its
|
|
6
|
+
write-back counterpart is /god-sync, which updates artifacts AFTER the work.
|
|
7
|
+
Checks PRD, ARCH, ROADMAP, STACK, REPO, DEPLOY, OBSERVE,
|
|
6
8
|
HARDEN, LAUNCH, BACKLOG, SEEDS, TODOS, THREADS, repository documentation,
|
|
7
9
|
repository surface, runtime feature awareness, source-system sync-back, and
|
|
8
10
|
host capabilities in parallel. Replaces /god-roadmap-check (kept for
|
package/skills/god-smite.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: god-smite
|
|
3
3
|
description: |
|
|
4
|
-
|
|
4
|
+
Clear the dependency cache (hard reset of the node-style layer): delete
|
|
5
5
|
node_modules / .venv / vendor / target / .next / dist / .nuxt /
|
|
6
6
|
.turbo / .nx as applicable, then reinstall. For when "have you tried
|
|
7
7
|
turning it off and on again" applies to the dependency layer.
|
package/skills/god-sync.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: god-sync
|
|
3
3
|
description: |
|
|
4
|
-
|
|
4
|
+
Update after feature work (write-back): sync all affected artifacts to match
|
|
5
|
+
what the work actually touched. This is the AFTER half of the pair; its
|
|
6
|
+
read-only counterpart is /god-reconcile, which checks for conflicts BEFORE
|
|
7
|
+
the work. Updates PRD, ARCH, ROADMAP,
|
|
5
8
|
STACK, DEPLOY, OBSERVE, HARDEN, LAUNCH, BACKLOG, SEEDS, TODOS, THREADS
|
|
6
9
|
based on what the work actually touched. Closes the loop after
|
|
7
10
|
/god-reconcile + feature execution.
|