@pareto4/pp-core 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/pp-code-fixer.md +9 -5
- package/agents/pp-code-reviewer.md +1 -0
- package/agents/pp-codebase-mapper.md +3 -0
- package/agents/pp-debugger.md +3 -2
- package/agents/pp-executor.md +16 -7
- package/agents/pp-integration-checker.md +1 -0
- package/agents/pp-phase-researcher.md +3 -1
- package/agents/pp-plan-checker.md +3 -0
- package/agents/pp-planner.md +5 -1
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-auditor.md +1 -0
- package/agents/pp-ui-researcher.md +3 -1
- package/agents/pp-verifier.md +10 -2
- package/commands/pp-add-tests.md +1 -1
- package/commands/pp-debug.md +1 -0
- package/commands/pp-init.md +4 -3
- package/commands/pp-quick.md +3 -0
- package/commands/pp-review-backlog.md +2 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pp-core/bin/lib/capability-registry.cjs +2 -2
- package/pp-core/bin/lib/check-command-router.cjs +60 -20
- package/pp-core/bin/lib/commands.cjs +79 -4
- package/pp-core/bin/lib/compose-bindings.cjs +97 -14
- package/pp-core/bin/lib/discovery-rollup.cjs +9 -6
- package/pp-core/bin/lib/discovery-screens.cjs +20 -2
- package/pp-core/bin/lib/discovery-sweep.cjs +12 -3
- package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
- package/pp-core/bin/lib/init-contract.cjs +48 -7
- package/pp-core/bin/lib/vault-coordinates.cjs +1 -1
- package/pp-core/bin/lib/vault-index.cjs +18 -5
- package/pp-core/bin/lib/vault-update.cjs +4 -0
- package/pp-core/bin/lib/vault.cjs +2 -2
- package/pp-core/bin/pp-tools.cjs +10 -4
- package/pp-core/org-profile.json +1 -1
- package/pp-core/references/autonomous-best-effort.md +3 -2
- package/pp-core/references/autonomous-lifecycle.md +1 -0
- package/pp-core/references/autonomous-smart-discuss.md +5 -1
- package/pp-core/references/discovery-id-conventions.md +35 -2
- package/pp-core/references/execute-mvp-tdd.md +3 -3
- package/pp-core/references/git-integration.md +57 -33
- package/pp-core/references/git-planning-commit.md +31 -9
- package/pp-core/references/phase-argument-parsing.md +1 -0
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-revision.md +3 -1
- package/pp-core/references/planner-tdd.md +4 -4
- package/pp-core/references/planning-config.md +3 -3
- package/pp-core/references/shell-portability.md +28 -0
- package/pp-core/references/tdd.md +20 -16
- package/pp-core/references/worktree-path-safety.md +1 -0
- package/pp-core/templates/milestone.md +5 -5
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/add-tests.md +5 -2
- package/pp-core/workflows/add-todo.md +3 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -1
- package/pp-core/workflows/audit-fix.md +4 -3
- package/pp-core/workflows/audit-milestone.md +1 -0
- package/pp-core/workflows/autonomous.md +6 -0
- package/pp-core/workflows/check-todos.md +2 -1
- package/pp-core/workflows/cleanup.md +3 -1
- package/pp-core/workflows/code-review-fix.md +12 -5
- package/pp-core/workflows/code-review.md +15 -1
- package/pp-core/workflows/complete-milestone.md +13 -3
- package/pp-core/workflows/debug.md +2 -0
- package/pp-core/workflows/diagnose-issues.md +1 -1
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +1 -1
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +51 -7
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +5 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +10 -8
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +11 -2
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +5 -5
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +3 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +14 -2
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +10 -10
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +11 -8
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +5 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +9 -3
- package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
- package/pp-core/workflows/discovery-sprint.md +11 -8
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -0
- package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
- package/pp-core/workflows/discuss-phase.md +8 -3
- package/pp-core/workflows/docs-update.md +1 -0
- package/pp-core/workflows/eval-review.md +2 -1
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -6
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +12 -0
- package/pp-core/workflows/execute-phase.md +19 -7
- package/pp-core/workflows/execute-plan.md +18 -7
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/forensics.md +4 -2
- package/pp-core/workflows/health.md +1 -0
- package/pp-core/workflows/import.md +2 -1
- package/pp-core/workflows/ingest-docs.md +1 -0
- package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
- package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
- package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
- package/pp-core/workflows/init.md +56 -66
- package/pp-core/workflows/map-codebase.md +5 -1
- package/pp-core/workflows/milestone-summary.md +2 -1
- package/pp-core/workflows/mvp-phase.md +1 -0
- package/pp-core/workflows/new-milestone.md +9 -5
- package/pp-core/workflows/new-project.md +9 -8
- package/pp-core/workflows/new-workspace.md +1 -0
- package/pp-core/workflows/next.md +2 -1
- package/pp-core/workflows/pause-work.md +6 -4
- package/pp-core/workflows/plan-milestone-gaps.md +3 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
- package/pp-core/workflows/plan-phase.md +21 -6
- package/pp-core/workflows/plan-review-convergence.md +2 -0
- package/pp-core/workflows/plant-seed.md +4 -2
- package/pp-core/workflows/pr-branch.md +2 -0
- package/pp-core/workflows/progress.md +5 -0
- package/pp-core/workflows/quick.md +11 -4
- package/pp-core/workflows/reapply-patches.md +3 -0
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/resume-project.md +3 -0
- package/pp-core/workflows/review.md +7 -1
- package/pp-core/workflows/secure-phase.md +2 -1
- package/pp-core/workflows/session-report.md +1 -0
- package/pp-core/workflows/ship.md +8 -1
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +3 -2
- package/pp-core/workflows/spec-phase.md +2 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +4 -3
- package/pp-core/workflows/sync-skills.md +5 -2
- package/pp-core/workflows/thread.md +3 -2
- package/pp-core/workflows/transition.md +6 -0
- package/pp-core/workflows/ui-phase.md +3 -1
- package/pp-core/workflows/ui-review.md +2 -1
- package/pp-core/workflows/ultraplan-phase.md +1 -0
- package/pp-core/workflows/undo.md +14 -10
- package/pp-core/workflows/update.md +5 -0
- package/pp-core/workflows/validate-phase.md +6 -3
- package/pp-core/workflows/vault-review.md +2 -1
- package/pp-core/workflows/vault-write-back.md +5 -0
- package/pp-core/workflows/verify-phase.md +5 -0
- package/pp-core/workflows/verify-work.md +8 -1
- package/scripts/prompt-injection-scan.sh +1 -1
- package/scripts/sync-runtime-launcher.cjs +115 -5
|
@@ -225,7 +225,7 @@ function isVaultEnabledInConfig(parsed) {
|
|
|
225
225
|
// ─── buildInitQuestions ────────────────────────────────────────────────────
|
|
226
226
|
/** The canonical /pp-init question set, as data; the session may answer some itself (see "Automatic answers").
|
|
227
227
|
* No org-profile question in this fork (PBI 74316); provider options come from `PROVIDERS`, preselected via `resolveProvider`. */
|
|
228
|
-
function buildInitQuestions(cwd) {
|
|
228
|
+
function buildInitQuestions(cwd, opts = {}) {
|
|
229
229
|
let preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
230
230
|
try {
|
|
231
231
|
preselected = (0, git_provider_cjs_1.resolveProvider)(cwd).provider;
|
|
@@ -247,6 +247,7 @@ function buildInitQuestions(cwd) {
|
|
|
247
247
|
prompt: 'Client slug (kebab-case, e.g. "acme") — type "internal" if none',
|
|
248
248
|
optionalAnswer: false,
|
|
249
249
|
},
|
|
250
|
+
...clientVaultQuestion(cwd, opts),
|
|
250
251
|
{ id: 'description', prompt: 'One-line description (what this project does)', optionalAnswer: false },
|
|
251
252
|
{
|
|
252
253
|
id: 'provider',
|
|
@@ -259,6 +260,23 @@ function buildInitQuestions(cwd) {
|
|
|
259
260
|
{ id: 'visibility', prompt: 'Visibility (private / public / internal)', optionalAnswer: false },
|
|
260
261
|
];
|
|
261
262
|
}
|
|
263
|
+
/** The client-vault question, asked while no vault is bound and a binding can be composed (`clientVaultChoice`).
|
|
264
|
+
* Blank or "none" keeps the org profile's seeded vault, if any; an answer replaces it in the binding.
|
|
265
|
+
* An internal project is answered "none" automatically (see `autoAnswerFor`). */
|
|
266
|
+
function clientVaultQuestion(cwd, opts) {
|
|
267
|
+
const choice = (0, compose_bindings_cjs_1.clientVaultChoice)(cwd, opts);
|
|
268
|
+
if (!choice.offered)
|
|
269
|
+
return [];
|
|
270
|
+
return [
|
|
271
|
+
{
|
|
272
|
+
id: 'client_vault',
|
|
273
|
+
prompt: choice.defaultRepo === null
|
|
274
|
+
? 'Client vault repo ("owner/name" or a full clone URL) — type "none" to bind no client vault'
|
|
275
|
+
: `Client vault repo ("owner/name" or a full clone URL) — type "none" to use the default vault (${choice.defaultRepo})`,
|
|
276
|
+
optionalAnswer: true,
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
}
|
|
262
280
|
// ─── Automatic answers (PBI 74299): an org profile init section fixes answers and can derive slugs ───
|
|
263
281
|
/** The visibility answers the question accepts. */
|
|
264
282
|
const VISIBILITY_OPTIONS = Object.freeze(['private', 'public', 'internal']);
|
|
@@ -320,6 +338,9 @@ function autoAnswerFor(id, answers, init) {
|
|
|
320
338
|
const value = init.fixed[id];
|
|
321
339
|
return value === undefined ? null : { value, source: 'org-profile' };
|
|
322
340
|
}
|
|
341
|
+
if (id === 'client_vault' && answers.client_slug === 'internal') {
|
|
342
|
+
return { value: 'none', source: 'derived' };
|
|
343
|
+
}
|
|
323
344
|
if (init.deriveSlugs && (id === 'project_slug' || id === 'client_slug')) {
|
|
324
345
|
const name = answers[id === 'project_slug' ? 'project_name' : 'client_name'];
|
|
325
346
|
const slug = typeof name === 'string' ? deriveSlug(name) : null;
|
|
@@ -328,10 +349,10 @@ function autoAnswerFor(id, answers, init) {
|
|
|
328
349
|
return null;
|
|
329
350
|
}
|
|
330
351
|
/** The session's automatic answers, in question order. */
|
|
331
|
-
function listAutoAnswers(cwd, session) {
|
|
352
|
+
function listAutoAnswers(cwd, session, opts) {
|
|
332
353
|
const sources = session.autoAnswers ?? {};
|
|
333
354
|
const out = [];
|
|
334
|
-
for (const q of buildInitQuestions(cwd)) {
|
|
355
|
+
for (const q of buildInitQuestions(cwd, { _globalRoot: opts._globalRoot })) {
|
|
335
356
|
const source = sources[q.id];
|
|
336
357
|
const value = session.answers[q.id];
|
|
337
358
|
if (source !== undefined && typeof value === 'string')
|
|
@@ -758,10 +779,15 @@ function normalizeYesNo(value) {
|
|
|
758
779
|
}
|
|
759
780
|
/** Decide the current step, performing its idempotent side effects once: automatic answers, the identity write,
|
|
760
781
|
* the compose decision and `doneAt`. Recomputes the question set on every call. */
|
|
782
|
+
/** The client vault the operator named, or `undefined` to keep the seeded one. */
|
|
783
|
+
function answeredClientVault(session) {
|
|
784
|
+
const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(session.answers.client_vault);
|
|
785
|
+
return checked.ok && checked.value !== null ? checked.value : undefined;
|
|
786
|
+
}
|
|
761
787
|
function computeCurrentStep(cwd, session, opts) {
|
|
762
788
|
// Any failure degrades to a `write_failed` step instead of throwing.
|
|
763
789
|
try {
|
|
764
|
-
const questions = buildInitQuestions(cwd);
|
|
790
|
+
const questions = buildInitQuestions(cwd, { _globalRoot: opts._globalRoot });
|
|
765
791
|
// PBI 74299: read once per call; only consulted for an unanswered question.
|
|
766
792
|
let profileInit = null;
|
|
767
793
|
for (const q of questions) {
|
|
@@ -784,7 +810,11 @@ function computeCurrentStep(cwd, session, opts) {
|
|
|
784
810
|
persistSession(cwd, session);
|
|
785
811
|
}
|
|
786
812
|
if (session.composeDecision === null) {
|
|
787
|
-
const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
813
|
+
const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
814
|
+
offline: opts.offline === true,
|
|
815
|
+
_globalRoot: opts._globalRoot,
|
|
816
|
+
clientVaultRepo: answeredClientVault(session),
|
|
817
|
+
});
|
|
788
818
|
// Offer compose_confirm only when approving would change something; 'unchanged' and 'skipped' are distinct end states.
|
|
789
819
|
if (dryRun.composable === true && dryRun.action !== 'unchanged') {
|
|
790
820
|
// VAULT-EGRESS-1: `info` is display-only (the CLI and the TTY renderer print it); approval
|
|
@@ -889,7 +919,7 @@ function initSessionNext(cwd, opts = {}) {
|
|
|
889
919
|
}
|
|
890
920
|
const session = loaded.session;
|
|
891
921
|
const state = currentStepToState(computeCurrentStep(cwd, session, opts));
|
|
892
|
-
const autoAnswered = listAutoAnswers(cwd, session);
|
|
922
|
+
const autoAnswered = listAutoAnswers(cwd, session, opts);
|
|
893
923
|
return autoAnswered.length > 0 ? { ...state, autoAnswered } : state;
|
|
894
924
|
}
|
|
895
925
|
/** Record one answer for the current step only; out-of-order, blank required, unparseable or post-done answers are rejected. */
|
|
@@ -929,6 +959,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
929
959
|
return { ok: false, reason: 'answer_required', message: `Question "${stepId}" requires a non-blank answer.` };
|
|
930
960
|
}
|
|
931
961
|
const answerValue = typeof value === 'string' ? value : '';
|
|
962
|
+
if (stepId === 'client_vault') {
|
|
963
|
+
const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(answerValue);
|
|
964
|
+
if (!checked.ok) {
|
|
965
|
+
return { ok: false, reason: 'invalid_answer', message: `Question "client_vault": ${checked.reason}` };
|
|
966
|
+
}
|
|
967
|
+
}
|
|
932
968
|
session.answers[stepId] = answerValue;
|
|
933
969
|
persistSession(cwd, session);
|
|
934
970
|
return { ok: true };
|
|
@@ -951,7 +987,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
|
|
|
951
987
|
}
|
|
952
988
|
if (normalized === true) {
|
|
953
989
|
// Check the write result before recording "approved": the file can change between the dry run and this write.
|
|
954
|
-
const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
990
|
+
const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
|
|
991
|
+
write: true,
|
|
992
|
+
offline: opts.offline === true,
|
|
993
|
+
_globalRoot: opts._globalRoot,
|
|
994
|
+
clientVaultRepo: answeredClientVault(session),
|
|
995
|
+
});
|
|
955
996
|
if (writeResult.composable !== true || writeResult.wrote !== true) {
|
|
956
997
|
// Leave the decision unset so the next call re-evaluates and can re-offer compose_confirm.
|
|
957
998
|
return {
|
|
@@ -73,7 +73,7 @@ exports.DEFAULT_HOST_FOR_PROVIDER = Object.freeze({
|
|
|
73
73
|
github: 'github.com',
|
|
74
74
|
});
|
|
75
75
|
/** The values moving out of `src/vault.cts`'s `DEFAULT_FALLBACK_REPO`/`DEFAULT_FALLBACK_REF`. */
|
|
76
|
-
exports.DEFAULT_SHARED_REPO = '
|
|
76
|
+
exports.DEFAULT_SHARED_REPO = 'pareto-ooo/pareto-vault-releases';
|
|
77
77
|
exports.DEFAULT_SHARED_REF = 'main';
|
|
78
78
|
/** Dotted `.planning/config.json` keys, named so no consumer hand-writes a key string. */
|
|
79
79
|
exports.SHARED_REPO_CONFIG_KEY = 'vault.fallback_repo';
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* has already sanitised.
|
|
40
40
|
*/
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.VAULT_INDEX_WITHHELD_KINDS = exports.VAULT_INDEX_UNCHECKED_CAUSES = exports.VAULT_INDEX_UNPARSEABLE_CAUSES = exports.VAULT_INDEX_REASON = exports.DISPLAY_UNSAFE_TEXT_CHAR_RE = exports.VAULT_BINDING_NOTE = exports.VAULT_PATH_REASONS = exports.VAULT_INDEX_LIST_BOUND = exports.VAULT_TEXT_MAX = exports.VAULT_INDEX_GRAMMAR = exports.VAULT_SECTION_HEADING_PREFIX = exports.VAULT_USED_WHEN_RE = exports.VAULT_USED_WHEN_MARKER = exports.VAULT_ENTRY_SEPARATOR = exports.VAULT_ENTRY_PREFIX = exports.VAULT_INDEX_BYTE_CAP = exports.VAULT_INDEX_FILENAME = void 0;
|
|
42
|
+
exports.VAULT_INDEX_WITHHELD_KINDS = exports.VAULT_INDEX_UNCHECKED_CAUSES = exports.VAULT_INDEX_UNPARSEABLE_CAUSES = exports.VAULT_INDEX_REASON = exports.DISPLAY_UNSAFE_TEXT_CHAR_RE = exports.VAULT_BINDING_NOTE = exports.VAULT_PATH_REASONS = exports.VAULT_INDEX_LIST_BOUND = exports.VAULT_TEXT_MAX = exports.VAULT_INDEX_GRAMMAR = exports.VAULT_CROSS_CUTTING_MARK = exports.VAULT_SECTION_HEADING_PREFIX = exports.VAULT_USED_WHEN_RE = exports.VAULT_USED_WHEN_MARKER = exports.VAULT_ENTRY_SEPARATOR = exports.VAULT_ENTRY_PREFIX = exports.VAULT_INDEX_BYTE_CAP = exports.VAULT_INDEX_FILENAME = void 0;
|
|
43
43
|
exports.classifyVaultPath = classifyVaultPath;
|
|
44
44
|
exports.isSafeVaultPath = isSafeVaultPath;
|
|
45
45
|
exports.splitVaultEntryLine = splitVaultEntryLine;
|
|
@@ -74,6 +74,13 @@ exports.VAULT_USED_WHEN_MARKER = '; used when ';
|
|
|
74
74
|
exports.VAULT_USED_WHEN_RE = /;\s+used when\s+/i;
|
|
75
75
|
/** A section is opened by a level-2 ATX heading at column 0. */
|
|
76
76
|
exports.VAULT_SECTION_HEADING_PREFIX = '## ';
|
|
77
|
+
/**
|
|
78
|
+
* The cross-cutting mark (issue #12, items 16-17): an entry whose remainder
|
|
79
|
+
* starts with it holds a rule every Discovery session must read — approval
|
|
80
|
+
* routes, folder layout, naming — whatever the session's topics are. It is
|
|
81
|
+
* stripped from the description and recorded as `crossCutting: true`.
|
|
82
|
+
*/
|
|
83
|
+
exports.VAULT_CROSS_CUTTING_MARK = '[cross-cutting]';
|
|
77
84
|
/** The six grammar values bundled, frozen, for a consumer that quotes the grammar whole. */
|
|
78
85
|
exports.VAULT_INDEX_GRAMMAR = Object.freeze({
|
|
79
86
|
filename: exports.VAULT_INDEX_FILENAME,
|
|
@@ -219,16 +226,22 @@ const LEVEL1_HEADING_RE = /^#(?:[ \t].*)?$/;
|
|
|
219
226
|
// D-28: an entry prefix indented 1-3 spaces is a near-miss, not a nested list.
|
|
220
227
|
const INDENTED_ENTRY_RE = /^ {1,3}- `/;
|
|
221
228
|
function buildEntry(section, path, rest, line) {
|
|
222
|
-
|
|
229
|
+
// The mark is read off the front of the remainder; a mark with nothing after
|
|
230
|
+
// it stays text, so the description is never left empty.
|
|
231
|
+
const lead = rest.trimStart();
|
|
232
|
+
const marked = lead.startsWith(exports.VAULT_CROSS_CUTTING_MARK) && lead.slice(exports.VAULT_CROSS_CUTTING_MARK.length).trim() !== '';
|
|
233
|
+
const body = marked ? lead.slice(exports.VAULT_CROSS_CUTTING_MARK.length) : rest;
|
|
234
|
+
const flag = marked ? { crossCutting: true } : {};
|
|
235
|
+
const m = exports.VAULT_USED_WHEN_RE.exec(body);
|
|
223
236
|
if (m) {
|
|
224
|
-
const usedWhen =
|
|
237
|
+
const usedWhen = body.slice(m.index + m[0].length).trim();
|
|
225
238
|
if (usedWhen !== '') {
|
|
226
|
-
return { section, path, description:
|
|
239
|
+
return { section, path, description: body.slice(0, m.index).trim(), usedWhen, ...flag, line };
|
|
227
240
|
}
|
|
228
241
|
}
|
|
229
242
|
// No clause, or an empty one: the whole remainder is the description and the
|
|
230
243
|
// key is omitted — a clause is never invented and text is never dropped.
|
|
231
|
-
return { section, path, description:
|
|
244
|
+
return { section, path, description: body.trim(), ...flag, line };
|
|
232
245
|
}
|
|
233
246
|
/**
|
|
234
247
|
* Parse an INDEX.md text tolerantly. Never throws; a non-string is read as
|
|
@@ -109,6 +109,7 @@ function vaultUpdate(cwd, opts = {}) {
|
|
|
109
109
|
from: null,
|
|
110
110
|
to: null,
|
|
111
111
|
latest: null,
|
|
112
|
+
newer: null,
|
|
112
113
|
available: [],
|
|
113
114
|
fetch: null,
|
|
114
115
|
nextStep: null,
|
|
@@ -152,6 +153,9 @@ function vaultUpdate(cwd, opts = {}) {
|
|
|
152
153
|
tags = [...new Set(tags)].sort((a, b) => (0, semver_compare_cjs_1.compareSemverCore)(b, a));
|
|
153
154
|
result.available = tags.slice(0, exports.AVAILABLE_TAGS_CAP);
|
|
154
155
|
result.latest = tags[0] ?? null;
|
|
156
|
+
if (result.from && result.latest && (0, semver_compare_cjs_1.isStableTripletSemver)(result.from) && (0, semver_compare_cjs_1.compareSemverCore)(result.latest, result.from) > 0) {
|
|
157
|
+
result.newer = result.latest;
|
|
158
|
+
}
|
|
155
159
|
if (tags.length === 0) {
|
|
156
160
|
return fail('no_tags', `The vault repo "${redactCloneTarget(repo)}" has no stable vX.Y.Z tag to pin to.`);
|
|
157
161
|
}
|
|
@@ -2809,7 +2809,7 @@ function resolveRefDrift({ cwd, tier, cachedRef }) {
|
|
|
2809
2809
|
// DEVIATION from plan 08-01's step 3, recorded so the next reader does not
|
|
2810
2810
|
// "restore" it: the plan prescribed copying the miss branch's call-site-
|
|
2811
2811
|
// scoped `require('./git-provider.cjs')` / `require('./vault-coordinates.cjs')`
|
|
2812
|
-
// pair from src/check-command-router.cts:
|
|
2812
|
+
// pair from src/check-command-router.cts:1587-1594. That shape exists THERE
|
|
2813
2813
|
// because the check router does not otherwise import those leaves, so the
|
|
2814
2814
|
// require genuinely keeps its module graph narrow. It buys nothing HERE:
|
|
2815
2815
|
// this module already imports `resolveProvider` (:40) and
|
|
@@ -2833,7 +2833,7 @@ function resolveRefDrift({ cwd, tier, cachedRef }) {
|
|
|
2833
2833
|
// UNCONDITIONALLY, including when `repo` is null, so an ungated
|
|
2834
2834
|
// comparison reports phantom drift on every project that configures no
|
|
2835
2835
|
// client vault at all. The miss branch argues the same point at
|
|
2836
|
-
// src/check-command-router.cts:
|
|
2836
|
+
// src/check-command-router.cts:1596-1599, where it keeps the truthful
|
|
2837
2837
|
// `(no ref pinned)` placeholder for the same reason.
|
|
2838
2838
|
//
|
|
2839
2839
|
// D-08-39(a): a BLANK or WHITESPACE-ONLY `client.vaultRepo` is not a
|
package/pp-core/bin/pp-tools.cjs
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* state signal-resume Remove WAITING.json signal
|
|
20
20
|
* resolve-model <agent-type> Get model for agent based on profile
|
|
21
21
|
* find-phase <phase> Find phase directory by number
|
|
22
|
-
* commit <message> [--files f1 f2] [--no-verify] Commit planning docs
|
|
23
|
-
* commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
|
|
22
|
+
* commit <message> [--files f1 f2] [--phase id] [--quick id] [--no-verify] Commit planning docs
|
|
23
|
+
* commit-to-subrepo <msg> --files f1 f2 [--phase id] Route commits to sub-repos
|
|
24
24
|
* verify-summary <path> Verify a SUMMARY.md file
|
|
25
25
|
* generate-slug <text> Convert text to URL-safe slug
|
|
26
26
|
* current-timestamp [format] Get timestamp (full|date|filename)
|
|
@@ -1158,6 +1158,10 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1158
1158
|
}
|
|
1159
1159
|
|
|
1160
1160
|
case 'commit': {
|
|
1161
|
+
// --phase <id> / --quick <id> add a `Phase:` / `Quick:` trailer; pulled out
|
|
1162
|
+
// first so their values are never read as message words or --files paths.
|
|
1163
|
+
const trailerArgs = commands.extractCommitTrailerArgs(args);
|
|
1164
|
+
args = trailerArgs.args;
|
|
1161
1165
|
const amend = args.includes('--amend');
|
|
1162
1166
|
const noVerify = args.includes('--no-verify');
|
|
1163
1167
|
const filesIndex = args.indexOf('--files');
|
|
@@ -1168,7 +1172,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1168
1172
|
const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
|
|
1169
1173
|
const message = messageArgs.join(' ') || undefined;
|
|
1170
1174
|
const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
|
|
1171
|
-
commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
|
|
1175
|
+
commands.cmdCommit(cwd, message, files, raw, amend, noVerify, { phase: trailerArgs.phase, quick: trailerArgs.quick });
|
|
1172
1176
|
break;
|
|
1173
1177
|
}
|
|
1174
1178
|
|
|
@@ -1178,10 +1182,12 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
1178
1182
|
}
|
|
1179
1183
|
|
|
1180
1184
|
case 'commit-to-subrepo': {
|
|
1185
|
+
const trailerArgs = commands.extractCommitTrailerArgs(args);
|
|
1186
|
+
args = trailerArgs.args;
|
|
1181
1187
|
const message = args[1];
|
|
1182
1188
|
const filesIndex = args.indexOf('--files');
|
|
1183
1189
|
const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
|
|
1184
|
-
commands.cmdCommitToSubrepo(cwd, message, files, raw);
|
|
1190
|
+
commands.cmdCommitToSubrepo(cwd, message, files, raw, { phase: trailerArgs.phase, quick: trailerArgs.quick });
|
|
1185
1191
|
break;
|
|
1186
1192
|
}
|
|
1187
1193
|
|
package/pp-core/org-profile.json
CHANGED
|
@@ -83,7 +83,7 @@ grep -q "^handled=${PHASE_NUM}:" "$RUN_STATE" 2>/dev/null ||
|
|
|
83
83
|
printf 'handled=%s:%s\n' "$PHASE_NUM" "$OUTCOME" >> "$RUN_STATE"
|
|
84
84
|
PADDED_PHASE=$(basename "$PHASE_DIR" | cut -d- -f1)
|
|
85
85
|
if [ -f "$REPORT" ]; then
|
|
86
|
-
pp_run query commit "docs(
|
|
86
|
+
pp_run query commit "docs(planning): autonomous report" --phase "${PADDED_PHASE}" --files "$REPORT"
|
|
87
87
|
fi
|
|
88
88
|
```
|
|
89
89
|
|
|
@@ -400,6 +400,7 @@ phase. Printed by the lifecycle single-phase banner, the STOPPED banner and the
|
|
|
400
400
|
Used by `autonomous.md` §3a when `workflow.skip_discuss` is true. Get phase details:
|
|
401
401
|
|
|
402
402
|
```bash
|
|
403
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
403
404
|
DETAIL=$(pp_run query roadmap.get-phase ${PHASE_NUM})
|
|
404
405
|
```
|
|
405
406
|
|
|
@@ -452,7 +453,7 @@ None — discuss phase skipped.
|
|
|
452
453
|
Commit the minimal context:
|
|
453
454
|
|
|
454
455
|
```bash
|
|
455
|
-
pp_run query commit "docs(
|
|
456
|
+
pp_run query commit "docs(planning): auto-generated context (discuss skipped)" --phase "${padded_phase}" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
|
|
456
457
|
```
|
|
457
458
|
|
|
458
459
|
## Banner math
|
|
@@ -109,6 +109,7 @@ Skill(skill="pp-complete-milestone", args="${milestone_version}")
|
|
|
109
109
|
After complete-milestone returns, verify it produced output:
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
112
113
|
ls .planning/milestones/v${milestone_version}-ROADMAP.md 2>/dev/null || true
|
|
113
114
|
```
|
|
114
115
|
|
|
@@ -5,6 +5,7 @@ Smart discuss is the autonomous-optimized variant of `pp-discuss-phase`. It prop
|
|
|
5
5
|
**Inputs:** `PHASE_NUM` from execute_phase. Run init to get phase paths:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
8
9
|
PHASE_STATE=$(pp-tools query init.phase-op ${PHASE_NUM})
|
|
9
10
|
```
|
|
10
11
|
|
|
@@ -34,6 +35,7 @@ Extract from these:
|
|
|
34
35
|
**Read all prior CONTEXT.md files:**
|
|
35
36
|
|
|
36
37
|
```bash
|
|
38
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
37
39
|
(find "$(dirname "$STATE_PATH")/phases" -name "*-CONTEXT.md" 2>/dev/null || true) | sort
|
|
38
40
|
```
|
|
39
41
|
|
|
@@ -68,6 +70,7 @@ Lightweight codebase scan to inform grey area identification and proposals. Keep
|
|
|
68
70
|
**Check for existing codebase maps:**
|
|
69
71
|
|
|
70
72
|
```bash
|
|
73
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
71
74
|
ls .planning/codebase/*.md 2>/dev/null || true
|
|
72
75
|
```
|
|
73
76
|
|
|
@@ -96,6 +99,7 @@ Read the 3-5 most relevant files to understand existing patterns.
|
|
|
96
99
|
**Get phase details:**
|
|
97
100
|
|
|
98
101
|
```bash
|
|
102
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
99
103
|
DETAIL=$(pp-tools query roadmap.get-phase ${PHASE_NUM})
|
|
100
104
|
```
|
|
101
105
|
|
|
@@ -270,7 +274,7 @@ Write the file.
|
|
|
270
274
|
**Commit:**
|
|
271
275
|
|
|
272
276
|
```bash
|
|
273
|
-
|
|
277
|
+
pp_run query commit "docs(planning): smart discuss context" --phase "${PADDED_PHASE}" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
|
|
274
278
|
```
|
|
275
279
|
|
|
276
280
|
Display confirmation:
|
|
@@ -17,12 +17,13 @@ never `@`-imported.
|
|
|
17
17
|
| Data-gathering items | `DG-<nn>` — same shape as SP. Regex: `DG-\d{2}` | `DG-07` | Open data-gathering items in `_risks.md` |
|
|
18
18
|
| Research items | `RS-<nn>` — same shape as SP/DG: zero-padded 2-digit global sequence, no session component. Regex: `RS-\d{2}` | `RS-01` | Rows of `_research.md`, the research-only backlog |
|
|
19
19
|
| Screens | `SCR-<nn>` — zero-padded 2-digit global sequence, no session and no role component, minted by scan (D-12). Regex: `SCR-\d{2}` | `SCR-01` | Rows of `_screens.md`, the requirement-to-screen inventory; Role is a grouping column, not part of the ID (D-50) |
|
|
20
|
+
| Processes | `PRC-<nn>` — zero-padded 2-digit global sequence. Regex: `PRC-\d{2}` | `PRC-03` | Rows of the Session 2 **Process Inventory**, the as-is processes Session 3 transforms |
|
|
20
21
|
| Requirements | `<PREFIX><n>` — PREFIX is a registered domain prefix (uppercase letters/digits ending in `-`, e.g. a stakeholder-domain prefix, `N-` non-functional, `T-` technical); `<n>` is a numeric sequence, zero-padded to 2 digits by convention (`01`..`99`, then `100`). Regex: `[A-Z][A-Z0-9]*-\d+` | `N-01` | Rows of `_requirements.md`; the prefix carries the classification — there is no separate Type column |
|
|
21
22
|
| Input-package accessions | `NN` — zero-padded 2-digit number, assigned in arrival order and never renumbered; a mid-sprint document takes the next number (D-16). Regex: `\d{2}` | `03` | Numbered documents in `.planning/discovery-input/` (including its `confidential/` subdir — one number space spans both locations, D-21). Reading order is a README column, not the number |
|
|
22
23
|
|
|
23
24
|
## Reserved Prefixes
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
Seven prefixes are reserved by the ID families above and MUST NOT be registered as
|
|
26
27
|
domain prefixes in the `_requirements.md` Prefix Registry:
|
|
27
28
|
|
|
28
29
|
| Reserved | Owned by |
|
|
@@ -33,6 +34,7 @@ domain prefixes in the `_requirements.md` Prefix Registry:
|
|
|
33
34
|
| `DG-` | Data-gathering items |
|
|
34
35
|
| `RS-` | Research items |
|
|
35
36
|
| `SCR-` | Screens |
|
|
37
|
+
| `PRC-` | Processes |
|
|
36
38
|
|
|
37
39
|
Every one of them satisfies the requirement-ID regex `[A-Z][A-Z0-9]*-\d+`, so a
|
|
38
40
|
registry that claims one mints requirement IDs indistinguishable from the family that
|
|
@@ -43,7 +45,11 @@ collide, and no reader or parser can tell them apart.
|
|
|
43
45
|
sequence, so a registry claiming `SCR-` would make `SCR-01` ambiguous between a screen
|
|
44
46
|
and a requirement — the same collision, one artifact further along the traceability ring.
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
`PRC-` followed with issue #12: Session 2 used to number processes `P-<nn>`, which an
|
|
49
|
+
engagement had also registered as a domain prefix, so `P-03` meant two things. Process IDs
|
|
50
|
+
moved to `PRC-` and the prefix is reserved so the clash cannot come back.
|
|
51
|
+
|
|
52
|
+
Session 1's kick-off prefix selection (D-09) must refuse all seven: pick a different
|
|
47
53
|
domain prefix (any other uppercase letters/digits ending in `-`, e.g. `K-` for *Klant*).
|
|
48
54
|
Non-reserved prefixes are unaffected.
|
|
49
55
|
|
|
@@ -53,6 +59,33 @@ registry, so its requirement rows also report `unknown_prefix`. The check is ret
|
|
|
53
59
|
— an engagement that registered one of these before the rule existed turns red on
|
|
54
60
|
upgrade. The remedy is the same: choose a different domain prefix.
|
|
55
61
|
|
|
62
|
+
## Session-Table Row IDs
|
|
63
|
+
|
|
64
|
+
The Notes Templates number some table rows with their own short prefixes. These IDs are
|
|
65
|
+
local to their table: they are not requirements, never enter `_requirements.md`, and are
|
|
66
|
+
not reserved — but Session 1 should not propose a domain prefix that repeats one, or a
|
|
67
|
+
citation such as `C-01` reads two ways.
|
|
68
|
+
|
|
69
|
+
| Prefix | Table | Session |
|
|
70
|
+
|--------|-------|---------|
|
|
71
|
+
| `CON-` | Constraints and Risks Register (constraints) | 1 |
|
|
72
|
+
| `R-` | Constraints and Risks Register (risks), continued by the Risk Register | 1, 11 |
|
|
73
|
+
| `PP-` | Pain Points Register | 2 |
|
|
74
|
+
| `MP-` | Main Process Register | 3 |
|
|
75
|
+
| `BO-` | Business Objectives | 3 |
|
|
76
|
+
| `E-` | EPIC Register | 3 |
|
|
77
|
+
| `US-` | User Stories | 3 |
|
|
78
|
+
| `C-` | Component Inventory | 5 |
|
|
79
|
+
| `INT-` | Integration Points Map | 5 |
|
|
80
|
+
| `ADR-` | Architecture Decision Records | 5 |
|
|
81
|
+
| `CR-` | Compliance Requirements Register | 6 |
|
|
82
|
+
| `IF-` | Integration Design Register | 8 |
|
|
83
|
+
| `OI-` | Open Items Register | 13 |
|
|
84
|
+
|
|
85
|
+
Non-functional requirements are not in this list: they are `N-` rows of
|
|
86
|
+
`_requirements.md`, minted like every other requirement, and Session 3's
|
|
87
|
+
**Non-Functional Requirements** table cites them by that ID.
|
|
88
|
+
|
|
56
89
|
## Citation Rules
|
|
57
90
|
|
|
58
91
|
- Requirement IDs are never renumbered and never reused (REQM-02). A gap in a sequence
|
|
@@ -15,11 +15,11 @@ If any of these is false, the gate is inactive — execution proceeds normally.
|
|
|
15
15
|
|
|
16
16
|
For each task gated by MVP+TDD, the executor MUST verify (before running the implementation step):
|
|
17
17
|
|
|
18
|
-
1. **A failing-test commit exists.** Search git log on the current branch for a commit
|
|
18
|
+
1. **A failing-test commit exists.** Search git log on the current branch for a commit of type `test` (any scope) that carries the trailer `Phase: {phase}-{plan}` for the same plan as the current task. The commit must touch a test file (`*.test.*`, `*.spec.*`, `tests/**`).
|
|
19
19
|
2. **The test was actually red.** The commit message body or the executor's recent shell history must show the test failed when first run. Acceptable evidence:
|
|
20
20
|
- Commit message contains `RED:` prefix or `(RED)` tag
|
|
21
21
|
- Recent terminal output shows `FAIL` or non-zero exit on the new test before any implementation commit
|
|
22
|
-
3. **No implementation commit yet.** No `feat
|
|
22
|
+
3. **No implementation commit yet.** No `feat` commit carrying the trailer `Phase: {phase}-{plan}` may exist before the failing-test commit.
|
|
23
23
|
|
|
24
24
|
If any check fails, the gate trips.
|
|
25
25
|
|
|
@@ -51,7 +51,7 @@ The executor MUST:
|
|
|
51
51
|
|
|
52
52
|
Required next step:
|
|
53
53
|
1. Write a failing test for the behavior above.
|
|
54
|
-
2. Commit it as: test({
|
|
54
|
+
2. Commit it as: test({area}): {short description} — with the trailer Phase: {phase}-{plan}
|
|
55
55
|
3. Re-run /pp:execute-phase
|
|
56
56
|
```
|
|
57
57
|
|