@pareto4/pp-core 1.13.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +3 -3
- package/README.md +31 -12
- package/agents/pp-code-fixer.md +0 -11
- package/agents/pp-code-reviewer.md +1 -77
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +12 -29
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +34 -20
- package/agents/pp-vault-reviewer.md +100 -23
- package/agents/pp-verifier.md +1 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-manage.md +1 -1
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-promote.md +7 -8
- package/commands/pp-vault-review.md +6 -5
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +10 -18
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +201 -229
- package/pp-core/bin/lib/capability-state.cjs +93 -1
- package/pp-core/bin/lib/capability-writer.cjs +3 -3
- package/pp-core/bin/lib/check-command-router.cjs +1906 -1514
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +151 -62
- package/pp-core/bin/lib/decisions.cjs +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +11 -12
- package/pp-core/bin/lib/git-provider.cjs +9 -10
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +620 -738
- package/pp-core/bin/lib/init-interactive.cjs +62 -4
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +4 -2
- package/pp-core/bin/lib/loop-resolver.cjs +165 -129
- package/pp-core/bin/lib/org-profile.cjs +402 -161
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-command-router.cjs +1682 -477
- package/pp-core/bin/lib/vault-coordinates.cjs +16 -7
- package/pp-core/bin/lib/vault-index.cjs +656 -0
- package/pp-core/bin/lib/vault-seal.cjs +1851 -0
- package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
- package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
- package/pp-core/bin/lib/vault-standards.cjs +1504 -0
- package/pp-core/bin/lib/vault-trace.cjs +155 -2778
- package/pp-core/bin/lib/vault-update.cjs +10 -59
- package/pp-core/bin/lib/vault.cjs +2570 -950
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +157 -54
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +2 -2
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -25
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-standards.md +311 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +56 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -3
- package/pp-core/workflows/code-review.md +10 -57
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +207 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +10 -2
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +327 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +78 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +219 -20
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +147 -0
- package/pp-core/workflows/execute-phase.md +152 -128
- package/pp-core/workflows/execute-plan.md +54 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +161 -23
- package/pp-core/workflows/vault-write-back.md +73 -110
- package/pp-core/workflows/verify-work.md +12 -14
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +120 -13
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +3 -19
- package/scripts/lint-test-file-count.cjs +5 -6
- package/scripts/prompt-injection-scan.sh +19 -17
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/bin/lib/practices-review-check.cjs +0 -938
- package/pp-core/bin/lib/vault-audit.cjs +0 -2070
- package/pp-core/bin/lib/vault-practices.cjs +0 -1990
- package/pp-core/bin/lib/vault-review.cjs +0 -2359
- package/pp-core/bin/lib/vault-write-back.cjs +0 -877
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/references/planner-vault-practices.md +0 -112
- package/pp-core/templates/practices/README.md +0 -251
- package/pp-core/templates/practices/error-handling.md +0 -18
- package/pp-core/templates/practices/minimal-comments.md +0 -14
- package/pp-core/templates/practices/naming-convention.md +0 -18
- package/pp-core/templates/practices/practice-template.md +0 -23
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- package/scripts/lint-org-coordinates.cjs +0 -426
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Command surface: pp-tools loop render-hooks <point>
|
|
14
14
|
*
|
|
15
15
|
* Exports (three things):
|
|
16
|
-
* resolveLoopHooks({ point, registry, config }) → { point, activeHooks }
|
|
16
|
+
* resolveLoopHooks({ point, registry, config, recordedArtifacts? }) → { point, activeHooks }
|
|
17
17
|
* renderLoopHooks(resolved) → markdown string
|
|
18
18
|
* cmdLoopRenderHooks(cwd, point, raw, options) — I/O entry point
|
|
19
19
|
*
|
|
@@ -30,19 +30,17 @@
|
|
|
30
30
|
*/
|
|
31
31
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
32
|
const ioMod = require("./io.cjs");
|
|
33
|
-
const { output: coreOutput, error: coreError
|
|
33
|
+
const { output: coreOutput, error: coreError } = ioMod;
|
|
34
34
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
35
|
const configLoaderModule = require("./config-loader.cjs");
|
|
36
|
-
const { loadConfig
|
|
36
|
+
const { loadConfig } = configLoaderModule;
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
38
|
const capabilityStateModule = require("./capability-state.cjs");
|
|
39
|
-
const { resolveCapabilityRuntimeState } = capabilityStateModule;
|
|
39
|
+
const { resolveCapabilityRuntimeState, SEAL_FIELD, sealRenderVerdict, resolveInjectedRecordedArtifacts, resolveInjectedPlatformBindingsPresent, } = capabilityStateModule;
|
|
40
40
|
// ─── Capability-activation engine (single owner for config-key precedence) ────
|
|
41
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
42
|
const capabilityActivationModule = require("./capability-activation.cjs");
|
|
43
43
|
const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, resolveConfigKey } = capabilityActivationModule;
|
|
44
|
-
// ─── Phase ceremony tier (pack 9): `--phase` / `--tier` overlay ───────────────
|
|
45
|
-
const phase_tier_cjs_1 = require("./phase-tier.cjs");
|
|
46
44
|
// ─── Canonical points (derived from LOOP_HOST_CONTRACT — authoritative 12) ───
|
|
47
45
|
// FIX 2: Derive the authoritative canonical set from LOOP_HOST_CONTRACT so it
|
|
48
46
|
// cannot drift from the host contract. CANONICAL_POINTS_FALLBACK is kept as an
|
|
@@ -105,7 +103,7 @@ function _getCanonicalPoints(_registry) {
|
|
|
105
103
|
* resolved against `config`; active iff truthy. Inactive hooks are filtered out.
|
|
106
104
|
*/
|
|
107
105
|
function resolveLoopHooks(input) {
|
|
108
|
-
const { point, registry, config, cwd, capabilityStatesById } = input;
|
|
106
|
+
const { point, registry, config, cwd, capabilityStatesById, recordedArtifacts, platformBindingsPresent, } = input;
|
|
109
107
|
// Validate point
|
|
110
108
|
const canonicalPoints = _getCanonicalPoints(registry);
|
|
111
109
|
if (!canonicalPoints.includes(point)) {
|
|
@@ -150,6 +148,59 @@ function resolveLoopHooks(input) {
|
|
|
150
148
|
// `=== true` is the correct fail-closed check (not `!== false`).
|
|
151
149
|
return state.active === true;
|
|
152
150
|
}
|
|
151
|
+
// Phase 16 D-19: the state the seal rule reads for a gate's capability. With
|
|
152
|
+
// no state map at all, every capability is treated as active, exactly as
|
|
153
|
+
// `isCapabilityActive` does: normal gates keep today's behaviour and a seal
|
|
154
|
+
// gate drops (it never renders while its capability is active). With a map,
|
|
155
|
+
// a missing entry is `undefined`, which the rule drops (fail closed).
|
|
156
|
+
function gateStateOf(capId) {
|
|
157
|
+
if (!capabilityStatesById)
|
|
158
|
+
return { enabled: true, active: true };
|
|
159
|
+
return capabilityStatesById instanceof Map
|
|
160
|
+
? capabilityStatesById.get(capId)
|
|
161
|
+
: capabilityStatesById[capId];
|
|
162
|
+
}
|
|
163
|
+
// Phase 16 D-19 / T-16-33: the injected predicate is asked at most once per
|
|
164
|
+
// resolve, however many seal gates the point declares. An absent thunk is
|
|
165
|
+
// the inert answer (no seal renders).
|
|
166
|
+
let recordedArtifactsMemo;
|
|
167
|
+
function recordedArtifactsOnce() {
|
|
168
|
+
if (recordedArtifactsMemo === undefined) {
|
|
169
|
+
recordedArtifactsMemo = resolveInjectedRecordedArtifacts(recordedArtifacts);
|
|
170
|
+
}
|
|
171
|
+
return recordedArtifactsMemo;
|
|
172
|
+
}
|
|
173
|
+
// Quick 261015-ect (QD-13): the bindings measurement, asked at most once per
|
|
174
|
+
// resolve and only for a capability that declares it needs the file. A
|
|
175
|
+
// missing thunk is `null` (unmeasured), which drops nothing.
|
|
176
|
+
let bindingsPresentMemo;
|
|
177
|
+
function bindingsPresentOnce() {
|
|
178
|
+
if (bindingsPresentMemo === undefined) {
|
|
179
|
+
bindingsPresentMemo = resolveInjectedPlatformBindingsPresent(platformBindingsPresent);
|
|
180
|
+
}
|
|
181
|
+
return bindingsPresentMemo;
|
|
182
|
+
}
|
|
183
|
+
function requiresBindings(capId) {
|
|
184
|
+
// Prototype-pollution guard (inline literal, CodeQL barrier), as the
|
|
185
|
+
// registry generator guards capability ids.
|
|
186
|
+
if (capId === '' || capId === '__proto__' || capId === 'constructor' || capId === 'prototype')
|
|
187
|
+
return false;
|
|
188
|
+
const caps = registry['capabilities'];
|
|
189
|
+
if (!caps || typeof caps !== 'object' || Array.isArray(caps))
|
|
190
|
+
return false;
|
|
191
|
+
if (!Object.prototype.hasOwnProperty.call(caps, capId))
|
|
192
|
+
return false;
|
|
193
|
+
const cap = caps[capId];
|
|
194
|
+
if (!cap || typeof cap !== 'object')
|
|
195
|
+
return false;
|
|
196
|
+
return cap['requiresPlatformBindings'] === true;
|
|
197
|
+
}
|
|
198
|
+
// A capability that requires bindings renders nothing where the file is
|
|
199
|
+
// MEASURED absent: the project is not governed by it (its own description says
|
|
200
|
+
// it is inert unless the bindings exist).
|
|
201
|
+
function droppedForBindings(capId) {
|
|
202
|
+
return requiresBindings(capId) && bindingsPresentOnce() === false;
|
|
203
|
+
}
|
|
153
204
|
// Helper: safe string array
|
|
154
205
|
function toStringArray(v) {
|
|
155
206
|
if (!Array.isArray(v))
|
|
@@ -201,6 +252,8 @@ function resolveLoopHooks(input) {
|
|
|
201
252
|
const capId = typeof hook['capId'] === 'string' ? hook['capId'] : '';
|
|
202
253
|
if (!isCapabilityActive(capId))
|
|
203
254
|
continue;
|
|
255
|
+
if (droppedForBindings(capId))
|
|
256
|
+
continue;
|
|
204
257
|
if (!isActive(hook))
|
|
205
258
|
continue;
|
|
206
259
|
const ref = (typeof hook['ref'] === 'object' && hook['ref'] !== null)
|
|
@@ -235,6 +288,8 @@ function resolveLoopHooks(input) {
|
|
|
235
288
|
const capId = typeof hook['capId'] === 'string' ? hook['capId'] : '';
|
|
236
289
|
if (!isCapabilityActive(capId))
|
|
237
290
|
continue;
|
|
291
|
+
if (droppedForBindings(capId))
|
|
292
|
+
continue;
|
|
238
293
|
if (!isActive(hook))
|
|
239
294
|
continue;
|
|
240
295
|
const into = typeof hook['into'] === 'string' ? hook['into'] : undefined;
|
|
@@ -268,9 +323,18 @@ function resolveLoopHooks(input) {
|
|
|
268
323
|
if (!hook || typeof hook !== 'object')
|
|
269
324
|
continue;
|
|
270
325
|
const capId = typeof hook['capId'] === 'string' ? hook['capId'] : '';
|
|
271
|
-
|
|
326
|
+
// Quick 261015-ect (QD-13): gates too, seal gates included. A project with
|
|
327
|
+
// no bindings holds no engine-written vault records (D-37/D-42), and every
|
|
328
|
+
// vault gate already answers inert there, so nothing is left ungated.
|
|
329
|
+
if (droppedForBindings(capId))
|
|
272
330
|
continue;
|
|
273
|
-
|
|
331
|
+
const verdict = sealRenderVerdict(hook[SEAL_FIELD], gateStateOf(capId), recordedArtifactsOnce);
|
|
332
|
+
if (verdict === 'drop')
|
|
333
|
+
continue;
|
|
334
|
+
// 'normal': today's path, `when` evaluated. 'seal': the `when` IS the
|
|
335
|
+
// switch that is off (D-18), so it is not read; the seal rule already
|
|
336
|
+
// required an enabled, config-inactive capability and recorded artifacts.
|
|
337
|
+
if (verdict === 'normal' && !isActive(hook))
|
|
274
338
|
continue;
|
|
275
339
|
const when = typeof hook['when'] === 'string' ? hook['when'] : undefined;
|
|
276
340
|
const check = hook['check'] !== undefined ? hook['check'] : undefined;
|
|
@@ -382,80 +446,6 @@ function renderLoopHooks(resolved) {
|
|
|
382
446
|
return lines.join('\n');
|
|
383
447
|
}
|
|
384
448
|
// ─── I/O command handler ───────────────────────────────────────────────────────
|
|
385
|
-
/**
|
|
386
|
-
* The refusal text for a caller that resolves hooks over an unreadable config.
|
|
387
|
-
* One wording for every such caller (`loop render-hooks`, `phase.dispatch-plan`).
|
|
388
|
-
*/
|
|
389
|
-
function unreadableConfigRefusal(what, unreadable) {
|
|
390
|
-
return (`${what} refused: the project config cannot be read, so no hook can be resolved ` +
|
|
391
|
-
`(rendering anyway would silently drop every hook the config switches on, a required client vault among them). ` +
|
|
392
|
-
`${describeUnreadableConfig(unreadable)}. Repair or remove the file, then re-run. Stop this workflow here.`);
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Resolve the active hooks at `point` for the project at `cwd`, exactly as
|
|
396
|
-
* `loop render-hooks` does: refuse an unreadable config, load the config
|
|
397
|
-
* snapshot once, overlay the phase tier's gate booleans when one is given,
|
|
398
|
-
* resolve capability state over the same snapshot, then resolve the hooks.
|
|
399
|
-
* The ONE owner of "which hooks are active here" for every caller that needs
|
|
400
|
-
* the answer in code (`loop render-hooks`, `phase.dispatch-plan`'s executor
|
|
401
|
-
* contributions). Never exits; the caller decides how to report a refusal.
|
|
402
|
-
*/
|
|
403
|
-
function resolveProjectLoopHooks(cwd, point, opts = {}) {
|
|
404
|
-
// Fail closed on a config that exists but cannot be read. Every hook's `when`
|
|
405
|
-
// and activation key is read from it, and both readers below swallow a parse
|
|
406
|
-
// failure into the defaults, so rendering anyway drops exactly the hooks the
|
|
407
|
-
// project switched on — a required client vault's fetch and blocking gate
|
|
408
|
-
// among them. A MISSING config is not refused: that is a fresh project, and
|
|
409
|
-
// the defaults are its real settings.
|
|
410
|
-
const unreadable = unreadableConfigFiles(cwd);
|
|
411
|
-
if (unreadable.length > 0) {
|
|
412
|
-
return { ok: false, reason: 'config_unreadable', message: unreadableConfigRefusal(opts.what ?? `loop render-hooks ${point}`, unreadable) };
|
|
413
|
-
}
|
|
414
|
-
// Load the config snapshot ONCE and share it with both the capability-state
|
|
415
|
-
// resolver (via configOverride) and loop-hook resolution, so federated keys
|
|
416
|
-
// present in loadConfig resolve identically for `active` and for hook when/
|
|
417
|
-
// configValues. Keys absent from loadConfig still fall through to raw
|
|
418
|
-
// .planning/config.json reads (precedence levels 2-3) in each pass.
|
|
419
|
-
let config;
|
|
420
|
-
try {
|
|
421
|
-
config = loadConfig(cwd);
|
|
422
|
-
}
|
|
423
|
-
catch {
|
|
424
|
-
config = {};
|
|
425
|
-
}
|
|
426
|
-
if (opts.phaseTier)
|
|
427
|
-
config = (0, phase_tier_cjs_1.overlayTierGates)(config, opts.phaseTier);
|
|
428
|
-
const state = resolveCapabilityRuntimeState(cwd, opts.configDir, config);
|
|
429
|
-
// Registry is the static generated module — same object capability-state uses internally.
|
|
430
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
431
|
-
const registry = require('./capability-registry.cjs');
|
|
432
|
-
const capabilityStatesById = new Map();
|
|
433
|
-
for (const cap of state.capabilities || []) {
|
|
434
|
-
capabilityStatesById.set(cap.id, cap);
|
|
435
|
-
}
|
|
436
|
-
try {
|
|
437
|
-
const resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
|
|
438
|
-
return { ok: true, resolved, warnings: state.warnings ?? [] };
|
|
439
|
-
}
|
|
440
|
-
catch (err) {
|
|
441
|
-
return { ok: false, reason: 'invalid_point', message: (err instanceof Error) ? err.message : String(err) };
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* The `fragment.inline` text of every active contribution into `role`, in
|
|
446
|
-
* resolved order, with the capability that contributed it.
|
|
447
|
-
*/
|
|
448
|
-
function contributionsInto(resolved, role) {
|
|
449
|
-
const out = [];
|
|
450
|
-
for (const h of resolved.activeHooks) {
|
|
451
|
-
if (h.kind !== 'contribution' || h.into !== role)
|
|
452
|
-
continue;
|
|
453
|
-
if (typeof h.fragment?.inline !== 'string')
|
|
454
|
-
continue;
|
|
455
|
-
out.push({ capId: h.capId, fragment: h.fragment.inline });
|
|
456
|
-
}
|
|
457
|
-
return out;
|
|
458
|
-
}
|
|
459
449
|
/**
|
|
460
450
|
* Command entry point: load registry + config, resolve + render, emit envelope.
|
|
461
451
|
*
|
|
@@ -470,31 +460,37 @@ function contributionsInto(resolved, role) {
|
|
|
470
460
|
* pre-cutover keys like `workflow.ui_phase` that live in config.json but are not
|
|
471
461
|
* yet exposed through loadConfig's whitelist.
|
|
472
462
|
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
463
|
+
* --active-cap <capId>: when present, resolves hooks for <point> as the normal
|
|
464
|
+
* path does EXCEPT for one input — the bindings measurement (quick 261015-ect,
|
|
465
|
+
* QD-13) is not applied — then prints exactly `true` (if any resolved
|
|
466
|
+
* activeHook has capId === <capId>) or `false` followed by a single newline,
|
|
467
|
+
* and exits 0. So on a project with no `.platform-bindings.json` it still
|
|
468
|
+
* answers the config switch alone: /pp-init Step 2.6's unbound arm ("a
|
|
469
|
+
* resolving profile with no bindings halts loudly") is reachable only because
|
|
470
|
+
* this read ignores bindings (16-16 D-81), and the D-28 parity test states
|
|
471
|
+
* parity on bindings-present shapes only.
|
|
480
472
|
* No JSON envelope is emitted — output is clean for shell $(…) capture.
|
|
481
473
|
* Missing <capId> value → coreError + non-zero exit.
|
|
482
474
|
* Unknown/inactive capId → `false` (not an error).
|
|
483
475
|
*
|
|
484
|
-
* --into <role
|
|
485
|
-
* `fragment.inline` text of each active contribution whose
|
|
486
|
-
* <role>, in resolved order, separated by one blank line, with a
|
|
487
|
-
* newline when anything printed. Nothing at all (empty stdout, exit
|
|
488
|
-
* contribution matches. No JSON envelope; `--raw` is ignored.
|
|
489
|
-
*
|
|
476
|
+
* --into <role> (Phase 15 D-12/D-30): resolves exactly as the normal path does,
|
|
477
|
+
* then prints the `fragment.inline` text of each active contribution whose
|
|
478
|
+
* `into` equals <role>, in resolved order, separated by one blank line, with a
|
|
479
|
+
* trailing newline when anything printed. Nothing at all (empty stdout, exit
|
|
480
|
+
* 0) when no contribution matches. No JSON envelope; `--raw` is ignored. This
|
|
481
|
+
* is the text execute-phase interpolates verbatim into a spawn prompt (15-09).
|
|
482
|
+
* `--active-cap` takes precedence: given both, `--into` is ignored.
|
|
483
|
+
|
|
484
|
+
*
|
|
485
|
+
* options.resolveRecordedArtifacts: an injected `() => boolean` (pp-tools builds
|
|
486
|
+
* it on the seal leaf's hasRecordedVaultArtifacts); only literal `true` lets a
|
|
487
|
+
* seal gate render (Phase 16 D-19). `--active-cap <cap>` also requires the
|
|
488
|
+
* capability's own state to be active (D-27).
|
|
490
489
|
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* activation key of those gates answers the tier-resolved value. Without
|
|
496
|
-
* `--phase` the project's own booleans are read as before (an explicitly
|
|
497
|
-
* invoked gate command such as /pp-secure-phase is not governed by a tier).
|
|
490
|
+
* options.resolvePlatformBindingsPresent: an injected `() => boolean` (pp-tools
|
|
491
|
+
* builds it on the vault leaf's hasPlatformBindings); only literal `false`
|
|
492
|
+
* drops a `requiresPlatformBindings` capability's hooks (QD-13). `--into` and
|
|
493
|
+
* the envelope read the filtered set; `--active-cap` reads the unfiltered one.
|
|
498
494
|
*/
|
|
499
495
|
function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
500
496
|
if (!point) {
|
|
@@ -509,40 +505,82 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
509
505
|
}
|
|
510
506
|
// --into <role> mode: emit matching contribution fragment text only
|
|
511
507
|
const intoRole = typeof options['into'] === 'string' ? options['into'] : undefined;
|
|
512
|
-
if (activeCapId === undefined && intoRole !== undefined && intoRole
|
|
513
|
-
coreError('
|
|
508
|
+
if (activeCapId === undefined && intoRole !== undefined && intoRole === '') {
|
|
509
|
+
coreError('--into requires a <role> value (e.g. --into executor)');
|
|
514
510
|
return;
|
|
515
511
|
}
|
|
516
512
|
const runtimeConfigDir = typeof options['configDir'] === 'string'
|
|
517
513
|
? options['configDir']
|
|
518
514
|
: undefined;
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
515
|
+
// Load the config snapshot ONCE and share it with both the capability-state
|
|
516
|
+
// resolver (via configOverride) and loop-hook resolution, so federated keys
|
|
517
|
+
// present in loadConfig resolve identically for `active` and for hook when/
|
|
518
|
+
// configValues — eliminating the previous double loadConfig() call. Note: keys
|
|
519
|
+
// absent from loadConfig still fall through to raw .planning/config.json reads
|
|
520
|
+
// (precedence levels 2-3) in each pass; that residual re-read window is
|
|
521
|
+
// pre-existing (unchanged by this consolidation), not introduced here.
|
|
522
|
+
let config;
|
|
523
|
+
try {
|
|
524
|
+
config = loadConfig(cwd);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
config = {};
|
|
530
528
|
}
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
const state = resolveCapabilityRuntimeState(cwd, runtimeConfigDir, config);
|
|
530
|
+
// Registry is the static generated module — same object capability-state uses internally.
|
|
531
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
532
|
+
const registry = require('./capability-registry.cjs');
|
|
533
|
+
const capabilityStatesById = new Map();
|
|
534
|
+
for (const cap of state.capabilities || []) {
|
|
535
|
+
capabilityStatesById.set(cap.id, cap);
|
|
536
|
+
}
|
|
537
|
+
// Phase 16 D-19: the recorded-artifacts predicate arrives through an injected
|
|
538
|
+
// resolver (pp-tools
|
|
539
|
+
// passes one built on the seal leaf's hasRecordedVaultArtifacts). The resolver
|
|
540
|
+
// memoises it and asks it only for an enabled, config-inactive seal gate.
|
|
541
|
+
const resolveRecordedArtifacts = options['resolveRecordedArtifacts'];
|
|
542
|
+
const recordedArtifacts = () => resolveInjectedRecordedArtifacts(resolveRecordedArtifacts);
|
|
543
|
+
// Quick 261015-ect (QD-13): the bindings measurement arrives the same way
|
|
544
|
+
// (pp-tools passes one built on the vault leaf's hasPlatformBindings).
|
|
545
|
+
const resolvePlatformBindingsPresent = options['resolvePlatformBindingsPresent'];
|
|
546
|
+
const platformBindingsPresent = () => resolveInjectedPlatformBindingsPresent(resolvePlatformBindingsPresent);
|
|
547
|
+
let resolved;
|
|
548
|
+
try {
|
|
549
|
+
// --active-cap reads the UNFILTERED resolution (16-16 D-81, see above):
|
|
550
|
+
// without the bindings thunk, so it keeps answering the config switch.
|
|
551
|
+
resolved = resolveLoopHooks({
|
|
552
|
+
point, registry, config, cwd, capabilityStatesById, recordedArtifacts,
|
|
553
|
+
...(activeCapId === undefined ? { platformBindingsPresent } : {}),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
const msg = (err instanceof Error) ? err.message : String(err);
|
|
558
|
+
coreError(msg);
|
|
534
559
|
return;
|
|
535
560
|
}
|
|
536
|
-
const resolved = outcome.resolved;
|
|
537
561
|
// --active-cap mode: print exactly 'true' or 'false' with no envelope
|
|
538
562
|
if (activeCapId !== undefined) {
|
|
539
|
-
|
|
563
|
+
// Phase 16 D-27 (research C-3): a hook rendering is no longer enough — the
|
|
564
|
+
// capability must itself be active. A seal gate renders for a config-
|
|
565
|
+
// inactive vault, and the doors read `--active-cap` as their
|
|
566
|
+
// switch, so a seal must never make an inactive capability answer `true`.
|
|
567
|
+
// Equivalent to the old answer on every input without a seal: a normal
|
|
568
|
+
// hook renders only for an active capability.
|
|
569
|
+
const isActive = resolved.activeHooks.some((h) => h.capId === activeCapId)
|
|
570
|
+
&& capabilityStatesById.get(activeCapId)?.active === true;
|
|
540
571
|
process.stdout.write(isActive ? 'true\n' : 'false\n');
|
|
541
572
|
return;
|
|
542
573
|
}
|
|
543
574
|
// --into mode: fragment text of the active contributions targeting <role>
|
|
544
575
|
if (intoRole !== undefined) {
|
|
545
|
-
const texts =
|
|
576
|
+
const texts = [];
|
|
577
|
+
for (const h of resolved.activeHooks) {
|
|
578
|
+
if (h.kind !== 'contribution' || h.into !== intoRole)
|
|
579
|
+
continue;
|
|
580
|
+
if (typeof h.fragment?.inline !== 'string')
|
|
581
|
+
continue;
|
|
582
|
+
texts.push(h.fragment.inline);
|
|
583
|
+
}
|
|
546
584
|
if (texts.length > 0)
|
|
547
585
|
process.stdout.write(texts.join('\n\n') + '\n');
|
|
548
586
|
return;
|
|
@@ -553,11 +591,8 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
553
591
|
activeHooks: resolved.activeHooks,
|
|
554
592
|
rendered,
|
|
555
593
|
};
|
|
556
|
-
if (
|
|
557
|
-
envelope.
|
|
558
|
-
}
|
|
559
|
-
if (outcome.warnings.length > 0) {
|
|
560
|
-
envelope.warnings = outcome.warnings;
|
|
594
|
+
if (state.warnings && state.warnings.length > 0) {
|
|
595
|
+
envelope.warnings = state.warnings;
|
|
561
596
|
}
|
|
562
597
|
// No rawValue third argument, so --raw emits the JSON envelope rather than
|
|
563
598
|
// plain text. This is deliberate and load-bearing: 23 workflow call sites do
|
|
@@ -571,8 +606,6 @@ module.exports = {
|
|
|
571
606
|
resolveLoopHooks,
|
|
572
607
|
renderLoopHooks,
|
|
573
608
|
cmdLoopRenderHooks,
|
|
574
|
-
resolveProjectLoopHooks,
|
|
575
|
-
contributionsInto,
|
|
576
609
|
// Exported for tests
|
|
577
610
|
_getNestedConfigValue,
|
|
578
611
|
_resolveActivationValue,
|
|
@@ -580,6 +613,9 @@ module.exports = {
|
|
|
580
613
|
// Re-exported for identity parity guard (FIX 2: resolveConfigValues in this module
|
|
581
614
|
// calls resolveConfigKey; exporting it here makes the single-owner contract testable).
|
|
582
615
|
resolveConfigKey,
|
|
616
|
+
// Re-exported for the same identity pin: the seal rule has one owner,
|
|
617
|
+
// capability-state.cjs (Phase 16 D-19).
|
|
618
|
+
sealRenderVerdict,
|
|
583
619
|
CANONICAL_POINTS_FALLBACK,
|
|
584
620
|
CANONICAL_POINTS,
|
|
585
621
|
};
|