@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
@@ -18,10 +18,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  * no vault cache is present.
19
19
  *
20
20
  * Responsibility (Phase 24-02 — matcher + global-bucket + formatter half):
21
- * `globMatch`/`fallbackGlobToRegex` provide deterministic glob matching
22
- * through ONE hand-rolled, polynomial-time matcher on every runtime (the
23
- * native `path.posix.matchesGlob` backtracks on many-star segments and is
24
- * used only as the tests' oracle). `matchPractices` partitions discovered practices into a
21
+ * `globMatch`/`fallbackGlobToRegex` provide deterministic glob matching,
22
+ * feature-detecting `path.posix.matchesGlob` (Node >=22.5.0) with a hand-
23
+ * rolled, linear-time regex fallback for the 22.0–22.4 gap (engines floor
24
+ * is >=22.0.0). `matchPractices` partitions discovered practices into a
25
25
  * glob-matched `matched` bucket and an always-surfaced `global` bucket (a
26
26
  * bounded, fenced excerpt per global practice — the matcher itself makes
27
27
  * ZERO calls to any hosted model or scoring service; relevance of globals
@@ -35,8 +35,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  * - `./vault.cjs` (`usableFiles`, `isSecretFile`, `readManifest`,
36
36
  * `readHeadBounded`) — the existing vault-tier discovery/secret/manifest/
37
37
  * bounded-read helpers.
38
- * - no `minimatch`/`picomatch` dependency (RESEARCH STACK.md), and no
39
- * native `path.posix.matchesGlob` on the matching path (see `globMatch`).
38
+ * - `node:path`'s `path.posix.matchesGlob` (feature-detected) — no
39
+ * `minimatch`/`picomatch` dependency (RESEARCH STACK.md).
40
40
  *
41
41
  * Out of scope here (later plans): the CLI `practices match` router wiring
42
42
  * and cross-tier client-beats-shared id dedup (explicitly DEFERRED to
@@ -59,14 +59,16 @@ const node_fs_1 = __importDefault(require("node:fs"));
59
59
  const fmMod = require("./frontmatter.cjs");
60
60
  // eslint-disable-next-line @typescript-eslint/no-require-imports
61
61
  const vaultMod = require("./vault.cjs");
62
- // FIX-3: the ONE version resolver — the same function and
62
+ // D-17/D-26 (14-04): the fence markers and their defang have ONE owner, the
63
+ // dependency-free vault-index leaf, which src/vault.cts can import without the
64
+ // cycle this module's vault.cjs import would create.
65
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
66
+ const vaultIndexLeaf = require("./vault-index.cjs");
67
+ // Quick 261003-jby (FIX-3): the ONE version resolver — the same function and
63
68
  // candidate ladder `project.createdWith` is written from — so the block's
64
69
  // status stamp and the bindings can never name different engine versions.
65
70
  // compose-bindings imports nothing from vault-practices, so no cycle.
66
71
  const compose_bindings_cjs_1 = require("./compose-bindings.cjs");
67
- // The one source/non-source predicate (plan graph). A leaf with no imports of
68
- // its own, so no cycle.
69
- const plan_graph_cjs_1 = require("./plan-graph.cjs");
70
72
  const { extractFrontmatter } = fmMod;
71
73
  // ─── Constants ─────────────────────────────────────────────────────────────────
72
74
  // DoS-guard cap (T-24-04) mirroring vault.cts's SUMMARY_FILE_CAP discipline —
@@ -82,7 +84,7 @@ const PRACTICES_SUBDIR = 'practices/';
82
84
  // retained + warned, never dropped).
83
85
  //
84
86
  // `check` joined the set in 05-01 and `enforced-by` in 10-03 (MECH-03,
85
- // D-10-04). Both are step 1 of the degrade-loudly rule's fixed rollout order (engine
87
+ // D-10-04). Both are step 1 of ADR-71924 §5's fixed rollout order (engine
86
88
  // release → verified rollout → vault-repo merge): the engine admits the key
87
89
  // first, the rollout is verified, and only then does the vault repo merge
88
90
  // practices that use it. Neither addition bumped PRACTICE_SCHEMA_VERSION
@@ -114,7 +116,7 @@ const GLOB_PATTERN_MAX = 200;
114
116
  // Bounded markdown-body excerpt size for scope:global practices (D-15) —
115
117
  // mirrors vault.cts's SUMMARY_READ_BYTES bounded-read discipline.
116
118
  const PRACTICE_EXCERPT_BYTES = 1024;
117
- // Over-read factor for the excerpt's bounded read (FIX-1).
119
+ // Over-read factor for the excerpt's bounded read (quick 261003-jby, FIX-1).
118
120
  // The excerpt is normalised to LF before it is capped, and every carriage
119
121
  // return that normalisation removes consumed a raw byte as half of a CRLF pair.
120
122
  // Reading `PRACTICE_EXCERPT_BYTES * 2` body bytes therefore guarantees the cap
@@ -124,12 +126,17 @@ const EXCERPT_CRLF_OVERREAD_FACTOR = 2;
124
126
  // Matched-file list cap per practice (T-24-04) — matchedCount still carries
125
127
  // the true total so callers know the cap was hit.
126
128
  const MATCHED_FILES_CAP = 20;
127
- // The unmatched-source-file note's candidate set (FIX-2, QD-2) is the plan
128
- // graph's own source predicate (`isSourceFile`, src/plan-graph.cts) — the one
129
- // owner of "is this a source file" — not a second list of extensions. When a
130
- // source file matched no scoped practice the block says so in one plain line,
129
+ // The unmatched-source-file note's candidate set (quick 261003-jby, FIX-2,
130
+ // QD-2): a file passed to the matcher whose basename ends with one of these
131
+ // extensions (case-insensitive), or starts with the Dockerfile prefix (exact),
132
+ // is one a scoped practice is EXPECTED to govern in the client's stacks. When
133
+ // such a file matched no scoped practice the block says so in one plain line,
131
134
  // because a vault laid out for a different folder structure otherwise drops its
132
- // scoped practices with no word at all.
135
+ // scoped practices with no word at all (probe test 6: 20 scoped practices, 5 of
136
+ // them critical, silently matched nothing). Frozen constants so widening the
137
+ // set is a one-line edit, not a decision buried in a predicate.
138
+ const UNMATCHED_NOTE_SOURCE_EXTENSIONS = Object.freeze(['.cs', '.sql', '.ts', '.vue', '.kt']);
139
+ const UNMATCHED_NOTE_BASENAME_PREFIX = 'Dockerfile';
133
140
  // Bounded prefix read for practice files during DISCOVERY (CR-03) — frontmatter
134
141
  // + bodyOffset comfortably fit well inside this; the whole file is NEVER read.
135
142
  // Mirrors vault.cts's SUMMARY_READ_BYTES bounded-read discipline.
@@ -154,9 +161,6 @@ const WARNING_TOKEN_MAX = 80;
154
161
  // pre-22.5 fallback matcher 0.098 ms / pair (same corpus, feature
155
162
  // detect deleted before require)
156
163
  //
157
- // (The fallback matcher is now the only shipped engine — see `globMatch`.
158
- // The native figure is kept as the measurement the caps below were sized on.)
159
- //
160
164
  // 05-RESEARCH § OQ1 measured 1.47 ms/pair native over three points that were
161
165
  // linear in pair count, so `cost ≈ pairs × rate` is a measured model rather
162
166
  // than a hoped-for one. Its 5.0 ms/pair fallback figure did NOT reproduce on
@@ -181,8 +185,8 @@ const WARNING_TOKEN_MAX = 80;
181
185
  // makes the verdict machine-dependent, which is exactly the non-determinism
182
186
  // VERIFY-03 bars from a deterministic gate).
183
187
  //
184
- // Sized above the legitimate ceiling and well below the hostile one: a
185
- // measured production vault's 92 globs against the 2000-file ship diff cap is 184,000
188
+ // Sized above the legitimate ceiling and well below the hostile one: the real
189
+ // client vault's 92 globs against the 2000-file ship diff cap is 184,000
186
190
  // pairs, so this leaves ~5× headroom over any honest corpus while sitting
187
191
  // ~7.8× below the 7,800,000-pair hostile saturation bound.
188
192
  //
@@ -239,13 +243,13 @@ const PRACTICES_VAULT_UNREADABLE_SENTENCE = 'Practices were NOT checked — the
239
243
  */
240
244
  const PRACTICES_VAULT_ABSENT_SENTENCE = 'Practices were NOT checked — no client vault has been fetched. Run the vault fetch first.';
241
245
  /**
242
- * The FOURTH member of the family (FIX-4, QD-5).
246
+ * The FOURTH member of the family (quick 261003-jby, FIX-4, QD-5).
243
247
  *
244
248
  * A fetch that failed every tier with no prior copy leaves a manifest recording
245
249
  * a miss: no tier, no sha, no files. The vault directory is present, nothing
246
250
  * failed to READ, and discovery yields zero practices — so the ladder fell
247
251
  * through to "none found in the vault", telling a code-writing agent its client
248
- * documented no standards about a fetch that reached nothing.
252
+ * documented no standards about a fetch that reached nothing (probe test 2).
249
253
  *
250
254
  * The documented "could not be checked: <reason>." form, with the reason
251
255
  * phrase copied from the practice trace's own miss reason (`resolvePin` in
@@ -253,7 +257,18 @@ const PRACTICES_VAULT_ABSENT_SENTENCE = 'Practices were NOT checked — no clien
253
257
  */
254
258
  const PRACTICES_VAULT_MISS_SENTENCE = 'Practices could not be checked: the vault manifest records a miss — no vault content was reached.';
255
259
  /**
256
- * The status stamp's fixed opening (FIX-3, QD-4).
260
+ * The FIFTH member of the family (quick 261015-at3, Vince 2026-10-15).
261
+ *
262
+ * The vault was fetched and read, and it is a standards vault: `INDEX.md` at
263
+ * its root and no practice files. The project reads it in practices mode, so
264
+ * discovery yields zero practices and the ladder fell through to "none found in
265
+ * the vault" — telling a code-writing agent the client documented no practices,
266
+ * about a vault whose standards the project is not reading at all. Names the
267
+ * one repair: the mode, and the engine version that has it.
268
+ */
269
+ const PRACTICES_VAULT_STANDARDS_ONLY_SENTENCE = 'Practices were NOT checked — this vault holds standards (INDEX.md) and no practice files, but the project reads it in practices mode. Set "vault": {"mode": "standards"} in .planning/config.json (pp-core 1.16.0 or later) and re-plan.';
270
+ /**
271
+ * The status stamp's fixed opening (quick 261003-jby, FIX-3, QD-4).
257
272
  *
258
273
  * Every rendered `<vault_practices>` block carries ONE HTML comment as its
259
274
  * first inner line, written by this module, naming the vault ref, short sha,
@@ -273,6 +288,9 @@ const PRACTICES_STAMP_STATES = Object.freeze({
273
288
  VAULT_UNREADABLE: 'vault-unreadable',
274
289
  VAULT_ABSENT: 'vault-absent',
275
290
  VAULT_MISS: 'vault-miss',
291
+ // Quick 261015-at3: a standards vault (INDEX.md, no practice files) read in
292
+ // practices mode.
293
+ STANDARDS_ONLY: 'standards-only',
276
294
  ALL_REFUSED: 'all-refused',
277
295
  NONE_FOUND: 'none-found',
278
296
  NONE_APPLY: 'none-apply',
@@ -358,6 +376,12 @@ function boundedToken(v) {
358
376
  const s = safeStringify(v).replace(/[\r\n]+/g, ' ');
359
377
  return s.length > WARNING_TOKEN_MAX ? `${s.slice(0, WARNING_TOKEN_MAX)}…` : s;
360
378
  }
379
+ // ─── Glob matching (PRAC-02) ───────────────────────────────────────────────────
380
+ // Feature-detect once at module scope: `path.posix.matchesGlob` landed in
381
+ // Node 22.5.0 and stabilized in 22.20.0 (RESEARCH STACK.md), but this repo's
382
+ // engines floor is >=22.0.0 — the 22.0–22.4 gap must fall back to the
383
+ // hand-rolled matcher below rather than throwing `TypeError: not a function`.
384
+ const hasMatchesGlob = typeof node_path_1.default.posix?.matchesGlob === 'function';
361
385
  const NEVER_MATCHES = { test: () => false };
362
386
  // Bounds the total number of concrete patterns a `{a,b,c}` expansion can
363
387
  // produce (T-24-04) — client-authored globs never legitimately need more.
@@ -498,7 +522,7 @@ function globSegmentsMatch(patternSegs, subjectSegs) {
498
522
  *
499
523
  * ON THE NAME (05-04). Despite it, this function constructs NO `RegExp` — it
500
524
  * returns an object literal whose `test` runs the two-pointer matcher above.
501
- * the declarative-data rule says the same thing ("despite its name") and the confusion has
525
+ * ADR-71924 §3 says the same thing ("despite its name") and the confusion has
502
526
  * now cost more than one verification pass, so it is stated here at the
503
527
  * definition rather than only in the ADR. A rename to `compileGlobMatcher`
504
528
  * was considered and REJECTED: the rename guard's 2331-file baseline is not
@@ -521,18 +545,23 @@ function fallbackGlobToRegex(pattern) {
521
545
  }
522
546
  /**
523
547
  * True when `filePath` matches `pattern`. Normalizes `\` → `/` (Windows path
524
- * input) before matching, then runs the polynomial-time `fallbackGlobToRegex`
525
- * matcher on every runtime (see the note above `CompiledGlob` for why the
526
- * native engine is not used). Deterministic: identical inputs always yield the
527
- * identical boolean (PRAC-02).
548
+ * input) before matching. Uses `path.posix.matchesGlob` when the runtime
549
+ * supports it (feature-detected once at module scope), else the linear/
550
+ * polynomial-time `fallbackGlobToRegex` matcher. Deterministic: identical
551
+ * inputs always yield the identical boolean (PRAC-02).
528
552
  */
529
553
  function globMatch(filePath, pattern) {
530
- // Length guard (T-24-04): GLOB_PATTERN_MAX holds here as well as inside the
531
- // compiler, so an over-long pattern never matches on any path.
554
+ // Pre-dispatch length guard (T-24-04, gap #1 verifier ask b): apply
555
+ // GLOB_PATTERN_MAX BEFORE choosing an engine so the cap holds for the
556
+ // native `path.posix.matchesGlob` path too, not only the fallback
557
+ // compiler's internal check.
532
558
  if (typeof pattern !== 'string' || pattern.length === 0 || pattern.length > GLOB_PATTERN_MAX) {
533
559
  return false;
534
560
  }
535
561
  const normalized = filePath.replace(/\\/g, '/');
562
+ if (hasMatchesGlob) {
563
+ return node_path_1.default.posix.matchesGlob(normalized, pattern);
564
+ }
536
565
  return fallbackGlobToRegex(pattern).test(normalized);
537
566
  }
538
567
  // Unsupported glob metacharacters (gap #1 closure option a, D-14/D-07):
@@ -642,13 +671,13 @@ function normalizeCheckBoolean(v) {
642
671
  }
643
672
  /**
644
673
  * Parse and validate a practice's `check:` block. THE ONLY parser for this
645
- * shape (the one-parser rule): 05-05's evaluator receives a validated record and
674
+ * shape (ADR-71924 §2): 05-05's evaluator receives a validated record and
646
675
  * validates nothing itself, so a rule that is not enforced here is not
647
676
  * enforced anywhere.
648
677
  *
649
678
  * A violation never skips the practice. The block is dropped whole — never
650
679
  * half-built — and one bounded, single-line `checkInvalid` records the defect,
651
- * because the practice's prose still applies (the degrade-loudly rule). 05-05 maps
680
+ * because the practice's prose still applies (ADR §5). 05-05 maps
652
681
  * `checkInvalid` to a fail-closed FAIL so a broken block can never read as a
653
682
  * pass, nor be mistaken for "this practice was never checkable".
654
683
  *
@@ -657,7 +686,7 @@ function normalizeCheckBoolean(v) {
657
686
  * the evaluator and the reader drifting apart. The literal half is bounded by
658
687
  * CHECK_TOKEN_COUNT_CAP and CHECK_TOKEN_MAX, and is compared only by plain
659
688
  * substring containment: no client-authored byte reaches a RegExp constructor
660
- * or any other pattern compiler (the declarative-data rule, T-5-05).
689
+ * or any other pattern compiler (ADR §3, T-5-05).
661
690
  */
662
691
  function parseCheckBlock(id, rawCheck) {
663
692
  const invalid = (defect) => ({
@@ -827,7 +856,7 @@ function parsePracticeFile(filePath, raw) {
827
856
  // Unknown-key DEGRADE (IN-05, inverted by CHECK-02 in 05-01).
828
857
  //
829
858
  // This branch used to `return { ok: false }`, dropping the whole practice.
830
- // the degrade-loudly rule requires the opposite: an engine that meets a key it does
859
+ // ADR-71924 §5 requires the opposite: an engine that meets a key it does
831
860
  // not recognize must degrade LOUDLY — retain the practice, apply its prose
832
861
  // through the normal path, and warn naming the key. Dropping it meant the
833
862
  // practice silently governed nothing, which is the "governance check that
@@ -939,7 +968,7 @@ function parsePracticeFile(filePath, raw) {
939
968
  // Note the asymmetry with the `< 1` / non-integer arm just above, which
940
969
  // stays a HARD skip: a malformed value is not a forward-compat signal,
941
970
  // it is a broken file. Only a well-formed version this engine is too old
942
- // to understand degrades — that is the case the degrade-loudly rule's rollout order
971
+ // to understand degrades — that is the case ADR-71924 §5's rollout order
943
972
  // exists to make legible rather than silent.
944
973
  if (parsed > PRACTICE_SCHEMA_VERSION) {
945
974
  degradedWarnings.push(`${safePath} degraded: schemaVersion ${parsed} is newer than this pp-core supports ` +
@@ -951,7 +980,7 @@ function parsePracticeFile(filePath, raw) {
951
980
  const fmMatch = raw.match(/^---\r?\n[\s\S]+?\r?\n---/);
952
981
  const bodyOffset = fmMatch ? fmMatch[0].length : 0;
953
982
  // The check: block is validated HERE, alongside every other parse-time
954
- // rule, so there is exactly one parser for it (the one-parser rule). An invalid
983
+ // rule, so there is exactly one parser for it (ADR-71924 §2). An invalid
955
984
  // block never skips the practice — see `parseCheckBlock`.
956
985
  const checkOutcome = fm.check === undefined ? null : parseCheckBlock(id, fm.check);
957
986
  // `enforced-by:` is read HERE, in the position parseCheckBlock occupies —
@@ -1062,7 +1091,8 @@ function probePracticesDir(practicesDirPath) {
1062
1091
  *
1063
1092
  * @param cwd Project root (parent of `.planning/`).
1064
1093
  * @param vaultDir Optional cache directory to read instead of
1065
- * `<cwd>/.planning/vault` (worktree executors). Defaults to the cwd
1094
+ * `<cwd>/.planning/vault` (260915-ms2, UAT-6-F3 — the trace writer passes the
1095
+ * owning main checkout's cache from a linked worktree). Defaults to the cwd
1066
1096
  * join, so the absent-versus-unreadable distinction below is unchanged for
1067
1097
  * every caller that does not pass it.
1068
1098
  */
@@ -1078,7 +1108,7 @@ function discoverPracticeFiles(cwd, vaultDir) {
1078
1108
  // grounds stay separable for whoever has to repair it.
1079
1109
  return {
1080
1110
  practices: [], skipped: [], degraded: [], tier: null, notes: [], vaultPresent: false, readFailed: false,
1081
- vaultRef: null, vaultSha: null, vaultMiss: false,
1111
+ vaultRef: null, vaultSha: null, vaultMiss: false, vaultStandardsOnly: false,
1082
1112
  };
1083
1113
  }
1084
1114
  const probe = probePracticesDir(node_path_1.default.join(vaultDirPath, PRACTICES_SUBDIR.slice(0, -1)));
@@ -1095,12 +1125,24 @@ function discoverPracticeFiles(cwd, vaultDir) {
1095
1125
  }
1096
1126
  const manifest = vaultMod.readManifest(vaultDirPath);
1097
1127
  const tier = manifest && manifest.tier ? manifest.tier : null;
1098
- // FIX-3/FIX-4: the coordinates the status stamp carries,
1128
+ // Quick 261003-jby (FIX-3/FIX-4): the coordinates the status stamp carries,
1099
1129
  // and the recorded-miss fact the sentence ladder states. Typed facts on the
1100
1130
  // result, never sniffed out of notes prose.
1101
1131
  const vaultRef = manifest && typeof manifest.ref === 'string' ? manifest.ref : null;
1102
1132
  const vaultSha = manifest && typeof manifest.resolvedSha === 'string' ? manifest.resolvedSha : null;
1103
1133
  const vaultMiss = vaultMod.manifestRecordsMiss(manifest);
1134
+ // Quick 261015-at3 (Vince 2026-10-15): a standards vault read in practices
1135
+ // mode. Two facts from the ONE `allFiles` listing the practice filter read
1136
+ // above (exact-case INDEX match, the standards reader's own contract) plus
1137
+ // one mode read. The mode is part of the owner on purpose: the audit's and
1138
+ // the trace's readers have no standards-mode short-circuit of their own, so
1139
+ // a caller that forgot one still cannot trip this flag in standards mode.
1140
+ const modeVerdict = vaultMod.vaultModeVerdict(cwd);
1141
+ const vaultStandardsOnly = modeVerdict.active === true
1142
+ && modeVerdict.ok === true
1143
+ && modeVerdict.mode === 'practices'
1144
+ && allFiles.includes(vaultIndexLeaf.VAULT_INDEX_FILENAME)
1145
+ && practiceFiles.length === 0;
1104
1146
  const practices = [];
1105
1147
  const skipped = [];
1106
1148
  const degraded = [];
@@ -1142,7 +1184,7 @@ function discoverPracticeFiles(cwd, vaultDir) {
1142
1184
  }
1143
1185
  return {
1144
1186
  practices, skipped, degraded, tier, notes, vaultPresent: true, readFailed: probe.failed,
1145
- vaultRef, vaultSha, vaultMiss,
1187
+ vaultRef, vaultSha, vaultMiss, vaultStandardsOnly,
1146
1188
  };
1147
1189
  }
1148
1190
  catch (e) {
@@ -1168,16 +1210,16 @@ function discoverPracticeFiles(cwd, vaultDir) {
1168
1210
  vaultRef: null,
1169
1211
  vaultSha: null,
1170
1212
  vaultMiss: false,
1213
+ vaultStandardsOnly: false,
1171
1214
  };
1172
1215
  }
1173
1216
  }
1174
1217
  // ─── Matching + report (PRAC-02 / PRAC-03 / COMM-01) ───────────────────────────
1175
- // External-content fence (D-15 / T-24-05). Pack 6 D-08: the fence has ONE
1176
- // owner, src/vault.cts — the marker strings and `defangFenceMarkers` are
1177
- // imported from it rather than copied, so every path that hands vault text to
1178
- // an agent defangs every marker spelling (case, whitespace, HTML entities,
1179
- // compatibility forms, zero-width characters), not only the literal marker.
1180
- const { EXTERNAL_CONTENT_FENCE_START, EXTERNAL_CONTENT_FENCE_END, defangFenceMarkers } = vaultMod;
1218
+ // External-content fence markers and `defangFenceMarkers` (D-15 / CR-01 /
1219
+ // T-24-05): the owner moved to the vault-index leaf (D-17/D-26, 14-04), and
1220
+ // these bindings re-export it by reference so every consumer of this module
1221
+ // measures the leaf's one function rather than a copy.
1222
+ const { EXTERNAL_CONTENT_FENCE_START, EXTERNAL_CONTENT_FENCE_END, defangFenceMarkers } = vaultIndexLeaf;
1181
1223
  /**
1182
1224
  * Read a BOUNDED prefix of a practice file's markdown BODY (frontmatter
1183
1225
  * stripped) and wrap it in the `vault:external-content` fence + untrusted-
@@ -1189,16 +1231,16 @@ const { EXTERNAL_CONTENT_FENCE_START, EXTERNAL_CONTENT_FENCE_END, defangFenceMar
1189
1231
  * neutralized via `defangFenceMarkers` BEFORE being placed between the real
1190
1232
  * markers (CR-01), so an untrusted body can never spoof an early fence closer.
1191
1233
  *
1192
- * LINE ENDINGS (FIX-1). The body is sliced at `bodyOffset`
1234
+ * LINE ENDINGS (quick 261003-jby, FIX-1). The body is sliced at `bodyOffset`
1193
1235
  * FIRST — it is an index into the raw text, computed by `parsePracticeFile` on
1194
1236
  * the same bytes — and THEN normalised by deleting every carriage return, which
1195
1237
  * turns each CRLF pair into LF and drops any lone carriage return (a control
1196
1238
  * character that must not reach an agent context). Only then is it trimmed and capped, so the
1197
1239
  * `PRACTICE_EXCERPT_BYTES` cap is measured on normalised text and a CRLF
1198
1240
  * checkout renders the same excerpt as its LF twin. Before this, the cap was
1199
- * applied to raw text: a `core.autocrlf=true` checkout was measured losing
1200
- * 3–18 chars from 8 of 55 practices, with 610 carriage returns reaching the
1201
- * executor-facing block.
1241
+ * applied to raw text: probe test 4 measured 8 of 55 practices losing 3–18
1242
+ * chars on a `core.autocrlf=true` checkout, and 610 carriage returns reaching
1243
+ * the executor-facing block.
1202
1244
  *
1203
1245
  * @param filePath Absolute path to the practice `.md` file on disk.
1204
1246
  * @param bodyOffset Byte index where the markdown body begins (from the
@@ -1225,16 +1267,19 @@ function boundedFencedExcerpt(filePath, bodyOffset) {
1225
1267
  }
1226
1268
  /**
1227
1269
  * Is this file one the unmatched-source-file note reports when no scoped
1228
- * practice matched it? (FIX-2, QD-2.)
1270
+ * practice matched it? (quick 261003-jby, FIX-2, QD-2.)
1229
1271
  *
1230
- * Exactly the plan graph's source predicate: tests, docs, baselines,
1231
- * changesets and `.planning/` files are never reported, and everything else
1232
- * is. Windows separators are normalised first.
1272
+ * POSIX basename (Windows separators are normalised first); the extension
1273
+ * compare is case-insensitive, the `Dockerfile` prefix compare is exact.
1233
1274
  *
1234
1275
  * @param filePath A project-relative file path, either separator.
1235
1276
  */
1236
1277
  function isUnmatchedNoteCandidate(filePath) {
1237
- return (0, plan_graph_cjs_1.isSourceFile)(String(filePath).replace(/\\/g, '/'));
1278
+ const base = node_path_1.default.posix.basename(String(filePath).replace(/\\/g, '/'));
1279
+ if (base.startsWith(UNMATCHED_NOTE_BASENAME_PREFIX))
1280
+ return true;
1281
+ const lower = base.toLowerCase();
1282
+ return UNMATCHED_NOTE_SOURCE_EXTENSIONS.some((ext) => lower.endsWith(ext));
1238
1283
  }
1239
1284
  /**
1240
1285
  * Match discovered practices against a candidate file list, partitioning
@@ -1306,6 +1351,8 @@ function matchPractices(opts) {
1306
1351
  vaultRef: discovery.vaultRef,
1307
1352
  vaultSha: discovery.vaultSha,
1308
1353
  vaultMiss: discovery.vaultMiss,
1354
+ // Quick 261015-at3: threaded on BOTH return sites (05-13 rule).
1355
+ vaultStandardsOnly: discovery.vaultStandardsOnly === true,
1309
1356
  pairBudget: {
1310
1357
  pairs,
1311
1358
  cap: MATCH_PAIR_CAP,
@@ -1412,10 +1459,12 @@ function matchPractices(opts) {
1412
1459
  vaultAbsent: discovery.vaultPresent === false,
1413
1460
  unmatchedSourceFiles,
1414
1461
  // The manifest coordinates and recorded-miss fact, carried verbatim from
1415
- // discovery (FIX-3/FIX-4).
1462
+ // discovery (quick 261003-jby, FIX-3/FIX-4).
1416
1463
  vaultRef: discovery.vaultRef,
1417
1464
  vaultSha: discovery.vaultSha,
1418
1465
  vaultMiss: discovery.vaultMiss,
1466
+ // Quick 261015-at3: the standards-vault-in-practices-mode fact, verbatim.
1467
+ vaultStandardsOnly: discovery.vaultStandardsOnly === true,
1419
1468
  pairBudget: {
1420
1469
  pairs,
1421
1470
  cap: MATCH_PAIR_CAP,
@@ -1490,7 +1539,7 @@ function renderSkippedLines(skipped) {
1490
1539
  }
1491
1540
  /**
1492
1541
  * The one-line note naming candidate files that matched no scoped practice
1493
- * (FIX-2). `null` when there are none.
1542
+ * (quick 261003-jby, FIX-2). `null` when there are none.
1494
1543
  *
1495
1544
  * The paths are project-authored — the plan's `files_modified` or the
1496
1545
  * operator's argv, the same trust class `matched_files` already renders — so
@@ -1629,8 +1678,8 @@ function unwrapFencedExcerpt(fenced) {
1629
1678
  * `id` and `source` just as much as the excerpt prose — and this block is read
1630
1679
  * by a code-WRITING executor in Phase 26. So every field passes through
1631
1680
  * `boundedToken` (length bound, control-character strip) AND
1632
- * `defangFenceMarkers` (so a planted `<!-- vault:external-content-end -->`
1633
- * cannot terminate the fence early and smuggle text out of the data region)
1681
+ * `defangFenceMarkers` (so a planted end-of-fence marker, spelled only in the
1682
+ * vault-index leaf, cannot terminate the fence early and smuggle text out of the data region)
1634
1683
  * before it is placed between the markers. Nothing vault-authored is emitted
1635
1684
  * outside a fence anywhere in this block.
1636
1685
  */
@@ -1671,7 +1720,8 @@ function stampSha(sha) {
1671
1720
  return hex.length >= 8 ? hex.slice(0, 8) : '(none)';
1672
1721
  }
1673
1722
  /**
1674
- * Render the block's status stamp — ONE line, an HTML comment (FIX-3, QD-4).
1723
+ * Render the block's status stamp — ONE line, an HTML comment (quick
1724
+ * 261003-jby, FIX-3, QD-4).
1675
1725
  *
1676
1726
  * `ref=` and `sha=` name the vault read, `entries=<n>` counts the fenced
1677
1727
  * practice entries rendered OR `state=<token>` names the degraded state —
@@ -1696,7 +1746,7 @@ function renderPracticesStamp(parts) {
1696
1746
  }
1697
1747
  /**
1698
1748
  * Choose the sentence an entry-less block states AND its stamp token, in ONE
1699
- * place so the two cannot drift (FIX-3/FIX-4).
1749
+ * place so the two cannot drift (quick 261003-jby, FIX-3/FIX-4).
1700
1750
  *
1701
1751
  * ORDER IS LOAD-BEARING and pinned rung by rung. The pair-cap refusal stays
1702
1752
  * FIRST: it is a statement about work this engine declined to do. Read
@@ -1705,6 +1755,12 @@ function renderPracticesStamp(parts) {
1705
1755
  * none-found rungs, which are facts about a vault that WAS read. The miss rung
1706
1756
  * (FIX-4) sits after absence (an absent directory has no manifest to record a
1707
1757
  * miss in, so absence is the more basic fact) and before all-refused.
1758
+ *
1759
+ * The standards-only rung (quick 261015-at3) sits after the miss — a miss
1760
+ * reached no vault that could hold an index, so it is the more basic fact —
1761
+ * and ABOVE all-refused and none-found. It is a statement about a vault that
1762
+ * WAS read, which is why it is below the unread family; and "none found" is
1763
+ * false of a vault whose practice layer the project is not even reading.
1708
1764
  */
1709
1765
  function selectEmptyBlockSentence(result, skippedCount) {
1710
1766
  if (result?.pairBudget?.exceeded === true) {
@@ -1720,11 +1776,16 @@ function selectEmptyBlockSentence(result, skippedCount) {
1720
1776
  if (result?.vaultAbsent === true) {
1721
1777
  return { sentence: PRACTICES_VAULT_ABSENT_SENTENCE, state: PRACTICES_STAMP_STATES.VAULT_ABSENT };
1722
1778
  }
1723
- // FIX-4: a manifest that records a miss — the fetch ran
1779
+ // Quick 261003-jby (FIX-4): a manifest that records a miss — the fetch ran
1724
1780
  // and reached nothing.
1725
1781
  if (result?.vaultMiss === true) {
1726
1782
  return { sentence: PRACTICES_VAULT_MISS_SENTENCE, state: PRACTICES_STAMP_STATES.VAULT_MISS };
1727
1783
  }
1784
+ // Quick 261015-at3: a standards vault (INDEX.md, no practice files) read in
1785
+ // practices mode — NOT checked, never none-found.
1786
+ if (result?.vaultStandardsOnly === true) {
1787
+ return { sentence: PRACTICES_VAULT_STANDARDS_ONLY_SENTENCE, state: PRACTICES_STAMP_STATES.STANDARDS_ONLY };
1788
+ }
1728
1789
  if (result && result.practicesProcessed === 0 && skippedCount > 0) {
1729
1790
  // 05-REVIEW WR-02, the fifth rung. `practicesProcessed === 0` is true of a
1730
1791
  // THIRD kind of vault as well: one that holds practice files and had every
@@ -1759,7 +1820,7 @@ function selectEmptyBlockSentence(result, skippedCount) {
1759
1820
  function renderPracticesBlock(result, reason) {
1760
1821
  const open = '<vault_practices>';
1761
1822
  const close = '</vault_practices>';
1762
- // FIX-3: the status stamp is the first line inside the
1823
+ // FIX-3 (quick 261003-jby): the status stamp is the first line inside the
1763
1824
  // block on all three return paths, then the blank line, then the preamble.
1764
1825
  const engine = (0, compose_bindings_cjs_1.resolveCreatedWithFrom)(__dirname);
1765
1826
  const headWith = (stamp) => [
@@ -1850,7 +1911,7 @@ function renderPracticesBlock(result, reason) {
1850
1911
  '',
1851
1912
  ]
1852
1913
  : [];
1853
- // FIX-2: candidate files no scoped practice matched. The
1914
+ // FIX-2 (quick 261003-jby): candidate files no scoped practice matched. The
1854
1915
  // matcher owns the gating (QD-3); the renderer renders whenever the array is
1855
1916
  // non-empty. Engine-composed text around project-authored, bounded and
1856
1917
  // defanged paths — nothing vault-authored — so it sits outside a fence.
@@ -1876,7 +1937,7 @@ function renderPracticesBlock(result, reason) {
1876
1937
  //
1877
1938
  // The rungs themselves — and their order — live in
1878
1939
  // `selectEmptyBlockSentence`, which picks the sentence and the stamp token
1879
- // together so the two cannot drift.
1940
+ // together (quick 261003-jby) so the two cannot drift.
1880
1941
  const { sentence, state } = selectEmptyBlockSentence(result, skipped.length);
1881
1942
  const head = headWith({ state });
1882
1943
  return [...head, ...notesSection, ...degradedSection, ...refusedSection, ...noteSection, sentence, '', close].join('\n');
@@ -1911,7 +1972,7 @@ module.exports = {
1911
1972
  // class comes back: a second renderer is a second place a client-authored byte
1912
1973
  // can reach an agent context unfenced. Purely additive; no call site changed.
1913
1974
  renderPracticeEntry,
1914
- // D-05-07: exported so the trace scaffold digests the SAME
1975
+ // 261003-j89 (D-05-07 closed): exported so the trace scaffold digests the SAME
1915
1976
  // text `renderPracticeEntry` places under `ask:`/`excerpt:` — one unwrap, two
1916
1977
  // readers. A second unwrap in src/vault-trace.cts would be a second place the
1917
1978
  // fence-stripping rule could drift, and the digest would then disagree with
@@ -1973,17 +2034,23 @@ module.exports = {
1973
2034
  // exact hazard (see its doc comment) — never a raw Node error message, which
1974
2035
  // interpolates the absolute path CLAUDE.md § Logging forbids surfacing.
1975
2036
  errnoOf,
1976
- // FIX-1: the excerpt's CRLF over-read factor, exported so
2037
+ // Quick 261003-jby (FIX-1): the excerpt's CRLF over-read factor, exported so
1977
2038
  // the bounded-read test and the contract page HARVEST it.
1978
2039
  EXCERPT_CRLF_OVERREAD_FACTOR,
1979
- // FIX-2: the unmatched-source-file note's predicate and one-line composer —
1980
- // exported so tests and both rendered channels compose the line in one place.
2040
+ // Quick 261003-jby (FIX-2): the unmatched-source-file note's candidate set,
2041
+ // predicate and one-line composer — exported so tests and the contract page
2042
+ // harvest them and both rendered channels compose the line in one place.
2043
+ UNMATCHED_NOTE_SOURCE_EXTENSIONS,
2044
+ UNMATCHED_NOTE_BASENAME_PREFIX,
1981
2045
  isUnmatchedNoteCandidate,
1982
2046
  renderUnmatchedSourceNote,
1983
- // FIX-3/FIX-4: the status stamp's prefix, state vocabulary
2047
+ // Quick 261003-jby (FIX-3/FIX-4): the status stamp's prefix, state vocabulary
1984
2048
  // and renderer, and the recorded-miss sentence — exported so tests and the
1985
2049
  // contract page harvest them rather than copy them.
1986
2050
  PRACTICES_VAULT_MISS_SENTENCE,
2051
+ // Quick 261015-at3: the standards-vault-in-practices-mode sentence, exported
2052
+ // on the same harvest rule.
2053
+ PRACTICES_VAULT_STANDARDS_ONLY_SENTENCE,
1987
2054
  PRACTICES_STAMP_PREFIX,
1988
2055
  PRACTICES_STAMP_STATES,
1989
2056
  renderPracticesStamp,