@pareto4/pp-core 1.22.0 → 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 +1 -1
- 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-input-check.cjs +11 -11
- 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/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 +33 -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 +68 -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-charter.md +2 -2
- 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/sessions/01-kick-off.md +4 -1
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
- package/pp-core/workflows/discovery-sprint.md +14 -0
- package/pp-core/workflows/discovery-to-build.md +8 -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 -135
- package/scripts/test-env.cjs +64 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message catalog engine (Phase 41: MSG-02, MSG-03, MSG-04; D-01..D-06, D-20).
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
|
|
6
|
+
* compiles to the gitignored `pp-core/bin/lib/messages.cjs`; do NOT hand-edit
|
|
7
|
+
* the compiled output.
|
|
8
|
+
*
|
|
9
|
+
* One catalog keyed by a stable code. Each entry carries `developer` (today's
|
|
10
|
+
* text, frozen byte for byte), optional `engineer`, `business` and `explain`.
|
|
11
|
+
* A business lookup resolves business, then engineer, then developer; an
|
|
12
|
+
* engineer lookup engineer, then developer; a developer lookup developer
|
|
13
|
+
* (D-01). Business and engineer messages end with a `Ref: <code>` line (D-05,
|
|
14
|
+
* D-06); developer text never does.
|
|
15
|
+
*
|
|
16
|
+
* Params are data. Substitution is a single pass with a function replacer, so
|
|
17
|
+
* a value that holds `{x}` or `$&` is never re-expanded (T-41-12). For business
|
|
18
|
+
* and engineer every param except the slots an entry marks `rawParams` is
|
|
19
|
+
* stripped of control characters, whitespace-collapsed and capped (T-41-11): at
|
|
20
|
+
* ECHO_CAP for an echo, at DIAG_CAP for the slots an entry marks `longParams`
|
|
21
|
+
* (diagnostics and approval values the person must read in full); the developer path
|
|
22
|
+
* passes params through untouched (D-08). An unknown code renders the code
|
|
23
|
+
* itself, never blank and never a throw (D-04). Lookups use a Map only, so
|
|
24
|
+
* `__proto__` and `constructor` are unknown codes (T-41-13).
|
|
25
|
+
*
|
|
26
|
+
* Machine channels never route through the audience path: `io.errorCode` writes
|
|
27
|
+
* `msgDeveloper` to the `--json-errors` message for every audience. This module
|
|
28
|
+
* must never import `io.cjs` (io imports this module). It imports Node built-ins
|
|
29
|
+
* (through the audience leaf), `audience.cjs` and the slices only, so hooks can
|
|
30
|
+
* load it later (D-02).
|
|
31
|
+
*
|
|
32
|
+
* The audience is resolved through the Phase 39 leaf (`PP_AUDIENCE`, then the
|
|
33
|
+
* person file, then developer), silently (D-20): warnings are printed by the
|
|
34
|
+
* commands that own them, never by a lookup.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.SLICES = exports.DIAG_CAP = exports.ECHO_CAP = void 0;
|
|
38
|
+
exports.duplicateCodesOf = duplicateCodesOf;
|
|
39
|
+
exports.registry = registry;
|
|
40
|
+
exports.has = has;
|
|
41
|
+
exports.audienceOf = audienceOf;
|
|
42
|
+
exports.sanitizeParam = sanitizeParam;
|
|
43
|
+
exports.fill = fill;
|
|
44
|
+
exports.pick = pick;
|
|
45
|
+
exports.refLine = refLine;
|
|
46
|
+
exports.msgDeveloper = msgDeveloper;
|
|
47
|
+
exports.text = text;
|
|
48
|
+
exports.msg = msg;
|
|
49
|
+
exports.explainText = explainText;
|
|
50
|
+
const audience_cjs_1 = require("./audience.cjs");
|
|
51
|
+
const core_cjs_1 = require("./messages/core.cjs");
|
|
52
|
+
const discovery_cjs_1 = require("./messages/discovery.cjs");
|
|
53
|
+
const init_cjs_1 = require("./messages/init.cjs");
|
|
54
|
+
const progress_cjs_1 = require("./messages/progress.cjs");
|
|
55
|
+
const gate_cjs_1 = require("./messages/gate.cjs");
|
|
56
|
+
const hook_cjs_1 = require("./messages/hook.cjs");
|
|
57
|
+
/**
|
|
58
|
+
* Every control character `\s` does not match (C0 minus `\t\n\v\f\r`, DEL and
|
|
59
|
+
* C1), plus every Unicode format character (category Cf: bidi overrides and
|
|
60
|
+
* isolates, zero-width characters, the BOM, tag characters). A right-to-left
|
|
61
|
+
* override in a file name would make the shown name differ from the real one
|
|
62
|
+
* (Trojan Source). Stripped BEFORE the whitespace collapse.
|
|
63
|
+
*/
|
|
64
|
+
const CONTROL_CHARS_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\p{Cf}]/gu;
|
|
65
|
+
const SLOT_RE = /\{([a-z][a-z0-9_]*)\}/g;
|
|
66
|
+
/** The cap for a slot that echoes a typed value (T-41-11). */
|
|
67
|
+
exports.ECHO_CAP = 120;
|
|
68
|
+
/**
|
|
69
|
+
* The cap for a `longParams` slot. `composeBindings` bounds each warning at 200
|
|
70
|
+
* characters, so this holds five full warnings joined with `; `.
|
|
71
|
+
*/
|
|
72
|
+
exports.DIAG_CAP = 1000;
|
|
73
|
+
exports.SLICES = Object.freeze([
|
|
74
|
+
['core', core_cjs_1.core],
|
|
75
|
+
['discovery', discovery_cjs_1.discovery],
|
|
76
|
+
['init', init_cjs_1.init],
|
|
77
|
+
['progress', progress_cjs_1.progress],
|
|
78
|
+
['gate', gate_cjs_1.gate],
|
|
79
|
+
['hook', hook_cjs_1.hook],
|
|
80
|
+
]);
|
|
81
|
+
/** Codes defined in more than one slice, sorted. */
|
|
82
|
+
function duplicateCodesOf(slices) {
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
const dup = new Set();
|
|
85
|
+
for (const [, slice] of slices) {
|
|
86
|
+
for (const code of Object.keys(slice)) {
|
|
87
|
+
if (seen.has(code))
|
|
88
|
+
dup.add(code);
|
|
89
|
+
seen.add(code);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [...dup].sort();
|
|
93
|
+
}
|
|
94
|
+
let _registry = null;
|
|
95
|
+
/** The catalog as one Map, built once. On a duplicate code the first slice wins; the gate test reports duplicates. */
|
|
96
|
+
function registry() {
|
|
97
|
+
if (_registry === null) {
|
|
98
|
+
const map = new Map();
|
|
99
|
+
for (const [, slice] of exports.SLICES) {
|
|
100
|
+
for (const code of Object.keys(slice)) {
|
|
101
|
+
if (!map.has(code))
|
|
102
|
+
map.set(code, slice[code]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_registry = map;
|
|
106
|
+
}
|
|
107
|
+
return _registry;
|
|
108
|
+
}
|
|
109
|
+
function has(code) {
|
|
110
|
+
return registry().has(code);
|
|
111
|
+
}
|
|
112
|
+
/** The audience for this process (D-20). Never prints, never throws; developer on any failure. */
|
|
113
|
+
function audienceOf(env = process.env) {
|
|
114
|
+
try {
|
|
115
|
+
return (0, audience_cjs_1.resolveAudience)(env, (0, audience_cjs_1.readPerson)(env)).audience;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return 'developer';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Strip control and format characters, collapse whitespace, trim, cap at `cap` (T-41-11). Order matters. */
|
|
122
|
+
function sanitizeParam(value, cap = exports.ECHO_CAP) {
|
|
123
|
+
let raw;
|
|
124
|
+
if (typeof value === 'number')
|
|
125
|
+
raw = String(value);
|
|
126
|
+
else if (typeof value === 'string')
|
|
127
|
+
raw = value;
|
|
128
|
+
else
|
|
129
|
+
return '';
|
|
130
|
+
const collapsed = raw.replace(CONTROL_CHARS_RE, '').replace(/\s+/g, ' ').trim();
|
|
131
|
+
if (collapsed.length <= cap)
|
|
132
|
+
return collapsed;
|
|
133
|
+
return `${collapsed.slice(0, cap)}…`;
|
|
134
|
+
}
|
|
135
|
+
function rawParam(value) {
|
|
136
|
+
return typeof value === 'string' || typeof value === 'number' ? String(value) : '';
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* One pass of `{name}` substitution with a function replacer. A missing param
|
|
140
|
+
* renders empty in the developer text (unchanged, D-08) and as its own `{name}`
|
|
141
|
+
* slot for business and engineer, so a renderer that fails to supply a value
|
|
142
|
+
* shows visibly instead of leaving a silent blank (D-04). When sanitising, only
|
|
143
|
+
* the slots in `opts.rawParams` (the entry's own marking) pass through as written,
|
|
144
|
+
* and the slots in `opts.longParams` are capped at DIAG_CAP instead of ECHO_CAP.
|
|
145
|
+
*/
|
|
146
|
+
function fill(template, params, opts) {
|
|
147
|
+
return template.replace(SLOT_RE, (match, name) => {
|
|
148
|
+
if (!Object.hasOwn(params, name))
|
|
149
|
+
return opts.sanitize ? match : '';
|
|
150
|
+
const value = params[name];
|
|
151
|
+
if (!opts.sanitize || (opts.rawParams?.includes(name) ?? false))
|
|
152
|
+
return rawParam(value);
|
|
153
|
+
return sanitizeParam(value, (opts.longParams?.includes(name) ?? false) ? exports.DIAG_CAP : exports.ECHO_CAP);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** The wording for an audience: business, engineer, then developer fallbacks (D-01). */
|
|
157
|
+
function pick(entry, audience) {
|
|
158
|
+
if (audience === 'business')
|
|
159
|
+
return entry.business ?? entry.engineer ?? entry.developer;
|
|
160
|
+
if (audience === 'engineer')
|
|
161
|
+
return entry.engineer ?? entry.developer;
|
|
162
|
+
return entry.developer;
|
|
163
|
+
}
|
|
164
|
+
function refLine(code) {
|
|
165
|
+
return `Ref: ${code}`;
|
|
166
|
+
}
|
|
167
|
+
/** The developer text: today's wording, params untouched (D-05, D-08). Unknown code renders the code. */
|
|
168
|
+
function msgDeveloper(code, params = {}, _registry) {
|
|
169
|
+
const entry = (_registry ?? registry()).get(code);
|
|
170
|
+
if (entry === undefined)
|
|
171
|
+
return code;
|
|
172
|
+
return fill(entry.developer, params, { sanitize: false });
|
|
173
|
+
}
|
|
174
|
+
/** The text for an audience with no Ref line. Unknown code renders the code. */
|
|
175
|
+
function text(code, params = {}, opts) {
|
|
176
|
+
const entry = (opts._registry ?? registry()).get(code);
|
|
177
|
+
if (entry === undefined)
|
|
178
|
+
return code;
|
|
179
|
+
if (opts.audience === 'developer')
|
|
180
|
+
return msgDeveloper(code, params, opts._registry);
|
|
181
|
+
return fill(pick(entry, opts.audience), params, {
|
|
182
|
+
sanitize: true,
|
|
183
|
+
rawParams: entry.rawParams,
|
|
184
|
+
longParams: entry.longParams,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/** The message for an audience: business and engineer messages end with the Ref line (D-05, D-06). */
|
|
188
|
+
function msg(code, params = {}, opts) {
|
|
189
|
+
const entry = (opts._registry ?? registry()).get(code);
|
|
190
|
+
if (entry === undefined)
|
|
191
|
+
return code;
|
|
192
|
+
if (opts.audience === 'developer')
|
|
193
|
+
return msgDeveloper(code, params, opts._registry);
|
|
194
|
+
const body = text(code, params, opts);
|
|
195
|
+
if (entry.part === true)
|
|
196
|
+
return body;
|
|
197
|
+
return `${body}\n${refLine(code)}`;
|
|
198
|
+
}
|
|
199
|
+
/** What `pp-tools explain <code>` shows, or null for an unknown code. */
|
|
200
|
+
function explainText(code, opts) {
|
|
201
|
+
const entry = (opts._registry ?? registry()).get(code);
|
|
202
|
+
if (entry === undefined)
|
|
203
|
+
return null;
|
|
204
|
+
const body = typeof entry.explain === 'string' && entry.explain !== ''
|
|
205
|
+
? entry.explain
|
|
206
|
+
: pick(entry, opts.audience).replace(SLOT_RE, (_m, name) => `<${name}>`);
|
|
207
|
+
return `${body}\n${refLine(code)}`;
|
|
208
|
+
}
|
|
@@ -39,6 +39,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
40
|
const childProcess = require("node:child_process");
|
|
41
41
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
42
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
42
43
|
// CR-04 (03-11): this router's path arms resolve their containment boundary the
|
|
43
44
|
// same way `cmdVaultTrace` in src/check-command-router.cts does. project-root.cjs is a
|
|
44
45
|
// leaf (node builtins only), so this adds no cycle.
|
|
@@ -484,6 +485,12 @@ function routeVaultCommand({ args, cwd, raw, error, _vault, _core, _exec, _orgPr
|
|
|
484
485
|
* `config-unreadable`, one more line per unreadable file names the file and the
|
|
485
486
|
* repair; `--raw` prints the verdict object instead.
|
|
486
487
|
*
|
|
488
|
+
* Phase 43 (D-12): for a business user or engineer the `--explain` lines are the
|
|
489
|
+
* card for the stopped check (`gate.vault.admission.blocked`), its `Ref:` line,
|
|
490
|
+
* `Details:` and then the same repair lines. The card is display text: the first
|
|
491
|
+
* line is the token for every audience, and without `--explain`, with `--raw`
|
|
492
|
+
* and for every other state the output is unchanged.
|
|
493
|
+
*
|
|
487
494
|
* The answer comes from `vaultAdmissionVerdict` in the REAL src/vault.cts, never
|
|
488
495
|
* from the injectable `_vault` seam: a governance decision must not be
|
|
489
496
|
* switchable by a test mock.
|
|
@@ -518,9 +525,15 @@ function admissionVerb({ cwd, rest, raw, error, c }) {
|
|
|
518
525
|
}
|
|
519
526
|
const lines = [verdict.state];
|
|
520
527
|
if (explain && verdict.state === 'config-unreadable') {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
528
|
+
const code = 'gate.vault.admission.blocked';
|
|
529
|
+
const repair = verdict.unreadable.map((u) => (0, messages_cjs_1.msgDeveloper)(code, { file: u.file, error: u.error }));
|
|
530
|
+
const audience = (0, messages_cjs_1.audienceOf)(process.env);
|
|
531
|
+
if (audience === 'developer') {
|
|
532
|
+
lines.push(...repair);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
// D-12: the card for a business user or engineer, then today's repair line(s) under Details.
|
|
536
|
+
lines.push((0, messages_cjs_1.text)(code, {}, { audience }), (0, messages_cjs_1.refLine)(code), 'Details:', ...repair);
|
|
524
537
|
}
|
|
525
538
|
}
|
|
526
539
|
c.output(verdict, true, `${lines.join('\n')}\n`);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared violation renderer (Phase 41: MSG-01, MSG-02, MSG-03; D-07, D-11, D-13).
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
|
|
6
|
+
* compiles to the gitignored `pp-core/bin/lib/violation-render.cjs`; do NOT
|
|
7
|
+
* hand-edit the compiled output.
|
|
8
|
+
*
|
|
9
|
+
* D-11: the only place a discovery violation becomes text. Built for every
|
|
10
|
+
* discovery domain; only `discovery.session` and `discovery.input` are cut over
|
|
11
|
+
* in this phase (the other 17 formatViolation copies stay behind the
|
|
12
|
+
* shrink-only ratchet in tests/fixtures/format-violation-allowlist.json).
|
|
13
|
+
*
|
|
14
|
+
* A violation is `{ reason, ...fields }`; its catalog code is
|
|
15
|
+
* `<domain>.<reason>`. A developer gets the exact line the old code printed
|
|
16
|
+
* (`msgDeveloper`, params untouched). A business user or engineer gets a block:
|
|
17
|
+
* the message with its own `Ref:` line, the first line prefixed `- ` and every
|
|
18
|
+
* following line indented, so each problem carries its code (D-05, D-07).
|
|
19
|
+
*
|
|
20
|
+
* Canonical values stay canonical (D-13): the engines pass the literals the
|
|
21
|
+
* person must write (`None identified in Discovery.`, `[TO CONFIRM]`,
|
|
22
|
+
* `Completed`) through `extra`; this module only adds a display `status` through
|
|
23
|
+
* `label()`. It never imports a discovery engine (the engines import it).
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.violationParams = violationParams;
|
|
27
|
+
exports.renderViolation = renderViolation;
|
|
28
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
29
|
+
const message_labels_cjs_1 = require("./message-labels.cjs");
|
|
30
|
+
const SESSION_FILE_RE = /session-(\d{2})\.md$/;
|
|
31
|
+
const SESSION_ID_RE = /^session (\d+)$/;
|
|
32
|
+
function sessionNumberOf(v) {
|
|
33
|
+
for (const field of ['artifact', 'session']) {
|
|
34
|
+
const value = v[field];
|
|
35
|
+
if (typeof value !== 'string')
|
|
36
|
+
continue;
|
|
37
|
+
const m = SESSION_FILE_RE.exec(value);
|
|
38
|
+
if (m !== null)
|
|
39
|
+
return String(Number(m[1]));
|
|
40
|
+
}
|
|
41
|
+
if (typeof v.id === 'string') {
|
|
42
|
+
const m = SESSION_ID_RE.exec(v.id);
|
|
43
|
+
if (m !== null)
|
|
44
|
+
return String(Number(m[1]));
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/** The catalog params for a violation: every string or number field except `reason`, plus the session number, `extra`, and a display `status`. */
|
|
49
|
+
function violationParams(v, opts = {}) {
|
|
50
|
+
const fields = { ...v };
|
|
51
|
+
const params = {};
|
|
52
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
53
|
+
if (key === 'reason')
|
|
54
|
+
continue;
|
|
55
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
56
|
+
params[key] = String(value);
|
|
57
|
+
}
|
|
58
|
+
const n = sessionNumberOf(fields);
|
|
59
|
+
if (n !== null)
|
|
60
|
+
params.session_number = n;
|
|
61
|
+
const extra = opts.extra ?? {};
|
|
62
|
+
for (const [key, value] of Object.entries(extra))
|
|
63
|
+
params[key] = String(value);
|
|
64
|
+
const statusWord = extra.status_word;
|
|
65
|
+
if (typeof statusWord === 'string') {
|
|
66
|
+
params.status = (0, message_labels_cjs_1.label)('discovery.status', statusWord, { audience: opts.audience ?? 'developer' });
|
|
67
|
+
}
|
|
68
|
+
return params;
|
|
69
|
+
}
|
|
70
|
+
/** One violation as text for an audience. An unknown reason renders its code, never throws. */
|
|
71
|
+
function renderViolation(domain, v, opts) {
|
|
72
|
+
const code = `${domain}.${v.reason}`;
|
|
73
|
+
const params = violationParams(v, opts);
|
|
74
|
+
if (opts.audience === 'developer')
|
|
75
|
+
return (0, messages_cjs_1.msgDeveloper)(code, params);
|
|
76
|
+
const lines = (0, messages_cjs_1.msg)(code, params, { audience: opts.audience }).split('\n');
|
|
77
|
+
return lines.map((line, i) => (i === 0 ? `- ${line}` : ` ${line}`)).join('\n');
|
|
78
|
+
}
|
package/pp-core/bin/pp-tools.cjs
CHANGED
|
@@ -71,6 +71,23 @@
|
|
|
71
71
|
* [--plan P] [--id ID]
|
|
72
72
|
* timeline summary <phase> [--banner] Fold a phase's runs: wall-clock, agent time, longest
|
|
73
73
|
*
|
|
74
|
+
* Audience (Phase 39):
|
|
75
|
+
* audience status [--raw] Show the person's audience, language and where each came from
|
|
76
|
+
* audience set <business|engineer|developer> [--language en|nl | --clear-language]
|
|
77
|
+
* Save the setting in ~/.gsd/person.json (GSD_HOME-rooted); no
|
|
78
|
+
* --language keeps a stored language, --clear-language removes it
|
|
79
|
+
* audience set --clear-language Remove only the stored language; the stored audience is untouched
|
|
80
|
+
* audience answer <reply> Save a first-run reply (1, 2, 3 or a name); refused when the
|
|
81
|
+
* reply is blank or an audience is already saved
|
|
82
|
+
* PP_AUDIENCE=<value> Override the audience for one command or shell; never stored
|
|
83
|
+
*
|
|
84
|
+
* Explain (Phase 41):
|
|
85
|
+
* explain <code> [--raw] Longer explanation of a message's Ref code, in the person's wording
|
|
86
|
+
*
|
|
87
|
+
* Gate card (Phase 43):
|
|
88
|
+
* gate-card <query> [--state blocked|advisory|error] [--details] [--raw]
|
|
89
|
+
* The stopped-check card in the person's wording; prints nothing for a developer
|
|
90
|
+
*
|
|
74
91
|
* Ceremony (Phase 37):
|
|
75
92
|
* ceremony classify <phase> [--full] [--research] [--skip-research] [--skip-verify] [--write]
|
|
76
93
|
* Classify a phase (trust boundary, codebase to match, single-concern), print the
|
|
@@ -210,7 +227,7 @@ const path = require('path');
|
|
|
210
227
|
const readline = require('readline');
|
|
211
228
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
212
229
|
const io = require('./lib/io.cjs');
|
|
213
|
-
const { error, ERROR_REASON, setJsonErrorMode, output } = io;
|
|
230
|
+
const { error, errorCode, ERROR_REASON, setJsonErrorMode, output } = io;
|
|
214
231
|
const projectRoot = require('./lib/project-root.cjs');
|
|
215
232
|
// Resolve findProjectRoot lazily at call time rather than binding it at module
|
|
216
233
|
// load. It is sourced from project-root.cjs; a call-time lookup is robust
|
|
@@ -226,6 +243,9 @@ const verify = require('./lib/verify.cjs');
|
|
|
226
243
|
const config = require('./lib/config.cjs');
|
|
227
244
|
const platformBindings = require('./lib/platform-bindings.cjs');
|
|
228
245
|
const timeline = require('./lib/timeline.cjs');
|
|
246
|
+
const audience = require('./lib/audience-command.cjs');
|
|
247
|
+
const explain = require('./lib/explain-command.cjs');
|
|
248
|
+
const gateCard = require('./lib/gate-card.cjs');
|
|
229
249
|
const composeBindings = require('./lib/compose-bindings.cjs');
|
|
230
250
|
const initContract = require('./lib/init-contract.cjs');
|
|
231
251
|
const initInteractive = require('./lib/init-interactive.cjs');
|
|
@@ -538,11 +558,11 @@ async function main() {
|
|
|
538
558
|
// discovery; previously it was a partial subset that didn't include
|
|
539
559
|
// phase / roadmap / milestone / progress / etc.
|
|
540
560
|
const TOP_LEVEL_USAGE = 'Usage: pp-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>] [--json-errors]\n' +
|
|
541
|
-
'Commands: agent, agent-skills, audit-open, audit-uat, check, check-commit, commit, commit-to-subrepo, ' +
|
|
561
|
+
'Commands: agent, agent-skills, audience, audit-open, audit-uat, check, check-commit, commit, commit-to-subrepo, ' +
|
|
542
562
|
'compose-bindings, config-ensure-section, config-get, config-new-project, config-path, config-set, migrate-config, ' +
|
|
543
|
-
'current-timestamp, detect-custom-files, discovery, docs-init, drift-guard, effort, extract-messages, find-phase, ' +
|
|
563
|
+
'current-timestamp, detect-custom-files, discovery, docs-init, drift-guard, effort, explain, extract-messages, find-phase, ' +
|
|
544
564
|
'from-gsd2, frontmatter, gap-analysis, generate-claude-md, generate-claude-profile, ' +
|
|
545
|
-
'generate-client-context, generate-dev-preferences, generate-slug, graphify, history-digest, init, init-contract, intel, ' +
|
|
565
|
+
'gate-card, generate-client-context, generate-dev-preferences, generate-slug, graphify, history-digest, init, init-contract, intel, ' +
|
|
546
566
|
'capability, ceremony, classify-confidence, git, learnings, list-todos, loop, milestone, package-legitimacy, phase, phase-plan-index, phases, platform-bindings, platform-latest-tag, profile-questionnaire, ' +
|
|
547
567
|
'profile-sample, progress, prompt-budget, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, state, ' +
|
|
548
568
|
'task, template, timeline, user-story, validate, validate-bindings, verify, verify-path-exists, verify-summary, workstream, worktree\n\n' +
|
|
@@ -570,7 +590,8 @@ async function main() {
|
|
|
570
590
|
// through this dispatcher. Rendering top-level usage on --help is strictly
|
|
571
591
|
// better UX than the old short-circuit that printed unrelated usage text.
|
|
572
592
|
const HELP_FLAGS = new Set(['-h', '--help', '-?', '--h', '--usage']);
|
|
573
|
-
|
|
593
|
+
// Phase 41: `explain` answers its own help (its usage line names the Ref code), so it is not intercepted here.
|
|
594
|
+
if (command !== 'explain' && args.some((a) => HELP_FLAGS.has(a))) {
|
|
574
595
|
process.stdout.write(TOP_LEVEL_USAGE + '\n');
|
|
575
596
|
return;
|
|
576
597
|
}
|
|
@@ -732,14 +753,19 @@ function createReadlinePrompter({ input = process.stdin, output = process.stdout
|
|
|
732
753
|
const rl = readline.createInterface({ input, output });
|
|
733
754
|
return {
|
|
734
755
|
ask(question) {
|
|
756
|
+
// Phase 41: the chrome is catalog text. A developer (no `audience` on the question) renders
|
|
757
|
+
// exactly what this prompter always printed; business and engineer get their wording and a Ref line.
|
|
758
|
+
const messages = require('./lib/messages.cjs');
|
|
759
|
+
const aud = question.audience === 'business' || question.audience === 'engineer' ? question.audience : 'developer';
|
|
760
|
+
const chrome = (code, params) => messages.text(code, params, { audience: aud });
|
|
735
761
|
let rendered = question.prompt;
|
|
736
762
|
if (question.options && question.options.length > 0) {
|
|
737
763
|
const optionsRendered = question.options
|
|
738
764
|
.map((opt) =>
|
|
739
|
-
opt === question.preselected ? `${opt} (${question.preselectedLabel || 'preselected'})` : opt,
|
|
765
|
+
opt === question.preselected ? `${opt} (${question.preselectedLabel || chrome('init.chrome.preselected', {})})` : opt,
|
|
740
766
|
)
|
|
741
767
|
.join(' / ');
|
|
742
|
-
rendered += `\n
|
|
768
|
+
rendered += `\n ${chrome('init.chrome.options', { options: optionsRendered })}`;
|
|
743
769
|
}
|
|
744
770
|
if (question.optionalAnswer && !/press Enter to skip/i.test(question.prompt)) {
|
|
745
771
|
// UAT-13-F2: a detected org profile is a HINT inside the prompt text
|
|
@@ -747,7 +773,10 @@ function createReadlinePrompter({ input = process.stdin, output = process.stdout
|
|
|
747
773
|
// text may already carry the skip hint (the org profile prompts do),
|
|
748
774
|
// so it is added only for an optional question whose text lacks it
|
|
749
775
|
// (I-1).
|
|
750
|
-
rendered +=
|
|
776
|
+
rendered += `\n ${chrome('init.chrome.skip_hint', {})}`;
|
|
777
|
+
}
|
|
778
|
+
if (aud !== 'developer' && typeof question.code === 'string') {
|
|
779
|
+
rendered += `\n${messages.refLine(question.code)}`;
|
|
751
780
|
}
|
|
752
781
|
return new Promise((resolve) => {
|
|
753
782
|
rl.question(`${rendered}\n> `, (answer) => resolve(answer));
|
|
@@ -866,6 +895,21 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
866
895
|
break;
|
|
867
896
|
}
|
|
868
897
|
|
|
898
|
+
case 'audience': {
|
|
899
|
+
audience.cmdAudience(cwd, args.slice(1), raw);
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
case 'explain': {
|
|
904
|
+
explain.cmdExplain(cwd, args.slice(1), raw);
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
case 'gate-card': {
|
|
909
|
+
gateCard.cmdGateCard(cwd, args.slice(1), raw);
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
|
|
869
913
|
case 'ceremony': {
|
|
870
914
|
// WR-04: loaded here, not at the top, so a missing ceremony-gates.json fails
|
|
871
915
|
// only the ceremony command, never every pp-tools command.
|
|
@@ -1659,35 +1703,28 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1659
1703
|
const INIT_INTERACTIVE_FLAGS = ['--offline', '--reset'];
|
|
1660
1704
|
const unrecognizedInitFlag = args.slice(1).find((a) => !INIT_INTERACTIVE_FLAGS.includes(a));
|
|
1661
1705
|
if (unrecognizedInitFlag !== undefined) {
|
|
1662
|
-
|
|
1663
|
-
`Unrecognized flag "${unrecognizedInitFlag}" for init. Supported: --offline, --reset. ` +
|
|
1664
|
-
'For init workflow subcommands (execute-phase, plan-phase, new-project, ...), use "pp-tools query init.<subcommand>".',
|
|
1665
|
-
ERROR_REASON.SDK_FAIL_FAST,
|
|
1666
|
-
);
|
|
1706
|
+
errorCode('init.unknown_flag', { flag: unrecognizedInitFlag }, ERROR_REASON.SDK_FAIL_FAST);
|
|
1667
1707
|
break;
|
|
1668
1708
|
}
|
|
1669
1709
|
// Refuse to prompt when stdin is not a TTY — never block on stdin
|
|
1670
1710
|
// in CI; a hang here would be worse than a failure (T-34-71).
|
|
1671
1711
|
if (!process.stdin.isTTY) {
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
'This command cannot run inside an AI-agent shell or a piped session — open a regular ' +
|
|
1675
|
-
'terminal window (e.g. Terminal, iTerm, or Windows Terminal) and run it there. ' +
|
|
1676
|
-
'For a non-interactive/CI-driven session, use "pp-tools query init-contract --session-next" / ' +
|
|
1677
|
-
'"--session-answer <stepId>=<value>" directly instead.',
|
|
1678
|
-
ERROR_REASON.INIT_NON_INTERACTIVE,
|
|
1679
|
-
);
|
|
1712
|
+
// The catalog code is the ERROR_REASON value (D-03); --json-errors keeps the developer text for every audience.
|
|
1713
|
+
errorCode(ERROR_REASON.INIT_NON_INTERACTIVE, {}, ERROR_REASON.INIT_NON_INTERACTIVE);
|
|
1680
1714
|
break;
|
|
1681
1715
|
}
|
|
1682
1716
|
if (args.includes('--reset')) {
|
|
1683
1717
|
initContract.initSessionReset(cwd);
|
|
1684
1718
|
}
|
|
1685
1719
|
const prompter = createReadlinePrompter();
|
|
1720
|
+
const initAudience = require('./lib/messages.cjs').audienceOf(process.env);
|
|
1686
1721
|
try {
|
|
1687
1722
|
const result = await initInteractive.runInitInteractive(cwd, prompter, {
|
|
1688
1723
|
offline: args.includes('--offline'),
|
|
1724
|
+
audience: initAudience,
|
|
1689
1725
|
});
|
|
1690
|
-
|
|
1726
|
+
// Business users and engineers read the human result above; the JSON stays for developers and --raw.
|
|
1727
|
+
if (initInteractive.printsResultJson(initAudience, raw)) output(result, raw);
|
|
1691
1728
|
if (!result.ok) {
|
|
1692
1729
|
process.exitCode = 1;
|
|
1693
1730
|
}
|
|
@@ -2786,6 +2823,14 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2786
2823
|
} catch (testsCheckErr) {
|
|
2787
2824
|
error(`discovery tests-check: ${testsCheckErr && testsCheckErr.message ? testsCheckErr.message : String(testsCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2788
2825
|
}
|
|
2826
|
+
} else if (subcommand === 'status') {
|
|
2827
|
+
// discovery status [--raw] — Phase 42 DOOR-01: discovery-only vs build, sprint state and next step; read-only. Exits 1 only on a usage error, or before it runs when the dispatcher refuses an invalid GSD_WORKSTREAM
|
|
2828
|
+
const discoveryStatus = require('./lib/discovery-status.cjs');
|
|
2829
|
+
try {
|
|
2830
|
+
discoveryStatus.cmdDiscoveryStatus(cwd, args, raw);
|
|
2831
|
+
} catch (statusErr) {
|
|
2832
|
+
error(`discovery status: ${statusErr && statusErr.message ? statusErr.message : String(statusErr)}`, ERROR_REASON.UNKNOWN);
|
|
2833
|
+
}
|
|
2789
2834
|
} else if (subcommand === 'flow-page') {
|
|
2790
2835
|
// discovery flow-page <MP-nn> [--out <file.html>] — render one main
|
|
2791
2836
|
// process (swimlane, sub-processes, step matrix) from _processes.md
|
|
@@ -2803,7 +2848,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2803
2848
|
// the engine's exported exit identity, splits this string on commas and
|
|
2804
2849
|
// asserts membership. A spelling missing here is a command the engine
|
|
2805
2850
|
// claims to be and the router does not dispatch.
|
|
2806
|
-
error('Unknown discovery subcommand. Available: rollup, input-check, research-check, screen-check, element-sweep, rebaseline-check, vault-check, charter-check, entity-check, process-check, rules-check, stories-check, phases-check, for-phase, roles-check, integrations-check, migration-check, deviations-check, session-check, tests-check, flow-page', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2851
|
+
error('Unknown discovery subcommand. Available: rollup, input-check, research-check, screen-check, element-sweep, rebaseline-check, vault-check, charter-check, entity-check, process-check, rules-check, stories-check, phases-check, for-phase, roles-check, integrations-check, migration-check, deviations-check, session-check, tests-check, flow-page, status', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2807
2852
|
}
|
|
2808
2853
|
break;
|
|
2809
2854
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Audience moments
|
|
2
|
+
|
|
3
|
+
The business and engineer forms for five moments (Next Up, a stopped check, a verification result, the fallback-standards question and a confirmation before an irreversible action) plus the discovery narration rule. Read from `audience.md`'s pointer line, never `@`-included.
|
|
4
|
+
|
|
5
|
+
## When to use this
|
|
6
|
+
|
|
7
|
+
Use a form below only when the payload (or `pp_run query audience fields`) carries `audience`; pick the section for that audience. A developer never reads this file, and a developer's text stays as it is.
|
|
8
|
+
|
|
9
|
+
Fill every `{…}` from facts you hold (the `ROADMAP.md` goal, the plan's objective, `VERIFICATION.md`, the check's own output) and never invent one. Keep commands, file names, IDs and paths in backticks, unchanged (`audience.md`, Never reword). When the CLI already printed a card (a `GATE_CARDS=` line, or the lines `vault admission --explain` printed), relay that card word for word and never compose a second one.
|
|
10
|
+
|
|
11
|
+
## Business user forms
|
|
12
|
+
|
|
13
|
+
### Next Up
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Next: {plain action}, because {reason}. Start a fresh conversation first (`/clear`) so the assistant has its full working memory, then type `{command}`.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- {plain action} names the step and the plan by its name, taken from the plan's objective. Never show a plan ID.
|
|
20
|
+
- {reason} is one clause from that stage's `ROADMAP.md` goal or from the result just shown.
|
|
21
|
+
- Say "stage N of the work", never the internal word for it.
|
|
22
|
+
- A line that would show a command to print a report file becomes "Ask me to show the full report."
|
|
23
|
+
- Several options become one Next line plus "Also possible:" lines in the same shape.
|
|
24
|
+
|
|
25
|
+
### Stopped check
|
|
26
|
+
|
|
27
|
+
`The {name} check stopped this step: {why}. What to do: {action}.`
|
|
28
|
+
|
|
29
|
+
Then `Ref: {code}`, then `Details:` followed by the check's own message exactly as printed. An advisory says the step carries on. Use the card the CLI printed; compose this shape only when none was printed (an older engine).
|
|
30
|
+
|
|
31
|
+
### Verification result
|
|
32
|
+
|
|
33
|
+
Status line: `Done` when the report status is `passed`, `Not finished yet` for `gaps_found`, `Needs your check` for `human_needed`. Show the internal names only inside backticks.
|
|
34
|
+
|
|
35
|
+
Then three headed lists, each item one plain sentence:
|
|
36
|
+
|
|
37
|
+
- What works ({N} of {M})
|
|
38
|
+
- What is missing
|
|
39
|
+
- What needs your own test
|
|
40
|
+
|
|
41
|
+
End with the Next Up line.
|
|
42
|
+
|
|
43
|
+
### Vault fallback question
|
|
44
|
+
|
|
45
|
+
Your organisation's own standards could not be loaded ({reason}). May the build follow the general Pareto standards instead?
|
|
46
|
+
|
|
47
|
+
Then: Type `confirmed` to go ahead. This answer is never given for the person and never assumed in an unattended run. {reason} is the plain reason, for example "the connection to your organisation's standards did not answer".
|
|
48
|
+
|
|
49
|
+
### Irreversible action
|
|
50
|
+
|
|
51
|
+
Say the action and its target before the person answers, and keep the workflow's own way of answering: its options, or the typed name for `remove-workspace`. One row per action:
|
|
52
|
+
|
|
53
|
+
- `undo`: This will undo `{commits}`, the saved changes of `{plan name}`; they stay in the history. Then ask exactly as the workflow does, with the same options.
|
|
54
|
+
- `remove-workspace`: This will delete the folder `{workspace path}` and every file in it; it cannot be undone. Type `{workspace name}` to go ahead.
|
|
55
|
+
- `complete-milestone`: In the workflow's question, describe its "Delete without merging" option as: This will delete `{branch}`, the separate line of work for this release; use it when that work is already on the main line or not needed. Keep all of the workflow's options.
|
|
56
|
+
- `transition`: This will skip `{plan names}`, which are then not built in this stage of the work. Then ask exactly as the workflow does, with the same options.
|
|
57
|
+
- `vault-write-back`: This will write `{file}` into your organisation's standards at `{target}`, where others will see it. Then ask exactly as the workflow does, with the same options.
|
|
58
|
+
- `discovery-redo`: This will replace the notes of session {n} in `session-{nn}.md` and hold the session again. Then ask exactly as the workflow does, with the same options.
|
|
59
|
+
- `charter-redraft`: This will reopen `PROJECT-CHARTER.md` as version `{version}`; it must be signed again. Then ask exactly as the workflow does, with the same options.
|
|
60
|
+
- `init-compose`: This will write `.platform-bindings.json` with the answers you gave. Then ask exactly as the workflow does, with the same options.
|
|
61
|
+
|
|
62
|
+
### Discovery narration
|
|
63
|
+
|
|
64
|
+
In a discovery session with a business user, never show file paths, internal ID counts (a tally per prefix), check codes or the `vault:` lines. Never ask them to design ID prefixes: choose the prefixes yourself from the people and areas they named and say the split in one plain sentence. Refer to a recorded item as "decision 3" or "open item 2". Keep `/pp-discovery N` commands in backticks.
|
|
65
|
+
|
|
66
|
+
## Business engineer forms
|
|
67
|
+
|
|
68
|
+
### Next Up
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Next: {plain action} (phase {N}), because {reason}. Run `/clear` first so the assistant starts with a fresh context window, then run `{command}`.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- {plain action} names the step and the plan by its name from the plan's objective; never a plan ID.
|
|
75
|
+
- {reason} is one clause from the phase goal in `ROADMAP.md` or from the result just shown.
|
|
76
|
+
- Explain a real term such as "context window" once, with its line from the Explain once table in `audience.md`.
|
|
77
|
+
- A line that would show a command to print a report file becomes "Ask me to show the full report."
|
|
78
|
+
- Several options become one Next line plus "Also possible:" lines in the same shape.
|
|
79
|
+
|
|
80
|
+
### Stopped check
|
|
81
|
+
|
|
82
|
+
`The {name} check stopped this step: {why}. What to do: {action}.`
|
|
83
|
+
|
|
84
|
+
Then `Ref: {code}`, then `Details:` followed by the check's own message exactly as printed. An advisory says the step carries on. Use the card the CLI printed, word for word; its `Ref:` line already names the query. Compose this shape only when none was printed (an older engine), and then name the check by its query, for example `vault.trace`.
|
|
85
|
+
|
|
86
|
+
### Verification result
|
|
87
|
+
|
|
88
|
+
Status line: `Done` for `passed`, `Not finished yet` for `gaps_found` (a required outcome is missing; name the status once in backticks), `Needs your check` for `human_needed`.
|
|
89
|
+
|
|
90
|
+
Then three headed lists, each item one plain sentence:
|
|
91
|
+
|
|
92
|
+
- What works ({N} of {M})
|
|
93
|
+
- What is missing
|
|
94
|
+
- What needs your own test
|
|
95
|
+
|
|
96
|
+
End with the Next Up line.
|
|
97
|
+
|
|
98
|
+
### Vault fallback question
|
|
99
|
+
|
|
100
|
+
Your client's standards vault could not be loaded ({reason}); the fallback tier would apply the general Pareto standards instead. Proceed with the fallback? Type `confirmed` to go ahead.
|
|
101
|
+
|
|
102
|
+
This answer is never given for the person and never assumed in an unattended run.
|
|
103
|
+
|
|
104
|
+
### Irreversible action
|
|
105
|
+
|
|
106
|
+
Say the action and its target before the person answers, and keep the workflow's own way of answering: its options, or the typed name for `remove-workspace`. One row per action:
|
|
107
|
+
|
|
108
|
+
- `undo`: This will revert `{commits}` with `git revert`; history is kept. Then ask exactly as the workflow does, with the same options.
|
|
109
|
+
- `remove-workspace`: This will delete the folder `{workspace path}` and every file in it; it cannot be undone. Type `{workspace name}` to go ahead.
|
|
110
|
+
- `complete-milestone`: In the workflow's question, describe its "Delete without merging" option as: This will delete the branch `{branch}` of this milestone. Keep all of the workflow's options.
|
|
111
|
+
- `transition`: This will skip `{plan names}`; they are not built in phase {N}. Then ask exactly as the workflow does, with the same options.
|
|
112
|
+
- `vault-write-back`: This will write `{file}` to the repository `{repo}` at `{target}`, visible to everyone with access. Then ask exactly as the workflow does, with the same options.
|
|
113
|
+
- `discovery-redo`: This will overwrite the notes of session {n} in `session-{nn}.md` and run the session again. Then ask exactly as the workflow does, with the same options.
|
|
114
|
+
- `charter-redraft`: This will reopen `PROJECT-CHARTER.md` as version `{version}`; it must be signed again. Then ask exactly as the workflow does, with the same options.
|
|
115
|
+
- `init-compose`: This will write `.platform-bindings.json` with the answers you gave. Then ask exactly as the workflow does, with the same options.
|
|
116
|
+
|
|
117
|
+
### Discovery narration
|
|
118
|
+
|
|
119
|
+
Show an ID next to its meaning ("decision D-03-2: one database"). Paths and check codes may appear in backticks. Keep `/pp-discovery N` commands in backticks.
|