@pareto4/pp-core 1.13.0 → 1.17.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.
Files changed (201) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +2 -1
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +10 -13
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +244 -21
  9. package/agents/pp-verifier.md +7 -3
  10. package/bin/install.js +10 -3
  11. package/commands/pp-execute-phase.md +1 -3
  12. package/commands/pp-ns-review.md +1 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +5 -3
  15. package/commands/pp-verify-work.md +1 -2
  16. package/gemini-extension.json +1 -1
  17. package/hooks/dist/pp-check-update.js +0 -0
  18. package/hooks/dist/pp-cursor-session-start.js +1 -1
  19. package/hooks/dist/pp-statusline.js +1 -1
  20. package/hooks/dist/pp-update-banner.js +0 -0
  21. package/hooks/pp-check-update.js +0 -0
  22. package/hooks/pp-cursor-session-start.js +1 -1
  23. package/hooks/pp-statusline.js +1 -1
  24. package/hooks/pp-update-banner.js +0 -0
  25. package/package.json +14 -19
  26. package/pareto-skills/_shared/create-project-board.sh +0 -0
  27. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  28. package/pp-core/bin/check-latest-version.cjs +0 -0
  29. package/pp-core/bin/detect-build-commands.sh +2 -2
  30. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  31. package/pp-core/bin/lib/capability-registry.cjs +396 -82
  32. package/pp-core/bin/lib/capability-state.cjs +160 -4
  33. package/pp-core/bin/lib/capability-writer.cjs +19 -5
  34. package/pp-core/bin/lib/check-command-router.cjs +3334 -946
  35. package/pp-core/bin/lib/clusters.cjs +3 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  39. package/pp-core/bin/lib/config-loader.cjs +7 -103
  40. package/pp-core/bin/lib/config-schema.cjs +1 -1
  41. package/pp-core/bin/lib/config.cjs +154 -62
  42. package/pp-core/bin/lib/decisions.cjs +4 -4
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  45. package/pp-core/bin/lib/git-base-branch.cjs +13 -11
  46. package/pp-core/bin/lib/git-provider.cjs +4 -4
  47. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  48. package/pp-core/bin/lib/init-contract.cjs +648 -738
  49. package/pp-core/bin/lib/init-interactive.cjs +76 -4
  50. package/pp-core/bin/lib/init.cjs +24 -51
  51. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  52. package/pp-core/bin/lib/io.cjs +8 -0
  53. package/pp-core/bin/lib/loop-resolver.cjs +189 -129
  54. package/pp-core/bin/lib/org-profile.cjs +469 -152
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -253
  59. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  60. package/pp-core/bin/lib/practices-review-check.cjs +15 -9
  61. package/pp-core/bin/lib/profile-output.cjs +1 -1
  62. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  63. package/pp-core/bin/lib/state.cjs +11 -85
  64. package/pp-core/bin/lib/template.cjs +1 -1
  65. package/pp-core/bin/lib/vault-audit.cjs +650 -126
  66. package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
  67. package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
  68. package/pp-core/bin/lib/vault-index.cjs +699 -0
  69. package/pp-core/bin/lib/vault-mode.cjs +235 -0
  70. package/pp-core/bin/lib/vault-practices.cjs +139 -72
  71. package/pp-core/bin/lib/vault-review.cjs +194 -451
  72. package/pp-core/bin/lib/vault-seal.cjs +1936 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1505 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +607 -254
  75. package/pp-core/bin/lib/vault-update.cjs +9 -33
  76. package/pp-core/bin/lib/vault-write-back.cjs +70 -42
  77. package/pp-core/bin/lib/vault.cjs +2734 -920
  78. package/pp-core/bin/lib/verify.cjs +0 -11
  79. package/pp-core/bin/portable-timeout.sh +1 -1
  80. package/pp-core/bin/pp-tools.cjs +184 -54
  81. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  82. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  83. package/pp-core/bin/shared/model-catalog.json +2 -2
  84. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  85. package/pp-core/org-profile.json +26 -0
  86. package/pp-core/references/autonomous-best-effort.md +8 -7
  87. package/pp-core/references/loop-hook-dispatch.md +0 -25
  88. package/pp-core/references/planner-gap-closure.md +0 -1
  89. package/pp-core/references/planner-tdd.md +0 -1
  90. package/pp-core/references/planner-vault-practices.md +17 -34
  91. package/pp-core/references/planner-vault-standards.md +320 -0
  92. package/pp-core/references/planning-config.md +0 -8
  93. package/pp-core/references/practice-arithmetic-contract.md +121 -0
  94. package/pp-core/references/tdd.md +0 -1
  95. package/pp-core/references/verifier-practice-verdicts.md +263 -0
  96. package/pp-core/templates/README.md +1 -0
  97. package/pp-core/templates/VAULT-REVIEW.md +202 -0
  98. package/pp-core/templates/phase-prompt.md +2 -6
  99. package/pp-core/templates/practices/README.md +20 -23
  100. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  101. package/pp-core/workflows/add-tests.md +55 -4
  102. package/pp-core/workflows/audit-fix.md +63 -2
  103. package/pp-core/workflows/audit-milestone.md +1 -1
  104. package/pp-core/workflows/autonomous.md +85 -11
  105. package/pp-core/workflows/check-todos.md +13 -2
  106. package/pp-core/workflows/code-review-fix.md +59 -3
  107. package/pp-core/workflows/code-review.md +10 -1
  108. package/pp-core/workflows/complete-milestone.md +1 -1
  109. package/pp-core/workflows/debug.md +54 -4
  110. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  111. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  112. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  113. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  117. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  118. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  123. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  124. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  127. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  128. package/pp-core/workflows/discovery-sprint.md +7 -3
  129. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  130. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  131. package/pp-core/workflows/discovery-to-build.md +10 -2
  132. package/pp-core/workflows/discuss-phase.md +3 -10
  133. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  134. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  135. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  136. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  137. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  138. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  139. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  140. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
  141. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  142. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  143. package/pp-core/workflows/execute-phase.md +155 -128
  144. package/pp-core/workflows/execute-plan.md +56 -6
  145. package/pp-core/workflows/fast.md +53 -4
  146. package/pp-core/workflows/help/modes/full.md +3 -3
  147. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  148. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  149. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  150. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  151. package/pp-core/workflows/init.md +232 -272
  152. package/pp-core/workflows/new-project.md +31 -23
  153. package/pp-core/workflows/plan-phase.md +98 -45
  154. package/pp-core/workflows/quick.md +117 -3
  155. package/pp-core/workflows/secure-phase.md +1 -1
  156. package/pp-core/workflows/ship.md +57 -28
  157. package/pp-core/workflows/undo.md +1 -1
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +58 -3
  160. package/pp-core/workflows/vault-review.md +282 -22
  161. package/pp-core/workflows/verify-work.md +12 -14
  162. package/scripts/changeset/cli.cjs +6 -5
  163. package/scripts/changeset/lint.cjs +0 -0
  164. package/scripts/changeset/new.cjs +33 -50
  165. package/scripts/dev-env.cjs +25 -85
  166. package/scripts/gen-capability-registry.cjs +199 -11
  167. package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
  168. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  169. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  170. package/scripts/lint-docs-required.cjs +0 -0
  171. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  172. package/scripts/lint-test-file-count.allowlist.json +12 -10
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -17
  175. package/scripts/record-vault-mode-golden.cjs +89 -0
  176. package/scripts/release-channel.cjs +0 -0
  177. package/scripts/sync-manifest-versions.cjs +5 -4
  178. package/scripts/sync-rulesets.sh +1 -1
  179. package/scripts/update-size-baseline.cjs +30 -274
  180. package/scripts/vault-calibrate.cjs +996 -0
  181. package/scripts/verify-opengsd-sweep.cjs +3 -5
  182. package/scripts/workflow-size.cjs +9 -61
  183. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  186. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  187. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  188. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  189. package/pp-core/references/executor-vault-trace.md +0 -47
  190. package/pp-core/references/planner-plan-graph.md +0 -66
  191. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  192. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  193. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  194. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  195. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  196. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  197. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  198. package/scripts/lib/size-budget.cjs +0 -311
  199. package/scripts/lint-neutrality-names.cjs +0 -694
  200. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  201. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -15,6 +15,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  return (mod && mod.__esModule) ? mod : { "default": mod };
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.GIT_LOCATION_UNSET = exports.GIT_LOCATION_VARS = void 0;
18
19
  exports.hookCommandNeedsPowerShellCallOperator = hookCommandNeedsPowerShellCallOperator;
