godpowers 3.13.0 → 3.13.1

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 CHANGED
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.13.1] - 2026-06-16
11
+
12
+ Maintenance release that drives a full self-audit (`codeaudit.md`) to zero: one
13
+ High finding, plus the Medium and Low findings, fixed across runtime correctness,
14
+ security hardening, the test gate, documentation, and de-duplication. No public
15
+ command, agent, workflow, or recipe surface changes (counts stay 120 / 40 / 13 / 44).
16
+
17
+ ### Fixed
18
+ - **Ledger record loss under concurrency (ERR-001):** `lib/evidence.js`
19
+ `appendJsonlAtomic` did a read-modify-write of the whole ledger, so two
20
+ concurrent `verify`/`outcome check` processes lost each other's records and
21
+ every append was O(n). It now uses `fs.appendFileSync` (O_APPEND), mirroring
22
+ `lib/events.js`; a concurrency regression test asserts 8 writers x 25 records
23
+ all survive.
24
+ - **Buffer-overflow verdicts (ERR-003):** a `maxBuffer` (ENOBUFS) overflow was
25
+ recorded as a plain command failure; it is now surfaced distinctly. The 16 MB
26
+ cap is the named constant `MAX_OUTPUT_BYTES`.
27
+ - **Doc accuracy:** `SECURITY.md` no longer recommends the non-existent
28
+ `npm install --verify` (use `npm audit signatures`); the stale
29
+ `ARCHITECTURE-MAP.md` counts are regenerated and now machine-guarded.
30
+
31
+ ### Security
32
+ - **Advisory hook (SEC-001):** `hooks/pre-tool-use.sh` is reframed as a
33
+ best-effort typo guard (not a security boundary) and now normalizes whitespace
34
+ and matches common destructive-command variants (`rm -fr`, `-r -f`, `./`
35
+ prefix, `git push -f`/`--force-with-lease`). Covered by `scripts/test-hooks.js`.
36
+ - **Disk-sourced verifier (SEC-002):** `outcome check` now announces the verifier
37
+ command and its `goal.json` source path before executing, so running it in an
38
+ untrusted cloned repo cannot silently run a planted command.
39
+ - **Ledger secrets (SEC-003):** the human-readable `LEDGER-LOG.md` command echo
40
+ masks obvious secret shapes; `SECURITY.md` documents that `.godpowers/ledger/`
41
+ may capture command output.
42
+ - **Codex sandbox (SEC-004):** `SECURITY.md` documents the Codex
43
+ `sandbox_mode = "workspace-write"` install default.
44
+
45
+ ### Changed
46
+ - **Test gate (TEST-001/002/003):** `coverage:lib` now enforces `--branches 75`;
47
+ new `scripts/test-runtime-audit.js` raises `runtime-audit.js` line coverage
48
+ 68.8% -> 77.8%; `scripts/test-router.js` no longer shares cumulative state and
49
+ cleans up its temp dirs.
50
+ - **De-duplication (ARC-001/002, QUAL-001/002):** the five `*-sync` modules share
51
+ `lib/sync-fs.js`; the ANSI logger moves to `lib/cli-log.js` and `slugify` to
52
+ `lib/text-util.js`; `installer-args.parseArgs` is now table-driven (was a
53
+ 358-line function); `state.STATE_FILE` is the canonical state-file constant and
54
+ `artifact-map.js`'s scope is documented accurately.
55
+ - **Re-audit follow-ups (ARC-003, QUAL-003, DOC-004/005, ERR-004, TEST-005):** a
56
+ fresh self-audit of the above confirmed no regressions and closed the residual
57
+ gaps: `installer-core.js` now imports the shared logger; `dashboard.js`/
58
+ `planning-systems.js` use `sync-fs`; the `lib/README` module catalog is complete
59
+ and guarded by a completeness check; the corrupt-state error is typed
60
+ (`err.code = 'CORRUPT_STATE'`) instead of message-matched; and the hook tests
61
+ assert each warning's text, not just its exit code.
62
+
10
63
  ## [3.13.0] - 2026-06-16
11
64
 
