@pareto4/pp-core 1.18.0 → 1.20.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 +13 -1
- package/agents/pp-plan-checker.md +3 -0
- package/agents/pp-planner.md +20 -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-vault-reviewer.md +2 -0
- package/agents/pp-verifier.md +22 -10
- package/commands/pp-add-tests.md +1 -1
- package/commands/pp-debug.md +1 -0
- package/commands/pp-discovery.md +3 -2
- 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 +6 -3
- package/pareto-skills/_shared/pp-claude-md-protocol.md +1 -1
- package/pareto-skills/pp-project-setup/SKILL.md +18 -343
- package/pp-core/bin/lib/capability-registry.cjs +3 -3
- 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-access.cjs +276 -0
- package/pp-core/bin/lib/discovery-charter.cjs +664 -0
- package/pp-core/bin/lib/discovery-deviations.cjs +264 -0
- package/pp-core/bin/lib/discovery-entities.cjs +323 -0
- package/pp-core/bin/lib/discovery-for-phase.cjs +628 -0
- package/pp-core/bin/lib/discovery-integrations.cjs +286 -0
- package/pp-core/bin/lib/discovery-migration.cjs +246 -0
- package/pp-core/bin/lib/discovery-phases.cjs +233 -0
- package/pp-core/bin/lib/discovery-processes.cjs +355 -0
- package/pp-core/bin/lib/discovery-research-track.cjs +5 -4
- package/pp-core/bin/lib/discovery-rollup.cjs +12 -8
- package/pp-core/bin/lib/discovery-rules.cjs +216 -0
- package/pp-core/bin/lib/discovery-screens.cjs +21 -3
- package/pp-core/bin/lib/discovery-session-check.cjs +264 -0
- package/pp-core/bin/lib/discovery-stories.cjs +273 -0
- package/pp-core/bin/lib/discovery-sweep.cjs +13 -4
- package/pp-core/bin/lib/discovery-table.cjs +55 -0
- 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/io.cjs +22 -0
- 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-standards-review.cjs +50 -2
- 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 +153 -5
- 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-access.md +112 -0
- package/pp-core/references/discovery-build-consumption.md +256 -0
- package/pp-core/references/discovery-charter.md +127 -0
- package/pp-core/references/discovery-data-model.md +145 -0
- package/pp-core/references/discovery-id-conventions.md +77 -8
- package/pp-core/references/discovery-integrations.md +151 -0
- package/pp-core/references/discovery-processes.md +190 -0
- package/pp-core/references/discovery-rebaseline.md +2 -2
- package/pp-core/references/discovery-research-track.md +5 -6
- package/pp-core/references/discovery-session-output.md +70 -0
- package/pp-core/references/discovery-standards.md +107 -0
- package/pp-core/references/discovery-traceability.md +2 -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-discovery-extract.md +35 -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/verifier-discovery-conformance.md +36 -0
- package/pp-core/references/worktree-path-safety.md +1 -0
- package/pp-core/templates/milestone.md +5 -5
- package/pp-core/templates/project-charter.md +161 -100
- package/pp-core/templates/project.md +3 -3
- package/pp-core/templates/requirements.md +18 -0
- package/pp-core/templates/verification-report.md +16 -0
- 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/charter.md +114 -0
- package/pp-core/workflows/discovery-sprint/prefill.md +1 -1
- package/pp-core/workflows/discovery-sprint/prototype.md +4 -4
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +11 -1
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +54 -7
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +31 -7
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +64 -37
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +12 -3
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +45 -22
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +30 -15
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +43 -25
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +63 -24
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +28 -13
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +35 -16
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +32 -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 +29 -12
- package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
- package/pp-core/workflows/discovery-sprint.md +23 -23
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +81 -29
- package/pp-core/workflows/discovery-to-build/steps/{screen-inventory-gate.md → handoff-gates.md} +111 -3
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +12 -9
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +27 -26
- package/pp-core/workflows/discovery-to-build.md +70 -34
- package/pp-core/workflows/discuss-phase/discovery.md +83 -0
- package/pp-core/workflows/discuss-phase/templates/context.md +4 -0
- package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
- package/pp-core/workflows/discuss-phase.md +18 -10
- 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 +12 -11
- 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/steps/discovery-extract.md +41 -0
- package/pp-core/workflows/plan-phase.md +36 -16
- 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 +7 -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
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +0 -266
|
@@ -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
|
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.REVIEW_FILES_CAP = exports.REVIEW_VERDICTS_HEADING = exports.REVIEW_ARTIFACT_MARKER = exports.REVIEW_ARTIFACT_SUFFIX = exports.REVIEW_VERDICTS = void 0;
|
|
6
|
+
exports.DEVIATIONS_FILE = exports.REVIEW_FILES_CAP = exports.REVIEW_VERDICTS_HEADING = exports.REVIEW_ARTIFACT_MARKER = exports.REVIEW_ARTIFACT_SUFFIX = exports.REVIEW_VERDICTS = void 0;
|
|
7
|
+
exports.readDeviations = readDeviations;
|
|
7
8
|
exports.deriveStandardsReview = deriveStandardsReview;
|
|
8
9
|
exports.formatReviewCounts = formatReviewCounts;
|
|
9
10
|
exports.validateReviewArtifact = validateReviewArtifact;
|
|
@@ -59,6 +60,8 @@ exports.REVIEW_VERDICTS_HEADING = '## Standard Verdicts';
|
|
|
59
60
|
exports.REVIEW_FILES_CAP = 200;
|
|
60
61
|
/** Bound on a git query a review makes. */
|
|
61
62
|
const REVIEW_GIT_TIMEOUT_MS = 30_000;
|
|
63
|
+
/** Where the Discovery Sprint records agreed departures from the vault. */
|
|
64
|
+
exports.DEVIATIONS_FILE = '.planning/discovery/_deviations.md';
|
|
62
65
|
// ─── Derivation ──────────────────────────────────────────────────────────────
|
|
63
66
|
const PLAN_RE = /(^|-)PLAN\.md$/;
|
|
64
67
|
const SUMMARY_RE = /(^|-)SUMMARY\.md$/;
|
|
@@ -73,6 +76,47 @@ function readBounded(abs) {
|
|
|
73
76
|
return null;
|
|
74
77
|
}
|
|
75
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The deviations on file, read by header name so Batch 3's full grammar can
|
|
81
|
+
* add or reorder columns without touching this reader. `gap` is set when the
|
|
82
|
+
* file exists but yields no readable table with both join columns.
|
|
83
|
+
*/
|
|
84
|
+
function readDeviations(root) {
|
|
85
|
+
const abs = node_path_1.default.join(root, exports.DEVIATIONS_FILE);
|
|
86
|
+
if (!node_fs_1.default.existsSync(abs))
|
|
87
|
+
return { deviations: [], gap: null };
|
|
88
|
+
const text = readBounded(abs);
|
|
89
|
+
if (text === null)
|
|
90
|
+
return { deviations: [], gap: `${exports.DEVIATIONS_FILE} could not be read (unreadable or over the size cap), so deviations on file are unknown` };
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
92
|
+
const table = require('./discovery-table.cjs');
|
|
93
|
+
const deviations = [];
|
|
94
|
+
let joinable = false;
|
|
95
|
+
for (const section of table.splitSections(text.split(/\r?\n/), 2)) {
|
|
96
|
+
for (const tbl of table.parseTables(section.body)) {
|
|
97
|
+
const names = tbl.header.map((h) => h.trim().toLowerCase());
|
|
98
|
+
const col = (name) => names.indexOf(name);
|
|
99
|
+
const standard = col('standard');
|
|
100
|
+
const status = col('status');
|
|
101
|
+
if (standard === -1 || status === -1)
|
|
102
|
+
continue;
|
|
103
|
+
joinable = true;
|
|
104
|
+
const cell = (row, i) => (i === -1 ? '' : (row.cells[i] ?? '').trim());
|
|
105
|
+
for (const row of tbl.rows) {
|
|
106
|
+
deviations.push({
|
|
107
|
+
id: cell(row, col('id')),
|
|
108
|
+
standard: cell(row, standard),
|
|
109
|
+
status: cell(row, status),
|
|
110
|
+
decision: cell(row, col('decision')),
|
|
111
|
+
approver: cell(row, col('approver')),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!joinable)
|
|
117
|
+
return { deviations: [], gap: `${exports.DEVIATIONS_FILE} carries no table with Standard and Status columns, so deviations on file are unknown` };
|
|
118
|
+
return { deviations, gap: null };
|
|
119
|
+
}
|
|
76
120
|
function consultedText(entry, hasSummary) {
|
|
77
121
|
if (!hasSummary)
|
|
78
122
|
return 'no summary';
|
|
@@ -206,6 +250,9 @@ function deriveStandardsReview(root, cwd, positional, opts = {}) {
|
|
|
206
250
|
});
|
|
207
251
|
}
|
|
208
252
|
}
|
|
253
|
+
const onFile = readDeviations(root);
|
|
254
|
+
if (onFile.gap !== null)
|
|
255
|
+
gaps.push(onFile.gap);
|
|
209
256
|
if (plans.length === 0 || documents.size === 0) {
|
|
210
257
|
return {
|
|
211
258
|
status: 'no-records',
|
|
@@ -216,9 +263,10 @@ function deriveStandardsReview(root, cwd, positional, opts = {}) {
|
|
|
216
263
|
plans,
|
|
217
264
|
documents: [],
|
|
218
265
|
gaps,
|
|
266
|
+
deviations: onFile.deviations,
|
|
219
267
|
};
|
|
220
268
|
}
|
|
221
|
-
return { status: 'ok', phaseDir: phase.rel, plans, documents: [...documents.values()], gaps };
|
|
269
|
+
return { status: 'ok', phaseDir: phase.rel, plans, documents: [...documents.values()], gaps, deviations: onFile.deviations };
|
|
222
270
|
}
|
|
223
271
|
/** The counts line, in verdict order: `p PASS · f FAIL · n N-A · i INCONCLUSIVE (of N documents)`. */
|
|
224
272
|
function formatReviewCounts(counts) {
|
|
@@ -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
|
|
|
@@ -2558,13 +2564,155 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2558
2564
|
} catch (vaultCheckErr) {
|
|
2559
2565
|
error(`discovery vault-check: ${vaultCheckErr && vaultCheckErr.message ? vaultCheckErr.message : String(vaultCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2560
2566
|
}
|
|
2567
|
+
} else if (subcommand === 'charter-check') {
|
|
2568
|
+
// discovery charter-check [--require-signed]
|
|
2569
|
+
//
|
|
2570
|
+
// Structural check of docs/discovery/PROJECT-CHARTER.md: frontmatter
|
|
2571
|
+
// enums, the twenty `<!-- charter:<key> -->` sections in order, every
|
|
2572
|
+
// cited ID resolving to a session table or _requirements.md row, the
|
|
2573
|
+
// Go / No-Go table equal to Session 13's, must-resolve open items
|
|
2574
|
+
// closed or listed as conditions, sign-off rows agreeing with status.
|
|
2575
|
+
// --require-signed also fails (discovery_charter_unsigned) unless the
|
|
2576
|
+
// status is Signed or Signed with conditions — the handoff gate's arm.
|
|
2577
|
+
const charter = require('./lib/discovery-charter.cjs');
|
|
2578
|
+
try {
|
|
2579
|
+
charter.cmdDiscoveryCharterCheck(cwd, args, raw);
|
|
2580
|
+
} catch (charterCheckErr) {
|
|
2581
|
+
error(`discovery charter-check: ${charterCheckErr && charterCheckErr.message ? charterCheckErr.message : String(charterCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2582
|
+
}
|
|
2583
|
+
} else if (subcommand === 'entity-check') {
|
|
2584
|
+
// discovery entity-check [--require-complete] — _entities.md structure
|
|
2585
|
+
// (improvement plan, Batch 2): named entities, closed lifecycle and
|
|
2586
|
+
// classification cells, attributes per entity, Mermaid cardinalities,
|
|
2587
|
+
// step-matrix data objects resolving. --require-complete also fails on
|
|
2588
|
+
// pending `—` cells (discovery_entity_check_incomplete).
|
|
2589
|
+
const entities = require('./lib/discovery-entities.cjs');
|
|
2590
|
+
try {
|
|
2591
|
+
entities.cmdDiscoveryEntityCheck(cwd, args, raw);
|
|
2592
|
+
} catch (entityCheckErr) {
|
|
2593
|
+
error(`discovery entity-check: ${entityCheckErr && entityCheckErr.message ? entityCheckErr.message : String(entityCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2594
|
+
}
|
|
2595
|
+
} else if (subcommand === 'process-check') {
|
|
2596
|
+
// discovery process-check [--require-cited] — _processes.md structure:
|
|
2597
|
+
// PRC-/MP- sequences, Replaces resolving, one swimlane and one step
|
|
2598
|
+
// matrix per main process, actors, data objects, requirement IDs.
|
|
2599
|
+
// --require-cited also fails on a step citing no requirement.
|
|
2600
|
+
const processes = require('./lib/discovery-processes.cjs');
|
|
2601
|
+
try {
|
|
2602
|
+
processes.cmdDiscoveryProcessCheck(cwd, args, raw);
|
|
2603
|
+
} catch (processCheckErr) {
|
|
2604
|
+
error(`discovery process-check: ${processCheckErr && processCheckErr.message ? processCheckErr.message : String(processCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2605
|
+
}
|
|
2606
|
+
} else if (subcommand === 'rules-check') {
|
|
2607
|
+
// discovery rules-check — _rules.md structure: BR- sequence, layer
|
|
2608
|
+
// vocabulary, every rule citing an existing requirement and an existing
|
|
2609
|
+
// entity or process.
|
|
2610
|
+
const rules = require('./lib/discovery-rules.cjs');
|
|
2611
|
+
try {
|
|
2612
|
+
rules.cmdDiscoveryRulesCheck(cwd, args, raw);
|
|
2613
|
+
} catch (rulesCheckErr) {
|
|
2614
|
+
error(`discovery rules-check: ${rulesCheckErr && rulesCheckErr.message ? rulesCheckErr.message : String(rulesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2615
|
+
}
|
|
2616
|
+
} else if (subcommand === 'stories-check') {
|
|
2617
|
+
// discovery stories-check — _stories.md structure (improvement plan,
|
|
2618
|
+
// Batch 4): US- sequence, EPIC shape, every story citing an existing
|
|
2619
|
+
// requirement, source step resolving, every MVP domain requirement
|
|
2620
|
+
// carrying at least one story.
|
|
2621
|
+
const stories = require('./lib/discovery-stories.cjs');
|
|
2622
|
+
try {
|
|
2623
|
+
stories.cmdDiscoveryStoriesCheck(cwd, args, raw);
|
|
2624
|
+
} catch (storiesCheckErr) {
|
|
2625
|
+
error(`discovery stories-check: ${storiesCheckErr && storiesCheckErr.message ? storiesCheckErr.message : String(storiesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2626
|
+
}
|
|
2627
|
+
} else if (subcommand === 'phases-check') {
|
|
2628
|
+
// discovery phases-check — _phases.md structure (improvement plan,
|
|
2629
|
+
// Batch 4): contiguous phases, requirement IDs resolving and not
|
|
2630
|
+
// dropped, dependencies on earlier phases, every MVP requirement in
|
|
2631
|
+
// exactly one phase.
|
|
2632
|
+
const phasesCheck = require('./lib/discovery-phases.cjs');
|
|
2633
|
+
try {
|
|
2634
|
+
phasesCheck.cmdDiscoveryPhasesCheck(cwd, args, raw);
|
|
2635
|
+
} catch (phasesCheckErr) {
|
|
2636
|
+
error(`discovery phases-check: ${phasesCheckErr && phasesCheckErr.message ? phasesCheckErr.message : String(phasesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2637
|
+
}
|
|
2638
|
+
} else if (subcommand === 'for-phase') {
|
|
2639
|
+
// discovery for-phase <N> [--markdown | --conformance-template |
|
|
2640
|
+
// --verify <file>] — the rows of every canonical artifact that cite
|
|
2641
|
+
// the phase's requirement IDs as one extract between
|
|
2642
|
+
// <!-- discovery:auto --> markers (improvement plan, Batch 4, R1);
|
|
2643
|
+
// the verifier's conformance block and its check (R3). Fails loud on
|
|
2644
|
+
// artifact structure only; no discovery package → exit 0, empty.
|
|
2645
|
+
const forPhase = require('./lib/discovery-for-phase.cjs');
|
|
2646
|
+
try {
|
|
2647
|
+
forPhase.cmdDiscoveryForPhase(cwd, args, raw);
|
|
2648
|
+
} catch (forPhaseErr) {
|
|
2649
|
+
error(`discovery for-phase: ${forPhaseErr && forPhaseErr.message ? forPhaseErr.message : String(forPhaseErr)}`, ERROR_REASON.UNKNOWN);
|
|
2650
|
+
}
|
|
2651
|
+
} else if (subcommand === 'roles-check') {
|
|
2652
|
+
// discovery roles-check — _roles.md structure (improvement plan,
|
|
2653
|
+
// Batch 3): every role a persona, a step-matrix actor or System;
|
|
2654
|
+
// permissions naming a known role, an existing entity, lowercase
|
|
2655
|
+
// operations and at least one requirement; screen rows resolving.
|
|
2656
|
+
const roles = require('./lib/discovery-access.cjs');
|
|
2657
|
+
try {
|
|
2658
|
+
roles.cmdDiscoveryRolesCheck(cwd, args, raw);
|
|
2659
|
+
} catch (rolesCheckErr) {
|
|
2660
|
+
error(`discovery roles-check: ${rolesCheckErr && rolesCheckErr.message ? rolesCheckErr.message : String(rolesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2661
|
+
}
|
|
2662
|
+
} else if (subcommand === 'integrations-check') {
|
|
2663
|
+
// discovery integrations-check [--require-cited] — _integrations.md
|
|
2664
|
+
// structure: IF- sequence, direction vocabulary, contract and error
|
|
2665
|
+
// rows naming a known interface, step-matrix IF- tokens resolving.
|
|
2666
|
+
// --require-cited also fails on an uncited interface and on a step
|
|
2667
|
+
// naming a system without an IF- (discovery_integrations_uncited).
|
|
2668
|
+
const integrations = require('./lib/discovery-integrations.cjs');
|
|
2669
|
+
try {
|
|
2670
|
+
integrations.cmdDiscoveryIntegrationsCheck(cwd, args, raw);
|
|
2671
|
+
} catch (integrationsCheckErr) {
|
|
2672
|
+
error(`discovery integrations-check: ${integrationsCheckErr && integrationsCheckErr.message ? integrationsCheckErr.message : String(integrationsCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2673
|
+
}
|
|
2674
|
+
} else if (subcommand === 'migration-check') {
|
|
2675
|
+
// discovery migration-check [--require-cited] — _migration.md
|
|
2676
|
+
// structure: MG- sequence, source system in Session 2's landscape,
|
|
2677
|
+
// entities resolving, requirement IDs resolving. --require-cited
|
|
2678
|
+
// also fails on an uncited row (discovery_migration_uncited).
|
|
2679
|
+
const migration = require('./lib/discovery-migration.cjs');
|
|
2680
|
+
try {
|
|
2681
|
+
migration.cmdDiscoveryMigrationCheck(cwd, args, raw);
|
|
2682
|
+
} catch (migrationCheckErr) {
|
|
2683
|
+
error(`discovery migration-check: ${migrationCheckErr && migrationCheckErr.message ? migrationCheckErr.message : String(migrationCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2684
|
+
}
|
|
2685
|
+
} else if (subcommand === 'session-check') {
|
|
2686
|
+
// discovery session-check <N> [<N>…] | --all — every Notes Template
|
|
2687
|
+
// section of the session content file present exactly once in
|
|
2688
|
+
// session-NN.md, each table with a data row or the literal
|
|
2689
|
+
// `None identified in Discovery.`; --all covers the Completed sessions.
|
|
2690
|
+
const sessionCheck = require('./lib/discovery-session-check.cjs');
|
|
2691
|
+
try {
|
|
2692
|
+
sessionCheck.cmdDiscoverySessionCheck(cwd, args, raw);
|
|
2693
|
+
} catch (sessionCheckErr) {
|
|
2694
|
+
error(`discovery session-check: ${sessionCheckErr && sessionCheckErr.message ? sessionCheckErr.message : String(sessionCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2695
|
+
}
|
|
2696
|
+
} else if (subcommand === 'deviations-check') {
|
|
2697
|
+
// discovery deviations-check [--require-approved] — _deviations.md
|
|
2698
|
+
// structure: DEV- sequence, a [vault: path] standard present in the
|
|
2699
|
+
// cache, reversible and status vocabularies, approver in Session 1's
|
|
2700
|
+
// Approval Matrix, decision ID shape. --require-approved also fails
|
|
2701
|
+
// on a hard-to-reverse deviation still Proposed
|
|
2702
|
+
// (discovery_deviations_unapproved).
|
|
2703
|
+
const deviations = require('./lib/discovery-deviations.cjs');
|
|
2704
|
+
try {
|
|
2705
|
+
deviations.cmdDiscoveryDeviationsCheck(cwd, args, raw);
|
|
2706
|
+
} catch (deviationsCheckErr) {
|
|
2707
|
+
error(`discovery deviations-check: ${deviationsCheckErr && deviationsCheckErr.message ? deviationsCheckErr.message : String(deviationsCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2708
|
+
}
|
|
2561
2709
|
} else {
|
|
2562
2710
|
// WIRING, NOT HELP TEXT: tests/discovery-artifacts.test.cjs derives each
|
|
2563
2711
|
// subcommand spelling by regexing `<namespace>_<subcommand>_failed` out of
|
|
2564
2712
|
// the engine's exported exit identity, splits this string on commas and
|
|
2565
2713
|
// asserts membership. A spelling missing here is a command the engine
|
|
2566
2714
|
// claims to be and the router does not dispatch.
|
|
2567
|
-
error('Unknown discovery subcommand. Available: rollup, input-check, research-check, screen-check, element-sweep, rebaseline-check, vault-check', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2715
|
+
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', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2568
2716
|
}
|
|
2569
2717
|
break;
|
|
2570
2718
|
}
|
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:
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Discovery Access Model — `_roles.md` Template & Parser Contract
|
|
2
|
+
|
|
3
|
+
Single home for the roles artifact the build reads instead of Session 7's notes (Discovery Sprint
|
|
4
|
+
improvement plan, Batch 3): `_roles.md` holds the roles, what each may do to which entity, and which
|
|
5
|
+
screens each may open. Its gate is `pp_run query discovery.roles-check`. This file is Read lazily; it
|
|
6
|
+
is never `@`-imported.
|
|
7
|
+
|
|
8
|
+
## When the file appears
|
|
9
|
+
|
|
10
|
+
| Session | `_roles.md` |
|
|
11
|
+
|---|---|
|
|
12
|
+
| 7 | created from the template; topic 1 writes `## Roles` and `## Permissions`, topic 4 writes `## Screens`; `roles-check` in the session |
|
|
13
|
+
| 8 | `roles-check` in the Closing Step (reporting) |
|
|
14
|
+
| 13 | in the Step 0 gates (reporting) |
|
|
15
|
+
| handoff | `roles-check` hard-blocks |
|
|
16
|
+
|
|
17
|
+
Session 7's **Roles** section keeps a reading summary and points here: the canonical rows live in
|
|
18
|
+
the artifact, exactly as the requirements table lives in `_requirements.md`. The old preset matrix
|
|
19
|
+
(Admin / Manager / User / Viewer with one CRUD column per entity) is gone: a role is whatever the
|
|
20
|
+
client's process needs, and a permission is one row per role and entity with the operations as a
|
|
21
|
+
list, so a build phase can read exactly the rows that cite its requirements.
|
|
22
|
+
|
|
23
|
+
## Grammar
|
|
24
|
+
|
|
25
|
+
- **A role is an actor the process already has.** Every `## Roles` row names a Session 2 persona
|
|
26
|
+
(spelled as the User Personas table spells it), an actor a step matrix of `_processes.md` names,
|
|
27
|
+
or `System`. A role nobody plays in any step is a role the build cannot test; name the persona or
|
|
28
|
+
add the step. Unresolved while neither `session-02.md` nor `_processes.md` is on file.
|
|
29
|
+
- **Recovery path** is how the role is recovered when its last holder leaves or is locked out — the
|
|
30
|
+
vault's rule that no role may become unreachable (`[vault: <path>]` when the vault states it).
|
|
31
|
+
- **An operation is a lowercase verb** (`create`, `read`, `update`, `delete`, `assign`, `export`,
|
|
32
|
+
`approve`), comma-separated; the vocabulary is the product's, the shape is fixed.
|
|
33
|
+
- **An entity is a row of `_entities.md`**, one PascalCase token (`references/discovery-data-model.md`).
|
|
34
|
+
- **Condition** narrows a permission (`own team only`, `assignee or lead`); `—` is unconditional.
|
|
35
|
+
- **Requirements** names the `_requirements.md` IDs the permission realises — at least one; a
|
|
36
|
+
permission nothing requires is a question for the user, not a row.
|
|
37
|
+
- **A screen row is the authorization join**: the role may open the screen. `_screens.md` keeps its
|
|
38
|
+
own `Role` column as a reading aid (D-50); this table is the one the forbidden-path tests read.
|
|
39
|
+
- **Nothing is deleted.** A retired role keeps its row; a change after sign-off edits the row in
|
|
40
|
+
place and appends `→ amended by D-<ss>-<n>` (`references/discovery-id-conventions.md` § Precedence).
|
|
41
|
+
|
|
42
|
+
## `_roles.md`
|
|
43
|
+
|
|
44
|
+
Illustration only — not part of the template, names invented:
|
|
45
|
+
|
|
46
|
+
| Role | Description | Scope | Recovery path | Source |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| Team lead | Runs a team's work and membership | Teams they lead | An operator with `Teams.Recover` names a member lead | S7 |
|
|
49
|
+
|
|
50
|
+
| Role | Entity | Operation | Condition | Requirements |
|
|
51
|
+
|---|---|---|---|---|
|
|
52
|
+
| Team lead | Membership | create, delete, update | own team only | K-13, K-14 |
|
|
53
|
+
|
|
54
|
+
| Role | Screen |
|
|
55
|
+
|---|---|
|
|
56
|
+
| Team lead | SCR-05 |
|
|
57
|
+
|
|
58
|
+
Bootstrap template — Session 7 copies it verbatim to `.planning/discovery/_roles.md`, replacing
|
|
59
|
+
`[PROJECT_NAME]`:
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
# Roles — [PROJECT_NAME]
|
|
63
|
+
|
|
64
|
+
**Conventions:** pp-core reference `references/discovery-access.md`. A role is a Session 2 persona, a step-matrix actor or `System`; one permission row per role and entity, operations as a comma list; a screen row says the role may open the screen. Rows are never deleted; a change after sign-off edits the row in place and appends `→ amended by D-<ss>-<n>`.
|
|
65
|
+
|
|
66
|
+
## Roles
|
|
67
|
+
|
|
68
|
+
| Role | Description | Scope | Recovery path | Source |
|
|
69
|
+
|------|-------------|-------|---------------|--------|
|
|
70
|
+
|
|
71
|
+
## Permissions
|
|
72
|
+
|
|
73
|
+
| Role | Entity | Operation | Condition | Requirements |
|
|
74
|
+
|------|--------|-----------|-----------|--------------|
|
|
75
|
+
|
|
76
|
+
## Screens
|
|
77
|
+
|
|
78
|
+
| Role | Screen |
|
|
79
|
+
|------|--------|
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Parser contract — `pp_run query discovery.roles-check`
|
|
83
|
+
|
|
84
|
+
No positional path, no flags. Faults name the artifact, the 1-based line, a validated ID or name, a
|
|
85
|
+
section or column name and the reason code — never cell prose. Absent `_roles.md` → exit 0,
|
|
86
|
+
`present: false`.
|
|
87
|
+
|
|
88
|
+
| Cell / field | Grammar | Fault when violated |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `## Roles`, `## Permissions`, `## Screens` | each once as a `##` heading holding one table with the template's columns | `missing_section`, `bad_table_shape` naming the section |
|
|
91
|
+
| Roles `Role` | non-empty, listed once; a Session 2 persona, a step-matrix `Actor` token or `System` | `empty_cell (Role)`, `duplicate_role`, `unknown_role` naming the role. **Not resolved** while neither `session-02.md` nor `_processes.md` is on file |
|
|
92
|
+
| Roles `Description`, `Scope`, `Recovery path` | non-empty (`—` is a pending cell the facilitator owns) | `empty_cell` naming the column |
|
|
93
|
+
| Roles `Source` | free text | **not mechanically checked** |
|
|
94
|
+
| Permissions / Screens `Role` | a `## Roles` row | `unknown_role` |
|
|
95
|
+
| Permissions `Entity` | one PascalCase token present in `_entities.md` § Entities when on file | `bad_entity`; `unknown_entity` naming the entity |
|
|
96
|
+
| Permissions `Operation` | a comma list of `^[a-z][a-z-]*$` tokens | `bad_operation` |
|
|
97
|
+
| Permissions `Condition` | free text (`—` allowed) | **not mechanically checked** |
|
|
98
|
+
| Permissions `Requirements` | at least one requirement ID, each present in `_requirements.md` when on file and parseable | `no_requirement`, `unknown_requirement_id` |
|
|
99
|
+
| Screens `Screen` | `SCR-\d{2}`, present in `_screens.md` § Screens when on file | `bad_screen`; `unknown_screen` naming the screen |
|
|
100
|
+
| `_requirements.md` present but unparseable | the rollup grammar is owned by `discovery rollup` | `requirements_unparseable`, once; IDs are then shape-checked only |
|
|
101
|
+
| a source present but unreadable | present-and-readable | `artifact_unreadable` naming the path |
|
|
102
|
+
|
|
103
|
+
**Exit identity.** `discovery_roles_check_failed`. Payload: `ok`, `present`, `file`, `roles`,
|
|
104
|
+
`permissions`, `screens`.
|
|
105
|
+
|
|
106
|
+
## How the build reads it
|
|
107
|
+
|
|
108
|
+
`pp_run query discovery.for-phase <N>` selects the § Permissions rows citing the phase's requirement
|
|
109
|
+
IDs, then the § Roles row of every role those permissions name and the § Screens rows of those roles
|
|
110
|
+
and of the phase's screens (`references/discovery-build-consumption.md`). The conformance key is the
|
|
111
|
+
role name. The planner's auth phase reads the extract; the forbidden-path tests are written from the
|
|
112
|
+
screen rows and the permission conditions.
|