19
20
  exports.formatHookCommandForRuntime = formatHookCommandForRuntime;
20
21
  exports.shellHookOmitsBashRunner = shellHookOmitsBashRunner;
@@ -36,6 +37,7 @@ exports.renderShellActionLines = renderShellActionLines;
36
37
  exports.projectPathActionProjection = projectPathActionProjection;
37
38
  exports.projectPersistentPathExportActions = projectPersistentPathExportActions;
38
39
  exports.execGit = execGit;
40
+ exports.execGitLocal = execGitLocal;
39
41
  exports.execNpm = execNpm;
40
42
  exports.execTool = execTool;
41
43
  exports.probeTty = probeTty;
@@ -412,10 +414,24 @@ function _spawnResult(result, program) {
412
414
  error: result.error ?? null,
413
415
  };
414
416
  }
417
+ /**
418
+ * Run git bounded and non-interactive; never throws (`_spawnResult` maps a
419
+ * spawn error, a timeout or ENOBUFS to `exitCode: 1`).
420
+ *
421
+ * `input` (16-03) is written to git's stdin when given and nothing is passed
422
+ * otherwise, so every existing call behaves exactly as before. SEAL-01 passes
423
+ * revisions for `rev-list --stdin` through it rather than as argv, because a
424
+ * Windows command line is capped at 32767 characters (CreateProcess) and a
425
+ * long-lived project can record more anchors than that holds (16-RESEARCH
426
+ * Pitfall 6).
427
+ */
415
428
  function execGit(args, opts = {}) {
416
429
  // Non-interactive defaults: a hung credential prompt or terminal-input
417
430
  // probe must surface as a timeout, not block the tool forever. Callers
418
- // can override via opts.env.
431
+ // can override via opts.env. A key whose opts.env value is `undefined`
432
+ // is UNSET for this call rather than inherited: spawnSync ignores an
433
+ // undefined env value (260915-mzx R2, proven by the inherited-GIT_DIR arm
434
+ // in tests/vault.test.cjs running real git).
419
435
  const env = {
420
436
  ...process.env,
421
437
  GIT_TERMINAL_PROMPT: '0',
@@ -429,9 +445,96 @@ function execGit(args, opts = {}) {
429
445
  stdio: 'pipe',
430
446
  timeout: opts.timeout ?? 10_000,
431
447
  windowsHide: true,
448
+ ...(opts.input !== undefined ? { input: opts.input } : {}),
432
449
  });
433
450
  return _spawnResult(result, 'git');
434
451
  }
