@pareto4/pp-core 1.17.1 → 1.19.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 +1 -1
- package/README.md +5 -3
- package/agents/pp-code-fixer.md +9 -16
- package/agents/pp-code-reviewer.md +2 -78
- package/agents/pp-codebase-mapper.md +3 -0
- package/agents/pp-debugger.md +3 -2
- package/agents/pp-executor.md +25 -30
- package/agents/pp-integration-checker.md +1 -0
- package/agents/pp-phase-researcher.md +3 -1
- package/agents/pp-plan-checker.md +3 -0
- package/agents/pp-planner.md +16 -12
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-auditor.md +1 -0
- package/agents/pp-ui-researcher.md +3 -1
- package/agents/pp-vault-reviewer.md +43 -189
- package/agents/pp-verifier.md +10 -8
- package/commands/pp-add-tests.md +1 -1
- package/commands/pp-debug.md +1 -0
- package/commands/pp-init.md +4 -3
- package/commands/pp-ns-manage.md +1 -1
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-quick.md +3 -0
- package/commands/pp-review-backlog.md +2 -1
- package/commands/pp-vault-promote.md +7 -8
- package/commands/pp-vault-review.md +6 -7
- package/gemini-extension.json +1 -1
- package/package.json +3 -6
- package/pp-core/bin/lib/capability-registry.cjs +15 -357
- package/pp-core/bin/lib/capability-state.cjs +15 -79
- package/pp-core/bin/lib/capability-writer.cjs +2 -16
- package/pp-core/bin/lib/check-command-router.cjs +342 -2298
- package/pp-core/bin/lib/clusters.cjs +2 -2
- package/pp-core/bin/lib/commands.cjs +79 -4
- package/pp-core/bin/lib/compose-bindings.cjs +99 -16
- package/pp-core/bin/lib/config.cjs +3 -6
- package/pp-core/bin/lib/discovery-rollup.cjs +9 -6
- package/pp-core/bin/lib/discovery-screens.cjs +20 -2
- package/pp-core/bin/lib/discovery-sweep.cjs +12 -3
- package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
- package/pp-core/bin/lib/git-base-branch.cjs +10 -13
- package/pp-core/bin/lib/git-provider.cjs +5 -6
- package/pp-core/bin/lib/init-contract.cjs +55 -42
- package/pp-core/bin/lib/init-interactive.cjs +6 -20
- package/pp-core/bin/lib/io.cjs +2 -8
- package/pp-core/bin/lib/loop-resolver.cjs +8 -32
- package/pp-core/bin/lib/org-profile.cjs +16 -92
- package/pp-core/bin/lib/vault-command-router.cjs +174 -1206
- package/pp-core/bin/lib/vault-coordinates.cjs +4 -4
- package/pp-core/bin/lib/vault-index.cjs +44 -74
- package/pp-core/bin/lib/vault-seal.cjs +37 -122
- 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 +15 -16
- package/pp-core/bin/lib/vault-trace.cjs +40 -3016
- package/pp-core/bin/lib/vault-update.cjs +5 -26
- package/pp-core/bin/lib/vault.cjs +122 -316
- package/pp-core/bin/pp-tools.cjs +15 -36
- package/pp-core/org-profile.json +1 -1
- package/pp-core/references/autonomous-best-effort.md +4 -3
- package/pp-core/references/autonomous-lifecycle.md +1 -0
- package/pp-core/references/autonomous-smart-discuss.md +5 -1
- package/pp-core/references/discovery-id-conventions.md +35 -2
- package/pp-core/references/execute-mvp-tdd.md +3 -3
- package/pp-core/references/git-integration.md +57 -33
- package/pp-core/references/git-planning-commit.md +31 -9
- package/pp-core/references/phase-argument-parsing.md +1 -0
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-revision.md +3 -1
- package/pp-core/references/planner-tdd.md +4 -4
- package/pp-core/references/planner-vault-standards.md +16 -25
- package/pp-core/references/planning-config.md +3 -3
- package/pp-core/references/shell-portability.md +28 -0
- package/pp-core/references/tdd.md +20 -16
- package/pp-core/references/worktree-path-safety.md +1 -0
- package/pp-core/templates/README.md +1 -1
- package/pp-core/templates/VAULT-REVIEW.md +24 -170
- package/pp-core/templates/milestone.md +5 -5
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/add-tests.md +8 -5
- package/pp-core/workflows/add-todo.md +3 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -1
- package/pp-core/workflows/audit-fix.md +7 -6
- package/pp-core/workflows/audit-milestone.md +1 -0
- package/pp-core/workflows/autonomous.md +6 -0
- package/pp-core/workflows/check-todos.md +2 -1
- package/pp-core/workflows/cleanup.md +3 -1
- package/pp-core/workflows/code-review-fix.md +15 -8
- package/pp-core/workflows/code-review.md +16 -58
- package/pp-core/workflows/complete-milestone.md +13 -3
- package/pp-core/workflows/debug.md +4 -2
- package/pp-core/workflows/diagnose-issues.md +1 -1
- package/pp-core/workflows/discovery-sprint/prototype.md +2 -2
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +1 -1
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +51 -7
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +5 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +10 -8
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +11 -2
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +5 -5
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +3 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +14 -2
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +10 -10
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +11 -8
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +5 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +9 -3
- package/pp-core/workflows/discovery-sprint/vault-context.md +17 -12
- package/pp-core/workflows/discovery-sprint.md +11 -8
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -0
- package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
- package/pp-core/workflows/discuss-phase.md +8 -3
- package/pp-core/workflows/docs-update.md +1 -0
- package/pp-core/workflows/eval-review.md +2 -1
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +13 -14
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +5 -25
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +18 -6
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +6 -109
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +1 -17
- package/pp-core/workflows/execute-phase.md +21 -12
- package/pp-core/workflows/execute-plan.md +24 -15
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/forensics.md +4 -2
- package/pp-core/workflows/health.md +1 -0
- package/pp-core/workflows/help/modes/full.md +1 -1
- package/pp-core/workflows/import.md +2 -1
- package/pp-core/workflows/ingest-docs.md +1 -0
- package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
- package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
- package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
- package/pp-core/workflows/init.md +56 -66
- package/pp-core/workflows/map-codebase.md +5 -1
- package/pp-core/workflows/milestone-summary.md +2 -1
- package/pp-core/workflows/mvp-phase.md +1 -0
- package/pp-core/workflows/new-milestone.md +9 -5
- package/pp-core/workflows/new-project.md +9 -8
- package/pp-core/workflows/new-workspace.md +1 -0
- package/pp-core/workflows/next.md +2 -1
- package/pp-core/workflows/pause-work.md +6 -4
- package/pp-core/workflows/plan-milestone-gaps.md +3 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
- package/pp-core/workflows/plan-phase.md +21 -6
- package/pp-core/workflows/plan-review-convergence.md +2 -0
- package/pp-core/workflows/plant-seed.md +4 -2
- package/pp-core/workflows/pr-branch.md +2 -0
- package/pp-core/workflows/progress.md +5 -0
- package/pp-core/workflows/quick.md +19 -12
- package/pp-core/workflows/reapply-patches.md +3 -0
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/resume-project.md +3 -0
- package/pp-core/workflows/review.md +7 -1
- package/pp-core/workflows/secure-phase.md +2 -1
- package/pp-core/workflows/session-report.md +1 -0
- package/pp-core/workflows/ship.md +8 -1
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +3 -2
- package/pp-core/workflows/spec-phase.md +2 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +4 -3
- package/pp-core/workflows/sync-skills.md +5 -2
- package/pp-core/workflows/thread.md +3 -2
- package/pp-core/workflows/transition.md +6 -0
- package/pp-core/workflows/ui-phase.md +3 -1
- package/pp-core/workflows/ui-review.md +2 -1
- package/pp-core/workflows/ultraplan-phase.md +1 -0
- package/pp-core/workflows/undo.md +14 -10
- package/pp-core/workflows/update.md +5 -0
- package/pp-core/workflows/validate-phase.md +9 -6
- package/pp-core/workflows/vault-review.md +65 -186
- package/pp-core/workflows/vault-write-back.md +78 -110
- package/pp-core/workflows/verify-phase.md +5 -0
- package/pp-core/workflows/verify-work.md +9 -2
- package/scripts/gen-capability-registry.cjs +12 -93
- package/scripts/lint-test-file-count.allowlist.json +1 -19
- package/scripts/lint-test-file-count.cjs +5 -6
- package/scripts/prompt-injection-scan.sh +1 -4
- package/scripts/sync-runtime-launcher.cjs +115 -5
- package/pp-core/bin/lib/practices-review-check.cjs +0 -944
- package/pp-core/bin/lib/vault-audit.cjs +0 -2594
- package/pp-core/bin/lib/vault-mode.cjs +0 -235
- package/pp-core/bin/lib/vault-practices.cjs +0 -2057
- package/pp-core/bin/lib/vault-review.cjs +0 -2102
- package/pp-core/bin/lib/vault-write-back.cjs +0 -905
- package/pp-core/references/planner-vault-practices.md +0 -95
- package/pp-core/references/practice-arithmetic-contract.md +0 -121
- package/pp-core/references/verifier-practice-verdicts.md +0 -263
- package/pp-core/templates/practices/README.md +0 -248
- 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/execute-phase/steps/verifier-practice-payload.md +0 -165
- package/scripts/gen-practice-arithmetic-contract.cjs +0 -412
- package/scripts/practice-arithmetic-contract.allow.json +0 -35
- package/scripts/record-vault-mode-golden.cjs +0 -89
- package/scripts/vault-calibrate.cjs +0 -996
|
@@ -7,11 +7,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.INIT_SESSION_WRITE_FAILED = exports.INIT_SESSION_MALFORMED = exports.INIT_CONTRACT_FLAGS = exports.INIT_PROBE_STATUS = exports.INIT_PROBE_NOTE_KEY = exports.INIT_PROBE_WHO_KEY = exports.INIT_PROBE_WHEN_KEY = exports.INIT_PROBE_STATUS_KEY = exports.INIT_ORG_PROFILE_DECLINED_KEY = exports.INIT_COMPOSE_DECLINED_KEY = void 0;
|
|
9
9
|
exports.resolveVaultEnabledState = resolveVaultEnabledState;
|
|
10
|
-
exports.resolveVaultPracticesEnabledState = resolveVaultPracticesEnabledState;
|
|
11
10
|
exports.resolveVaultRequiredState = resolveVaultRequiredState;
|
|
12
11
|
exports.readVaultEnabledState = readVaultEnabledState;
|
|
13
|
-
exports.readVaultPracticesEnabledState = readVaultPracticesEnabledState;
|
|
14
|
-
exports.readVaultStandardsModeActive = readVaultStandardsModeActive;
|
|
15
12
|
exports.readVaultRequiredState = readVaultRequiredState;
|
|
16
13
|
exports.buildInitQuestions = buildInitQuestions;
|
|
17
14
|
exports.deriveSlug = deriveSlug;
|
|
@@ -116,18 +113,12 @@ function getDotted(obj, dottedPath) {
|
|
|
116
113
|
}
|
|
117
114
|
/** Arms the vault capability; written by the session machine, never by an org profile. */
|
|
118
115
|
const VAULT_ENABLED_CONFIG_KEY = 'vault.enabled';
|
|
119
|
-
/** Arms vault-practices; set beside `vault.enabled` under the same absent-only rule, inert without it. */
|
|
120
|
-
const VAULT_PRACTICES_ENABLED_CONFIG_KEY = 'vault.practices_enabled';
|
|
121
116
|
/** The single reading of `vault.enabled` in this module, in the form the gate honours
|
|
122
117
|
* (`resolveGateHonouredKeyState`, Phase 15 D-32). `value` is the observed value, never a
|
|
123
118
|
* coercion, so reports name what is really in the file. */
|
|
124
119
|
function resolveVaultEnabledState(parsed) {
|
|
125
120
|
return resolveGateHonouredKeyState(parsed, VAULT_ENABLED_CONFIG_KEY);
|
|
126
121
|
}
|
|
127
|
-
/** Four-state reading of `vault.practices_enabled`, like `resolveVaultEnabledState`. */
|
|
128
|
-
function resolveVaultPracticesEnabledState(parsed) {
|
|
129
|
-
return resolveGateHonouredKeyState(parsed, VAULT_PRACTICES_ENABLED_CONFIG_KEY);
|
|
130
|
-
}
|
|
131
122
|
/** Four-state reading of the project's OWN `vault.required` (not the profile-layered gate value),
|
|
132
123
|
* so the arming guard respects an explicit per-project `false`. */
|
|
133
124
|
function resolveVaultRequiredState(parsed) {
|
|
@@ -151,12 +142,11 @@ function resolveBooleanKeyState(parsed, key) {
|
|
|
151
142
|
}
|
|
152
143
|
/**
|
|
153
144
|
* Four-state reading of a vault switch whose gates read ONLY the nested form (Phase 15
|
|
154
|
-
* D-32): `vault.enabled
|
|
155
|
-
* nested defaults, so the gates' evaluator answers at its first level and never reaches a
|
|
145
|
+
* D-32): `vault.enabled`. `loadConfig` materialises its nested default, so the gates' evaluator answers at its first level and never reaches a
|
|
156
146
|
* hand-written FLAT key (it warns that the key is ignored). Reading the flat form as
|
|
157
147
|
* `armed` therefore told /pp-init that a switch was on while no gate rendered. The
|
|
158
148
|
* absent-only arming then skipped it, and the done line promised governance nobody
|
|
159
|
-
* enforced
|
|
149
|
+
* enforced.
|
|
160
150
|
*
|
|
161
151
|
* - nested present: its value decides, exactly as before;
|
|
162
152
|
* - flat-only `true`: `absent`, because the gate sees nothing. The arming writes the
|
|
@@ -222,21 +212,6 @@ function readRootKeyState(cwd, resolve) {
|
|
|
222
212
|
function readVaultEnabledState(cwd) {
|
|
223
213
|
return agreeWithGate(readRootKeyState(cwd, resolveVaultEnabledState), gateReadsOn((v) => v.isVaultGateActive(cwd)));
|
|
224
214
|
}
|
|
225
|
-
/** `vault.practices_enabled` for the project at `cwd`: `armed` exactly when
|
|
226
|
-
* `isPracticesGateActive`, the one practices-switch reader (15-02), is ON. Never throws. */
|
|
227
|
-
function readVaultPracticesEnabledState(cwd) {
|
|
228
|
-
return agreeWithGate(readRootKeyState(cwd, resolveVaultPracticesEnabledState), gateReadsOn((v) => v.isPracticesGateActive(cwd)));
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Whether the project at `cwd` is in standards mode, asked of the one mode owner
|
|
232
|
-
* (`isStandardsModeActive`, which honours the org-profile ladder). The /pp-init done line
|
|
233
|
-
* uses it so that, in standards mode, it names the standards record the `vault.trace`
|
|
234
|
-
* gate checks instead of practice recording, which standards mode does not do
|
|
235
|
-
* (15-REVIEW WR-09). Any throw reads as not standards. Never throws.
|
|
236
|
-
*/
|
|
237
|
-
function readVaultStandardsModeActive(cwd) {
|
|
238
|
-
return gateReadsOn((v) => v.isStandardsModeActive(cwd));
|
|
239
|
-
}
|
|
240
215
|
/** `vault.required` for the project at `cwd`: `armed` exactly when the load gate's own
|
|
241
216
|
* answer (`resolveVaultRequiredFlag`) is true. Never throws. */
|
|
242
217
|
function readVaultRequiredState(cwd) {
|
|
@@ -250,7 +225,7 @@ function isVaultEnabledInConfig(parsed) {
|
|
|
250
225
|
// ─── buildInitQuestions ────────────────────────────────────────────────────
|
|
251
226
|
/** The canonical /pp-init question set, as data; the session may answer some itself (see "Automatic answers").
|
|
252
227
|
* No org-profile question in this fork (PBI 74316); provider options come from `PROVIDERS`, preselected via `resolveProvider`. */
|
|
253
|
-
function buildInitQuestions(cwd) {
|
|
228
|
+
function buildInitQuestions(cwd, opts = {}) {
|
|
254
229
|
let preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
255
230
|
try {
|
|
256
231
|
preselected = (0, git_provider_cjs_1.resolveProvider)(cwd).provider;
|
|
@@ -272,6 +247,7 @@ function buildInitQuestions(cwd) {
|
|
|
272
247
|
prompt: 'Client slug (kebab-case, e.g. "acme") — type "internal" if none',
|
|
273
248
|
optionalAnswer: false,
|
|
274
249
|
},
|
|
250
|
+
...clientVaultQuestion(cwd, opts),
|
|
275
251
|
{ id: 'description', prompt: 'One-line description (what this project does)', optionalAnswer: false },
|
|
276
252
|
{
|
|
277
253
|
id: 'provider',
|
|
@@ -284,6 +260,23 @@ function buildInitQuestions(cwd) {
|
|
|
284
260
|
{ id: 'visibility', prompt: 'Visibility (private / public / internal)', optionalAnswer: false },
|
|
285
261
|
];
|
|
286
262
|
}
|
|
263
|
+
/** The client-vault question, asked while no vault is bound and a binding can be composed (`clientVaultChoice`).
|
|
264
|
+
* Blank or "none" keeps the org profile's seeded vault, if any; an answer replaces it in the binding.
|
|
265
|
+
* An internal project is answered "none" automatically (see `autoAnswerFor`). */
|
|
266
|
+
function clientVaultQuestion(cwd, opts) {
|
|
267
|
+
const choice = (0, compose_bindings_cjs_1.clientVaultChoice)(cwd, opts);
|
|
268
|
+
if (!choice.offered)
|
|
269
|
+
return [];
|
|
270
|
+
return [
|
|
271
|
+
{
|
|
272
|
+
id: 'client_vault',
|
|
273
|
+
prompt: choice.defaultRepo === null
|
|
274
|
+
? 'Client vault repo ("owner/name" or a full clone URL) — type "none" to bind no client vault'
|
|
275
|
+
: `Client vault repo ("owner/name" or a full clone URL) — type "none" to use the default vault (${choice.defaultRepo})`,
|
|
276
|
+
optionalAnswer: true,
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
}
|
|
287
280
|
// ─── Automatic answers (PBI 74299): an org profile init section fixes answers and can derive slugs ───
|
|
288
281
|
/** The visibility answers the question accepts. */
|
|
289
282
|
const VISIBILITY_OPTIONS = Object.freeze(['private', 'public', 'internal']);
|
|
@@ -345,6 +338,9 @@ function autoAnswerFor(id, answers, init) {
|
|
|
345
338
|
const value = init.fixed[id];
|
|
346
339
|
return value === undefined ? null : { value, source: 'org-profile' };
|
|
347
340
|
}
|
|
341
|
+
if (id === 'client_vault' && answers.client_slug === 'internal') {
|
|
342
|
+
return { value: 'none', source: 'derived' };
|
|
343
|
+
}
|
|
348
344
|
if (init.deriveSlugs && (id === 'project_slug' || id === 'client_slug')) {
|
|
349
345
|
const name = answers[id === 'project_slug' ? 'project_name' : 'client_name'];
|
|
350
346
|
const slug = typeof name === 'string' ? deriveSlug(name) : null;
|
|
@@ -353,10 +349,10 @@ function autoAnswerFor(id, answers, init) {
|
|
|
353
349
|
return null;
|
|
354
350
|
}
|
|
355
351
|
/** The session's automatic answers, in question order. */
|
|
356
|
-
function listAutoAnswers(cwd, session) {
|
|
352
|
+
function listAutoAnswers(cwd, session, opts) {
|
|
357
353
|
const sources = session.autoAnswers ?? {};
|
|
358
354
|
const out = [];
|
|
359
|
-
for (const q of buildInitQuestions(cwd)) {
|
|
355
|
+
for (const q of buildInitQuestions(cwd, { _globalRoot: opts._globalRoot })) {
|
|
360
356
|
const source = sources[q.id];
|
|
361
357
|
const value = session.answers[q.id];
|
|
362
358
|
if (source !== undefined && typeof value === 'string')
|
|
@@ -662,27 +658,24 @@ function writeIdentityAndProvider(cwd, answers) {
|
|
|
662
658
|
writeConfigMerge(cwd, patch);
|
|
663
659
|
}
|
|
664
660
|
/** The ONE owner of the arm-where-absent rule (quick 261003-kmd): arms `vault.enabled`,
|
|
665
|
-
*
|
|
661
|
+
* and `vault.required` only where ABSENT, from one read in one write.
|
|
666
662
|
*
|
|
667
|
-
* A successful composition means a client vault exists, so the
|
|
663
|
+
* A successful composition means a client vault exists, so the two keys are armed (OD-1,
|
|
668
664
|
* 260915-lmc, 260916-s5n). An explicit `false` or a malformed value is kept — that is the
|
|
669
665
|
* operator's decision, never overwritten (07-09 WR-07). The write happens only when there is
|
|
670
666
|
* something to arm: the nested- and flat-form byte-identity pins depend on a no-op writing
|
|
671
667
|
* nothing. Callers: the session's `'unchanged'` decision (how existing projects get armed by a
|
|
672
668
|
* plain re-run), the approved `compose_confirm`, and `armVaultFromProfile` (`/pp-new-project`).
|
|
673
|
-
* It exists because the two session copies had already drifted once
|
|
674
|
-
*
|
|
669
|
+
* It exists because the two session copies had already drifted once, and a third route would
|
|
670
|
+
* have been a third copy.
|
|
675
671
|
*
|
|
676
|
-
* @returns the armed key names, in the fixed order enabled,
|
|
672
|
+
* @returns the armed key names, in the fixed order enabled, required. */
|
|
677
673
|
function armVaultKeysWhereAbsent(cwd) {
|
|
678
674
|
const { parsed } = readConfigFile(cwd);
|
|
679
675
|
const vaultPatch = {};
|
|
680
676
|
if (resolveVaultEnabledState(parsed).state === 'absent') {
|
|
681
677
|
vaultPatch.enabled = true;
|
|
682
678
|
}
|
|
683
|
-
if (resolveVaultPracticesEnabledState(parsed).state === 'absent') {
|
|
684
|
-
vaultPatch.practices_enabled = true;
|
|
685
|
-
}
|
|
686
679
|
if (resolveVaultRequiredState(parsed).state === 'absent') {
|
|
687
680
|
vaultPatch.required = true;
|
|
688
681
|
}
|
|
@@ -786,10 +779,15 @@ function normalizeYesNo(value) {
|
|
|
786
779
|
}
|
|
787
780
|
/** Decide the current step, performing its idempotent side effects once: automatic answers, the identity write,
|
|
788
781
|
* the compose decision and `doneAt`. Recomputes the question set on every call. */
|
|
782
|
+
/** The client vault the operator named, or `undefined` to keep the seeded one. */
|
|
783
|
+
function answeredClientVault(session) {
|
|
784
|
+
const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(session.answers.client_vault);
|
|
785
|
+
return checked.ok && checked.value !== null ? checked.value : undefined;
|
|
786
|
+
}
|
|
789
787
|
function computeCurrentStep(cwd, session, opts) {
|
|
790
788
|
// Any failure degrades to a `write_failed` step instead of throwing.
|
|
791
789
|
try {
|
|
792
|
-
const questions = buildInitQuestions(cwd);
|
|
790
|
+
const questions = buildInitQuestions(cwd, { _globalRoot: opts._globalRoot });
|
|
793
791
|
// PBI 74299: read once per call; only consulted for an unanswered question.
|
|
794
792
|
let profileInit = null;
|
|
795
793
|
for (const q of questions) {
|
|
@@ -812,7 +810,11 @@ function computeCurrentStep(cwd, session, opts) {
|
|
|
812
810
|
persistSession(cwd, session);
|
|
813
811
|
}
|
|
814
812
|
if (session.composeDecision === null) {
|
|
815
|
-
const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
813
|
+
const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
814
|
+
offline: opts.offline === true,
|
|
815
|
+
_globalRoot: opts._globalRoot,
|
|
816
|
+
clientVaultRepo: answeredClientVault(session),
|
|
817
|
+
});
|
|
816
818
|
// Offer compose_confirm only when approving would change something; 'unchanged' and 'skipped' are distinct end states.
|
|
817
819
|
if (dryRun.composable === true && dryRun.action !== 'unchanged') {
|
|
818
820
|
// VAULT-EGRESS-1: `info` is display-only (the CLI and the TTY renderer print it); approval
|
|
@@ -917,7 +919,7 @@ function initSessionNext(cwd, opts = {}) {
|
|
|
917
919
|
}
|
|
918
920
|
const session = loaded.session;
|
|
919
921
|
const state = currentStepToState(computeCurrentStep(cwd, session, opts));
|
|
920
|
-
const autoAnswered = listAutoAnswers(cwd, session);
|
|
922
|
+
const autoAnswered = listAutoAnswers(cwd, session, opts);
|
|
921
923
|
return autoAnswered.length > 0 ? { ...state, autoAnswered } : state;
|
|
922
924
|
}
|
|
923
925
|
/** Record one answer for the current step only; out-of-order, blank required, unparseable or post-done answers are rejected. */
|
|
@@ -957,6 +959,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
957
959
|
return { ok: false, reason: 'answer_required', message: `Question "${stepId}" requires a non-blank answer.` };
|
|
958
960
|
}
|
|
959
961
|
const answerValue = typeof value === 'string' ? value : '';
|
|
962
|
+
if (stepId === 'client_vault') {
|
|
963
|
+
const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(answerValue);
|
|
964
|
+
if (!checked.ok) {
|
|
965
|
+
return { ok: false, reason: 'invalid_answer', message: `Question "client_vault": ${checked.reason}` };
|
|
966
|
+
}
|
|
967
|
+
}
|
|
960
968
|
session.answers[stepId] = answerValue;
|
|
961
969
|
persistSession(cwd, session);
|
|
962
970
|
return { ok: true };
|
|
@@ -979,7 +987,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
979
987
|
}
|
|
980
988
|
if (normalized === true) {
|
|
981
989
|
// Check the write result before recording "approved": the file can change between the dry run and this write.
|
|
982
|
-
const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
990
|
+
const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
991
|
+
write: true,
|
|
992
|
+
offline: opts.offline === true,
|
|
993
|
+
_globalRoot: opts._globalRoot,
|
|
994
|
+
clientVaultRepo: answeredClientVault(session),
|
|
995
|
+
});
|
|
983
996
|
if (writeResult.composable !== true || writeResult.wrote !== true) {
|
|
984
997
|
// Leave the decision unset so the next call re-evaluates and can re-offer compose_confirm.
|
|
985
998
|
return {
|
|
@@ -161,36 +161,22 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
161
161
|
'INIT_VAULT_ENABLED_MALFORMED; store the boolean with pp-tools config-set vault.enabled true.');
|
|
162
162
|
}
|
|
163
163
|
else if (vaultState === 'armed') {
|
|
164
|
-
// Quick 260915-lmc: vault.practices_enabled is armed beside it under
|
|
165
|
-
// the same absent-only rule, so the line names it only when the
|
|
166
|
-
// READING says true — an explicit false is the project's choice and
|
|
167
|
-
// is not contradicted here.
|
|
168
|
-
const practicesArmed = (0, init_contract_cjs_1.readVaultPracticesEnabledState)(cwd).state === 'armed';
|
|
169
164
|
// Quick 260916-s5n: vault.required is named only when the READING
|
|
170
165
|
// says armed, by the same rule — an explicit false is the project's
|
|
171
166
|
// choice and the last line printed must not contradict it.
|
|
172
167
|
const requiredArmed = (0, init_contract_cjs_1.readVaultRequiredState)(cwd).state === 'armed';
|
|
173
|
-
// Built as a head plus
|
|
174
|
-
//
|
|
175
|
-
// stop used to live inside the practices branch, which made every
|
|
176
|
-
// further clause a sentence continuing past its own period.
|
|
168
|
+
// Built as a head plus clauses carrying no terminator of their own,
|
|
169
|
+
// with exactly one full stop appended at the end.
|
|
177
170
|
const head = 'Done — .platform-bindings.json was composed and written; vault.enabled is now true ' +
|
|
178
171
|
'(the vault capability is armed — /pp-init Step 2.6 probes reachability next)';
|
|
179
|
-
// 15-REVIEW WR-09:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// practices switch reads.
|
|
183
|
-
const practicesClause = (0, init_contract_cjs_1.readVaultStandardsModeActive)(cwd)
|
|
184
|
-
? '; vault.mode is standards, so each plan records which client standards documents governed it ' +
|
|
185
|
-
'(<vault_standards>), checked by the vault.trace gate'
|
|
186
|
-
: practicesArmed
|
|
187
|
-
? '; vault.practices_enabled is true, so each phase records which client vault practices it applied'
|
|
188
|
-
: '';
|
|
172
|
+
// 15-REVIEW WR-09: the clause names what is recorded and checked.
|
|
173
|
+
const standardsClause = '; each plan records which client standards documents governed it ' +
|
|
174
|
+
'(<vault_standards>), checked by the vault.trace gate';
|
|
189
175
|
const requiredClause = requiredArmed
|
|
190
176
|
? '; vault.required is true, so a client vault miss, a failed fetch or an unwritable vault summary ' +
|
|
191
177
|
'stops the build at plan:pre and execute:pre'
|
|
192
178
|
: '';
|
|
193
|
-
prompter.print(head +
|
|
179
|
+
prompter.print(head + standardsClause + requiredClause + '.');
|
|
194
180
|
}
|
|
195
181
|
else {
|
|
196
182
|
// 'absent' — unreachable after an approved compose (the arming
|
package/pp-core/bin/lib/io.cjs
CHANGED
|
@@ -180,19 +180,13 @@ const ERROR_REASON = Object.freeze({
|
|
|
180
180
|
PLATFORM_BINDINGS_PARSE_FAILED: 'platform_bindings_parse_failed',
|
|
181
181
|
// init (Phase 34-09 gap closure: `pp-tools init` interactive command)
|
|
182
182
|
INIT_NON_INTERACTIVE: 'init_non_interactive',
|
|
183
|
-
// vault
|
|
183
|
+
// vault promote (a standards document into the client vault)
|
|
184
184
|
VAULT_PROMOTE_DISABLED: 'vault_promote_disabled',
|
|
185
|
-
|
|
185
|
+
VAULT_PROMOTE_INVALID_DOCUMENT: 'vault_promote_invalid_document',
|
|
186
186
|
VAULT_PROMOTE_SECRET_DETECTED: 'vault_promote_secret_detected',
|
|
187
187
|
VAULT_PROMOTE_TARGET_REFUSED: 'vault_promote_target_refused',
|
|
188
188
|
VAULT_PROMOTE_CONCURRENT_EDIT: 'vault_promote_concurrent_edit',
|
|
189
189
|
VAULT_PROMOTE_TRANSPORT_FAILED: 'vault_promote_transport_failed',
|
|
190
|
-
// vault.mode (Vault Assurance 13-03, D-08/D-14): an unrecognised mode value,
|
|
191
|
-
// and the standards-mode promote refusal
|
|
192
|
-
VAULT_MODE_INVALID: 'vault_mode_invalid',
|
|
193
|
-
// 13-REVIEW WR-01: the mode resolver failed; refused like an invalid value
|
|
194
|
-
VAULT_MODE_UNRESOLVED: 'vault_mode_unresolved',
|
|
195
|
-
VAULT_PROMOTE_NOT_APPLICABLE: 'vault_promote_not_applicable',
|
|
196
190
|
// discovery rollup (discovery-v2 Phase 1: D-07/D-08 gate)
|
|
197
191
|
DISCOVERY_ROLLUP_PARSE_FAILED: 'discovery_rollup_parse_failed',
|
|
198
192
|
DISCOVERY_ROLLUP_MISMATCH: 'discovery_rollup_mismatch',
|
|
@@ -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,
|
|
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
|
*
|
|
@@ -36,7 +36,7 @@ const configLoaderModule = require("./config-loader.cjs");
|
|
|
36
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,
|
|
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");
|
|
@@ -103,7 +103,7 @@ function _getCanonicalPoints(_registry) {
|
|
|
103
103
|
* resolved against `config`; active iff truthy. Inactive hooks are filtered out.
|
|
104
104
|
*/
|
|
105
105
|
function resolveLoopHooks(input) {
|
|
106
|
-
const { point, registry, config, cwd, capabilityStatesById,
|
|
106
|
+
const { point, registry, config, cwd, capabilityStatesById, recordedArtifacts, platformBindingsPresent, } = input;
|
|
107
107
|
// Validate point
|
|
108
108
|
const canonicalPoints = _getCanonicalPoints(registry);
|
|
109
109
|
if (!canonicalPoints.includes(point)) {
|
|
@@ -122,12 +122,6 @@ function resolveLoopHooks(input) {
|
|
|
122
122
|
}
|
|
123
123
|
const entryMap = entry;
|
|
124
124
|
const activeHooks = [];
|
|
125
|
-
// Phase 15 D-02/D-28: the per-hook `vaultMode` filter, one rule owned by
|
|
126
|
-
// capability-state (`vaultModeFilterVerdict`), evaluated against the injected
|
|
127
|
-
// mode VALUE. The field is read here and never copied into an ActiveHook.
|
|
128
|
-
function modeOk(hook, kind) {
|
|
129
|
-
return vaultModeFilterVerdict(hook['vaultMode'], kind, vaultMode);
|
|
130
|
-
}
|
|
131
125
|
// Helper: check activation using single-key precedence resolver (FIX 1 + FIX 3)
|
|
132
126
|
function isActive(hook) {
|
|
133
127
|
const when = hook['when'];
|
|
@@ -260,8 +254,6 @@ function resolveLoopHooks(input) {
|
|
|
260
254
|
continue;
|
|
261
255
|
if (droppedForBindings(capId))
|
|
262
256
|
continue;
|
|
263
|
-
if (!modeOk(hook, 'step'))
|
|
264
|
-
continue;
|
|
265
257
|
if (!isActive(hook))
|
|
266
258
|
continue;
|
|
267
259
|
const ref = (typeof hook['ref'] === 'object' && hook['ref'] !== null)
|
|
@@ -298,8 +290,6 @@ function resolveLoopHooks(input) {
|
|
|
298
290
|
continue;
|
|
299
291
|
if (droppedForBindings(capId))
|
|
300
292
|
continue;
|
|
301
|
-
if (!modeOk(hook, 'contribution'))
|
|
302
|
-
continue;
|
|
303
293
|
if (!isActive(hook))
|
|
304
294
|
continue;
|
|
305
295
|
const into = typeof hook['into'] === 'string' ? hook['into'] : undefined;
|
|
@@ -338,12 +328,6 @@ function resolveLoopHooks(input) {
|
|
|
338
328
|
// vault gate already answers inert there, so nothing is left ungated.
|
|
339
329
|
if (droppedForBindings(capId))
|
|
340
330
|
continue;
|
|
341
|
-
// Phase 16 D-19 / research C-1: the mode filter runs FIRST, so a
|
|
342
|
-
// mode-filtered seal gate never consults the recorded-artifacts predicate
|
|
343
|
-
// (a default standards project pays no planning-tree read for a seal it
|
|
344
|
-
// cannot render). `modeOk` is pure, so the order changes no other result.
|
|
345
|
-
if (!modeOk(hook, 'gate'))
|
|
346
|
-
continue;
|
|
347
331
|
const verdict = sealRenderVerdict(hook[SEAL_FIELD], gateStateOf(capId), recordedArtifactsOnce);
|
|
348
332
|
if (verdict === 'drop')
|
|
349
333
|
continue;
|
|
@@ -496,9 +480,7 @@ function renderLoopHooks(resolved) {
|
|
|
496
480
|
* 0) when no contribution matches. No JSON envelope; `--raw` is ignored. This
|
|
497
481
|
* is the text execute-phase interpolates verbatim into a spawn prompt (15-09).
|
|
498
482
|
* `--active-cap` takes precedence: given both, `--into` is ignored.
|
|
499
|
-
|
|
500
|
-
* options.resolveVaultMode: an injected `() => mode | null` (pp-tools builds it
|
|
501
|
-
* on vaultModeVerdict). This module never imports vault code (D-02).
|
|
483
|
+
|
|
502
484
|
*
|
|
503
485
|
* options.resolveRecordedArtifacts: an injected `() => boolean` (pp-tools builds
|
|
504
486
|
* it on the seal leaf's hasRecordedVaultArtifacts); only literal `true` lets a
|
|
@@ -552,11 +534,8 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
552
534
|
for (const cap of state.capabilities || []) {
|
|
553
535
|
capabilityStatesById.set(cap.id, cap);
|
|
554
536
|
}
|
|
555
|
-
// Phase
|
|
556
|
-
//
|
|
557
|
-
// non-mode answer is `null`, the fail-closed undefined mode (D-28).
|
|
558
|
-
const vaultMode = resolveInjectedVaultMode(options['resolveVaultMode']);
|
|
559
|
-
// Phase 16 D-19: the recorded-artifacts predicate arrives the same way (pp-tools
|
|
537
|
+
// Phase 16 D-19: the recorded-artifacts predicate arrives through an injected
|
|
538
|
+
// resolver (pp-tools
|
|
560
539
|
// passes one built on the seal leaf's hasRecordedVaultArtifacts). The resolver
|
|
561
540
|
// memoises it and asks it only for an enabled, config-inactive seal gate.
|
|
562
541
|
const resolveRecordedArtifacts = options['resolveRecordedArtifacts'];
|
|
@@ -570,7 +549,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
570
549
|
// --active-cap reads the UNFILTERED resolution (16-16 D-81, see above):
|
|
571
550
|
// without the bindings thunk, so it keeps answering the config switch.
|
|
572
551
|
resolved = resolveLoopHooks({
|
|
573
|
-
point, registry, config, cwd, capabilityStatesById,
|
|
552
|
+
point, registry, config, cwd, capabilityStatesById, recordedArtifacts,
|
|
574
553
|
...(activeCapId === undefined ? { platformBindingsPresent } : {}),
|
|
575
554
|
});
|
|
576
555
|
}
|
|
@@ -583,7 +562,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
|
|
|
583
562
|
if (activeCapId !== undefined) {
|
|
584
563
|
// Phase 16 D-27 (research C-3): a hook rendering is no longer enough — the
|
|
585
564
|
// capability must itself be active. A seal gate renders for a config-
|
|
586
|
-
// inactive vault
|
|
565
|
+
// inactive vault, and the doors read `--active-cap` as their
|
|
587
566
|
// switch, so a seal must never make an inactive capability answer `true`.
|
|
588
567
|
// Equivalent to the old answer on every input without a seal: a normal
|
|
589
568
|
// hook renders only for an active capability.
|
|
@@ -634,9 +613,6 @@ module.exports = {
|
|
|
634
613
|
// Re-exported for identity parity guard (FIX 2: resolveConfigValues in this module
|
|
635
614
|
// calls resolveConfigKey; exporting it here makes the single-owner contract testable).
|
|
636
615
|
resolveConfigKey,
|
|
637
|
-
// Re-exported for the same identity pin: the mode-filter rule has one owner,
|
|
638
|
-
// capability-state.cjs (Phase 15 D-02/D-28).
|
|
639
|
-
vaultModeFilterVerdict,
|
|
640
616
|
// Re-exported for the same identity pin: the seal rule has one owner,
|
|
641
617
|
// capability-state.cjs (Phase 16 D-19).
|
|
642
618
|
sealRenderVerdict,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* explicit opt-out.
|
|
19
19
|
*
|
|
20
20
|
* It imports ONLY node builtins (`node:fs`, `node:path`) at the top level —
|
|
21
|
-
* no `./vault.cjs`, `./vault-coordinates.cjs`,
|
|
21
|
+
* no `./vault.cjs`, `./vault-coordinates.cjs`,
|
|
22
22
|
* `./io.cjs`, `./shell-command-projection.cjs`. This is load-bearing: in
|
|
23
23
|
* 33-03 BOTH `src/git-provider.cts` and `src/vault-coordinates.cts` import
|
|
24
24
|
* THIS module at their own top level. If this module imported either of
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* inside `profileValueDisposition`, executed only when a profile value is
|
|
34
34
|
* actually being validated, never at module load. This is the same
|
|
35
35
|
* lazy-require seam `src/vault-command-router.cts` already uses for
|
|
36
|
-
* `./vault.cjs
|
|
36
|
+
* `./vault.cjs`, and it is safe here precisely
|
|
37
37
|
* because `git-provider.cjs` has no top-level dependency on this module —
|
|
38
38
|
* by the time any value is being validated, that module is either already
|
|
39
39
|
* in the require cache or loads cleanly on its own. D-10 requires reusing
|
|
@@ -41,13 +41,6 @@
|
|
|
41
41
|
* than writing a second regex; the lazy seam is how a zero-import leaf can
|
|
42
42
|
* still do that.
|
|
43
43
|
*
|
|
44
|
-
* The second exception (Vault Assurance 13-03, D-23) is a top-level import
|
|
45
|
-
* of `./vault-mode.cjs`, the mode vocabulary leaf. That module imports
|
|
46
|
-
* nothing at all, so it can never require this module back and cannot close
|
|
47
|
-
* a cycle; it is imported so `resolveVaultMode` and the `vault.mode`
|
|
48
|
-
* disposition quote the key and the two literals from their one owner rather
|
|
49
|
-
* than spelling them here.
|
|
50
|
-
*
|
|
51
44
|
* This file's own prose and code deliberately never name a concrete org
|
|
52
45
|
* profile package or any client — the pointer is described only as "the
|
|
53
46
|
* npm package name an operator configures". `tests/org-profile.test.cjs`
|
|
@@ -68,12 +61,8 @@ exports.resolveOrgProfile = resolveOrgProfile;
|
|
|
68
61
|
exports.readProfileValue = readProfileValue;
|
|
69
62
|
exports.booleanPolicyValue = booleanPolicyValue;
|
|
70
63
|
exports.resolveVaultRequired = resolveVaultRequired;
|
|
71
|
-
exports.resolveVaultMode = resolveVaultMode;
|
|
72
64
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
73
65
|
const node_path_1 = __importDefault(require("node:path"));
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
75
|
-
const vaultMode = require("./vault-mode.cjs");
|
|
76
|
-
const { VAULT_MODE_CONFIG_KEY, DEFAULT_VAULT_MODE, VAULT_MODE_REASON, isVaultMode } = vaultMode;
|
|
77
66
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
78
67
|
/** The dotted `.planning/config.json` key naming the profile package. Deliberately
|
|
79
68
|
* top-level, NOT nested under `vault.`, because the profile also supplies
|
|
@@ -98,7 +87,7 @@ exports.ORG_PROFILE_NONE = 'none';
|
|
|
98
87
|
* separate package. It carries coordinates only — never a vault ref: the
|
|
99
88
|
* vault version a project builds on is committed per project in its
|
|
100
89
|
* `.platform-bindings.json` (`client.vaultRef`), so no pp-core release can
|
|
101
|
-
* move it.
|
|
90
|
+
* move it.
|
|
102
91
|
*/
|
|
103
92
|
exports.EMBEDDED_ORG_PROFILE_NAME = 'embedded';
|
|
104
93
|
/**
|
|
@@ -159,10 +148,10 @@ function readEmbeddedProfileVersion(dataPath) {
|
|
|
159
148
|
* guard nor `resolveVaultRequired` hand-restates the string. */
|
|
160
149
|
exports.VAULT_REQUIRED_CONFIG_KEY = 'vault.required';
|
|
161
150
|
/**
|
|
162
|
-
* D-06's fixed allowlist: a profile may contribute exactly these
|
|
163
|
-
* config keys (five coordinates and
|
|
164
|
-
*
|
|
165
|
-
*
|
|
151
|
+
* D-06's fixed allowlist: a profile may contribute exactly these six dotted
|
|
152
|
+
* config keys (five coordinates and one policy key). Entry order is not
|
|
153
|
+
* contractual; membership is. Anything else present in a profile document is
|
|
154
|
+
* dropped with a bounded warning, except the retired keys below.
|
|
166
155
|
*
|
|
167
156
|
* FIVE COORDINATES AND ONE POLICY (02-04, D-VH-4 / STRICT-01). The first five
|
|
168
157
|
* members answer *where to fetch from* — a provider, three shared-vault
|
|
@@ -197,15 +186,14 @@ exports.ORG_PROFILE_ALLOWLIST = Object.freeze([
|
|
|
197
186
|
'vault.fallback_host',
|
|
198
187
|
'platform.host',
|
|
199
188
|
'vault.required',
|
|
200
|
-
// Vault Assurance 13-03 (D-06): the second POLICY key, after vault.required
|
|
201
|
-
// (02-04). Unlike vault.required, a profile value here can make a gate LESS
|
|
202
|
-
// strict — `standards` turns the blocking vault.audit gate into
|
|
203
|
-
// not_applicable — so the widening is accepted and bounded in 13-CONTEXT
|
|
204
|
-
// D-24 (threat T-13-01): project config outranks the profile, only the two
|
|
205
|
-
// literals pass the disposition below, and the embedded fork profile does
|
|
206
|
-
// not seed it. vault.enabled and init.* stay absent from this list.
|
|
207
|
-
VAULT_MODE_CONFIG_KEY,
|
|
208
189
|
]);
|
|
190
|
+
/**
|
|
191
|
+
* Keys a profile written for an older pp-core may still carry, which this
|
|
192
|
+
* engine no longer reads (FORK-ONLY: the vault once had a second, practices
|
|
193
|
+
* mode). They are dropped without a warning: a profile that still says
|
|
194
|
+
* `vault.mode` needs no attention, because the standards vault is the only vault.
|
|
195
|
+
*/
|
|
196
|
+
const RETIRED_PROFILE_KEYS = new Set(['vault.mode', 'vault.practices_enabled']);
|
|
209
197
|
/**
|
|
210
198
|
* The single top-level key naming the Phase 34 init-only seed section.
|
|
211
199
|
*
|
|
@@ -656,15 +644,6 @@ function profileValueDisposition(key, value) {
|
|
|
656
644
|
}
|
|
657
645
|
return { ok: false, reason: 'value must be the boolean true or false' };
|
|
658
646
|
}
|
|
659
|
-
// 13-03 (D-06, D-09): the mode accepts only its two literals, trimmed.
|
|
660
|
-
// Anything else is dropped through the existing warning / rejected[]
|
|
661
|
-
// path, and the mode then resolves to the default. The reason names the
|
|
662
|
-
// allowed values and never echoes the rejected one.
|
|
663
|
-
if (key === VAULT_MODE_CONFIG_KEY) {
|
|
664
|
-
if (isVaultMode(value))
|
|
665
|
-
return { ok: true, value: value.trim() };
|
|
666
|
-
return { ok: false, reason: 'value must be practices or standards' };
|
|
667
|
-
}
|
|
668
647
|
if (typeof value !== 'string') {
|
|
669
648
|
return { ok: false, reason: 'value must be a string' };
|
|
670
649
|
}
|
|
@@ -950,6 +929,8 @@ function resolveOrgProfile(cwd, opts) {
|
|
|
950
929
|
if (leaf.key === exports.ORG_PROFILE_INIT_SECTION || leaf.key.startsWith(`${exports.ORG_PROFILE_INIT_SECTION}.`)) {
|
|
951
930
|
continue;
|
|
952
931
|
}
|
|
932
|
+
if (RETIRED_PROFILE_KEYS.has(leaf.key))
|
|
933
|
+
continue;
|
|
953
934
|
if (!exports.ORG_PROFILE_ALLOWLIST.includes(leaf.key)) {
|
|
954
935
|
ignored.push(leaf.key);
|
|
955
936
|
if (ignoredWarningCount < PROFILE_IGNORED_WARNING_CAP) {
|
|
@@ -1128,60 +1109,3 @@ function resolveVaultRequired(cwd, opts) {
|
|
|
1128
1109
|
// Level 3 — the registered default from capabilities/vault/capability.json.
|
|
1129
1110
|
return withIgnored({ required: false, source: 'default', warnings: profile.warnings });
|
|
1130
1111
|
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Resolve `vault.mode` for `cwd`: project config, then the org profile, then
|
|
1133
|
-
* the registered default `practices` — the same three levels as
|
|
1134
|
-
* `resolveVaultRequired` above.
|
|
1135
|
-
*
|
|
1136
|
-
* Like `resolveVaultRequired` (the one ladder both the load gate and the
|
|
1137
|
-
* report ask, since 13-REVIEW CR-02), this resolver is the ONE reader of the
|
|
1138
|
-
* mode for every mode-aware surface: the `vault
|
|
1139
|
-
* org-profile` report (13-05), the gate, refusals and status field (13-06)
|
|
1140
|
-
* and the parked practices surfaces (13-07). No surface re-reads the key
|
|
1141
|
-
* from config itself; the negative-grep anti-drift test in
|
|
1142
|
-
* `tests/vault-mode.test.cjs` is what keeps a second reader from appearing.
|
|
1143
|
-
*
|
|
1144
|
-
* Level 1 reads RAW config (D-21): `resolveConfigKey` with `config: {}` and
|
|
1145
|
-
* `registry: {}` reads the workstream config.json and then the root
|
|
1146
|
-
* config.json, each in either dotted form (nested first), with NO schema
|
|
1147
|
-
* default and no federated fallback. The merged config from
|
|
1148
|
-
* `config-loader.cts` is deliberately not used:
|
|
1149
|
-
* `federated-config`'s `_typeMatches` substitutes the schema default for an
|
|
1150
|
-
* out-of-enum value, and its warning is suppressed at load
|
|
1151
|
-
* (`config-loader.cts`), so `"whatever"` would silently read as `practices`.
|
|
1152
|
-
* A project value that is present but not one of the two literals returns
|
|
1153
|
-
* `ok: false` with `vault_mode_invalid` and NEVER falls back (D-08): an
|
|
1154
|
-
* unknown mode must be refused, not treated as practices.
|
|
1155
|
-
*
|
|
1156
|
-
* Level 2 is `readProfileValue`; `profileValueDisposition` has already
|
|
1157
|
-
* narrowed the profile value to one of the two literals or dropped it with a
|
|
1158
|
-
* warning (D-09), so an invalid profile value lands at level 3.
|
|
1159
|
-
*
|
|
1160
|
-
* KNOWN GAP (D-25), recorded rather than hidden: `config-get` reads only the
|
|
1161
|
-
* planning dir's config.json, while level 1 here reads the workstream config
|
|
1162
|
-
* and then the root config. In flat mode the two agree (pinned by the parity
|
|
1163
|
-
* test in `tests/vault-mode.test.cjs`); on a partitioned project a root-only
|
|
1164
|
-
* `vault.mode` can read differently. Filed as a todo; the same gap pre-exists
|
|
1165
|
-
* for `vault.required`.
|
|
1166
|
-
*
|
|
1167
|
-
* Never throws: both seams already degrade to a miss on any fault.
|
|
1168
|
-
*/
|
|
1169
|
-
function resolveVaultMode(cwd, opts) {
|
|
1170
|
-
// Level 1 — project config, raw (ws then root, either dotted form).
|
|
1171
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1172
|
-
const activation = require('./capability-activation.cjs');
|
|
1173
|
-
const fromConfig = activation.resolveConfigKey(VAULT_MODE_CONFIG_KEY, { config: {}, cwd, registry: {} });
|
|
1174
|
-
if (fromConfig.found) {
|
|
1175
|
-
if (isVaultMode(fromConfig.value)) {
|
|
1176
|
-
return { ok: true, mode: fromConfig.value.trim(), source: 'config', warnings: [] };
|
|
1177
|
-
}
|
|
1178
|
-
return { ok: false, source: 'config', reason: VAULT_MODE_REASON.INVALID, warnings: [] };
|
|
1179
|
-
}
|
|
1180
|
-
// Level 2 — the org profile, already narrowed by profileValueDisposition.
|
|
1181
|
-
const profile = readProfileValue(cwd, VAULT_MODE_CONFIG_KEY, opts);
|
|
1182
|
-
if (isVaultMode(profile.value)) {
|
|
1183
|
-
return { ok: true, mode: profile.value.trim(), source: 'profile', warnings: profile.warnings };
|
|
1184
|
-
}
|
|
1185
|
-
// Level 3 — the registered default from capabilities/vault/capability.json.
|
|
1186
|
-
return { ok: true, mode: DEFAULT_VAULT_MODE, source: 'default', warnings: profile.warnings };
|
|
1187
|
-
}
|