@pareto4/pp-core 1.17.1 → 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 +1 -1
- package/README.md +5 -3
- package/agents/pp-code-fixer.md +0 -11
- package/agents/pp-code-reviewer.md +1 -78
- package/agents/pp-executor.md +9 -23
- package/agents/pp-planner.md +11 -11
- package/agents/pp-vault-reviewer.md +43 -189
- package/agents/pp-verifier.md +0 -6
- package/commands/pp-ns-manage.md +1 -1
- package/commands/pp-ns-review.md +1 -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 +13 -355
- 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 +277 -2273
- package/pp-core/bin/lib/clusters.cjs +2 -2
- package/pp-core/bin/lib/compose-bindings.cjs +2 -2
- package/pp-core/bin/lib/config.cjs +3 -6
- 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 +7 -35
- 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 +3 -3
- package/pp-core/bin/lib/vault-index.cjs +27 -70
- 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 +1 -26
- package/pp-core/bin/lib/vault.cjs +120 -314
- package/pp-core/bin/pp-tools.cjs +5 -32
- package/pp-core/references/autonomous-best-effort.md +1 -1
- package/pp-core/references/planner-vault-standards.md +16 -25
- package/pp-core/templates/README.md +1 -1
- package/pp-core/templates/VAULT-REVIEW.md +24 -170
- package/pp-core/workflows/add-tests.md +3 -3
- package/pp-core/workflows/audit-fix.md +3 -3
- package/pp-core/workflows/code-review-fix.md +3 -3
- package/pp-core/workflows/code-review.md +1 -57
- package/pp-core/workflows/debug.md +2 -2
- package/pp-core/workflows/discovery-sprint/prototype.md +2 -2
- package/pp-core/workflows/discovery-sprint/vault-context.md +4 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +11 -13
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +5 -25
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +6 -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 +2 -5
- package/pp-core/workflows/execute-plan.md +6 -8
- package/pp-core/workflows/help/modes/full.md +1 -1
- package/pp-core/workflows/quick.md +8 -8
- package/pp-core/workflows/validate-phase.md +3 -3
- package/pp-core/workflows/vault-review.md +64 -186
- package/pp-core/workflows/vault-write-back.md +73 -110
- package/pp-core/workflows/verify-work.md +1 -1
- 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 +0 -3
- 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
|
@@ -45,8 +45,8 @@ exports.CLUSTERS = Object.freeze({
|
|
|
45
45
|
'add-tests',
|
|
46
46
|
'secure-phase',
|
|
47
47
|
// Retroactive, non-gating review of a completed phase against the client
|
|
48
|
-
// vault's
|
|
49
|
-
// secure-phase / ui-review / validate-phase.
|
|
48
|
+
// vault's standards documents (`check vault-review`, a report) — the same
|
|
49
|
+
// family as secure-phase / ui-review / validate-phase.
|
|
50
50
|
'vault-review',
|
|
51
51
|
]),
|
|
52
52
|
milestone: Object.freeze([
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
* `JSON.stringify`/object literals — never by `.md` shell string
|
|
32
32
|
* concatenation.
|
|
33
33
|
*
|
|
34
|
-
* Its importers today are `src/vault-update.cts`, `src/init-contract.cts
|
|
35
|
-
*
|
|
34
|
+
* Its importers today are `src/vault-update.cts`, `src/init-contract.cts`
|
|
35
|
+
* and (quick 261003-lqi) `src/check-command-router.cts`
|
|
36
36
|
* — and this module imports none of them, so its top-level imports of `./org-profile.cjs`, `./platform-bindings.cjs`,
|
|
37
37
|
* `./git-provider.cjs`, `./vault-coordinates.cjs`, `./io.cjs`,
|
|
38
38
|
* `./project-root.cjs`, `./package-identity.cjs` and
|
|
@@ -527,7 +527,6 @@ function isStringMember(list, value) {
|
|
|
527
527
|
*/
|
|
528
528
|
const BOOLEAN_ONLY_CONFIG_KEYS = Object.freeze([
|
|
529
529
|
'vault.enabled',
|
|
530
|
-
'vault.practices_enabled',
|
|
531
530
|
'vault.required',
|
|
532
531
|
'workflow.schema_drift_gate',
|
|
533
532
|
'workflow.nyquist_validation',
|
|
@@ -641,8 +640,8 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
641
640
|
error(`Invalid plan_review.source_grounding_authority '${val}'. Valid values: ${VALID_SOURCE_GROUNDING_AUTHORITIES.join(', ')}`);
|
|
642
641
|
}
|
|
643
642
|
// Capability enum keys, in ONE place (Vault Assurance 13-05, D-07). Measured
|
|
644
|
-
// 2026-10-09: `config-set workflow.code_review_depth bogus`
|
|
645
|
-
//
|
|
643
|
+
// 2026-10-09: `config-set workflow.code_review_depth bogus` wrote at exit 0,
|
|
644
|
+
// because no guard
|
|
646
645
|
// consulted the registry's declared `values`. This runs AFTER every
|
|
647
646
|
// hand-coded guard above, so a key that already has its own guard
|
|
648
647
|
// (`workflow.drift_action`) keeps its message byte-identical; the
|
|
@@ -653,9 +652,7 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
653
652
|
// The TYPE is checked before membership (13-REVIEW CR-01): a value starting
|
|
654
653
|
// with `[` is JSON-parsed above, and `String(['standards']) === 'standards'`,
|
|
655
654
|
// so a membership-only test let a one-element array through and wrote it —
|
|
656
|
-
// which `
|
|
657
|
-
// `federated-config` silently reads back as the default for the other enum
|
|
658
|
-
// keys. Every declared capability enum value is a string, so a non-string
|
|
655
|
+
// which `federated-config` silently reads back as the default. Every declared capability enum value is a string, so a non-string
|
|
659
656
|
// parse (array, object, number, boolean) can never be a member.
|
|
660
657
|
const enumValues = capabilityEnumValues(kp);
|
|
661
658
|
if (enumValues && !isStringMember(enumValues, parsedValue)) {
|
|
@@ -118,12 +118,11 @@ function isPlausibleBranchRef(value) {
|
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
120
|
* WHAT A CALLER SAYS WHEN THE OVERRIDE IS REFUSED. One sentence, exported, for
|
|
121
|
-
* every channel —
|
|
121
|
+
* every channel — a caller states it and never spells it.
|
|
122
122
|
*
|
|
123
123
|
* A second spelling of this sentence would be a second definition of what the
|
|
124
|
-
* mechanism did,
|
|
125
|
-
*
|
|
126
|
-
* `check ship.practices` message all carry these exact bytes.
|
|
124
|
+
* mechanism did, so every channel that states the refusal carries these exact
|
|
125
|
+
* bytes.
|
|
127
126
|
*
|
|
128
127
|
* The reason carries NO copy of the refused value: the value is client-supplied
|
|
129
128
|
* and one of its shapes is an absolute filesystem path, so echoing it back onto
|
|
@@ -296,15 +295,13 @@ function resolveBaseBranchResult(cwd, deps) {
|
|
|
296
295
|
* Consults the full precedence ladder and always returns a non-empty string.
|
|
297
296
|
* Never throws.
|
|
298
297
|
*
|
|
299
|
-
* The bare-string form.
|
|
300
|
-
* `resolveBaseBranchResult` instead and states the refusal (W16):
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* sentence. A new caller reaching for this form should ask whether that is true
|
|
307
|
-
* of it as well; if it has a channel, it owes the operator the reason.
|
|
298
|
+
* The bare-string form. A consumer that computes a range takes
|
|
299
|
+
* `resolveBaseBranchResult` instead and states the refusal (W16): a gate that
|
|
300
|
+
* refused in silence would run over a range the project never configured and
|
|
301
|
+
* report success on it. What remains here is `cmdGitBaseBranch`, whose entire
|
|
302
|
+
* contract is one branch name on stdout and which therefore genuinely has
|
|
303
|
+
* nowhere to put a sentence. A new caller reaching for this form should ask
|
|
304
|
+
* whether that is true of it as well; if it has a channel, it owes the operator the reason.
|
|
308
305
|
*/
|
|
309
306
|
function resolveBaseBranch(cwd, deps) {
|
|
310
307
|
return resolveBaseBranchResult(cwd, deps).branch;
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* provider, what environment a clone needs, and whether a host CLI (`glab`)
|
|
15
15
|
* may be spawned at all. It imports node builtins plus `./org-profile.cjs` —
|
|
16
16
|
* still never `./vault.cjs`, `./vault-coordinates.cjs`, `./io.cjs` or
|
|
17
|
-
* `./shell-command-projection.cjs` — so that
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* `./shell-command-projection.cjs` — so that `src/vault.cts`, the vault write
|
|
18
|
+
* path and the other vault modules can import it without forming a require
|
|
19
|
+
* cycle in the CommonJS graph `require()` loads.
|
|
20
20
|
* `./org-profile.cjs` is the one exception: it is itself a zero-import leaf
|
|
21
21
|
* (node builtins only, see its own header), so importing it here creates no
|
|
22
22
|
* load-time cycle even though it lazily requires this module back at
|
|
@@ -176,9 +176,8 @@ const compiledLiteralCloneTargetPatterns = exports.LITERAL_CLONE_TARGET_PATTERNS
|
|
|
176
176
|
* absolute path, a scheme-qualified URL, or scp-style `user@host:path` —
|
|
177
177
|
* rather than a `group/project` slug that must be composed onto a host.
|
|
178
178
|
*
|
|
179
|
-
* This is now the SINGLE definition. `src/vault.cts`
|
|
180
|
-
*
|
|
181
|
-
* each keeping a copy — a read/write divergence in this predicate was defect
|
|
179
|
+
* This is now the SINGLE definition. `src/vault.cts` imports it from here
|
|
180
|
+
* (30-02) rather than keeping a copy — a read/write divergence in this predicate was defect
|
|
182
181
|
* P-1 in Phase 29 (the write path used the value verbatim while the read path
|
|
183
182
|
* always composed a URL, so a slug broke writing and a path broke reading).
|
|
184
183
|
*
|
|
@@ -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) {
|
|
@@ -662,27 +637,24 @@ function writeIdentityAndProvider(cwd, answers) {
|
|
|
662
637
|
writeConfigMerge(cwd, patch);
|
|
663
638
|
}
|
|
664
639
|
/** The ONE owner of the arm-where-absent rule (quick 261003-kmd): arms `vault.enabled`,
|
|
665
|
-
*
|
|
640
|
+
* and `vault.required` only where ABSENT, from one read in one write.
|
|
666
641
|
*
|
|
667
|
-
* A successful composition means a client vault exists, so the
|
|
642
|
+
* A successful composition means a client vault exists, so the two keys are armed (OD-1,
|
|
668
643
|
* 260915-lmc, 260916-s5n). An explicit `false` or a malformed value is kept — that is the
|
|
669
644
|
* operator's decision, never overwritten (07-09 WR-07). The write happens only when there is
|
|
670
645
|
* something to arm: the nested- and flat-form byte-identity pins depend on a no-op writing
|
|
671
646
|
* nothing. Callers: the session's `'unchanged'` decision (how existing projects get armed by a
|
|
672
647
|
* 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
|
-
*
|
|
648
|
+
* It exists because the two session copies had already drifted once, and a third route would
|
|
649
|
+
* have been a third copy.
|
|
675
650
|
*
|
|
676
|
-
* @returns the armed key names, in the fixed order enabled,
|
|
651
|
+
* @returns the armed key names, in the fixed order enabled, required. */
|
|
677
652
|
function armVaultKeysWhereAbsent(cwd) {
|
|
678
653
|
const { parsed } = readConfigFile(cwd);
|
|
679
654
|
const vaultPatch = {};
|
|
680
655
|
if (resolveVaultEnabledState(parsed).state === 'absent') {
|
|
681
656
|
vaultPatch.enabled = true;
|
|
682
657
|
}
|
|
683
|
-
if (resolveVaultPracticesEnabledState(parsed).state === 'absent') {
|
|
684
|
-
vaultPatch.practices_enabled = true;
|
|
685
|
-
}
|
|
686
658
|
if (resolveVaultRequiredState(parsed).state === 'absent') {
|
|
687
659
|
vaultPatch.required = true;
|
|
688
660
|
}
|
|
@@ -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
|
-
}
|