@pareto4/pp-core 1.10.0 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +5 -3
- package/pareto-skills/pp-brand/SKILL.md +1 -1
- package/pp-core/bin/lib/capability-registry.cjs +49 -1
- package/pp-core/bin/lib/compose-bindings.cjs +785 -0
- package/pp-core/bin/lib/git-provider.cjs +279 -0
- package/pp-core/bin/lib/init-contract.cjs +1080 -0
- package/pp-core/bin/lib/init-interactive.cjs +202 -0
- package/pp-core/bin/lib/io.cjs +2 -0
- package/pp-core/bin/lib/org-profile.cjs +696 -0
- package/pp-core/bin/lib/platform-bindings.cjs +268 -64
- package/pp-core/bin/lib/vault-command-router.cjs +133 -2
- package/pp-core/bin/lib/vault-coordinates.cjs +485 -0
- package/pp-core/bin/lib/vault-write-back.cjs +178 -92
- package/pp-core/bin/lib/vault.cjs +158 -81
- package/pp-core/bin/pp-tools.cjs +209 -2
- package/pp-core/workflows/discovery-sprint.md +1 -1
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -1
- package/pp-core/workflows/init.md +137 -76
- package/scripts/gen-platform-bindings-schema.cjs +148 -0
|
@@ -11,9 +11,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11
11
|
* `scanForSecrets`) plus the preflight guard chain (`promotePreflight`)
|
|
12
12
|
* that runs before any git subprocess exists in this module: the
|
|
13
13
|
* activation gate, the Phase 24 format validator reused verbatim, the
|
|
14
|
-
* secret scan, and the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* secret scan, and the `assertVaultWriteTarget` write-target check. That
|
|
15
|
+
* guard's identity is now the resolved `(repo, host)` pair for both
|
|
16
|
+
* tiers (Phase 31, CFG-02/D-10) — it gained a required target-host
|
|
17
|
+
* parameter, and this arm passes one derived from the coordinate
|
|
18
|
+
* resolver rather than a config-only host chain. Every stage emits the
|
|
19
|
+
* uniform refusal contract (D-16) via the `refusal` helper.
|
|
17
20
|
*
|
|
18
21
|
* Deliberately a NEW module (not folded into `src/vault.cts` or
|
|
19
22
|
* `src/vault-command-router.cts`) — the name is load-bearing: it forms a
|
|
@@ -62,7 +65,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
62
65
|
* validator in the write path (D-03). Reused verbatim, never
|
|
63
66
|
* re-derived.
|
|
64
67
|
* - `./vault.cjs` (`cfgGet`, `readBindings`, `assertVaultWriteTarget`) —
|
|
65
|
-
* `assertVaultWriteTarget`
|
|
68
|
+
* this module delegates to `assertVaultWriteTarget` and reimplements
|
|
69
|
+
* none of it (D-05); the guard's signature gained a required target
|
|
70
|
+
* host in Phase 31 (D-10), which this call site passes from the
|
|
71
|
+
* coordinate resolver.
|
|
66
72
|
* - `./io.cjs` (`ERROR_REASON`) — the typed refusal reason codes added
|
|
67
73
|
* alongside this module.
|
|
68
74
|
*
|
|
@@ -81,6 +87,8 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
81
87
|
const node_path_1 = __importDefault(require("node:path"));
|
|
82
88
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
83
89
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
90
|
+
const git_provider_cjs_1 = require("./git-provider.cjs");
|
|
91
|
+
const vault_coordinates_cjs_1 = require("./vault-coordinates.cjs");
|
|
84
92
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
85
93
|
const io = require("./io.cjs");
|
|
86
94
|
const { ERROR_REASON } = io;
|
|
@@ -266,17 +274,76 @@ function promotePreflight({ cwd, filePath, _vault, _practices }) {
|
|
|
266
274
|
'Fix it in .platform-bindings.json and run the promotion again.');
|
|
267
275
|
}
|
|
268
276
|
const targetRef = client && typeof client.vaultRef === 'string' && client.vaultRef.trim() ? client.vaultRef.trim() : 'main';
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
277
|
+
// Resolve the provider ONCE, here in the preflight (PROV-02/T-30-16) — both
|
|
278
|
+
// previewPromotion and confirmPromotion re-run this preflight and read the
|
|
279
|
+
// same `provider` field back off the result, so they cannot compose two
|
|
280
|
+
// different remotes for the same promotion. Moved ahead of target-host
|
|
281
|
+
// resolution (Phase 31, D-10): the host now comes from the coordinate
|
|
282
|
+
// resolver below, which itself takes the provider as an argument.
|
|
283
|
+
const providerResolution = (0, git_provider_cjs_1.resolveProvider)(cwd);
|
|
284
|
+
const provider = providerResolution.provider;
|
|
285
|
+
const providerWarnings = providerResolution.warnings.slice();
|
|
286
|
+
// Target host — from the coordinate resolver (Phase 31, D-10), not a
|
|
287
|
+
// config-only host chain. `client.vaultHost` wins when set; otherwise
|
|
288
|
+
// `.platform-bindings.json`'s `platform.host` (D-09), warned; otherwise
|
|
289
|
+
// the provider's default host. The `.planning/config.json`-only
|
|
290
|
+
// `platform.host` read this preflight used to fall back to last is gone
|
|
291
|
+
// — leaving it in place beside the resolver would not fix D-09, it would
|
|
292
|
+
// add a third disagreeing source of the same value.
|
|
293
|
+
const clientCoord = (0, vault_coordinates_cjs_1.resolveClientCoordinates)(cwd, provider);
|
|
294
|
+
const targetHost = clientCoord.host;
|
|
295
|
+
if (clientCoord.warnings.length > 0)
|
|
296
|
+
providerWarnings.push(...clientCoord.warnings);
|
|
297
|
+
// Shared-tier coordinates — resolved ONCE here in the preflight (D-09,
|
|
298
|
+
// T-31-09), carried forward on the success shape below rather than
|
|
299
|
+
// resolved a second time by `previewPromotion`. This is the same
|
|
300
|
+
// construction already used for `provider` and for `clientCoord` above:
|
|
301
|
+
// both `previewPromotion` and `confirmPromotion` re-run this preflight and
|
|
302
|
+
// read fields back off its result, so a promotion cannot end up resolving
|
|
303
|
+
// two disagreeing answers for its own shared-shadow lookup.
|
|
304
|
+
//
|
|
305
|
+
// `applyBuiltInDefault: false` is load-bearing, not a stylistic default.
|
|
306
|
+
// This path has never applied the built-in shared-vault default — it used
|
|
307
|
+
// to read `vault.fallback_repo` with an empty-string fallback, so a
|
|
308
|
+
// project that never set the key performed no shared-shadow clone at all.
|
|
309
|
+
// Passing `true` here would give every existing gitlab install a shared
|
|
310
|
+
// clone it does not perform today, breaking ROADMAP SC3. The read-path
|
|
311
|
+
// ladder and `assertVaultWriteTarget` both pass `true` because both of
|
|
312
|
+
// them already apply the built-in default today (they always have) — this
|
|
313
|
+
// is the one caller that deliberately withholds it.
|
|
314
|
+
const sharedCoord = (0, vault_coordinates_cjs_1.resolveSharedCoordinates)(cwd, provider, { applyBuiltInDefault: false });
|
|
315
|
+
if (sharedCoord.warnings.length > 0)
|
|
316
|
+
providerWarnings.push(...sharedCoord.warnings);
|
|
317
|
+
// D-13/WR-01: read the resolver's OWN verdict straight off `sharedCoord`
|
|
318
|
+
// — no local re-derivation. Before this fix, `sharedCoord.suppressed` was
|
|
319
|
+
// unconditionally `false` on this call regardless of provider, because
|
|
320
|
+
// withholding the built-in default (`applyBuiltInDefault: false`, above)
|
|
321
|
+
// also disabled the resolver's suppression branch. The resolver now
|
|
322
|
+
// reports `suppressed`/`suppressedKind` correctly even with the built-in
|
|
323
|
+
// default withheld, so this module no longer needs to (and must not)
|
|
324
|
+
// approximate the fact from `sharedRepo`/`provider` itself — that
|
|
325
|
+
// approximation was exactly the WR-01 defect (PROJECT.md: an operator who
|
|
326
|
+
// deliberately disabled the shared tier via an empty `vault.fallback_repo`
|
|
327
|
+
// was shown a note asking them to set the very key they already set,
|
|
328
|
+
// because this module could not tell that apart from "never configured").
|
|
329
|
+
const sharedSuppressed = sharedCoord.suppressed;
|
|
330
|
+
const sharedSuppressedKind = sharedCoord.suppressedKind;
|
|
331
|
+
// Any warning composing the primary clone target for THIS
|
|
332
|
+
// provider/repo/host (an operator-supplied `.git` suffix under azure) is
|
|
333
|
+
// folded in here too, alongside the provider and coordinate warnings
|
|
334
|
+
// already collected above, so a single `notes` push in the preview
|
|
335
|
+
// surfaces all of them.
|
|
336
|
+
if (!(0, git_provider_cjs_1.isLiteralCloneTarget)(targetRepo.trim())) {
|
|
337
|
+
const composedForWarnings = (0, git_provider_cjs_1.composeCloneTarget)(provider, targetRepo, targetHost);
|
|
338
|
+
if (composedForWarnings.warnings.length > 0)
|
|
339
|
+
providerWarnings.push(...composedForWarnings.warnings);
|
|
340
|
+
}
|
|
341
|
+
// 6. Write-target guard (D-05, D-10) — this arm delegates to
|
|
342
|
+
// assertVaultWriteTarget and never re-derives, wraps, relaxes, or
|
|
343
|
+
// reimplements any of its three refusals; it passes the resolved target
|
|
344
|
+
// repo and target host straight through.
|
|
278
345
|
try {
|
|
279
|
-
vault.assertVaultWriteTarget(cwd, targetRepo);
|
|
346
|
+
vault.assertVaultWriteTarget(cwd, targetRepo, targetHost);
|
|
280
347
|
}
|
|
281
348
|
catch (e) {
|
|
282
349
|
const msg = (e instanceof Error ? e.message : String(e)).replace(/^vault write refused:\s*/, '');
|
|
@@ -293,6 +360,13 @@ function promotePreflight({ cwd, filePath, _vault, _practices }) {
|
|
|
293
360
|
ref: targetRef,
|
|
294
361
|
host: targetHost,
|
|
295
362
|
destination,
|
|
363
|
+
provider,
|
|
364
|
+
providerWarnings,
|
|
365
|
+
sharedRepo: sharedCoord.repo,
|
|
366
|
+
sharedRef: sharedCoord.ref,
|
|
367
|
+
sharedHost: sharedCoord.host,
|
|
368
|
+
sharedSuppressed,
|
|
369
|
+
sharedSuppressedKind,
|
|
296
370
|
};
|
|
297
371
|
}
|
|
298
372
|
// ─── Preview (read-only half of the write path) ─────────────────────────────────
|
|
@@ -319,70 +393,31 @@ function buildExpectToken(localRaw, remoteBaseline) {
|
|
|
319
393
|
* never reads or writes the project's own vault read cache (a fresh
|
|
320
394
|
* mkdtemp working copy is used and always removed, even on a thrown error).
|
|
321
395
|
*/
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
* version used `value.includes('://')`, so `--upload-pack=…#x://` — a git
|
|
331
|
-
* option carrying `://` anywhere — passed through untouched and reached
|
|
332
|
-
* `git clone` as a flag.
|
|
333
|
-
* - the scp-style test may not begin with `-`. `[\w.-]+@` accepted a leading
|
|
334
|
-
* hyphen, so `--config=core.sshCommand=…@x` matched too.
|
|
335
|
-
* A leading `-` is additionally refused outright by `flagShapedRepoRefusal`, and
|
|
336
|
-
* every clone call site passes `--` before the target, so all three would have
|
|
337
|
-
* to fail together for an option to reach git.
|
|
338
|
-
*/
|
|
339
|
-
function isLiteralCloneTarget(value) {
|
|
340
|
-
if (value.startsWith('-'))
|
|
341
|
-
return false;
|
|
342
|
-
return (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(value) ||
|
|
343
|
-
/^[\w.][\w.-]*@/.test(value) ||
|
|
344
|
-
value.startsWith('/') ||
|
|
345
|
-
value.startsWith('./') ||
|
|
346
|
-
value.startsWith('../') ||
|
|
347
|
-
value.startsWith('~') ||
|
|
348
|
-
/^[A-Za-z]:[\\/]/.test(value));
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Resolve a configured vault repo value into something `git clone` can reach.
|
|
352
|
-
*
|
|
353
|
-
* `client.vaultRepo` carries two legitimate shapes, and before this resolver
|
|
354
|
-
* existed only the first one worked on the write path:
|
|
355
|
-
*
|
|
356
|
-
* - a literal clone target — an absolute path, a `file://`/`https://`/`ssh://`
|
|
357
|
-
* URL, or scp-style `git@host:group/project` — used verbatim. Every test
|
|
358
|
-
* fixture and every disposable bare-repo harness uses this shape, which is
|
|
359
|
-
* why the gap below stayed invisible to the suite.
|
|
360
|
-
* - a `group/project` slug, which is the ONLY shape
|
|
361
|
-
* `schemas/platform-bindings.schema.json` (`vaultRepo.pattern`) and its own
|
|
362
|
-
* documented example `pareto4-clients/acme/acme-vault` actually permit. A
|
|
363
|
-
* slug handed straight to `git clone` is read as a RELATIVE PATH and fails
|
|
364
|
-
* with "repository does not exist" — so the write-back arm could never
|
|
365
|
-
* reach a real client vault, and the refusal blamed the operator's access.
|
|
366
|
-
*
|
|
367
|
-
* The URL built for a slug mirrors `cloneTier` in `src/vault.cts` byte for byte
|
|
368
|
-
* (`https://<host>/<repo>.git`), so the read and write paths address the same
|
|
369
|
-
* remote instead of disagreeing about what `client.vaultRepo` means.
|
|
370
|
-
*/
|
|
371
|
-
function resolveCloneTarget(repo, host) {
|
|
372
|
-
const value = repo.trim();
|
|
373
|
-
if (isLiteralCloneTarget(value))
|
|
374
|
-
return value;
|
|
375
|
-
return `https://${host}/${value}.git`;
|
|
376
|
-
}
|
|
396
|
+
// `isLiteralCloneTarget` and clone-target composition — the single
|
|
397
|
+
// definitions live in `src/git-provider.cts` (imported above) and are shared
|
|
398
|
+
// with the read path (`src/vault.cts`, 30-02). Do NOT reintroduce a local
|
|
399
|
+
// copy of either here: a read/write divergence in this predicate was defect
|
|
400
|
+
// P-1 in Phase 29 (the write path used `client.vaultRepo` verbatim while the
|
|
401
|
+
// read path always composed a URL, so a slug broke writing and a path broke
|
|
402
|
+
// reading) — duplicating the fix a second time is exactly how that class of
|
|
403
|
+
// bug recurs.
|
|
377
404
|
function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
378
405
|
const gitExec = _exec ?? shell_command_projection_cjs_1.execGit;
|
|
379
406
|
// 1. Run the full preflight guard chain; refuse unchanged with no clone.
|
|
380
407
|
const pre = promotePreflight({ cwd, filePath, _vault, _practices });
|
|
381
408
|
if (pre.status !== 'ok')
|
|
382
409
|
return pre;
|
|
383
|
-
//
|
|
384
|
-
|
|
410
|
+
// No local `./vault.cjs` handle is needed here any more (Phase 31,
|
|
411
|
+
// Task 1): the shared-shadow lookup below reads its coordinates off `pre`
|
|
412
|
+
// instead of calling `vault.cfgGet` directly, so this function no longer
|
|
413
|
+
// touches the vault module at all — `_vault` is still threaded through to
|
|
414
|
+
// `promotePreflight` above for the test-seam injection convention.
|
|
385
415
|
const notes = [];
|
|
416
|
+
// Surface any warning resolving the provider or composing the primary
|
|
417
|
+
// clone target produced (an unrecognized `vault.provider` value, or an
|
|
418
|
+
// operator-supplied `.git` suffix under azure) — visible in the preview an
|
|
419
|
+
// operator reads before confirming, never a silent degrade.
|
|
420
|
+
notes.push(...pre.providerWarnings);
|
|
386
421
|
// The disposable working copy is a fresh mkdtemp directory — never the
|
|
387
422
|
// read cache, which is often a shallow/sparse clone from a different tier
|
|
388
423
|
// and is refreshed independently after a push. Removed on every exit path.
|
|
@@ -391,10 +426,19 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
391
426
|
cloneDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'pp-vault-promote-'));
|
|
392
427
|
// 2. Clone the resolved client repo at the resolved ref — a full
|
|
393
428
|
// single-branch clone (no shallow-depth flag: the confirmed path needs
|
|
394
|
-
// real history to fast-forward against). The target goes through
|
|
395
|
-
//
|
|
396
|
-
// resolved host instead of a relative path
|
|
397
|
-
|
|
429
|
+
// real history to fast-forward against). The target goes through the
|
|
430
|
+
// provider seam's composeCloneTarget so a `group/project` slug becomes a
|
|
431
|
+
// real URL on the resolved host and provider, instead of a relative path
|
|
432
|
+
// git cannot find.
|
|
433
|
+
const cl = gitExec([
|
|
434
|
+
'clone',
|
|
435
|
+
'--branch',
|
|
436
|
+
pre.ref,
|
|
437
|
+
'--single-branch',
|
|
438
|
+
'--',
|
|
439
|
+
(0, git_provider_cjs_1.composeCloneTarget)(pre.provider, pre.repo, pre.host).url,
|
|
440
|
+
cloneDir,
|
|
441
|
+
], { timeout: 120_000 });
|
|
398
442
|
if (cl.exitCode !== 0) {
|
|
399
443
|
const cause = boundedToken((cl.stderr || '').split(/\r?\n/)[0] || 'unknown error');
|
|
400
444
|
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.`);
|
|
@@ -422,28 +466,41 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
422
466
|
else {
|
|
423
467
|
content = pre.raw;
|
|
424
468
|
}
|
|
425
|
-
// 6. Shared-shadow lookup —
|
|
426
|
-
//
|
|
427
|
-
//
|
|
469
|
+
// 6. Shared-shadow lookup — the shared coordinates are resolved exactly
|
|
470
|
+
// ONCE, in the preflight above, with the built-in default explicitly
|
|
471
|
+
// withheld (D-09, T-31-09); this function reads them back off `pre`
|
|
472
|
+
// rather than resolving them a second time. Skipped entirely (flag stays
|
|
473
|
+
// false) when no shared repo resolved, or it equals the client repo —
|
|
474
|
+
// the same skip shape as before, now expressed against the resolved
|
|
475
|
+
// field: a `null` `pre.sharedRepo` is treated the way the empty string
|
|
476
|
+
// used to be treated.
|
|
477
|
+
//
|
|
478
|
+
// The shared vault is addressed on the resolved shared host, in D-03's
|
|
479
|
+
// order: `vault.fallback_host` first, then the authoritative
|
|
480
|
+
// `platform.host` (`.platform-bindings.json` first, `.planning/config.json`
|
|
481
|
+
// as a warned fallback per D-09), then the provider-derived default —
|
|
482
|
+
// never the client's own `client.vaultHost`, mirroring the fallback tier
|
|
483
|
+
// in `src/vault.cts`'s `cloneTier` so a client with a custom vaultHost
|
|
484
|
+
// never sends the shared-shadow lookup to their own host. Composed under
|
|
485
|
+
// the SAME resolved provider as the client clone (deliberate and
|
|
486
|
+
// symmetric with `cloneTier`'s one-provider-per-fetch rule).
|
|
487
|
+
//
|
|
488
|
+
// Per-tier provider selection does NOT arrive with this phase's CFG-01:
|
|
489
|
+
// D-01 answered that with a deliberate, permanent no. One provider per
|
|
490
|
+
// project stands — a client does not straddle two providers — and the
|
|
491
|
+
// fix for the azure-composing-a-gitlab-URL hazard is that the shared
|
|
492
|
+
// coordinates became per-client config, which is exactly what resolving
|
|
493
|
+
// them once in the preflight, above, does.
|
|
428
494
|
let sharedShadow = false;
|
|
429
495
|
let sharedShadowNote = null;
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
const sharedRef = typeof sharedRefRaw === 'string' && sharedRefRaw ? sharedRefRaw : 'main';
|
|
496
|
+
const sharedRepo = pre.sharedRepo;
|
|
497
|
+
const sharedRef = pre.sharedRef;
|
|
498
|
+
const sharedHost = pre.sharedHost;
|
|
434
499
|
if (sharedRepo && sharedRepo !== pre.repo) {
|
|
435
500
|
let sharedDir = null;
|
|
436
501
|
try {
|
|
437
502
|
sharedDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'pp-vault-shared-'));
|
|
438
503
|
// Shallow single-branch: only file presence matters, no history needed.
|
|
439
|
-
// The shared vault is addressed on the PLATFORM host, not the client's
|
|
440
|
-
// — mirroring the fallback tier in src/vault.cts's cloneTier, so a
|
|
441
|
-
// client with a custom vaultHost never sends the shared-shadow lookup
|
|
442
|
-
// to their own host.
|
|
443
|
-
const sharedPlatformHostRaw = vault.cfgGet(cwd, 'platform.host', null);
|
|
444
|
-
const sharedHost = typeof sharedPlatformHostRaw === 'string' && sharedPlatformHostRaw.trim()
|
|
445
|
-
? sharedPlatformHostRaw.trim()
|
|
446
|
-
: 'gitlab.com';
|
|
447
504
|
const sc = gitExec([
|
|
448
505
|
'clone',
|
|
449
506
|
'--depth',
|
|
@@ -452,7 +509,7 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
452
509
|
sharedRef,
|
|
453
510
|
'--single-branch',
|
|
454
511
|
'--',
|
|
455
|
-
|
|
512
|
+
(0, git_provider_cjs_1.composeCloneTarget)(pre.provider, sharedRepo, sharedHost).url,
|
|
456
513
|
sharedDir,
|
|
457
514
|
], { timeout: 120_000 });
|
|
458
515
|
if (sc.exitCode === 0) {
|
|
@@ -476,6 +533,27 @@ function previewPromotion({ cwd, filePath, _exec, _vault, _practices }) {
|
|
|
476
533
|
node_fs_1.default.rmSync(sharedDir, { recursive: true, force: true });
|
|
477
534
|
}
|
|
478
535
|
}
|
|
536
|
+
// Suppression note (D-02, D-11, D-13/WR-01) — pushed ONLY when the
|
|
537
|
+
// shared resolution reports suppression AND the kind is
|
|
538
|
+
// 'provider-default-unavailable'. A gitlab project with no
|
|
539
|
+
// `vault.fallback_repo` resolves `sharedSuppressed: false` (nothing was
|
|
540
|
+
// suppressed — no built-in default was withheld from a provider that
|
|
541
|
+
// would otherwise have gotten one) and emits nothing here, exactly as it
|
|
542
|
+
// did before this phase — this note is SC3-safe by construction.
|
|
543
|
+
//
|
|
544
|
+
// D-13/WR-01: an operator who set `vault.fallback_repo` to an empty
|
|
545
|
+
// string deliberately disabled the shared tier — `sharedSuppressedKind`
|
|
546
|
+
// is `'explicit-opt-out'` for that case, and telling them to set the key
|
|
547
|
+
// they already set is exactly the defect this closes. NO second note is
|
|
548
|
+
// added for the opt-out case: silence is the correct output for a
|
|
549
|
+
// choice the operator already made, and inventing a new note would be
|
|
550
|
+
// new behaviour rather than a fix. D-11's requirement to surface the
|
|
551
|
+
// skipped shared check is already met by the write guard's own refusal
|
|
552
|
+
// sentence (plan 03); this note additionally explains why, in the
|
|
553
|
+
// read-only preview an operator reads before confirming.
|
|
554
|
+
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 gitlab provider. Set vault.fallback_repo to enable the shared tier.`);
|
|
556
|
+
}
|
|
479
557
|
// 7 & 8. Pin both sides in the opaque expect token, then return.
|
|
480
558
|
const expect = buildExpectToken(pre.raw, baseline);
|
|
481
559
|
return {
|
|
@@ -558,7 +636,15 @@ function confirmPromotion({ cwd, filePath, expect, _exec, _vault, _practices })
|
|
|
558
636
|
let cloneDir = null;
|
|
559
637
|
try {
|
|
560
638
|
cloneDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'pp-vault-confirm-'));
|
|
561
|
-
const cl = gitExec([
|
|
639
|
+
const cl = gitExec([
|
|
640
|
+
'clone',
|
|
641
|
+
'--branch',
|
|
642
|
+
pre.ref,
|
|
643
|
+
'--single-branch',
|
|
644
|
+
'--',
|
|
645
|
+
(0, git_provider_cjs_1.composeCloneTarget)(pre.provider, pre.repo, pre.host).url,
|
|
646
|
+
cloneDir,
|
|
647
|
+
], { timeout: 120_000 });
|
|
562
648
|
if (cl.exitCode !== 0)
|
|
563
649
|
return transportRefusal(cl);
|
|
564
650
|
// 6. Fetch the resolved ref from origin, then absorb any unrelated
|