@pareto4/pp-core 1.12.1 → 1.17.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 +3 -3
- package/README.md +26 -9
- package/agents/pp-code-reviewer.md +3 -2
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +7 -21
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +23 -9
- package/agents/pp-vault-reviewer.md +274 -0
- package/agents/pp-verifier.md +7 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-review.md +2 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-review.md +35 -0
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +14 -19
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +526 -22
- package/pp-core/bin/lib/capability-state.cjs +160 -4
- package/pp-core/bin/lib/capability-writer.cjs +19 -5
- package/pp-core/bin/lib/check-command-router.cjs +3867 -274
- package/pp-core/bin/lib/clusters.cjs +4 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -28
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +253 -76
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +154 -62
- package/pp-core/bin/lib/decisions.cjs +104 -27
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -1
- package/pp-core/bin/lib/git-base-branch.cjs +138 -10
- package/pp-core/bin/lib/git-provider.cjs +4 -4
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +653 -629
- package/pp-core/bin/lib/init-interactive.cjs +77 -21
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +8 -0
- package/pp-core/bin/lib/loop-resolver.cjs +169 -47
- package/pp-core/bin/lib/org-profile.cjs +544 -53
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -23
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -203
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/practices-review-check.cjs +15 -9
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-audit.cjs +2594 -0
- package/pp-core/bin/lib/vault-command-router.cjs +2756 -60
- package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
- package/pp-core/bin/lib/vault-index.cjs +699 -0
- package/pp-core/bin/lib/vault-mode.cjs +235 -0
- package/pp-core/bin/lib/vault-practices.cjs +1194 -89
- package/pp-core/bin/lib/vault-review.cjs +2102 -0
- package/pp-core/bin/lib/vault-seal.cjs +1936 -0
- package/pp-core/bin/lib/vault-standards.cjs +1505 -0
- package/pp-core/bin/lib/vault-trace.cjs +3184 -0
- package/pp-core/bin/lib/vault-update.cjs +242 -0
- package/pp-core/bin/lib/vault-write-back.cjs +78 -31
- package/pp-core/bin/lib/vault.cjs +2713 -634
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +184 -37
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +1 -0
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -14
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-practices.md +31 -3
- package/pp-core/references/planner-vault-standards.md +320 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/practice-arithmetic-contract.md +121 -0
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/references/verifier-practice-verdicts.md +263 -0
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +202 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/templates/practices/README.md +137 -11
- package/pp-core/templates/project-charter.md +226 -0
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -0
- package/pp-core/workflows/audit-fix.md +63 -0
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -1
- package/pp-core/workflows/code-review.md +10 -1
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +56 -2
- package/pp-core/workflows/discovery-sprint/prefill.md +21 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -48
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +9 -1
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -18
- package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
- package/pp-core/workflows/execute-phase.md +154 -110
- package/pp-core/workflows/execute-plan.md +58 -4
- package/pp-core/workflows/fast.md +53 -0
- package/pp-core/workflows/help/modes/full.md +1 -0
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +99 -43
- package/pp-core/workflows/quick.md +120 -2
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -24
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +60 -1
- package/pp-core/workflows/vault-review.md +325 -0
- package/pp-core/workflows/verify-work.md +13 -12
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +199 -0
- package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +13 -2
- package/scripts/practice-arithmetic-contract.allow.json +35 -0
- package/scripts/prompt-injection-scan.sh +22 -15
- package/scripts/record-vault-mode-golden.cjs +89 -0
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/vault-calibrate.cjs +996 -0
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -472
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/phase-tier.cjs +0 -467
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -72
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -34
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- package/scripts/lint-org-coordinates.cjs +0 -426
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* src/vault-update.cts — `pp-tools vault update [<tag>] [--yes]` (PBI 74316).
|
|
4
|
+
*
|
|
5
|
+
* FORK-ONLY. Moves a project to another version of its client vault. The
|
|
6
|
+
* vault version is a PROJECT property: `client.vaultRef` in the committed
|
|
7
|
+
* `.platform-bindings.json` is the one place it lives, so no pp-core release
|
|
8
|
+
* can move it — changing it is a deliberate act, and this is its command.
|
|
9
|
+
*
|
|
10
|
+
* Distinct from its neighbours:
|
|
11
|
+
* - `pp-tools vault fetch` re-reads the vault at the version the project is
|
|
12
|
+
* already pinned to; it never changes `client.vaultRef`.
|
|
13
|
+
* - `/pp-update` updates pp-core itself, not a project's vault.
|
|
14
|
+
*
|
|
15
|
+
* Two steps, so the confirmation is explicit and works the same for a person
|
|
16
|
+
* and for an agent shell: without `--yes` it only reports the plan
|
|
17
|
+
* (current → target) and writes nothing; with `--yes` it writes
|
|
18
|
+
* `client.vaultRef`, fetches the vault at the new version, and reports any
|
|
19
|
+
* practice whose `schemaVersion` is newer than this pp-core supports (the
|
|
20
|
+
* vault version a project picks implies a minimum pp-core, never the reverse).
|
|
21
|
+
* It never commits: the command it prints is the operator's to run.
|
|
22
|
+
*
|
|
23
|
+
* ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
|
|
24
|
+
* compiles to the git-ignored `pp-core/bin/lib/vault-update.cjs`.
|
|
25
|
+
*/
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.AVAILABLE_TAGS_CAP = void 0;
|
|
31
|
+
exports.vaultUpdate = vaultUpdate;
|
|
32
|
+
exports.renderVaultUpdate = renderVaultUpdate;
|
|
33
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
34
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
35
|
+
const project_root_cjs_1 = require("./project-root.cjs");
|
|
36
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
37
|
+
const semver_compare_cjs_1 = require("./semver-compare.cjs");
|
|
38
|
+
const compose_bindings_cjs_1 = require("./compose-bindings.cjs");
|
|
39
|
+
const git_provider_cjs_1 = require("./git-provider.cjs");
|
|
40
|
+
const vault_coordinates_cjs_1 = require("./vault-coordinates.cjs");
|
|
41
|
+
const platform_bindings_cjs_1 = require("./platform-bindings.cjs");
|
|
42
|
+
// VAULT-EGRESS-1 (quick 261005-8py): the one coordinate redactor. Cycle-free — src/vault.cts imports
|
|
43
|
+
// nothing from this module; the lazy `require` in runFetch exists for the `_fetch` seam default.
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
45
|
+
const vaultModule = require("./vault.cjs");
|
|
46
|
+
const { redactCloneTarget } = vaultModule;
|
|
47
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
48
|
+
/** How many tags a report lists — enough to pick from, bounded for a long history. */
|
|
49
|
+
exports.AVAILABLE_TAGS_CAP = 10;
|
|
50
|
+
const BINDINGS_FILE = '.platform-bindings.json';
|
|
51
|
+
/** Matches the degrade warning `parsePracticeFile` emits for a newer schemaVersion. */
|
|
52
|
+
const NEWER_SCHEMA_WARNING_RE = /schemaVersion \d+ is newer than this pp-core supports/;
|
|
53
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
54
|
+
function isObject(v) {
|
|
55
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
56
|
+
}
|
|
57
|
+
function usableString(v) {
|
|
58
|
+
return typeof v === 'string' && v.trim() !== '' ? v.trim() : null;
|
|
59
|
+
}
|
|
60
|
+
function resolveRoot(cwd) {
|
|
61
|
+
try {
|
|
62
|
+
return (0, project_root_cjs_1.findProjectRoot)(cwd) || cwd;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return cwd;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Resolve a requested tag against the available list; accepts a missing `v`. */
|
|
69
|
+
function matchRequestedTag(requested, tags) {
|
|
70
|
+
const trimmed = requested.trim();
|
|
71
|
+
if (tags.includes(trimmed))
|
|
72
|
+
return trimmed;
|
|
73
|
+
if (!trimmed.startsWith('v') && tags.includes(`v${trimmed}`))
|
|
74
|
+
return `v${trimmed}`;
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function runFetch(root, opts) {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
79
|
+
const fetchFn = opts._fetch ?? require('./vault.cjs').vaultFetch;
|
|
80
|
+
const manifest = fetchFn(root);
|
|
81
|
+
const warnings = [];
|
|
82
|
+
if (typeof manifest.warning === 'string')
|
|
83
|
+
warnings.push(manifest.warning);
|
|
84
|
+
if (Array.isArray(manifest.warnings)) {
|
|
85
|
+
for (const w of manifest.warnings)
|
|
86
|
+
if (typeof w === 'string' && !warnings.includes(w))
|
|
87
|
+
warnings.push(w);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
source: typeof manifest.source === 'string' ? manifest.source : 'unknown',
|
|
91
|
+
ref: typeof manifest.ref === 'string' ? manifest.ref : null,
|
|
92
|
+
warnings,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function findNewerSchemaPractices(root, opts) {
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
97
|
+
const discover = opts._discover ?? require('./vault-practices.cjs').discoverPracticeFiles;
|
|
98
|
+
const discovery = discover(root);
|
|
99
|
+
const ids = [];
|
|
100
|
+
for (const entry of discovery.degraded ?? []) {
|
|
101
|
+
if (entry.warnings.some((w) => NEWER_SCHEMA_WARNING_RE.test(w)) && !ids.includes(entry.id))
|
|
102
|
+
ids.push(entry.id);
|
|
103
|
+
}
|
|
104
|
+
return ids;
|
|
105
|
+
}
|
|
106
|
+
// ─── vaultUpdate ─────────────────────────────────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Plan — and with `apply` perform — a move of the project's client vault to
|
|
109
|
+
* another stable tag. Never throws: every fault is a result with `ok: false`
|
|
110
|
+
* and a `reason`. Writes exactly one field (`client.vaultRef`) of an existing
|
|
111
|
+
* `.platform-bindings.json`, and only after the whole document still passes
|
|
112
|
+
* `validateBindings`.
|
|
113
|
+
*/
|
|
114
|
+
function vaultUpdate(cwd, opts = {}) {
|
|
115
|
+
const root = resolveRoot(cwd);
|
|
116
|
+
const bindingsPath = node_path_1.default.join(root, BINDINGS_FILE);
|
|
117
|
+
const result = {
|
|
118
|
+
ok: false,
|
|
119
|
+
applied: false,
|
|
120
|
+
reason: null,
|
|
121
|
+
message: '',
|
|
122
|
+
bindingsPath,
|
|
123
|
+
repo: null,
|
|
124
|
+
from: null,
|
|
125
|
+
to: null,
|
|
126
|
+
latest: null,
|
|
127
|
+
available: [],
|
|
128
|
+
fetch: null,
|
|
129
|
+
newerSchemaPractices: [],
|
|
130
|
+
nextStep: null,
|
|
131
|
+
};
|
|
132
|
+
const fail = (reason, message) => ({
|
|
133
|
+
...result,
|
|
134
|
+
ok: false,
|
|
135
|
+
reason,
|
|
136
|
+
message,
|
|
137
|
+
});
|
|
138
|
+
if (!node_fs_1.default.existsSync(bindingsPath)) {
|
|
139
|
+
return fail('no_bindings', `No ${BINDINGS_FILE} in ${root} — run "pp-tools init" first; the vault version lives there.`);
|
|
140
|
+
}
|
|
141
|
+
let bindings;
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(bindingsPath, 'utf-8'));
|
|
144
|
+
if (!isObject(parsed))
|
|
145
|
+
throw new Error('not an object');
|
|
146
|
+
bindings = parsed;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return fail('bindings_unreadable', `${BINDINGS_FILE} could not be read as a JSON object — fix it before updating the vault.`);
|
|
150
|
+
}
|
|
151
|
+
const client = isObject(bindings.client) ? bindings.client : null;
|
|
152
|
+
const repo = client ? usableString(client.vaultRepo) : null;
|
|
153
|
+
if (!client || !repo) {
|
|
154
|
+
return fail('no_client_vault', `${BINDINGS_FILE} names no client vault (client.vaultRepo) — there is no vault version to update.`);
|
|
155
|
+
}
|
|
156
|
+
result.repo = redactCloneTarget(repo);
|
|
157
|
+
result.from = usableString(client.vaultRef);
|
|
158
|
+
let tags;
|
|
159
|
+
try {
|
|
160
|
+
const provider = (0, git_provider_cjs_1.resolveProvider)(root).provider;
|
|
161
|
+
const host = usableString(client.vaultHost) ?? (0, vault_coordinates_cjs_1.resolvePlatformHost)(root).host ?? (0, vault_coordinates_cjs_1.defaultHostForProvider)(provider);
|
|
162
|
+
const target = (0, git_provider_cjs_1.composeCloneTarget)(provider, repo, host);
|
|
163
|
+
tags = (0, compose_bindings_cjs_1.listStableTagsAt)(target.url, { _lsRemote: opts._lsRemote });
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return fail('tag_lookup_failed', `The tags of the vault repo "${redactCloneTarget(repo)}" could not be listed — check your network and git access to it.`);
|
|
167
|
+
}
|
|
168
|
+
tags = [...new Set(tags)].sort((a, b) => (0, semver_compare_cjs_1.compareSemverCore)(b, a));
|
|
169
|
+
result.available = tags.slice(0, exports.AVAILABLE_TAGS_CAP);
|
|
170
|
+
result.latest = tags[0] ?? null;
|
|
171
|
+
if (tags.length === 0) {
|
|
172
|
+
return fail('no_tags', `The vault repo "${redactCloneTarget(repo)}" has no stable vX.Y.Z tag to pin to.`);
|
|
173
|
+
}
|
|
174
|
+
const requested = usableString(opts.target);
|
|
175
|
+
const to = requested ? matchRequestedTag(requested, tags) : tags[0];
|
|
176
|
+
if (!to) {
|
|
177
|
+
return fail('unknown_tag', `The vault repo has no tag "${requested}". Available (newest first): ${result.available.join(', ')}.`);
|
|
178
|
+
}
|
|
179
|
+
result.to = to;
|
|
180
|
+
const fromLabel = result.from ?? 'main (unpinned)';
|
|
181
|
+
if (result.from === to) {
|
|
182
|
+
return { ...result, ok: true, message: `The project is already on vault ${to}.` };
|
|
183
|
+
}
|
|
184
|
+
if (opts.apply !== true) {
|
|
185
|
+
return {
|
|
186
|
+
...result,
|
|
187
|
+
ok: true,
|
|
188
|
+
message: `The project would move from vault ${fromLabel} to ${to}. Nothing was changed.`,
|
|
189
|
+
nextStep: `pp-tools vault update ${to} --yes`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const updated = { ...bindings, client: { ...client, vaultRef: to } };
|
|
193
|
+
const validation = (0, platform_bindings_cjs_1.validateBindings)(updated);
|
|
194
|
+
if (!validation.valid) {
|
|
195
|
+
return fail('invalid_bindings', `The updated ${BINDINGS_FILE} would not be valid: ${validation.errors.join('; ')}. Nothing was changed.`);
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(bindingsPath, JSON.stringify(updated, null, 2) + '\n');
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return fail('write_failed', `${BINDINGS_FILE} could not be written. Nothing was changed.`);
|
|
202
|
+
}
|
|
203
|
+
const applied = {
|
|
204
|
+
...result,
|
|
205
|
+
ok: true,
|
|
206
|
+
applied: true,
|
|
207
|
+
message: `The project moved from vault ${fromLabel} to ${to}.`,
|
|
208
|
+
nextStep: `git add ${BINDINGS_FILE} && git commit -m "chore(vault): update client vault to ${to}"`,
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
applied.fetch = runFetch(root, opts);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
applied.fetch = { source: 'error', ref: null, warnings: ['The vault could not be fetched at the new version — run "pp-tools vault fetch" to retry.'] };
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
applied.newerSchemaPractices = findNewerSchemaPractices(root, opts);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
applied.newerSchemaPractices = [];
|
|
221
|
+
}
|
|
222
|
+
return applied;
|
|
223
|
+
}
|
|
224
|
+
/** Plain-text rendering of a `vaultUpdate` result for the non-`--raw` CLI path. */
|
|
225
|
+
function renderVaultUpdate(r) {
|
|
226
|
+
const lines = [r.message];
|
|
227
|
+
if (r.ok && r.latest && r.to !== r.latest) {
|
|
228
|
+
lines.push(`Newest available: ${r.latest}.`);
|
|
229
|
+
}
|
|
230
|
+
if (r.fetch) {
|
|
231
|
+
lines.push(`Fetched the vault: ${r.fetch.source}${r.fetch.ref ? ` at ${r.fetch.ref}` : ''}.`);
|
|
232
|
+
for (const w of r.fetch.warnings)
|
|
233
|
+
lines.push(w);
|
|
234
|
+
}
|
|
235
|
+
if (r.newerSchemaPractices.length > 0) {
|
|
236
|
+
lines.push(`${r.newerSchemaPractices.length} practice(s) in this vault version were written for a newer pp-core and apply only in degraded form: ${r.newerSchemaPractices.join(', ')}. Update pp-core to apply them fully.`);
|
|
237
|
+
}
|
|
238
|
+
if (r.nextStep) {
|
|
239
|
+
lines.push(r.applied ? `Commit the change: ${r.nextStep}` : `To apply: ${r.nextStep}`);
|
|
240
|
+
}
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
@@ -103,6 +103,8 @@ const PROMOTE_GUARD = Object.freeze({
|
|
|
103
103
|
WRITE_TARGET: 'write_target',
|
|
104
104
|
CONCURRENT_EDIT: 'concurrent_edit',
|
|
105
105
|
TRANSPORT: 'transport',
|
|
106
|
+
// 13-07 (D-14): standards mode — write-back has no practice layer to write to.
|
|
107
|
+
MODE: 'mode',
|
|
106
108
|
});
|
|
107
109
|
// ─── Secret-content scanner (WRB-03 / D-13 / D-14) ──────────────────────────────
|
|
108
110
|
//
|
|
@@ -200,7 +202,7 @@ function boundedToken(v, max = 80) {
|
|
|
200
202
|
}
|
|
201
203
|
// ─── Preflight guard chain ───────────────────────────────────────────────────────
|
|
202
204
|
/**
|
|
203
|
-
* Run the promote preflight guard chain: activation, input, format
|
|
205
|
+
* Run the promote preflight guard chain: activation, mode (13-07), input, format
|
|
204
206
|
* validation (D-03), secret scan (D-13/D-14), target resolution, and the
|
|
205
207
|
* write-target guard (D-05) — strictly in that order, refusing at the
|
|
206
208
|
* first failing stage and never continuing past it. Makes no network
|
|
@@ -211,16 +213,37 @@ function promotePreflight({ cwd, filePath, _vault, _practices }) {
|
|
|
211
213
|
const vault = _vault ?? require('./vault.cjs');
|
|
212
214
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
213
215
|
const practices = _practices ?? require('./vault-practices.cjs');
|
|
214
|
-
// 1. Activation —
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
216
|
+
// 1. Activation — the vault-practices capability must be on. Runs before
|
|
217
|
+
// any filesystem access; rides the two existing default-false config keys
|
|
218
|
+
// (no new config key, no capability surface widening). Phase 15 D-24/D-25:
|
|
219
|
+
// asked of the ONE practices-switch reader, `isPracticesGateActive`, which
|
|
220
|
+
// resolves both keys exactly as the vault-practices gates' `when` does. The
|
|
221
|
+
// `cfgGet` pair it replaces read only the root config, so a workstream-scoped
|
|
222
|
+
// switch refused here while the gates rendered (15-RESEARCH C-1). Read from
|
|
223
|
+
// the REAL vault leaf, not the `_vault` seam, like the mode guard below: a
|
|
224
|
+
// mocked config read must not be able to switch promotion on.
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
226
|
+
const switchOwner = require('./vault.cjs');
|
|
227
|
+
if (!switchOwner.isPracticesGateActive(cwd)) {
|
|
221
228
|
return refusal(PROMOTE_GUARD.ACTIVATION, ERROR_REASON.VAULT_PROMOTE_DISABLED, 'This practice was not promoted: vault write-back is switched off for this project. ' +
|
|
222
229
|
'Set both vault.enabled and vault.practices_enabled to true in .planning/config.json and run the promotion again.');
|
|
223
230
|
}
|
|
231
|
+
// 1b. Mode (13-07, D-14, PARK-03) — in standards mode the vault carries
|
|
232
|
+
// standards documents maintained through its INDEX.md, not practice files,
|
|
233
|
+
// so there is nothing a promotion could write. Refused here, after the
|
|
234
|
+
// activation guard and BEFORE the input guard reads `filePath` or any clone
|
|
235
|
+
// runs, so the preview and `--confirmed` share it (both call this chain
|
|
236
|
+
// first). Read from the REAL vault leaf, not the `_vault` seam: a governance
|
|
237
|
+
// decision must not be switchable by a test mock (the 13-06 precedent). The
|
|
238
|
+
// envelope carries NO mode key — a promote verdict already uses that key for
|
|
239
|
+
// create/update — so the mode is named in the message only.
|
|
240
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
241
|
+
const modeOwner = require('./vault.cjs');
|
|
242
|
+
if (modeOwner.isStandardsModeActive(cwd)) {
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
244
|
+
const { notApplicablePromoteMessage } = require('./vault-mode.cjs');
|
|
245
|
+
return refusal(PROMOTE_GUARD.MODE, ERROR_REASON.VAULT_PROMOTE_NOT_APPLICABLE, notApplicablePromoteMessage());
|
|
246
|
+
}
|
|
224
247
|
// 2. Input — a path to an already-authored practice file (D-02); this
|
|
225
248
|
// module never composes, rewrites, reformats, or extracts a practice.
|
|
226
249
|
if (typeof filePath !== 'string' ||
|
|
@@ -242,6 +265,44 @@ function promotePreflight({ cwd, filePath, _vault, _practices }) {
|
|
|
242
265
|
const cause = parsed.warning.replace(/^.*?\s+skipped:\s*/, '');
|
|
243
266
|
return refusal(PROMOTE_GUARD.VALIDATION, ERROR_REASON.VAULT_PROMOTE_INVALID_PRACTICE, `This practice was not promoted: ${cause} Fix the practice file and run the promotion again.`);
|
|
244
267
|
}
|
|
268
|
+
// 3b. Degraded-parse refusal (CHECK-02e, 05-01).
|
|
269
|
+
//
|
|
270
|
+
// WRITE-SIDE STRICTNESS IS DELIBERATE, and the asymmetry with the read side
|
|
271
|
+
// is the point. On the READ side a practice the engine does not fully
|
|
272
|
+
// understand is retained: it came from the vault, it already governs work,
|
|
273
|
+
// and dropping it would silently un-govern that work (ADR-71924 §5). On the
|
|
274
|
+
// WRITE side the same practice is refused, because promoting it pushes the
|
|
275
|
+
// defect INTO the fleet-wide vault that every developer machine and CI runner
|
|
276
|
+
// fetches — one unreviewed key becomes everyone's degraded warning. The read
|
|
277
|
+
// side tolerates what the write side refuses.
|
|
278
|
+
//
|
|
279
|
+
// Both consumers of DOCUMENTED_FRONTMATTER_KEYS therefore moved in the one
|
|
280
|
+
// change that widened it (T-4-49): had only the reader moved, an unknown key
|
|
281
|
+
// would have quietly become promotable.
|
|
282
|
+
if (parsed.degradedWarnings !== undefined && parsed.degradedWarnings.length > 0) {
|
|
283
|
+
const cause = parsed.degradedWarnings[0].replace(/^.*?\s+degraded:\s*/, '');
|
|
284
|
+
return refusal(PROMOTE_GUARD.VALIDATION, ERROR_REASON.VAULT_PROMOTE_INVALID_PRACTICE, `This practice was not promoted: ${cause} Fix the practice file and run the promotion again.`);
|
|
285
|
+
}
|
|
286
|
+
// 3c. Invalid-check-block refusal (CR-05 / G-3, 05-11).
|
|
287
|
+
//
|
|
288
|
+
// An invalid `check:` block is refused on the write side for the same reason
|
|
289
|
+
// 3b refuses a degraded parse, and the consequence here is worse. The reader
|
|
290
|
+
// reports this defect on a THIRD channel — `practice.checkInvalid` — which is
|
|
291
|
+
// neither `!parsed.ok` nor `degradedWarnings`, so both rungs above miss it;
|
|
292
|
+
// and the folding of `checkInvalid` into `degraded[]` happens inside
|
|
293
|
+
// `discoverPracticeFiles`, which this path never calls.
|
|
294
|
+
//
|
|
295
|
+
// Once such a practice is in the fleet vault the defect is not a warning for
|
|
296
|
+
// the next reader: `verdictForPractice`'s FIRST rung maps `checkInvalid` to a
|
|
297
|
+
// fail-closed FAIL(check_invalid), so it becomes `block: true` at ship:pre and
|
|
298
|
+
// verify:post on every machine that fetches the vault. One typo would stop the
|
|
299
|
+
// whole fleet's builds. The refusal covers ANY invalid block regardless of
|
|
300
|
+
// which assertion kind it declared (D-05-05), because `checkInvalid` is the
|
|
301
|
+
// single fail-closed signal for both.
|
|
302
|
+
if (typeof parsed.practice.checkInvalid === 'string' && parsed.practice.checkInvalid.length > 0) {
|
|
303
|
+
return refusal(PROMOTE_GUARD.VALIDATION, ERROR_REASON.VAULT_PROMOTE_INVALID_PRACTICE, `This practice was not promoted: ${parsed.practice.checkInvalid} ` +
|
|
304
|
+
'Fix the practice file and run the promotion again.');
|
|
305
|
+
}
|
|
245
306
|
// 4. Secret scan — a hit refuses hard, no override (D-14). The matched
|
|
246
307
|
// text is never carried, quoted, echoed, logged, or truncated into the
|
|
247
308
|
// refusal — a truncated secret is still a partial disclosure.
|
|
@@ -402,7 +463,9 @@ function buildExpectToken(localRaw, remoteBaseline) {
|
|
|
402
463
|
// reading) — duplicating the fix a second time is exactly how that class of
|
|
403
464
|
// bug recurs.
|
|
404
465
|
function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
405
|
-
const gitExec = _exec ?? shell_command_projection_cjs_1.
|
|
466
|
+
const gitExec = _exec ?? shell_command_projection_cjs_1.execGitLocal;
|
|
467
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
468
|
+
const vault = _vault ?? require('./vault.cjs');
|
|
406
469
|
// 1. Run the full preflight guard chain; refuse unchanged with no clone.
|
|
407
470
|
const pre = promotePreflight({ cwd, filePath, _vault, _practices });
|
|
408
471
|
if (pre.status !== 'ok')
|
|
@@ -440,7 +503,7 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
440
503
|
cloneDir,
|
|
441
504
|
], { timeout: 120_000 });
|
|
442
505
|
if (cl.exitCode !== 0) {
|
|
443
|
-
const cause =
|
|
506
|
+
const cause = boundedToken((cl.stderr || '').split(/\r?\n/)[0] || 'unknown error');
|
|
444
507
|
return refusal(PROMOTE_GUARD.TRANSPORT, ERROR_REASON.VAULT_PROMOTE_TRANSPORT_FAILED, `This practice was not pushed: git could not reach the vault (${cause}). Check your access to the vault and run the promotion again.`);
|
|
445
508
|
}
|
|
446
509
|
// 3. Capture the baseline BEFORE writing — an id already present in the
|
|
@@ -552,13 +615,13 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
552
615
|
// sentence (plan 03); this note additionally explains why, in the
|
|
553
616
|
// read-only preview an operator reads before confirming.
|
|
554
617
|
if (pre.sharedSuppressed && pre.sharedSuppressedKind === 'provider-default-unavailable') {
|
|
555
|
-
notes.push(`No shared vault is configured for the ${pre.provider} provider — the built-in Pareto shared default applies only to the
|
|
618
|
+
notes.push(`No shared vault is configured for the ${pre.provider} provider — the built-in Pareto shared default applies only to the github provider. Set vault.fallback_repo to enable the shared tier.`);
|
|
556
619
|
}
|
|
557
620
|
// 7 & 8. Pin both sides in the opaque expect token, then return.
|
|
558
621
|
const expect = buildExpectToken(pre.raw, baseline);
|
|
559
622
|
return {
|
|
560
623
|
status: 'preview',
|
|
561
|
-
repo:
|
|
624
|
+
repo: vault.redactCloneTarget(pre.repo),
|
|
562
625
|
ref: pre.ref,
|
|
563
626
|
destination: pre.destination,
|
|
564
627
|
mode,
|
|
@@ -577,25 +640,9 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
577
640
|
}
|
|
578
641
|
}
|
|
579
642
|
// ─── Confirm (write half of the write path) ──────────────────────────────────────
|
|
580
|
-
/**
|
|
581
|
-
* Pack 6 D-07: the display-safe forms used in every verdict and refusal this
|
|
582
|
-
* module EMITS — the one owner in src/vault.cts, lazily required (the
|
|
583
|
-
* module's `_vault` seam stays the fetch seam; redaction is never mocked).
|
|
584
|
-
* Comparisons, the expect token and every git invocation keep the RAW values.
|
|
585
|
-
*/
|
|
586
|
-
function shownRepo(repo) {
|
|
587
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
588
|
-
const v = require('./vault.cjs');
|
|
589
|
-
return v.redactCloneTarget(repo) ?? repo;
|
|
590
|
-
}
|
|
591
|
-
function shownCause(stderr) {
|
|
592
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
593
|
-
const v = require('./vault.cjs');
|
|
594
|
-
return boundedToken(v.redactRemoteText((stderr || '').split(/\r?\n/)[0] || 'unknown error'));
|
|
595
|
-
}
|
|
596
643
|
/** Build a TRANSPORT refusal from a failed git invocation's stderr (first line only). */
|
|
597
644
|
function transportRefusal(res) {
|
|
598
|
-
const cause =
|
|
645
|
+
const cause = boundedToken((res.stderr || '').split(/\r?\n/)[0] || 'unknown error');
|
|
599
646
|
return refusal(PROMOTE_GUARD.TRANSPORT, ERROR_REASON.VAULT_PROMOTE_TRANSPORT_FAILED, `This practice was not pushed: git could not reach the vault (${cause}). Check your access to the vault and run the promotion again.`);
|
|
600
647
|
}
|
|
601
648
|
/**
|
|
@@ -617,7 +664,7 @@ function transportRefusal(res) {
|
|
|
617
664
|
* for free, so no separate `git show`/`cat-file` call is needed.
|
|
618
665
|
*/
|
|
619
666
|
function confirmPromotion({ cwd, filePath, expect, _exec, _vault, _practices }) {
|
|
620
|
-
const gitExec = _exec ?? shell_command_projection_cjs_1.
|
|
667
|
+
const gitExec = _exec ?? shell_command_projection_cjs_1.execGitLocal;
|
|
621
668
|
// 1. Full preflight re-run — never trust the preview. Re-derives repo,
|
|
622
669
|
// ref, host, and destination from scratch; never accepts a caller-
|
|
623
670
|
// supplied id/destination/repo/ref.
|
|
@@ -755,7 +802,7 @@ function confirmPromotion({ cwd, filePath, expect, _exec, _vault, _practices })
|
|
|
755
802
|
}
|
|
756
803
|
return {
|
|
757
804
|
status: 'pushed',
|
|
758
|
-
repo:
|
|
805
|
+
repo: vault.redactCloneTarget(pre.repo),
|
|
759
806
|
ref: pre.ref,
|
|
760
807
|
destination: pre.destination,
|
|
761
808
|
mode,
|