452
+ /**
453
+ * The repository-location variables: the ones through which git decides WHICH
454
+ * repository, work tree, index, object store, config file, ref namespace and
455
+ * history shape a command answers for (D-116, 16-UAT test 8). A git hook exports
456
+ * `GIT_DIR`; an agent shell may leave any of them pointing at another checkout. A
457
+ * vault git invocation must answer for the repository it names by cwd or argv,
458
+ * whatever the caller inherited.
459
+ *
460
+ * The list is git's, not hand-built (D-121, code review iteration 4 CR-01/WR-01):
461
+ * every name `git rev-parse --local-env-vars` prints (git 2.55.0) MINUS the stated
462
+ * keep-list `GIT_CONFIG_PARAMETERS` and `GIT_CONFIG_COUNT` — the `GIT_CONFIG_*`
463
+ * channel D-117 keeps, with its `GIT_CONFIG_KEY_n`/`GIT_CONFIG_VALUE_n` pairs, for
464
+ * the suite's hermetic config and the golden harness's insteadOf rewrite — PLUS
465
+ * three names git does not list but that move discovery or the ref namespace
466
+ * (`GIT_CEILING_DIRECTORIES`, `GIT_DISCOVERY_ACROSS_FILESYSTEM`, `GIT_NAMESPACE`).
467
+ * It stays a static tuple: the engine never shells out to git to build it, and a
468
+ * test compares it against the live git so a name a future git adds cannot arrive
469
+ * unremarked.
470
+ *
471
+ * Four of them move an ancestry answer inside ONE repository (measured, git
472
+ * 2.55.0): `GIT_SHALLOW_FILE` naming the second of three commits dropped
473
+ * `rev-list --count HEAD` from 3 to 2 and flipped `merge-base --is-ancestor
474
+ * <first> HEAD` to no; `GIT_REPLACE_REF_BASE` naming a planted `refs/evil/`
475
+ * replacement of HEAD made `rev-parse HEAD^` answer the first commit;
476
+ * `GIT_NO_REPLACE_OBJECTS=1` hides a repository's own `refs/replace/` entries the
477
+ * same way; `GIT_GRAFT_FILE` naming an ungoverned commit made it a root, and the
478
+ * strict `check vault.trace` then answered clean over it (CR-01). `GIT_CONFIG` is
479
+ * a location variable, not transport: it names ONE config file and is read only
480
+ * by the `git config` command, for reads and writes alike, so inherited it sent
481
+ * promote's identity step into a foreign file (WR-01). This tuple is the only
482
+ * enumeration: `GIT_LOCATION_UNSET` is derived from it.
483
+ */
484
+ exports.GIT_LOCATION_VARS = Object.freeze([
485
+ 'GIT_DIR',
486
+ 'GIT_WORK_TREE',
487
+ 'GIT_COMMON_DIR',
488
+ 'GIT_INDEX_FILE',
489
+ 'GIT_OBJECT_DIRECTORY',
490
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
491
+ 'GIT_PREFIX',
492
+ 'GIT_CEILING_DIRECTORIES',
493
+ 'GIT_DISCOVERY_ACROSS_FILESYSTEM',
494
+ 'GIT_NAMESPACE',
495
+ 'GIT_SHALLOW_FILE',
496
+ 'GIT_REPLACE_REF_BASE',
497
+ 'GIT_NO_REPLACE_OBJECTS',
498
+ 'GIT_GRAFT_FILE',
499
+ 'GIT_IMPLICIT_WORK_TREE',
500
+ 'GIT_CONFIG',
501
+ ]);
502
+ /**
503
+ * The overlay `execGitLocal` applies: every `GIT_LOCATION_VARS` member as an own
504
+ * property equal to `undefined` (spawnSync treats an undefined env value as unset).
505
+ * Derived from the tuple, never written out, so there is one list. Exported only
506
+ * for a spawn that cannot go through `execGitLocal` — the `glab` clone, which
507
+ * runs through `execTool` and whose own git child inherits this environment
508
+ * (D-116).
509
+ */
510
+ exports.GIT_LOCATION_UNSET = Object.freeze(Object.fromEntries(exports.GIT_LOCATION_VARS.map((name) => [name, undefined])));
511
+ /**
512
+ * Run git exactly as `execGit` does, with every inherited repository-location
513
+ * variable (`GIT_LOCATION_VARS`) unset — the one owner of the environment of a
514
+ * vault git invocation (D-116, D-117). "Local" means the repository is chosen by
515
+ * cwd or argv and never by the environment, which is why the URL-addressed
516
+ * transport invocations (clones, `ls-remote`) use it too: git still reads the
517
+ * config of the repository the environment selects, and a clone refuses an
518
+ * inherited `GIT_WORK_TREE` and writes into an inherited `GIT_INDEX_FILE`.
519
+ *
520
+ * It is location-only, not the whole `GIT_*` prefix: transport (`GIT_SSH_COMMAND`,
521
+ * `GIT_ASKPASS`, the credential-helper family) is kept for the base-branch
522
+ * ladder's remote tier, every clone and the promote push; the `GIT_CONFIG_*`
523
+ * channel (`GIT_CONFIG_PARAMETERS`, `GIT_CONFIG_COUNT` and its pairs, the keep-list
524
+ * D-117/D-121 name) is kept because it keeps the suite's reads hermetic and carries
525
+ * the golden harness's insteadOf rewrite to its local bare vault; the legacy
526
+ * one-file `GIT_CONFIG` is a location variable and is unset. The two reads that
527
+ * must depend on no inherited git variable at all (certify, the cache resolver)
528
+ * keep the whole-prefix `scrubbedGitEnv` in `src/vault.cts`, a superset of this
529
+ * overlay.
530
+ *
531
+ * The overlay is applied LAST, over any caller `opts.env`, so no call site can
532
+ * re-point the repository; `cwd`, `timeout` and `input` are forwarded unchanged.
533
+ */
534
+ function execGitLocal(args, opts = {}) {
535
+ const env = { ...(opts.env || {}), ...exports.GIT_LOCATION_UNSET };
536
+ return execGit(args, { ...opts, env });
537
+ }
435
538
  function execNpm(args, opts = {}) {
436
539
  const result = node_child_process_1.default.spawnSync('npm', args, {
437
540
  cwd: opts.cwd,
@@ -19,7 +19,7 @@ const configLoaderMod = require("./config-loader.cjs");
19
19
  const { loadConfig } = configLoaderMod;
20
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
21
  const phaseIdMod = require("./phase-id.cjs");
22
- const { escapeRegex, normalizePhaseName } = phaseIdMod;
22
+ const { escapeRegex } = phaseIdMod;
23
23
  // eslint-disable-next-line @typescript-eslint/no-require-imports
24
24
  const roadmapParserMod = require("./roadmap-parser.cjs");
25
25
  const { getDeclaredMilestoneInfo, getMilestonePhaseFilter, extractCurrentMilestone, getClosedMilestonePhaseDirFilter } = roadmapParserMod;
@@ -76,9 +76,9 @@ function cmdStateLoad(cwd, raw) {
76
76
  const configExists = node_fs_1.default.existsSync(node_path_1.default.join(planDir, 'config.json'));
77
77
  const roadmapExists = node_fs_1.default.existsSync(node_path_1.default.join(planDir, 'ROADMAP.md'));
78
78
  const stateExists = stateRaw.length > 0;
79
- // Pack 6 D-07: the emitted config is display-safe for its remote coordinate
80
- // key (config.cjs's projection); `config` itself stays raw. Lazily required
81
- // so this module's load graph is unchanged.
79
+ // VAULT-EGRESS-1: the emitted config is display-safe for its one coordinate key (the
80
+ // shared-tier repo), through config.cjs's projection; `config` itself stays raw. Lazily
81
+ // required so this module's load graph is unchanged.
82
82
  // eslint-disable-next-line @typescript-eslint/no-require-imports
83
83
  const { redactConfigCoordinates } = require('./config.cjs');
84
84
  const result = {
@@ -181,23 +181,17 @@ function cmdStatePatch(cwd, patches, raw) {
181
181
  const shouldResync = shouldResyncStateProgress(Object.keys(patches));
182
182
  // Use atomic read-modify-write to prevent lost updates from concurrent agents
183
183
  const fmReport = readModifyWriteStateMd(statePath, (content) => {
184
- // Patch the BODY only, as cmdStateUpdate does. The plain-line pattern is
185
- // case-insensitive, so on the whole file `Status` matched the frontmatter
186
- // `status:` line first; the re-derivation then restored it from the untouched
187
- // body, and the verb reported a write it never made.
188
- let body = stripFrontmatter(content);
189
- const head = content.slice(0, content.length - body.length);
190
184
  for (const [field, value] of Object.entries(patches)) {
191
- const result = (0, state_document_cjs_1.stateReplaceField)(body, field, value);
185
+ const result = (0, state_document_cjs_1.stateReplaceField)(content, field, value);
192
186
  if (result) {
193
- body = result;
187
+ content = result;
194
188
  results.updated.push(field);
195
189
  }
196
190
  else {
197
191
  results.failed.push(field);
198
192
  }
199
193
  }
200
- return head + body;
194
+ return content;
201
195
  }, cwd, { resync: shouldResync });
202
196
  output({ ...results, frontmatter_updated: fmReport.frontmatter_updated }, raw, results.updated.length > 0 ? 'true' : 'false');
203
197
  }
@@ -1285,20 +1279,7 @@ function cmdStateSnapshot(cwd, raw) {
1285
1279
  * or the first Built phase of a milestone would be counted Complete.
1286
1280
  */
1287
1281
  function roadmapCompletedPhases(roadmapRaw, roadmapScope) {
1288
- // The phases the current milestone's scope names. A progress table may also carry a
1289
- // shipped milestone's rows (the template's milestone-grouped layout keeps them, with a
1290
- // Milestone column): those rows grant nothing to this milestone.
1291
- const scopedIds = new Set();
1292
- const headingPattern = /#{2,4}\s*Phase\s+([\w][\w.-]*)\s*:/gi;
1293
- let hm;
1294
- while ((hm = headingPattern.exec(roadmapScope)) !== null) {
1295
- if (/\d/.test(hm[1]))
1296
- scopedIds.add(normalizePhaseName(hm[1]));
1297
- }
1298
1282
  const fromText = (text) => {
1299
- const table = countCompleteProgressRows(text, scopedIds);
1300
- if (table !== null)
1301
- return table;
1302
1283
  const derived = deriveProgressFromRoadmap(text);
1303
1284
  if (derived.completedPhases !== null)
1304
1285
  return derived.completedPhases;
@@ -1309,51 +1290,6 @@ function roadmapCompletedPhases(roadmapRaw, roadmapScope) {
1309
1290
  return scoped;
1310
1291
  return fromText(extractCurrentMilestone(roadmapRaw));
1311
1292
  }
1312
- /**
1313
- * Column-aware read of a ROADMAP progress table: the header row names the `Phase` and
1314
- * `Status` columns, so a table with a Milestone column (pp-core/templates/roadmap.md's
1315
- * milestone-grouped layout) is read as well as the four-column one. Counts the data
1316
- * rows whose Status cell is `Complete`, among the rows whose phase is in `phaseIds`
1317
- * (all rows when `phaseIds` is empty). Returns null when no such table is present, or
1318
- * when the table lists none of `phaseIds` — a table that says nothing about this
1319
- * milestone's phases is silence, not a grant of zero.
1320
- */
1321
- function countCompleteProgressRows(text, phaseIds) {
1322
- const cellsOf = (line) => {
1323
- const t = line.trim();
1324
- if (!t.startsWith('|'))
1325
- return null;
1326
- return t.replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim());
1327
- };
1328
- const lines = text.split(/\r?\n/);
1329
- for (let i = 0; i + 1 < lines.length; i++) {
1330
- const header = cellsOf(lines[i]);
1331
- if (!header)
1332
- continue;
1333
- const phaseCol = header.findIndex((c) => /^phase$/i.test(c));
1334
- const statusCol = header.findIndex((c) => /^status$/i.test(c));
1335
- const separator = cellsOf(lines[i + 1]);
1336
- if (phaseCol === -1 || statusCol === -1 || !separator || !separator.every((c) => /^:?-+:?$/.test(c)))
1337
- continue;
1338
- let listed = 0;
1339
- let complete = 0;
1340
- for (let j = i + 2; j < lines.length; j++) {
1341
- const cells = cellsOf(lines[j]);
1342
- if (!cells)
1343
- break;
1344
- const id = cells[phaseCol] ?? '';
1345
- if (!/^\d/.test(id))
1346
- continue;
1347
- if (phaseIds.size > 0 && !phaseIds.has(normalizePhaseName(id)))
1348
- continue;
1349
- listed++;
1350
- if (/^complete$/i.test(cells[statusCol] ?? ''))
1351
- complete++;
1352
- }
1353
- return listed > 0 ? complete : null;
1354
- }
1355
- return null;
1356
- }
1357
1293
  /**
1358
1294
  * Read the milestone-scoped ROADMAP once and return the two counts the progress
1359
1295
  * block takes from it: the digit-bearing phase headings (single source of truth
@@ -2406,23 +2342,13 @@ function updatePerformanceMetricsSection(content, cwd, phaseNum, planCount, summ
2406
2342
  const byPhaseMatch = content.match(byPhaseTablePattern);
2407
2343
  if (byPhaseMatch) {
2408
2344
  let tableBody = byPhaseMatch[2].trim();
2409
- // The row for this phase is found by phase IDENTITY, not by the literal argument:
2410
- // `phase complete 4` must update a row spelled `04` (and vice versa), never append
2411
- // a second row for the same phase. The row keeps its own id spelling.
2412
- const wanted = normalizePhaseName(String(phaseNum));
2413
- const lines = tableBody.split('\n');
2414
- const rowIdx = lines.findIndex((line) => {
2415
- const cell = /^\|\s*([^|]*?)\s*\|/.exec(line);
2416
- return cell !== null && /^[A-Za-z0-9.-]+$/.test(cell[1]) && normalizePhaseName(cell[1]) === wanted;
2417
- });
2418
- if (rowIdx !== -1) {
2345
+ const phaseRowPattern = new RegExp(`^\\|\\s*${escapeRegex(String(phaseNum))}\\s*\\|.*$`, 'm');
2346
+ const newRow = `| ${phaseNum} | ${summaryCount} | - | - |`;
2347
+ if (phaseRowPattern.test(tableBody)) {
2419
2348
  // Update existing row
2420
- const rowId = /^\|\s*([^|]*?)\s*\|/.exec(lines[rowIdx])[1];
2421
- lines[rowIdx] = `| ${rowId} | ${summaryCount} | - | - |`;
2422
- tableBody = lines.join('\n');
2349
+ tableBody = tableBody.replace(phaseRowPattern, newRow);
2423
2350
  }
2424
2351
  else {
2425
- const newRow = `| ${phaseNum} | ${summaryCount} | - | - |`;
2426
2352
  // Remove placeholder row and add new row
2427
2353
  tableBody = tableBody.replace(/^\|\s*-\s*\|\s*-\s*\|\s*-\s*\|\s*-\s*\|$/m, '').trim();
2428
2354
  tableBody = tableBody ? tableBody + '\n' + newRow : newRow;
@@ -79,7 +79,7 @@ function cmdTemplateFill(cwd, templateType, options, raw) {
79
79
  if (!options.phase) {
80
80
  error('--phase required');
81
81
  }
82
- // D-02 (todo "template-fill-writes-into-an-archived-
82
+ // PBI 75323 / quick 261009-gvf D-02 (todo "template-fill-writes-into-an-archived-
83
83
  // phase-directory"): a writer never resolves into a closed milestone's archive. The
84
84
  // phase-scope owner hands back a live match only; an archive this phase still names
85
85
  // (current milestone, or no longer on the ROADMAP) is refused naming the archive, and a