@pareto4/pp-core 1.21.1 → 1.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/install.js +205 -28
- package/commands/pp-help.md +2 -1
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update-worker.js +1 -1
- package/hooks/dist/pp-context-monitor.js +3 -3
- package/hooks/dist/pp-statusline.js +88 -14
- package/hooks/dist/pp-update-banner.js +44 -4
- package/hooks/dist/pp-workflow-guard.js +3 -3
- package/hooks/pp-check-update-worker.js +1 -1
- package/hooks/pp-context-monitor.js +3 -3
- package/hooks/pp-statusline.js +88 -14
- package/hooks/pp-update-banner.js +44 -4
- package/hooks/pp-workflow-guard.js +3 -3
- package/package.json +2 -2
- package/pp-core/bin/lib/audience-command.cjs +271 -0
- package/pp-core/bin/lib/audience.cjs +293 -0
- package/pp-core/bin/lib/commands.cjs +26 -17
- package/pp-core/bin/lib/discovery-charter.cjs +13 -1
- package/pp-core/bin/lib/discovery-flow-page.cjs +191 -0
- package/pp-core/bin/lib/discovery-for-phase.cjs +33 -5
- package/pp-core/bin/lib/discovery-input-check.cjs +11 -11
- package/pp-core/bin/lib/discovery-processes.cjs +97 -4
- package/pp-core/bin/lib/discovery-session-check.cjs +7 -7
- package/pp-core/bin/lib/discovery-status.cjs +419 -0
- package/pp-core/bin/lib/discovery-stories.cjs +36 -5
- package/pp-core/bin/lib/discovery-tests.cjs +398 -0
- package/pp-core/bin/lib/explain-command.cjs +48 -0
- package/pp-core/bin/lib/gate-card.cjs +97 -0
- package/pp-core/bin/lib/hook-messages.cjs +132 -0
- package/pp-core/bin/lib/init-contract.cjs +116 -76
- package/pp-core/bin/lib/init-interactive.cjs +110 -49
- package/pp-core/bin/lib/init.cjs +44 -3
- package/pp-core/bin/lib/io.cjs +38 -0
- package/pp-core/bin/lib/message-labels.cjs +75 -0
- package/pp-core/bin/lib/messages/core.cjs +20 -0
- package/pp-core/bin/lib/messages/discovery.cjs +267 -0
- package/pp-core/bin/lib/messages/gate.cjs +257 -0
- package/pp-core/bin/lib/messages/hook.cjs +114 -0
- package/pp-core/bin/lib/messages/init.cjs +513 -0
- package/pp-core/bin/lib/messages/progress.cjs +110 -0
- package/pp-core/bin/lib/messages.cjs +208 -0
- package/pp-core/bin/lib/vault-command-router.cjs +16 -3
- package/pp-core/bin/lib/violation-render.cjs +78 -0
- package/pp-core/bin/pp-tools.cjs +92 -23
- package/pp-core/bin/shared/config-schema.manifest.json +1 -0
- package/pp-core/references/audience-moments.md +119 -0
- package/pp-core/references/audience.md +114 -0
- package/pp-core/references/discovery-build-consumption.md +5 -2
- package/pp-core/references/discovery-charter.md +2 -2
- package/pp-core/references/discovery-id-conventions.md +1 -1
- package/pp-core/references/discovery-processes.md +31 -7
- package/pp-core/references/discovery-session-output.md +1 -1
- package/pp-core/references/discovery-tests.md +186 -0
- package/pp-core/templates/project-charter.md +3 -4
- package/pp-core/workflows/add-phase.md +2 -0
- package/pp-core/workflows/add-tests.md +2 -0
- package/pp-core/workflows/add-todo.md +2 -0
- package/pp-core/workflows/ai-integration-phase.md +2 -0
- package/pp-core/workflows/audit-milestone.md +2 -0
- package/pp-core/workflows/autonomous.md +2 -0
- package/pp-core/workflows/check-todos.md +2 -0
- package/pp-core/workflows/code-review-fix.md +2 -0
- package/pp-core/workflows/code-review.md +2 -0
- package/pp-core/workflows/complete-milestone.md +10 -1
- package/pp-core/workflows/discovery-sprint/charter.md +3 -2
- package/pp-core/workflows/discovery-sprint/prefill.md +3 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +14 -0
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +4 -1
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +135 -76
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +94 -64
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +3 -2
- package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
- package/pp-core/workflows/discovery-sprint.md +16 -2
- package/pp-core/workflows/discovery-to-build/steps/handoff-gates.md +16 -9
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +1 -1
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +9 -1
- package/pp-core/workflows/discovery-to-build.md +10 -4
- package/pp-core/workflows/discuss-phase/discovery.md +2 -2
- package/pp-core/workflows/discuss-phase-assumptions.md +2 -0
- package/pp-core/workflows/discuss-phase.md +1 -1
- package/pp-core/workflows/edit-phase.md +2 -0
- package/pp-core/workflows/eval-review.md +2 -0
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +25 -1
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +2 -0
- package/pp-core/workflows/execute-phase.md +1 -1
- package/pp-core/workflows/execute-plan.md +2 -0
- package/pp-core/workflows/extract-learnings.md +2 -0
- package/pp-core/workflows/help/modes/business.md +56 -0
- package/pp-core/workflows/help.md +12 -2
- package/pp-core/workflows/import.md +10 -1
- package/pp-core/workflows/ingest-docs.md +2 -0
- package/pp-core/workflows/init.md +17 -1
- package/pp-core/workflows/insert-phase.md +2 -0
- package/pp-core/workflows/list-workspaces.md +2 -0
- package/pp-core/workflows/manager.md +2 -0
- package/pp-core/workflows/map-codebase.md +2 -0
- package/pp-core/workflows/milestone-summary.md +2 -0
- package/pp-core/workflows/new-milestone.md +10 -1
- package/pp-core/workflows/new-project.md +12 -2
- package/pp-core/workflows/new-workspace.md +2 -0
- package/pp-core/workflows/next.md +8 -0
- package/pp-core/workflows/plan-milestone-gaps.md +10 -1
- package/pp-core/workflows/plan-phase.md +2 -1
- package/pp-core/workflows/plan-review-convergence.md +1 -1
- package/pp-core/workflows/progress.md +10 -0
- package/pp-core/workflows/quick.md +11 -1
- package/pp-core/workflows/remove-phase.md +2 -0
- package/pp-core/workflows/remove-workspace.md +2 -0
- package/pp-core/workflows/resume-project.md +25 -0
- package/pp-core/workflows/review.md +2 -0
- package/pp-core/workflows/scan.md +2 -0
- package/pp-core/workflows/secure-phase.md +2 -0
- package/pp-core/workflows/settings.md +41 -0
- package/pp-core/workflows/ship.md +2 -0
- package/pp-core/workflows/spec-phase.md +1 -1
- package/pp-core/workflows/ui-phase.md +2 -0
- package/pp-core/workflows/ui-review.md +2 -0
- package/pp-core/workflows/ultraplan-phase.md +2 -0
- package/pp-core/workflows/validate-phase.md +2 -0
- package/pp-core/workflows/vault-review.md +2 -0
- package/pp-core/workflows/verify-phase.md +2 -0
- package/pp-core/workflows/verify-work.md +2 -0
- package/scripts/affected-tests-lib.cjs +26 -12
- package/scripts/ci-release-only.cjs +158 -0
- package/scripts/live-runs/driver.cjs +326 -0
- package/scripts/live-runs/run.cjs +906 -0
- package/scripts/live-runs/scenarios.cjs +326 -0
- package/scripts/live-runs/scratch-project.cjs +132 -0
- package/scripts/live-runs/scrub.cjs +291 -0
- package/scripts/live-runs/turn-guard.cjs +107 -0
- package/scripts/live-runs/walk-setup.cjs +198 -0
- package/scripts/run-tests.cjs +151 -132
- package/scripts/test-env.cjs +64 -0
|
@@ -13,6 +13,7 @@ exports.readVaultRequiredState = readVaultRequiredState;
|
|
|
13
13
|
exports.buildInitQuestions = buildInitQuestions;
|
|
14
14
|
exports.deriveSlug = deriveSlug;
|
|
15
15
|
exports.readProfileInitAnswers = readProfileInitAnswers;
|
|
16
|
+
exports.collectInvariantViolations = collectInvariantViolations;
|
|
16
17
|
exports.verifyInitInvariants = verifyInitInvariants;
|
|
17
18
|
exports.armVaultKeysWhereAbsent = armVaultKeysWhereAbsent;
|
|
18
19
|
exports.recordProbeOutcome = recordProbeOutcome;
|
|
@@ -21,6 +22,7 @@ exports.initSessionNext = initSessionNext;
|
|
|
21
22
|
exports.initSessionAnswer = initSessionAnswer;
|
|
22
23
|
exports.initSessionReset = initSessionReset;
|
|
23
24
|
exports.armVaultFromProfile = armVaultFromProfile;
|
|
25
|
+
exports.withoutDisplay = withoutDisplay;
|
|
24
26
|
exports.cmdInitContract = cmdInitContract;
|
|
25
27
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
26
28
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -34,6 +36,7 @@ const org_profile_cjs_1 = require("./org-profile.cjs");
|
|
|
34
36
|
const platform_bindings_cjs_1 = require("./platform-bindings.cjs");
|
|
35
37
|
const compose_bindings_cjs_1 = require("./compose-bindings.cjs");
|
|
36
38
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
39
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
37
40
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
38
41
|
/** Set when the operator declines `compose_confirm` — the only way to tell "declined" from "never ran". */
|
|
39
42
|
exports.INIT_COMPOSE_DECLINED_KEY = 'init.compose_declined';
|
|
@@ -234,30 +237,30 @@ function buildInitQuestions(cwd, opts = {}) {
|
|
|
234
237
|
preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
235
238
|
}
|
|
236
239
|
const providerOptions = Object.freeze([...git_provider_cjs_1.PROVIDERS, ...IDENTITY_ONLY_PROVIDER_OPTIONS]);
|
|
240
|
+
const ask = (id, params = {}, code = `init.step.${id}`) => ({
|
|
241
|
+
prompt: (0, messages_cjs_1.msgDeveloper)(code, params),
|
|
242
|
+
display: { prompt: { code, params } },
|
|
243
|
+
});
|
|
237
244
|
return [
|
|
238
|
-
{ id: 'project_name',
|
|
239
|
-
{ id: 'project_slug',
|
|
240
|
-
{
|
|
241
|
-
|
|
242
|
-
prompt: 'Client name (company / team, e.g. "Acme Corp") — type "internal" if none',
|
|
243
|
-
optionalAnswer: false,
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
id: 'client_slug',
|
|
247
|
-
prompt: 'Client slug (kebab-case, e.g. "acme") — type "internal" if none',
|
|
248
|
-
optionalAnswer: false,
|
|
249
|
-
},
|
|
245
|
+
{ id: 'project_name', ...ask('project_name'), optionalAnswer: false },
|
|
246
|
+
{ id: 'project_slug', ...ask('project_slug'), optionalAnswer: false },
|
|
247
|
+
{ id: 'client_name', ...ask('client_name'), optionalAnswer: false },
|
|
248
|
+
{ id: 'client_slug', ...ask('client_slug'), optionalAnswer: false },
|
|
250
249
|
...clientVaultQuestion(cwd, opts),
|
|
251
|
-
{ id: 'description',
|
|
250
|
+
{ id: 'description', ...ask('description'), optionalAnswer: false },
|
|
252
251
|
{
|
|
253
252
|
id: 'provider',
|
|
254
|
-
prompt:
|
|
253
|
+
prompt: (0, messages_cjs_1.msgDeveloper)('init.step.provider'),
|
|
255
254
|
optionalAnswer: false,
|
|
256
255
|
options: providerOptions,
|
|
257
256
|
preselected,
|
|
258
|
-
preselectedLabel:
|
|
257
|
+
preselectedLabel: (0, messages_cjs_1.msgDeveloper)('init.chrome.resolved_default', { provider: preselected }),
|
|
258
|
+
display: {
|
|
259
|
+
prompt: { code: 'init.step.provider', params: {} },
|
|
260
|
+
preselectedLabel: { code: 'init.chrome.resolved_default', params: { provider: preselected } },
|
|
261
|
+
},
|
|
259
262
|
},
|
|
260
|
-
{ id: 'visibility',
|
|
263
|
+
{ id: 'visibility', ...ask('visibility'), optionalAnswer: false },
|
|
261
264
|
];
|
|
262
265
|
}
|
|
263
266
|
/** The client-vault question, asked while no vault is bound and a binding can be composed (`clientVaultChoice`).
|
|
@@ -267,13 +270,14 @@ function clientVaultQuestion(cwd, opts) {
|
|
|
267
270
|
const choice = (0, compose_bindings_cjs_1.clientVaultChoice)(cwd, opts);
|
|
268
271
|
if (!choice.offered)
|
|
269
272
|
return [];
|
|
273
|
+
const code = choice.defaultRepo === null ? 'init.step.client_vault' : 'init.step.client_vault_default';
|
|
274
|
+
const params = choice.defaultRepo === null ? {} : { repo: choice.defaultRepo };
|
|
270
275
|
return [
|
|
271
276
|
{
|
|
272
277
|
id: 'client_vault',
|
|
273
|
-
prompt:
|
|
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})`,
|
|
278
|
+
prompt: (0, messages_cjs_1.msgDeveloper)(code, params),
|
|
276
279
|
optionalAnswer: true,
|
|
280
|
+
display: { prompt: { code, params } },
|
|
277
281
|
},
|
|
278
282
|
];
|
|
279
283
|
}
|
|
@@ -380,9 +384,10 @@ function readConfigFile(cwd) {
|
|
|
380
384
|
return { exists, parsed: {} };
|
|
381
385
|
}
|
|
382
386
|
}
|
|
383
|
-
/** Report every invariant the project's state at `cwd` violates
|
|
384
|
-
* each check needs a positive signal that init was attempted, so an empty directory is ok.
|
|
385
|
-
|
|
387
|
+
/** Report every invariant the project's state at `cwd` violates, each with the params its message was built from.
|
|
388
|
+
* Read-only and never throws; each check needs a positive signal that init was attempted, so an empty directory is ok.
|
|
389
|
+
* The three in-scope messages are the catalog's developer text; the rest keep their literals with no params. */
|
|
390
|
+
function collectInvariantViolations(cwd, opts = {}) {
|
|
386
391
|
const violations = [];
|
|
387
392
|
const { exists: configExists, parsed } = readConfigFile(cwd);
|
|
388
393
|
// INIT_IDENTITY_MISSING — only meaningful once a config file exists at
|
|
@@ -394,7 +399,8 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
394
399
|
if (!hasProjectBlock) {
|
|
395
400
|
violations.push({
|
|
396
401
|
code: INVARIANT_CODE.IDENTITY_MISSING,
|
|
397
|
-
message:
|
|
402
|
+
message: (0, messages_cjs_1.msgDeveloper)(INVARIANT_CODE.IDENTITY_MISSING),
|
|
403
|
+
params: {},
|
|
398
404
|
});
|
|
399
405
|
}
|
|
400
406
|
}
|
|
@@ -407,9 +413,11 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
407
413
|
const projectProvider = getDotted(parsed, 'project.provider');
|
|
408
414
|
const vaultProvider = getDotted(parsed, 'vault.provider');
|
|
409
415
|
if (git_provider_cjs_1.PROVIDERS.includes(projectProvider) && !isUsableString(vaultProvider)) {
|
|
416
|
+
const params = { provider: String(projectProvider) };
|
|
410
417
|
violations.push({
|
|
411
418
|
code: INVARIANT_CODE.VAULT_PROVIDER_UNSET,
|
|
412
|
-
message:
|
|
419
|
+
message: (0, messages_cjs_1.msgDeveloper)(INVARIANT_CODE.VAULT_PROVIDER_UNSET, params),
|
|
420
|
+
params,
|
|
413
421
|
});
|
|
414
422
|
}
|
|
415
423
|
}
|
|
@@ -426,8 +434,8 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
426
434
|
if (dryRun.composable === true) {
|
|
427
435
|
violations.push({
|
|
428
436
|
code: INVARIANT_CODE.COMPOSITION_NOT_RUN,
|
|
429
|
-
message:
|
|
430
|
-
|
|
437
|
+
message: (0, messages_cjs_1.msgDeveloper)(INVARIANT_CODE.COMPOSITION_NOT_RUN),
|
|
438
|
+
params: {},
|
|
431
439
|
});
|
|
432
440
|
}
|
|
433
441
|
}
|
|
@@ -458,6 +466,7 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
458
466
|
'Store the boolean with "pp-tools config-set vault.enabled true" (which accepts only ' +
|
|
459
467
|
'true or false, and refuses any other spelling), or "pp-tools config-set vault.enabled ' +
|
|
460
468
|
'false" to turn the capability off.',
|
|
469
|
+
params: {},
|
|
461
470
|
});
|
|
462
471
|
}
|
|
463
472
|
}
|
|
@@ -491,6 +500,7 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
491
500
|
message: `The reachability proof recorded FAILED on ${when} by ${who}: ${note}. ` +
|
|
492
501
|
'Re-run "/pp-init" after fixing the remote, or record an attributed deferral with ' +
|
|
493
502
|
'"pp-tools query init-contract --defer-probe <reason>".',
|
|
503
|
+
params: {},
|
|
494
504
|
});
|
|
495
505
|
}
|
|
496
506
|
else if (vaultArmed && status === exports.INIT_PROBE_STATUS.DEFERRED) {
|
|
@@ -499,6 +509,7 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
499
509
|
code: INVARIANT_CODE.PROBE_DEFERRED,
|
|
500
510
|
message: `The reachability proof was deferred by ${who} on ${when} for reason: ${note}. ` +
|
|
501
511
|
'Init remains unproven until a re-run of "/pp-init" records a passing probe.',
|
|
512
|
+
params: {},
|
|
502
513
|
});
|
|
503
514
|
}
|
|
504
515
|
else if (owesAProof) {
|
|
@@ -510,6 +521,7 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
510
521
|
message: `The org profile "${profile}" resolves, vault.enabled is true and .platform-bindings.json ` +
|
|
511
522
|
`is present, but ${clause} — /pp-init Step 2.6 appears not to have run.` +
|
|
512
523
|
notRunRemedy,
|
|
524
|
+
params: {},
|
|
513
525
|
});
|
|
514
526
|
if (status === exports.INIT_PROBE_STATUS.PASSED) {
|
|
515
527
|
// Only an attributed, timestamped pass clears the check.
|
|
@@ -530,7 +542,12 @@ function verifyInitInvariants(cwd, opts = {}) {
|
|
|
530
542
|
catch {
|
|
531
543
|
// fail open — one check's fault must not suppress the others
|
|
532
544
|
}
|
|
533
|
-
return
|
|
545
|
+
return violations;
|
|
546
|
+
}
|
|
547
|
+
/** The public invariant check: `{ code, message }` per violation, with no params (the `--verify` JSON is unchanged). */
|
|
548
|
+
function verifyInitInvariants(cwd, opts = {}) {
|
|
549
|
+
const collected = collectInvariantViolations(cwd, opts);
|
|
550
|
+
return { ok: collected.length === 0, violations: collected.map(({ code, message }) => ({ code, message })) };
|
|
534
551
|
}
|
|
535
552
|
// ─── Session state machine: one step at a time, every side effect performed by the CLI ───
|
|
536
553
|
function sessionFilePath(cwd) {
|
|
@@ -596,13 +613,14 @@ function loadSession(cwd) {
|
|
|
596
613
|
return { malformed: true };
|
|
597
614
|
}
|
|
598
615
|
}
|
|
599
|
-
/**
|
|
600
|
-
function
|
|
616
|
+
/** An unexpected write failure as a bounded message (never a raw stack trace) plus the catalog handle the driver words it from. */
|
|
617
|
+
function writeFailed(err) {
|
|
601
618
|
const detail = err instanceof Error ? err.message : String(err);
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
619
|
+
const ref = { code: exports.INIT_SESSION_WRITE_FAILED, params: { detail } };
|
|
620
|
+
return { message: (0, messages_cjs_1.msgDeveloper)(ref.code, ref.params), ref };
|
|
621
|
+
}
|
|
622
|
+
function writeFailedMessage(err) {
|
|
623
|
+
return writeFailed(err).message;
|
|
606
624
|
}
|
|
607
625
|
function persistSession(cwd, session) {
|
|
608
626
|
const dir = node_path_1.default.join(cwd, '.planning');
|
|
@@ -838,24 +856,30 @@ function computeCurrentStep(cwd, session, opts) {
|
|
|
838
856
|
persistSession(cwd, session);
|
|
839
857
|
}
|
|
840
858
|
// Three different nothing-written end states, each with its own message.
|
|
841
|
-
const
|
|
842
|
-
? '
|
|
859
|
+
const skippedCode = session.composeDecision === 'skipped'
|
|
860
|
+
? 'init.done.skipped'
|
|
843
861
|
: session.composeDecision === 'unchanged'
|
|
844
|
-
? '
|
|
862
|
+
? 'init.done.unchanged'
|
|
845
863
|
: undefined;
|
|
864
|
+
const skippedComposeInfo = skippedCode === undefined ? undefined : { text: (0, messages_cjs_1.msgDeveloper)(skippedCode), ref: { code: skippedCode, params: {} } };
|
|
846
865
|
// The session verify skips only the INIT_PROBE_* check: Step 2.6 records the proof after init returns.
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
866
|
+
const collected = collectInvariantViolations(cwd, { _globalRoot: opts._globalRoot, scope: 'session' });
|
|
867
|
+
if (collected.length === 0) {
|
|
849
868
|
if (!session.doneAt) {
|
|
850
869
|
session.doneAt = new Date().toISOString();
|
|
851
870
|
persistSession(cwd, session);
|
|
852
871
|
}
|
|
853
872
|
return { kind: 'done', skippedComposeInfo };
|
|
854
873
|
}
|
|
855
|
-
return {
|
|
874
|
+
return {
|
|
875
|
+
kind: 'verify',
|
|
876
|
+
violations: collected.map(({ code, message }) => ({ code, message })),
|
|
877
|
+
refs: collected.map(({ code, params }) => ({ code, params })),
|
|
878
|
+
skippedComposeInfo,
|
|
879
|
+
};
|
|
856
880
|
}
|
|
857
881
|
catch (err) {
|
|
858
|
-
return { kind: 'write_failed',
|
|
882
|
+
return { kind: 'write_failed', ...writeFailed(err) };
|
|
859
883
|
}
|
|
860
884
|
}
|
|
861
885
|
function currentStepToState(step) {
|
|
@@ -869,15 +893,18 @@ function currentStepToState(step) {
|
|
|
869
893
|
state.preselected = q.preselected;
|
|
870
894
|
if (q.preselectedLabel !== undefined)
|
|
871
895
|
state.preselectedLabel = q.preselectedLabel;
|
|
896
|
+
if (q.display !== undefined)
|
|
897
|
+
state.display = q.display;
|
|
872
898
|
return state;
|
|
873
899
|
}
|
|
874
900
|
case 'compose_confirm':
|
|
875
901
|
return {
|
|
876
902
|
done: false,
|
|
877
903
|
step: 'compose_confirm',
|
|
878
|
-
prompt:
|
|
904
|
+
prompt: (0, messages_cjs_1.msgDeveloper)('init.step.compose_confirm'),
|
|
879
905
|
optionalAnswer: false,
|
|
880
906
|
info: step.info,
|
|
907
|
+
display: { prompt: { code: 'init.step.compose_confirm', params: {} } },
|
|
881
908
|
};
|
|
882
909
|
case 'verify':
|
|
883
910
|
return {
|
|
@@ -885,14 +912,20 @@ function currentStepToState(step) {
|
|
|
885
912
|
step: 'verify',
|
|
886
913
|
prompt: null,
|
|
887
914
|
violations: step.violations,
|
|
888
|
-
...(step.skippedComposeInfo !== undefined ? { info: step.skippedComposeInfo } : {}),
|
|
915
|
+
...(step.skippedComposeInfo !== undefined ? { info: step.skippedComposeInfo.text } : {}),
|
|
916
|
+
display: {
|
|
917
|
+
violations: step.refs,
|
|
918
|
+
...(step.skippedComposeInfo !== undefined ? { info: step.skippedComposeInfo.ref } : {}),
|
|
919
|
+
},
|
|
889
920
|
};
|
|
890
921
|
case 'done':
|
|
891
922
|
return {
|
|
892
923
|
done: true,
|
|
893
924
|
step: 'done',
|
|
894
925
|
prompt: null,
|
|
895
|
-
...(step.skippedComposeInfo !== undefined
|
|
926
|
+
...(step.skippedComposeInfo !== undefined
|
|
927
|
+
? { info: step.skippedComposeInfo.text, display: { info: step.skippedComposeInfo.ref } }
|
|
928
|
+
: {}),
|
|
896
929
|
};
|
|
897
930
|
case 'write_failed':
|
|
898
931
|
return {
|
|
@@ -901,9 +934,14 @@ function currentStepToState(step) {
|
|
|
901
934
|
prompt: null,
|
|
902
935
|
error: exports.INIT_SESSION_WRITE_FAILED,
|
|
903
936
|
message: step.message,
|
|
937
|
+
display: { message: step.ref },
|
|
904
938
|
};
|
|
905
939
|
}
|
|
906
940
|
}
|
|
941
|
+
/** A refusal whose text is the catalog's developer wording and whose handle lets the driver word it for the person. */
|
|
942
|
+
function refusal(reason, ref) {
|
|
943
|
+
return { ok: false, reason, message: (0, messages_cjs_1.msgDeveloper)(ref.code, ref.params), display: { message: ref } };
|
|
944
|
+
}
|
|
907
945
|
/** Return the ONE next step of the session; malformed session state fails closed (recover with `--session-reset`). */
|
|
908
946
|
function initSessionNext(cwd, opts = {}) {
|
|
909
947
|
const loaded = loadSession(cwd);
|
|
@@ -913,8 +951,8 @@ function initSessionNext(cwd, opts = {}) {
|
|
|
913
951
|
step: null,
|
|
914
952
|
prompt: null,
|
|
915
953
|
error: exports.INIT_SESSION_MALFORMED,
|
|
916
|
-
message:
|
|
917
|
-
|
|
954
|
+
message: (0, messages_cjs_1.msgDeveloper)(exports.INIT_SESSION_MALFORMED),
|
|
955
|
+
display: { message: { code: exports.INIT_SESSION_MALFORMED, params: {} } },
|
|
918
956
|
};
|
|
919
957
|
}
|
|
920
958
|
const session = loaded.session;
|
|
@@ -926,19 +964,14 @@ function initSessionNext(cwd, opts = {}) {
|
|
|
926
964
|
function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
927
965
|
const loaded = loadSession(cwd);
|
|
928
966
|
if (loaded.malformed) {
|
|
929
|
-
return {
|
|
930
|
-
ok: false,
|
|
931
|
-
reason: 'session_malformed',
|
|
932
|
-
message: 'The init session file at .planning/.init-session.json exists but is malformed or unreadable. ' +
|
|
933
|
-
'Run "pp_run query init-contract --session-reset" to start over.',
|
|
934
|
-
};
|
|
967
|
+
return refusal('session_malformed', { code: 'init.answer.session_malformed', params: {} });
|
|
935
968
|
}
|
|
936
969
|
const session = loaded.session;
|
|
937
970
|
// Any write failure in this function degrades to `write_failed` instead of throwing.
|
|
938
971
|
try {
|
|
939
972
|
const step = computeCurrentStep(cwd, session, opts);
|
|
940
973
|
if (step.kind === 'write_failed') {
|
|
941
|
-
return { ok: false, reason: 'write_failed', message: step.message };
|
|
974
|
+
return { ok: false, reason: 'write_failed', message: step.message, display: { message: step.ref } };
|
|
942
975
|
}
|
|
943
976
|
if (step.kind === 'done') {
|
|
944
977
|
return {
|
|
@@ -956,13 +989,13 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
956
989
|
};
|
|
957
990
|
}
|
|
958
991
|
if (!step.question.optionalAnswer && (typeof value !== 'string' || value.trim() === '')) {
|
|
959
|
-
return {
|
|
992
|
+
return refusal('answer_required', { code: 'init.answer.answer_required', params: { step: stepId } });
|
|
960
993
|
}
|
|
961
994
|
const answerValue = typeof value === 'string' ? value : '';
|
|
962
995
|
if (stepId === 'client_vault') {
|
|
963
996
|
const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(answerValue);
|
|
964
997
|
if (!checked.ok) {
|
|
965
|
-
return {
|
|
998
|
+
return refusal('invalid_answer', { code: 'init.answer.invalid_client_vault', params: { detail: checked.reason } });
|
|
966
999
|
}
|
|
967
1000
|
}
|
|
968
1001
|
session.answers[stepId] = answerValue;
|
|
@@ -979,11 +1012,7 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
979
1012
|
}
|
|
980
1013
|
const normalized = normalizeYesNo(value);
|
|
981
1014
|
if (normalized === null) {
|
|
982
|
-
return {
|
|
983
|
-
ok: false,
|
|
984
|
-
reason: 'invalid_answer',
|
|
985
|
-
message: 'compose_confirm requires "yes" or "no" (also accepts approve/decline, y/n, true/false).',
|
|
986
|
-
};
|
|
1015
|
+
return refusal('invalid_answer', { code: 'init.answer.invalid_compose_confirm', params: {} });
|
|
987
1016
|
}
|
|
988
1017
|
if (normalized === true) {
|
|
989
1018
|
// Check the write result before recording "approved": the file can change between the dry run and this write.
|
|
@@ -995,14 +1024,9 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
995
1024
|
});
|
|
996
1025
|
if (writeResult.composable !== true || writeResult.wrote !== true) {
|
|
997
1026
|
// Leave the decision unset so the next call re-evaluates and can re-offer compose_confirm.
|
|
998
|
-
return
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
message: 'compose_confirm was approved, but the write to .platform-bindings.json did not succeed — ' +
|
|
1002
|
-
'this was NOT recorded as skipped or declined.' +
|
|
1003
|
-
(writeResult.warnings.length > 0 ? ` Warnings: ${writeResult.warnings.join('; ')}.` : '') +
|
|
1004
|
-
' Call --session-next to see the current state and try again.',
|
|
1005
|
-
};
|
|
1027
|
+
return refusal('compose_write_failed', writeResult.warnings.length > 0
|
|
1028
|
+
? { code: 'init.answer.compose_write_failed_warnings', params: { warnings: writeResult.warnings.join('; ') } }
|
|
1029
|
+
: { code: 'init.answer.compose_write_failed', params: {} });
|
|
1006
1030
|
}
|
|
1007
1031
|
session.composeDecision = 'approved';
|
|
1008
1032
|
armVaultKeysWhereAbsent(cwd);
|
|
@@ -1022,7 +1046,8 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
1022
1046
|
};
|
|
1023
1047
|
}
|
|
1024
1048
|
catch (err) {
|
|
1025
|
-
|
|
1049
|
+
const failed = writeFailed(err);
|
|
1050
|
+
return { ok: false, reason: 'write_failed', message: failed.message, display: { message: failed.ref } };
|
|
1026
1051
|
}
|
|
1027
1052
|
}
|
|
1028
1053
|
/** Delete the session file (the recovery from a malformed session). Never throws. */
|
|
@@ -1141,20 +1166,35 @@ function armVaultFromProfile(cwd, opts = {}) {
|
|
|
1141
1166
|
};
|
|
1142
1167
|
}
|
|
1143
1168
|
}
|
|
1169
|
+
/** A copy of `value` without its own top-level `display` handle; an array gets that per item (D-08).
|
|
1170
|
+
* The session JSON is a machine channel the /pp-init fallback path parses; the in-process catalog handles never reach it.
|
|
1171
|
+
* The handles are only ever attached at the top level of a session state, an answer result or a question. Nested data
|
|
1172
|
+
* (compose `info`, org-profile and binding values, auto-answers) is user data and passes through untouched, even a key
|
|
1173
|
+
* named `display`. */
|
|
1174
|
+
function withoutDisplay(value) {
|
|
1175
|
+
if (Array.isArray(value))
|
|
1176
|
+
return value.map((item) => withoutDisplay(item));
|
|
1177
|
+
if (typeof value === 'object' && value !== null && Object.hasOwn(value, 'display')) {
|
|
1178
|
+
const copy = { ...value };
|
|
1179
|
+
delete copy.display;
|
|
1180
|
+
return copy;
|
|
1181
|
+
}
|
|
1182
|
+
return value;
|
|
1183
|
+
}
|
|
1144
1184
|
// ─── cmdInitContract ────────────────────────────────────────────────────────
|
|
1145
1185
|
/** CLI adapter for `pp-tools query init-contract`: the session, record, arm-vault, verify and questions flags.
|
|
1146
1186
|
* Data verdicts set `exitCode = 1` when not ok, but the JSON result is always printed. */
|
|
1147
1187
|
function cmdInitContract(cwd, options, raw) {
|
|
1148
1188
|
if (options.sessionReset === true) {
|
|
1149
1189
|
const result = initSessionReset(cwd);
|
|
1150
|
-
output(result, raw);
|
|
1190
|
+
output(withoutDisplay(result), raw);
|
|
1151
1191
|
return;
|
|
1152
1192
|
}
|
|
1153
1193
|
if (options.sessionAnswer !== undefined) {
|
|
1154
1194
|
const result = initSessionAnswer(cwd, options.sessionAnswer.stepId, options.sessionAnswer.value, {
|
|
1155
1195
|
offline: options.offline === true,
|
|
1156
1196
|
});
|
|
1157
|
-
output(result, raw);
|
|
1197
|
+
output(withoutDisplay(result), raw);
|
|
1158
1198
|
if (!result.ok) {
|
|
1159
1199
|
process.exitCode = 1;
|
|
1160
1200
|
}
|
|
@@ -1162,7 +1202,7 @@ function cmdInitContract(cwd, options, raw) {
|
|
|
1162
1202
|
}
|
|
1163
1203
|
if (options.sessionNext === true) {
|
|
1164
1204
|
const state = initSessionNext(cwd, { offline: options.offline === true });
|
|
1165
|
-
output(state, raw);
|
|
1205
|
+
output(withoutDisplay(state), raw);
|
|
1166
1206
|
if (state.error !== undefined) {
|
|
1167
1207
|
process.exitCode = 1;
|
|
1168
1208
|
}
|
|
@@ -1171,7 +1211,7 @@ function cmdInitContract(cwd, options, raw) {
|
|
|
1171
1211
|
// Record writes run before `--verify`, so a combined call verifies the written state.
|
|
1172
1212
|
if (options.recordProbe !== undefined) {
|
|
1173
1213
|
const result = recordProbeOutcome(cwd, options.recordProbe);
|
|
1174
|
-
output(result, raw);
|
|
1214
|
+
output(withoutDisplay(result), raw);
|
|
1175
1215
|
if (!result.ok) {
|
|
1176
1216
|
process.exitCode = 1;
|
|
1177
1217
|
}
|
|
@@ -1179,7 +1219,7 @@ function cmdInitContract(cwd, options, raw) {
|
|
|
1179
1219
|
}
|
|
1180
1220
|
if (options.deferProbe !== undefined) {
|
|
1181
1221
|
const result = recordProbeDeferral(cwd, options.deferProbe);
|
|
1182
|
-
output(result, raw);
|
|
1222
|
+
output(withoutDisplay(result), raw);
|
|
1183
1223
|
if (!result.ok) {
|
|
1184
1224
|
process.exitCode = 1;
|
|
1185
1225
|
}
|
|
@@ -1188,7 +1228,7 @@ function cmdInitContract(cwd, options, raw) {
|
|
|
1188
1228
|
// An inert `armed: false` is a data verdict on exit 0, like the inert init path; only `ok: false` exits 1.
|
|
1189
1229
|
if (options.armVault === true) {
|
|
1190
1230
|
const result = armVaultFromProfile(cwd, { offline: options.offline === true });
|
|
1191
|
-
output(result, raw);
|
|
1231
|
+
output(withoutDisplay(result), raw);
|
|
1192
1232
|
if (result.ok === false) {
|
|
1193
1233
|
process.exitCode = 1;
|
|
1194
1234
|
}
|
|
@@ -1196,11 +1236,11 @@ function cmdInitContract(cwd, options, raw) {
|
|
|
1196
1236
|
}
|
|
1197
1237
|
if (options.verify === true) {
|
|
1198
1238
|
const result = verifyInitInvariants(cwd);
|
|
1199
|
-
output(result, raw);
|
|
1239
|
+
output(withoutDisplay(result), raw);
|
|
1200
1240
|
if (!result.ok) {
|
|
1201
1241
|
process.exitCode = 1;
|
|
1202
1242
|
}
|
|
1203
1243
|
return;
|
|
1204
1244
|
}
|
|
1205
|
-
output({ questions: buildInitQuestions(cwd) }, raw);
|
|
1245
|
+
output({ questions: buildInitQuestions(cwd).map(withoutDisplay) }, raw);
|
|
1206
1246
|
}
|