12
65
  ### Changed
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/aihxp/godpowers/actions/workflows/ci.yml/badge.svg)](https://github.com/aihxp/godpowers/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
- [![Version](https://img.shields.io/badge/version-3.13.0-blue)](CHANGELOG.md)
5
+ [![Version](https://img.shields.io/badge/version-3.13.1-blue)](CHANGELOG.md)
6
6
  [![npm](https://img.shields.io/npm/v/godpowers.svg)](https://www.npmjs.com/package/godpowers)
7
7
 
8
8
  **Ship fast. Ship right. Ship everything. Ship accountably.**
@@ -30,8 +30,13 @@ Godpowers makes AI coding accountable: every serious run should leave disk
30
30
  state, artifacts, validation gates, host guarantees, and a next action. Code is
31
31
  only one output. The project memory and proof trail matter too.
32
32
 
33
- Version 3.13.0 makes the default greenfield arc (`/god-mode`) miss less. The
34
- one-shot `full-arc` workflow now runs a whole-codebase code audit after the
33
+ Version 3.13.1 is a maintenance release that drives a full self-audit to zero:
34
+ the runtime ledger no longer loses verification records under concurrent writes,
35
+ the `outcome check` verifier and the pre-tool-use hook are hardened, the
36
+ `*-sync` modules share one filesystem helper, the argument parser is table-driven,
37
+ branch coverage is now gated, and the architecture map is kept in lockstep by a
38
+ machine guard. Version 3.13.0 makes the default greenfield arc (`/god-mode`) miss
39
+ less. The one-shot `full-arc` workflow now runs a whole-codebase code audit after the
35
40
  build (so it catches what the per-slice reviews missed in AI-generated code) and
36
41
  a documentation pass after harden (so the shipped product has docs verified
37
42
  against the code, not vibes). The arc goes build, code-audit, deploy, observe,
@@ -329,14 +334,14 @@ dependency to the main `godpowers` package:
329
334
 
330
335
  ```bash
331
336
  npx godpowers mcp-info --project=.
332
- npx -y -p godpowers@3.13.0 -p @godpowers/mcp@3.13.0 godpowers-mcp serve --project=.
337
+ npx -y -p godpowers@3.13.1 -p @godpowers/mcp@3.13.1 godpowers-mcp serve --project=.
333
338
  ```
334
339
 
335
340
  The companion exposes `status`, `next`, `gate_check`, `lint_artifact`, and
336
341
  `trace_requirement`. Host registration is opt-in:
337
342
 
338
343
  ```bash
339
- npx -y -p godpowers@3.13.0 -p @godpowers/mcp@3.13.0 godpowers-mcp setup --host=codex --project=. --write
344
+ npx -y -p godpowers@3.13.1 -p @godpowers/mcp@3.13.1 godpowers-mcp setup --host=codex --project=. --write
340
345
  ```
341
346
 
342
347
  See [MCP Companion](docs/mcp.md) for package boundaries and setup details.
package/RELEASE.md CHANGED
@@ -1,41 +1,41 @@
1
- # Godpowers 3.13.0 Release
1
+ # Godpowers 3.13.1 Release
2
2
 
3
3
  > Status: Prepared
4
4
  > Date: 2026-06-16
5
5
 
6
- [DECISION] Godpowers 3.13.0 is a minor release that hardens the default greenfield arc. The one-shot `full-arc` workflow run by `/god-mode` now audits the whole codebase after the build and writes verified documentation after harden, so a long-running idea-to-production run ships audited and documented without a separate manual pass.
7
- [DECISION] No new skill, agent, workflow, or recipe surface is added: both new steps reuse existing agents (`god-debt-assessor`, `god-docs-writer`). Surface counts are unchanged from 3.12.1.
8
- [DECISION] This release keeps `core` as the omitted installer profile, keeps `--profile=full` as the complete compatibility surface, and keeps the full 3.1.0-3.12.1 surface (fusion + codeauditor-grade audit + remediation loop).
6
+ [DECISION] Godpowers 3.13.1 is a maintenance release that drives a full self-audit (`codeaudit.md`, codeauditor-grade, nine weighted dimensions) to zero. It fixes one High finding plus the Medium and Low findings across runtime correctness, security hardening, the test gate, documentation, and de-duplication.
7
+ [DECISION] No new skill, agent, workflow, or recipe surface is added or removed. Surface counts are unchanged from 3.13.0: 120 slash commands, 40 specialist agents, 13 workflows, 44 recipes.
8
+ [DECISION] This release keeps `core` as the omitted installer profile, keeps `--profile=full` as the complete compatibility surface, and keeps the full 3.1.0-3.13.0 surface (fusion + codeauditor-grade audit + remediation loop + audited/documented greenfield arc).
9
9
 
10
10
  ## What's in this release
11
11
 
12
- - [DECISION] `full-arc` gains a `code-audit` job (`god-debt-assessor`, `mode: post-build-audit`) that runs after `build` and before `deploy`/`harden`. It gives the whole AI-generated codebase a scored, prioritized audit that catches issues the per-slice `god-spec-reviewer` + `god-quality-reviewer` reviews cannot see across files.
13
- - [DECISION] `full-arc` gains a `docs` job (`god-docs-writer`, `mode: product-docs-verify`) that runs after `harden` and before `launch`. It writes the project documentation and verifies every claim against the code (drift detected) before the product ships.
14
- - [DECISION] Dependencies were rewired: `deploy` and `harden` now `need` `code-audit`; `launch` now `needs` `docs`. The greenfield arc is build, code-audit, deploy, observe, harden, docs, launch, final-sync. The `full-arc` plan goes from 11 to 13 steps.
15
- - [DECISION] `GOD-ORCHESTRATOR-RUNBOOK` documents the audit and docs positions in the greenfield arc.
16
- - [DECISION] 120 slash commands, 40 specialist agents, 13 executable workflows, 44 intent recipes, and the full fusion + audit surface remain available.
12
+ - [DECISION] Runtime correctness: `lib/evidence.js` `appendJsonlAtomic` now appends with `fs.appendFileSync` (O_APPEND) instead of a read-modify-write, so concurrent `verify`/`outcome check` processes no longer lose ledger records and the append is no longer O(n) (ERR-001). A `maxBuffer` overflow is surfaced distinctly instead of as a plain failure (ERR-003).
13
+ - [DECISION] Security hardening: the pre-tool-use hook is reframed as a best-effort advisory typo guard and matches more destructive-command variants (SEC-001); `outcome check` announces a disk-sourced verifier before running it (SEC-002); the `LEDGER-LOG.md` command echo masks obvious secret shapes and `SECURITY.md` documents the ledger and Codex-sandbox trust boundaries (SEC-003, SEC-004); `SECURITY.md` replaces the non-existent `npm install --verify` with `npm audit signatures` (DOC-002).
14
+ - [DECISION] Test gate: `coverage:lib` now enforces `--branches 75` (TEST-001); a new `scripts/test-runtime-audit.js` raises `lib/runtime-audit.js` line coverage from 68.8% to 77.8% (TEST-002); `scripts/test-router.js` no longer shares cumulative state across tests and cleans up its temp dirs (TEST-003); new `scripts/test-hooks.js`, `scripts/test-cli-log.js`, and `scripts/test-text-util.js` cover the new code.
15
+ - [DECISION] De-duplication: the five `*-sync` modules share `lib/sync-fs.js`; the ANSI logger moves to `lib/cli-log.js` and `slugify` to `lib/text-util.js`; `installer-args.parseArgs` is now table-driven (ARC-001, QUAL-001, QUAL-002).
16
+ - [DECISION] Documentation: `ARCHITECTURE-MAP.md` counts are regenerated and now machine-guarded by `scripts/test-doc-surface-counts.js`; `state.STATE_FILE` is the canonical state-file constant and `artifact-map.js`'s scope is documented accurately (DOC-001, DOC-003, ARC-002).
17
+ - [DECISION] Re-audit follow-ups: a fresh self-audit confirmed no regressions and closed the residual gaps it found - `installer-core.js` imports the shared logger (QUAL-003); `dashboard.js`/`planning-systems.js` consume `sync-fs` (ARC-003); the `lib/README` module catalog is complete and now guarded by a completeness check (DOC-004); the ledger-append comment is corrected (DOC-005); the corrupt-state error is typed rather than message-matched (ERR-004); and the hook tests assert each warning's text (TEST-005).
17
18
 
18
19
  ## Changes
19
20
 
20
- - [DECISION] `package.json`, `package-lock.json`, and `packages/mcp/package.json` now publish the 3.13.0 version.
21
- - [DECISION] `workflows/full-arc.yaml` adds the `code-audit` and `docs` jobs and rewires `deploy`/`harden`/`launch` dependencies. No lib change.
22
- - [DECISION] CHANGELOG, RELEASE notes, README, roadmap, reference, architecture, and the MCP docs now reflect 3.13.0. SECURITY supported-version table adds the `3.13.x` row and moves `3.12.x` to security fixes only.
21
+ - [DECISION] `package.json`, `package-lock.json`, and `packages/mcp/package.json` now publish the 3.13.1 version.
22
+ - [DECISION] New runtime modules `lib/sync-fs.js`, `lib/cli-log.js`, and `lib/text-util.js` (lib module count 87 -> 90). No public command/agent/workflow/recipe surface change.
23
+ - [DECISION] CHANGELOG, RELEASE notes, README, roadmap, reference, architecture, and the architecture map now reflect 3.13.1. The SECURITY supported-version table already carries the `3.13.x` row.
23
24
 
24
25
  ## Validation
25
26
 
26
- - [DECISION] `npm run lint` passed with 29 static checks (agent contract + size + refs intact).
27
- - [DECISION] `node tests/integration/full-arc.test.js`, `node scripts/test-workflow-runner.js`, and `node scripts/test-agent-refs.js` passed: `full-arc` plans to 13 steps with a valid DAG and both `god-debt-assessor` and `god-docs-writer` are real agents.
28
- - [DECISION] `npm run release:check` passed `coverage:lib` above the 90 percent line floor for `lib/**/*.js`.
29
- - [DECISION] `npm run release:check` passed `npm audit --omit=dev` with 0 vulnerabilities.
30
- - [DECISION] `npm run release:check` passed public surface docs for version 3.13.0 with 120 skills, 40 agents, 13 workflows, and 44 recipes.
27
+ - [DECISION] `npm test` passed all command groups.
28
+ - [DECISION] `npm run release:check` passed `coverage:lib` above the 90 percent line floor and the new 75 percent branch floor for `lib/**/*.js`.
29
+ - [DECISION] `npm run release:check` passed `npm audit --omit=dev` with 0 vulnerabilities and `git diff --check`.
30
+ - [DECISION] `npm run release:check` passed public surface docs for version 3.13.1 with 120 skills, 40 agents, 13 workflows, and 44 recipes.
31
31
  - [DECISION] `npm run release:check` passed root and `@godpowers/mcp` package contents.
32
32
 
33
33
  ## Upgrade
34
34
 
35
- - [DECISION] Use `npm install -g godpowers@3.13.0` or `npx godpowers@3.13.0`.
36
- - [DECISION] No migration is required. Existing projects are unaffected; the change only adds steps to the greenfield one-shot arc, which now takes longer in exchange for an audited and documented product.
35
+ - [DECISION] Use `npm install -g godpowers@3.13.1` or `npx godpowers@3.13.1`.
36
+ - [DECISION] No migration is required. Existing projects are unaffected; the changes are internal correctness, security, test-gate, and maintainability fixes with no surface change.
37
37
 
38
38
  ## Notes
39
39
 
40
- - [DECISION] The publish targets are npm `godpowers@3.13.0`, npm `@godpowers/mcp@3.13.0`, and GitHub release `https://github.com/aihxp/godpowers/releases/tag/v3.13.0`.
40
+ - [DECISION] The publish targets are npm `godpowers@3.13.1`, npm `@godpowers/mcp@3.13.1`, and GitHub release `https://github.com/aihxp/godpowers/releases/tag/v3.13.1`.
41
41
  - [DECISION] The tag-triggered GitHub publish workflow remains the preferred npm path because it publishes with provenance. This release has not been tagged or published to npm yet.
package/bin/install.js CHANGED
@@ -21,6 +21,7 @@ const { describeProfiles } = require('../lib/install-profiles');
21
21
  const commandFamilies = require('../lib/command-families');
22
22
  const identity = require('../lib/package-identity');
23
23
  const cliDispatch = require('../lib/cli-dispatch');
24
+ const { log, success, warn, error } = require('../lib/cli-log');
24
25
 
25
26
  const VERSION = identity.PACKAGE_VERSION;
26
27
 
@@ -29,22 +30,6 @@ const BANNER = `
29
30
  Ship fast. Ship right. Ship everything.
30
31
  `;
31
32
 
32
- function log(msg) {
33
- console.log(` ${msg}`);
34
- }
35
-
36
- function success(msg) {
37
- console.log(` \x1b[32m+\x1b[0m ${msg}`);
38
- }
39
-
40
- function warn(msg) {
41
- console.log(` \x1b[33m!\x1b[0m ${msg}`);
42
- }
43
-
44
- function error(msg) {
45
- console.error(` \x1b[31mx\x1b[0m ${msg}`);
46
- }
47
-
48
33
  function showHelp() {
49
34
  console.log(BANNER);
50
35
  log('Usage: npx godpowers [command] [options]\n');
@@ -1,52 +1,64 @@
1
1
  #!/usr/bin/env bash
2
- # Godpowers PreToolUse Safety Hook
3
- # Runs before destructive tool calls in a Godpowers project.
4
- # Warns on: rm -rf, git reset --hard, force push to main, deleting .godpowers/,
5
- # and irreversible public release actions.
2
+ # Godpowers PreToolUse advisory hook (best-effort, NOT a security boundary).
3
+ #
4
+ # Warns before some common destructive command spellings when run inside a
5
+ # Godpowers project: deleting .godpowers/, git reset --hard, force push,
6
+ # npm publish, gh release create. It matches command text heuristically after
7
+ # normalizing whitespace, so it tolerates spacing and short-flag variants
8
+ # (rm -fr, -r -f, ./ prefix, trailing slash, push -f). It is still deliberately
9
+ # conservative and is easily bypassed by uncommon spellings, quoting, aliases,
10
+ # or a child process that does the deletion. Treat it as a typo guard that buys
11
+ # a confirmation prompt, not as a guarantee. See SECURITY.md.
6
12
 
7
13
  set -euo pipefail
8
14
 
9
- TOOL_NAME="${CLAUDE_TOOL_NAME:-}"
10
15
  TOOL_INPUT="${CLAUDE_TOOL_INPUT:-}"
11
16
 
12
17
  if [ ! -d ".godpowers" ]; then
13
18
  exit 0
14
19
  fi
15
20
 
16
- # Pattern matches that should warn
17
- case "$TOOL_INPUT" in
18
- *"rm -rf .godpowers"*)
19
- echo "WARNING: About to delete the .godpowers/ directory."
20
- echo "This destroys all PROGRESS, PRD, ARCH, ROADMAP, and other artifacts."
21
- echo "If this is intentional, confirm in chat before proceeding."
22
- exit 1
23
- ;;
24
- *"git reset --hard"*)
25
- echo "WARNING: git reset --hard discards uncommitted work."
26
- echo "If you have artifacts not yet committed, they will be lost."
27
- echo "Consider git stash first."
28
- exit 1
29
- ;;
30
- *"git push --force"*)
31
- echo "WARNING: Force pushing. If pushing to main/master, this can"
32
- echo "destroy collaborators' work."
33
- exit 1
34
- ;;
35
- *"npm publish"*)
36
- echo "WARNING: npm publish is a public release action."
37
- echo "Confirm release checklist, repo-doc-sync, repo-surface-sync,"
38
- echo "release-surface-sync, package contents, and installer smoke first."
39
- exit 1
40
- ;;
41
- *"gh release create"*)
42
- echo "WARNING: gh release create publishes public release notes."
43
- echo "Confirm README, badges, CHANGELOG, RELEASE, package, tag, and npm version agree."
44
- exit 1
45
- ;;
46
- *"rm -rf node_modules"*)
47
- # Allowed: this is just cache
48
- exit 0
49
- ;;
50
- esac
21
+ # Collapse tabs and runs of spaces so spacing variants normalize to one form.
22
+ norm="$(printf '%s' "$TOOL_INPUT" | tr '\t' ' ' | tr -s ' ')"
23
+
24
+ matches() {
25
+ printf '%s' "$norm" | grep -Eq -- "$1"
26
+ }
27
+
28
+ # rm targeting .godpowers (optional ./ or / prefix, optional trailing slash)
29
+ # that carries a recursive flag in any spelling: -rf, -fr, -r -f, -R, --recursive.
30
+ if matches 'rm( +-[a-zA-Z]+)* +\.?/?\.godpowers(/|$| )' && matches ' -[a-zA-Z]*[rR]|--recursive'; then
31
+ echo "WARNING: About to delete the .godpowers/ directory."
32
+ echo "This destroys all PROGRESS, PRD, ARCH, ROADMAP, and other artifacts."
33
+ echo "If this is intentional, confirm in chat before proceeding."
34
+ exit 1
35
+ fi
36
+
37
+ if matches 'git +reset +--hard'; then
38
+ echo "WARNING: git reset --hard discards uncommitted work."
39
+ echo "If you have artifacts not yet committed, they will be lost."
40
+ echo "Consider git stash first."
41
+ exit 1
42
+ fi
43
+
44
+ # git push with a force flag: --force, --force-with-lease, or a standalone -f.
45
+ if matches 'git +push +' && matches '(--force(-with-lease)?| -f( |$))'; then
46
+ echo "WARNING: Force pushing. If pushing to main/master, this can"
47
+ echo "destroy collaborators' work."
48
+ exit 1
49
+ fi
50
+
51
+ if matches 'npm +publish'; then
52
+ echo "WARNING: npm publish is a public release action."
53
+ echo "Confirm release checklist, repo-doc-sync, repo-surface-sync,"
54
+ echo "release-surface-sync, package contents, and installer smoke first."
55
+ exit 1
56
+ fi
57
+
58
+ if matches 'gh +release +create'; then
59
+ echo "WARNING: gh release create publishes public release notes."
60
+ echo "Confirm README, badges, CHANGELOG, RELEASE, package, tag, and npm version agree."
61
+ exit 1
62
+ fi
51
63
 
52
64
  exit 0
package/lib/README.md CHANGED
@@ -28,6 +28,7 @@ package-level integrations.
28
28
  | `cost-tracker.js` | Track token and cost estimates from event streams. |
29
29
  | `atomic-write.js` | Write load-bearing files through temp-file validation and atomic rename. |
30
30
  | `fs-async.js` | Promise-based file read/write helpers for non-blocking runtime paths. |
31
+ | `sync-fs.js` | Shared project-relative read/write/exists/readJson helpers for the `*-sync` modules. |
31
32
 
32
33
  ## Events and observability
33
34
 
@@ -38,12 +39,17 @@ package-level integrations.
38
39
  | `otel-exporter.js` | Export Godpowers events in an OpenTelemetry-shaped format. |
39
40
  | `runtime-audit.js` | Audit runtime health and expected project state. |
40
41
  | `runtime-test.js` | Provide runtime checks used by package tests. |
42
+ | `evidence.js` | Enforced producer of executed/attested verification records, the state.json rollup, gate events, reflections, memory, lessons, and outcome loops. |
43
+ | `evidence-import.js` | Import an existing `.mythify/` ledger into `.godpowers/ledger/`. |
44
+ | `work-report.js` | Render the verification play-by-play from the evidence ledger. |
45
+ | `adoption-metrics.js` | Derive adoption and outcome metrics from event streams. |
41
46
 
42
47
  ## Routing and execution
43
48
 
44
49
  | Module | Purpose |
45
50
  |--------|---------|
46
51
  | `router.js` | Resolve user intent to skills, agents, recipes, and workflows. |
52
+ | `quarterback.js` | Entry router that classifies a prompt into a play and refuses new work when the project is on red. |
47
53
  | `command-families.js` | Define UX command families, status views, decision ladders, and trigger precedence helpers. |
48
54
  | `recipes.js` | Load and validate routing recipes. |
49
55
  | `workflow-parser.js` | Parse workflow YAML into executable steps. |
@@ -59,7 +65,7 @@ package-level integrations.
59
65
 
60
66
  | Module | Purpose |
61
67
  |--------|---------|
62
- | `artifact-map.js` | Centralize canonical artifact paths for dashboards, gates, and helpers. |
68
+ | `artifact-map.js` | Tier gate artifact map: the per-tier required artifacts and state steps used by dashboards, gates, and doc-count checks. (Module-local paths stay in their owning module; `state.json` is named via `state.STATE_FILE`.) |
63
69
  | `artifact-linter.js` | Check artifacts for required labels, evidence, and domain precision. |
64
70
  | `artifact-diff.js` | Compare artifact changes for review and release workflows. |
65
71
  | `gate.js` | Run executable artifact gates for Phase 1 tier completion checks. |
@@ -98,6 +104,7 @@ package-level integrations.
98
104
  | `drift-detector.js` | Detect context drift between artifacts and implementation. |
99
105
  | `impact.js` | Summarize expected impact of proposed changes. |
100
106
  | `linkage.js` | Connect artifacts, stories, and implementation files. |
107
+ | `requirements.js` | Track which PRD requirements are done, in progress, or untouched from disk evidence. |
101
108
  | `multi-repo-detector.js` | Detect multi-repository workspaces. |
102
109
  | `reverse-sync.js` | Reflect implementation changes back into artifacts. |
103
110
  | `review-required.js` | Decide when review gates should block progress. |
@@ -111,6 +118,9 @@ package-level integrations.
111
118
  | `installer-files.js` | File-copy helpers shared by the installer and its tests. |
112
119
  | `installer-args.js` | Parse `bin/install.js` arguments and subcommands. |
113
120
  | `cli-dispatch.js` | Dispatch local CLI helper commands such as status, quick-proof, gate, dogfood, and extension-scaffold. |
121
+ | `cli-log.js` | Shared ANSI console logger (log/success/warn/error) for the binary and CLI dispatch. |
122
+ | `text-util.js` | Small shared string helpers (the canonical `slugify`). |
123
+ | `mcp-info.js` | Render read-only MCP companion setup instructions for `npx godpowers mcp-info`. |
114
124
  | `install-profiles.js` | Select smaller role-specific slash-command install surfaces. |
115
125
  | `surface-profile.js` | Preview and apply runtime command surface profile switches after install. |
116
126
  | `installer-runtimes.js` | Map supported runtimes to their config directories. |
@@ -1,5 +1,11 @@
1
1
  /**
2
2
  * Shared tier artifact map for dashboard, gates, and documentation checks.
3
+ *
4
+ * Scope: this owns the per-tier *gate artifacts* (which files a tier requires
5
+ * and the state step it maps to). It is not a flat registry of every
6
+ * `.godpowers/...` path: module-local artifacts (a sync module's log file, the
7
+ * evidence ledger) live in their owning module, and `state.json` is named via
8
+ * `state.STATE_FILE`.
3
9
  */
4
10
 
5
11
  const TIER_ARTIFACTS = {
@@ -5,25 +5,10 @@
5
5
  const gate = require('./gate');
6
6
  const identity = require('./package-identity');
7
7
  const stateAdvance = require('./state-advance');
8
+ const { log, success, warn, error } = require('./cli-log');
8
9
 
9
10
  const VERSION = identity.PACKAGE_VERSION;
10
11
 
11
- function log(msg) {
12
- console.log(` ${msg}`);
13
- }
14
-
15
- function success(msg) {
16
- console.log(` \x1b[32m+\x1b[0m ${msg}`);
17
- }
18
-
19
- function warn(msg) {
20
- console.log(` \x1b[33m!\x1b[0m ${msg}`);
21
- }
22
-
23
- function error(msg) {
24
- console.error(` \x1b[31mx\x1b[0m ${msg}`);
25
- }
26
-
27
12
  function runAutomationCommand(opts) {
28
13
  const automation = require('./automation-providers');
29
14
  const result = opts.command === 'automation-setup'
@@ -501,7 +486,20 @@ function runOutcomeCommand(opts) {
501
486
  projectRoot
502
487
  });
503
488
  } else if (action === 'check') {
504
- payload = evidence.outcome.check(opts.outcomeSlug, { projectRoot });
489
+ payload = evidence.outcome.check(opts.outcomeSlug, {
490
+ projectRoot,
491
+ // SEC-002: announce the disk-sourced verifier before it runs, so running
492
+ // `outcome check` inside an untrusted cloned repo cannot silently execute
493
+ // a planted command. Goes to stderr so it never corrupts --json on stdout.
494
+ notice: ({ verifier, source }) => {
495
+ const rel = relLedger({ project: projectRoot }, source) || source;
496
+ process.stderr.write(
497
+ ` notice: outcome '${opts.outcomeSlug}' runs a verifier loaded from ${rel}\n` +
498
+ ` command: ${verifier}\n` +
499
+ ` (.godpowers/ledger/ carries executable state; only run 'outcome check' in repos you trust)\n`
500
+ );
501
+ }
502
+ });
505
503
  } else if (action === 'stop') {
506
504
  payload = evidence.outcome.stop(opts.outcomeSlug, opts.reason || undefined, { projectRoot });
507
505
  } else {
@@ -626,11 +624,22 @@ const COMMAND_RUNNERS = {
626
624
 
627
625
  function runCommand(opts) {
628
626
  const runner = COMMAND_RUNNERS[opts.command];
629
- if (runner) {
627
+ if (!runner) return false;
628
+ try {
630
629
  runner(opts);
631
- return true;
630
+ } catch (err) {
631
+ // ERR-002: a corrupt state.json throws from state.read(). Surface the
632
+ // helpful message as a clean one-liner with a non-zero exit instead of a
633
+ // raw stack trace. Match the typed error code (ERR-004), not the message
634
+ // prose. Re-throw anything else so genuine bugs still surface.
635
+ if (err && err.code === 'CORRUPT_STATE') {
636
+ error(err.message);
637
+ process.exitCode = 1;
638
+ } else {
639
+ throw err;
640
+ }
632
641
  }
633
- return false;
642
+ return true;
634
643
  }
635
644
 
636
645
  module.exports = {
package/lib/cli-log.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared ANSI console logger for the installer binary and CLI dispatch (QUAL-002).
3
+ *
4
+ * bin/install.js and lib/cli-dispatch.js previously defined these four helpers
5
+ * verbatim. They now share one copy so the prefix/colour convention lives once.
6
+ */
7
+
8
+ function log(msg) {
9
+ console.log(` ${msg}`);
10
+ }
11
+
12
+ function success(msg) {
13
+ console.log(` \x1b[32m+\x1b[0m ${msg}`);
14
+ }
15
+
16
+ function warn(msg) {
17
+ console.log(` \x1b[33m!\x1b[0m ${msg}`);
18
+ }
19
+
20
+ function error(msg) {
21
+ console.error(` \x1b[31mx\x1b[0m ${msg}`);
22
+ }
23
+
24
+ module.exports = { log, success, warn, error };
package/lib/dashboard.js CHANGED
@@ -17,6 +17,7 @@ const repoDocSync = require('./repo-doc-sync');
17
17
  const repoSurfaceSync = require('./repo-surface-sync');
18
18
  const hostCapabilities = require('./host-capabilities');
19
19
  const artifactMap = require('./artifact-map');
20
+ const { read: readText, exists } = require('./sync-fs');
20
21
 
21
22
  const GOD_DIR = '.godpowers';
22
23
  const PRD_PATH = artifactMap.requiredArtifactsForTier('prd')[0].path;
@@ -25,16 +26,6 @@ const CHECKPOINT_PATH = '.godpowers/CHECKPOINT.md';
25
26
  const SYNC_LOG_PATH = '.godpowers/SYNC-LOG.md';
26
27
  const REVIEW_PATH = '.godpowers/REVIEW-REQUIRED.md';
27
28
 
28
- function exists(projectRoot, relPath) {
29
- return fs.existsSync(path.join(projectRoot, relPath));
30
- }
31
-
32
- function readText(projectRoot, relPath) {
33
- const file = path.join(projectRoot, relPath);
34
- if (!fs.existsSync(file)) return '';
35
- return fs.readFileSync(file, 'utf8');
36
- }
37
-
38
29
  function mtimeMs(projectRoot, relPath) {
39
30
  const file = path.join(projectRoot, relPath);
40
31
  if (!fs.existsSync(file)) return null;
package/lib/evidence.js CHANGED
@@ -10,8 +10,9 @@
10
10
  * Adaptations from the upstream Node engine (see .provenance.json):
11
11
  * - Mythify's plan/step context becomes Godpowers' arc/substep context.
12
12
  * - The .mythify/ state dir becomes .godpowers/ledger/.
13
- * - The jsonl append goes through lib/atomic-write.js (temp + rename) so a
14
- * torn record is never visible.
13
+ * - The jsonl append uses O_APPEND (fs.appendFileSync) so concurrent writers
14
+ * never clobber each other's records; a torn line from an interleaved large
15
+ * write is tolerated by the reader, which skips unparseable records.
15
16
  *
16
17
  * What this adds on top of the upstream engine (the Godpowers integration):
17
18
  * 1. .godpowers/ledger/verifications.jsonl: append-only, Mythify-shape record,
@@ -61,6 +62,9 @@ const events = require('./events');
61
62
  const TAIL_CHARS = 4000;
62
63
  const DEFAULT_TIMEOUT_SECONDS = 300;
63
64
  const DIAGNOSTICS_LIMIT = 1000;
65
+ // Cap on captured stdout/stderr per verify command. Exceeding it makes
66
+ // spawnSync raise ENOBUFS and truncate output, so the verdict is unreliable.
67
+ const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
64
68
 
65
69
  // Substeps whose close gate requires an executed, verified:true record (the
66
70
  // runtime/executable-gated tiers). Other substeps (planning, repo, observe,
@@ -161,19 +165,33 @@ function slugify(text) {
161
165
  .slice(0, 40);
162
166
  }
163
167
 
168
+ // SEC-003: mask obvious secret shapes before echoing a command into the
169
+ // human-readable LEDGER-LOG.md. The durable verifications.jsonl record keeps the
170
+ // exact command (it is the audit source of truth); this only protects the log
171
+ // echo. Output tails can still carry secrets, so SECURITY.md documents that
172
+ // .godpowers/ledger/ may capture sensitive output.
173
+ function redactSecrets(text) {
174
+ return String(text == null ? '' : text)
175
+ .replace(/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g, 'gh*_***REDACTED***')
176
+ .replace(/\bsk-[A-Za-z0-9_-]{16,}\b/g, 'sk-***REDACTED***')
177
+ .replace(/\bAKIA[0-9A-Z]{16}\b/g, 'AKIA***REDACTED***')
178
+ .replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, 'xox*-***REDACTED***')
179
+ .replace(/(--?(?:token|password|passwd|secret|api[-_]?key)[=\s])\S+/gi, '$1***REDACTED***');
180
+ }
181
+
164
182
  // ---------------------------------------------------------------------------
165
- // Ledger append (atomic, via lib/atomic-write.js) and tolerant read
183
+ // Ledger append (O_APPEND) and tolerant read
166
184
  // ---------------------------------------------------------------------------
167
185
 
168
186
  function appendJsonlAtomic(file, record) {
169
187
  fs.mkdirSync(path.dirname(file), { recursive: true });
170
- let existing = '';
171
- try {
172
- existing = fs.readFileSync(file, 'utf8');
173
- } catch (_) {
174
- existing = '';
175
- }
176
- atomic.writeFileAtomic(file, existing + JSON.stringify(record) + '\n');
188
+ // O_APPEND: each record is written at EOF in a single positioned write, so
189
+ // two concurrent writers never overwrite each other's record. The previous
190
+ // read-concat-rewrite was last-writer-wins (it lost records under concurrent
191
+ // invocation) and rewrote the whole file on every append (O(n) per record).
192
+ // A torn line from an interleaved oversized write is tolerated by readJsonl,
193
+ // which skips unparseable records. Mirrors the append in lib/events.js.
194
+ fs.appendFileSync(file, JSON.stringify(record) + '\n');
177
195
  return file;
178
196
  }
179
197
 
@@ -255,18 +273,27 @@ function runCommand(command, timeoutSeconds) {
255
273
  shell: true,
256
274
  encoding: 'utf8',
257
275
  timeout: Math.round(timeoutSeconds * 1000),
258
- maxBuffer: 16 * 1024 * 1024
276
+ maxBuffer: MAX_OUTPUT_BYTES
259
277
  });
260
278
  const durationSeconds = Number(process.hrtime.bigint() - startedAt) / 1e9;
261
279
  let stdoutTail = tail(run.stdout);
262
280
  let stderrTail = tail(run.stderr);
263
281
  const timedOut = Boolean(run.error && run.error.code === 'ETIMEDOUT');
282
+ const bufferOverflow = Boolean(run.error && run.error.code === 'ENOBUFS');
264
283
  let exitCode;
265
284
  let verified;
266
285
  if (timedOut) {
267
286
  exitCode = -1;
268
287
  verified = false;
269
288
  stderrTail = stderrTail + (stderrTail ? '\n' : '') + `(timed out after ${timeoutSeconds} seconds)`;
289
+ } else if (bufferOverflow) {
290
+ // Output exceeded the capture buffer: spawnSync truncated stdout/stderr, so
291
+ // we cannot trust the exit status. Surface this distinctly rather than
292
+ // folding it into a plain command failure.
293
+ exitCode = -1;
294
+ verified = false;
295
+ stderrTail = stderrTail + (stderrTail ? '\n' : '') +
296
+ `(output exceeded ${Math.round(MAX_OUTPUT_BYTES / (1024 * 1024))} MB buffer; output truncated and verdict unreliable)`;
270
297
  } else if (typeof run.status === 'number') {
271
298
  exitCode = run.status;
272
299
  verified = exitCode === 0;
@@ -280,7 +307,7 @@ function runCommand(command, timeoutSeconds) {
280
307
  : 'command did not produce an exit code';
281
308
  stderrTail = stderrTail + (stderrTail ? '\n' : '') + `(${reason})`;
282
309
  }
283
- return { exitCode, verified, durationSeconds, stdoutTail, stderrTail, timedOut };
310
+ return { exitCode, verified, durationSeconds, stdoutTail, stderrTail, timedOut, bufferOverflow };
284
311
  }
285
312
 
286
313
  // ---------------------------------------------------------------------------
@@ -449,7 +476,7 @@ function verify(command, opts = {}) {
449
476
  appendRecord(projectRoot, record);
450
477
  appendLog(
451
478
  projectRoot,
452
- `verify ${record.verified ? 'PASS' : 'FAIL'} substep=${context.substep || '-'} exit=${record.exit_code} cmd=\`${command}\``
479
+ `verify ${record.verified ? 'PASS' : 'FAIL'} substep=${context.substep || '-'} exit=${record.exit_code} cmd=\`${redactSecrets(command)}\``
453
480
  );
454
481
 
455
482
  const rollup = rollUp(projectRoot, opts.substep, record);
@@ -818,6 +845,15 @@ function outcomeCheck(name, opts = {}) {
818
845
  if (goal.status !== 'active') return { ran: false, reason: `outcome-${goal.status}`, goal };
819
846
  if (!goal.verifier) return { ran: false, reason: 'no-verifier', goal };
820
847
 
848
+ // SEC-002: the verifier is a shell command read from goal.json on disk, not
849
+ // from a live flag, so `outcome check` in a cloned untrusted repo would
850
+ // otherwise execute a planted command silently. Surface what is about to run
851
+ // and where it came from before executing. The notice is informational; the
852
+ // CLI passes one that prints to stderr.
853
+ if (typeof opts.notice === 'function') {
854
+ opts.notice({ verifier: goal.verifier, source: outcomeGoalPath(projectRoot, slug) });
855
+ }
856
+
821
857
  const result = verify(goal.verifier, {
822
858
  substep: goal.substep || undefined,
823
859
  claim: goal.title,
@@ -901,6 +937,7 @@ module.exports = {
901
937
  DEFAULT_TIMEOUT_SECONDS,
902
938
  // Internals exposed for tests and the re-sync script.
903
939
  _runCommand: runCommand,
940
+ _redactSecrets: redactSecrets,
904
941
  _toStateCommand: toStateCommand,
905
942
  _substepContext: substepContext,
906
943
  _rollUp: rollUp,