@pareto4/pp-core 1.13.0 → 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 +2 -1
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +10 -13
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +23 -9
- package/agents/pp-vault-reviewer.md +244 -21
- 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 +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-review.md +5 -3
- 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 +396 -82
- 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 +3334 -946
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- 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 +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +13 -11
- 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 +648 -738
- package/pp-core/bin/lib/init-interactive.cjs +76 -4
- 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 +189 -129
- package/pp-core/bin/lib/org-profile.cjs +469 -152
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- 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 +650 -126
- package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
- 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 +139 -72
- package/pp-core/bin/lib/vault-review.cjs +194 -451
- 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 +607 -254
- package/pp-core/bin/lib/vault-update.cjs +9 -33
- package/pp-core/bin/lib/vault-write-back.cjs +70 -42
- package/pp-core/bin/lib/vault.cjs +2734 -920
- 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 -54
- 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 +2 -2
- 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 -25
- 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 +17 -34
- 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 +20 -23
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- 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 -3
- package/pp-core/workflows/code-review.md +10 -1
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- 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 +10 -2
- 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 -20
- 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 +155 -128
- package/pp-core/workflows/execute-plan.md +56 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- 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 +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +282 -22
- package/pp-core/workflows/verify-work.md +12 -14
- 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 -11
- 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 +12 -10
- package/scripts/practice-arithmetic-contract.allow.json +35 -0
- package/scripts/prompt-injection-scan.sh +22 -17
- 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 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- 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 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- 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
|
@@ -43,9 +43,6 @@ const model_catalog_cjs_1 = require("./model-catalog.cjs");
|
|
|
43
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
44
44
|
const federatedConfigModule = require("./federated-config.cjs");
|
|
45
45
|
const { mergeFederatedConfig } = federatedConfigModule;
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
|
-
const capabilityActivationModule = require("./capability-activation.cjs");
|
|
48
|
-
const { activationConfigFilesFor } = capabilityActivationModule;
|
|
49
46
|
// The capability-registry.cjs is generated and lives in the same gsd-core/bin/lib/ output dir.
|
|
50
47
|
// Both config-loader.cjs and capability-registry.cjs land in gsd-core/bin/lib/ at build time.
|
|
51
48
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
@@ -373,16 +370,16 @@ function _readsGlobalDefaults(cwd, ws) {
|
|
|
373
370
|
}
|
|
374
371
|
/**
|
|
375
372
|
* Every config file `loadConfig(cwd, options)` reads, as absolute paths in read
|
|
376
|
-
* order. ONE owner of "which config files does the
|
|
377
|
-
* takes its paths from the same `_configPathsFor`, so a reader that
|
|
378
|
-
* an unreadable config (`
|
|
379
|
-
* files without restating the workstream ladder.
|
|
373
|
+
* order (16-04, D-28). ONE owner of "which config files does the gate read":
|
|
374
|
+
* `loadConfig` takes its paths from the same `_configPathsFor`, so a reader that
|
|
375
|
+
* must refuse an unreadable config (`vaultAdmissionVerdict` in src/vault.cts)
|
|
376
|
+
* can parse exactly these files without restating the workstream ladder.
|
|
380
377
|
*
|
|
381
378
|
* Why a caller needs it: `loadConfig` swallows a parse failure in every one of
|
|
382
379
|
* these files (root → "workstream config stands alone", active → the defaults),
|
|
383
|
-
* so its RESULT cannot say whether
|
|
384
|
-
* nothing. Files that do not exist are still listed; absence is
|
|
385
|
-
* for `loadConfig`, and the caller decides what it means.
|
|
380
|
+
* so its RESULT cannot say whether the gate read the project's config or read
|
|
381
|
+
* nothing (research C-4). Files that do not exist are still listed; absence is
|
|
382
|
+
* not a failure for `loadConfig`, and the caller decides what it means.
|
|
386
383
|
*/
|
|
387
384
|
function configFilesFor(cwd, options = {}) {
|
|
388
385
|
const ws = _activeWorkstreamFor(options);
|
|
@@ -395,97 +392,6 @@ function configFilesFor(cwd, options = {}) {
|
|
|
395
392
|
files.push(_globalDefaultsPath());
|
|
396
393
|
return files;
|
|
397
394
|
}
|
|
398
|
-
/** The error text is bounded; composed text never reaches the cap, but it holds anyway. */
|
|
399
|
-
const UNREADABLE_ERROR_CAP = 200;
|
|
400
|
-
/**
|
|
401
|
-
* Describe why a config file could not be used WITHOUT echoing it. V8's
|
|
402
|
-
* `JSON.parse` messages sometimes quote the input (`Unexpected token '}',
|
|
403
|
-
* "{"a":tru}" is not valid JSON`), so only the position is carried over.
|
|
404
|
-
*/
|
|
405
|
-
function _describeConfigReadFailure(err) {
|
|
406
|
-
const e = err;
|
|
407
|
-
if (e && typeof e.code === 'string' && /^[A-Z][A-Z0-9_]*$/.test(e.code))
|
|
408
|
-
return `cannot be read (${e.code})`;
|
|
409
|
-
const message = e && typeof e.message === 'string' ? e.message : '';
|
|
410
|
-
const lineCol = /\(line (\d+) column (\d+)\)/.exec(message);
|
|
411
|
-
if (lineCol)
|
|
412
|
-
return `not valid JSON (line ${lineCol[1]}, column ${lineCol[2]})`;
|
|
413
|
-
const position = /at position (\d+)/.exec(message);
|
|
414
|
-
if (position)
|
|
415
|
-
return `not valid JSON (at position ${position[1]})`;
|
|
416
|
-
if (/Unexpected end of JSON input/.test(message))
|
|
417
|
-
return 'not valid JSON (the text ends before the JSON does)';
|
|
418
|
-
return 'not valid JSON';
|
|
419
|
-
}
|
|
420
|
-
/** How a refusal names a config file: never an absolute user path. */
|
|
421
|
-
function _configDisplayPath(cwd, abs) {
|
|
422
|
-
const under = (base) => {
|
|
423
|
-
const rel = node_path_1.default.relative(base, abs);
|
|
424
|
-
return rel !== '' && !rel.startsWith('..') && !node_path_1.default.isAbsolute(rel) ? rel.split(node_path_1.default.sep).join('/') : null;
|
|
425
|
-
};
|
|
426
|
-
const inProject = under(cwd);
|
|
427
|
-
if (inProject !== null)
|
|
428
|
-
return inProject;
|
|
429
|
-
const homeOverride = process.env['GSD_HOME'];
|
|
430
|
-
const inHomeOverride = homeOverride ? under(homeOverride) : null;
|
|
431
|
-
if (inHomeOverride !== null)
|
|
432
|
-
return `$GSD_HOME/${inHomeOverride}`;
|
|
433
|
-
const inHome = under(node_os_1.default.homedir());
|
|
434
|
-
if (inHome !== null)
|
|
435
|
-
return `~/${inHome}`;
|
|
436
|
-
return node_path_1.default.basename(abs);
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Every config file the hook resolver would read that EXISTS but cannot be
|
|
440
|
-
* used: unreadable, not JSON, or JSON that is not an object (`loadConfig`
|
|
441
|
-
* reads none of an array's or a null's keys and answers the defaults, the same
|
|
442
|
-
* silent fallback as a parse failure). Empty when every file is usable or
|
|
443
|
-
* absent: a MISSING config is a fresh project and reads as the defaults.
|
|
444
|
-
*
|
|
445
|
-
* The files are the union, in first-seen order, of the loader's list
|
|
446
|
-
* (`configFilesFor`) and the activation resolver's raw levels
|
|
447
|
-
* (`activationConfigFilesFor`, which can name a root config the loader never
|
|
448
|
-
* opens under `GSD_PROJECT`). Reads go through `platformReadSync`, the reader
|
|
449
|
-
* `loadConfig` uses, so a file the loader can read is never called unreadable.
|
|
450
|
-
*
|
|
451
|
-
* This is the ONE owner of "can the project's config be read". Both swallowing
|
|
452
|
-
* readers (`loadConfig`, `resolveConfigKey`) stay tolerant for their many
|
|
453
|
-
* callers; a caller whose answer governs a gate (`loop render-hooks`, the vault
|
|
454
|
-
* load verdict) asks this first and refuses. Never throws.
|
|
455
|
-
*/
|
|
456
|
-
function unreadableConfigFiles(cwd, options = {}) {
|
|
457
|
-
const out = [];
|
|
458
|
-
let files;
|
|
459
|
-
try {
|
|
460
|
-
files = [...new Set([...configFilesFor(cwd, options), ...activationConfigFilesFor(cwd)])];
|
|
461
|
-
}
|
|
462
|
-
catch (err) {
|
|
463
|
-
// An invalid GSD_WORKSTREAM / GSD_PROJECT segment: the config cannot be located, so it cannot be read.
|
|
464
|
-
return [{ file: '.planning/config.json', error: `cannot be located (${err instanceof Error ? err.message : String(err)})`.slice(0, UNREADABLE_ERROR_CAP) }];
|
|
465
|
-
}
|
|
466
|
-
for (const file of files) {
|
|
467
|
-
let error = null;
|
|
468
|
-
try {
|
|
469
|
-
const raw = (0, shell_command_projection_cjs_1.platformReadSync)(file);
|
|
470
|
-
if (raw === null)
|
|
471
|
-
continue;
|
|
472
|
-
const parsed = JSON.parse(raw);
|
|
473
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
474
|
-
error = 'valid JSON but not an object, so none of its settings can be read';
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
catch (err) {
|
|
478
|
-
error = _describeConfigReadFailure(err);
|
|
479
|
-
}
|
|
480
|
-
if (error !== null)
|
|
481
|
-
out.push({ file: _configDisplayPath(cwd, file), error: error.slice(0, UNREADABLE_ERROR_CAP) });
|
|
482
|
-
}
|
|
483
|
-
return out;
|
|
484
|
-
}
|
|
485
|
-
/** One line per unreadable file, `<file>: <error>`, for a refusal message. */
|
|
486
|
-
function describeUnreadableConfig(unreadable) {
|
|
487
|
-
return unreadable.map((u) => `${u.file}: ${u.error}`).join('; ');
|
|
488
|
-
}
|
|
489
395
|
function loadConfig(cwd, options = {}) {
|
|
490
396
|
// When GSD_WORKSTREAM is set, load root config first so workstream config
|
|
491
397
|
// can inherit from it. This prevents users from duplicating model_overrides,
|
|
@@ -839,8 +745,6 @@ function loadConfig(cwd, options = {}) {
|
|
|
839
745
|
module.exports = {
|
|
840
746
|
loadConfig,
|
|
841
747
|
configFilesFor,
|
|
842
|
-
unreadableConfigFiles,
|
|
843
|
-
describeUnreadableConfig,
|
|
844
748
|
isGitIgnored,
|
|
845
749
|
CONFIG_DEFAULTS,
|
|
846
750
|
_getConfigDefault,
|
|
@@ -29,7 +29,7 @@ function isCapabilityConfigKey(keyPath) {
|
|
|
29
29
|
/**
|
|
30
30
|
* The declared `values` of a capability `type: "enum"` config key, or `null`
|
|
31
31
|
* when the key is not a capability key, is not an enum, or carries no value
|
|
32
|
-
* list. 13-05 / D-07: this is the ONE lookup of enum values —
|
|
32
|
+
* list. Vault Assurance 13-05 / D-07: this is the ONE lookup of enum values —
|
|
33
33
|
* `config-set` asks it rather than restating any list, so the registry stays
|
|
34
34
|
* the single source of what an enum key may hold.
|
|
35
35
|
*/
|
|
@@ -29,9 +29,14 @@ const { VALID_PROFILES, getAgentToModelMapForProfile, formatAgentToModelMapAsTab
|
|
|
29
29
|
const configSchema = require("./config-schema.cjs");
|
|
30
30
|
const { VALID_CONFIG_KEYS, isValidConfigKey, capabilityEnumValues } = configSchema;
|
|
31
31
|
const secrets_cjs_1 = require("./secrets.cjs");
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
const capabilityActivation = require("./capability-activation.cjs");
|
|
34
|
+
// GATE-05: reuse the activation resolver's flat-form lookup rather than growing
|
|
35
|
+
// a second copy of the rule here. capability-activation.cjs depends only on
|
|
36
|
+
// planning-workspace.cjs and node builtins, so this import adds no cycle.
|
|
37
|
+
const { _getFlatConfigValue } = capabilityActivation;
|
|
32
38
|
const review_reviewer_selection_cjs_1 = require("./review-reviewer-selection.cjs");
|
|
33
39
|
const configuration_cjs_1 = require("./configuration.cjs");
|
|
34
|
-
const phase_tier_cjs_1 = require("./phase-tier.cjs");
|
|
35
40
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
36
41
|
const CONFIG_KEY_SUGGESTIONS = {
|
|
37
42
|
'workflow.nyquist_validation_enabled': 'workflow.nyquist_validation',
|
|
@@ -465,6 +470,20 @@ function setConfigValues(cwd, entries) {
|
|
|
465
470
|
}
|
|
466
471
|
});
|
|
467
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* The complete `init.probe_status` vocabulary, as `pp-tools config-set`
|
|
475
|
+
* enforces it (07-11 / WR-04).
|
|
476
|
+
*
|
|
477
|
+
* The vocabulary's OWNER is `INIT_PROBE_STATUS` in `init-contract.cts`, which
|
|
478
|
+
* is where the record writers (`--record-probe` / `--defer-probe`) take it
|
|
479
|
+
* from. This is a deliberate copy, not a re-export: `config.cts` is a leaf and
|
|
480
|
+
* `init-contract.cjs` pulls in capability-activation, compose-bindings and
|
|
481
|
+
* org-profile, so importing it here would introduce a leaf-to-adapter edge.
|
|
482
|
+
* What keeps the copy honest is the two-direction parity test in
|
|
483
|
+
* `tests/config.test.cjs` — widening either vocabulary without the other
|
|
484
|
+
* reddens it.
|
|
485
|
+
*/
|
|
486
|
+
const VALID_INIT_PROBE_STATUSES = Object.freeze(['passed', 'failed', 'deferred']);
|
|
468
487
|
/**
|
|
469
488
|
* The ONE membership test every enum guard in `cmdConfigSet` asks (13-REVIEW
|
|
470
489
|
* CR-01). The raw value is parsed before the guards — `true`/`false`/numbers
|
|
@@ -485,14 +504,21 @@ function isStringMember(list, value) {
|
|
|
485
504
|
* (`"FALSE"`, `"no"`, `"off"`) or a number. `capability-activation`'s
|
|
486
505
|
* `_resolveActivationValue` ends in `Boolean(r.value)`, so those strings read as
|
|
487
506
|
* ON — an operator typing `config-set vault.enabled FALSE` to switch the vault
|
|
488
|
-
* off instead ARMED
|
|
489
|
-
*
|
|
507
|
+
* off instead ARMED the two `blocking: true` / `onError: halt` gates
|
|
508
|
+
* `capabilities/vault` hangs on that key, against CLAUDE.md's constraint that a
|
|
509
|
+
* `vault.enabled=false` project stays completely inert.
|
|
490
510
|
*
|
|
491
|
-
* The list, not
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
511
|
+
* The list, not eleven copies of the same `if`: the first eight are every key
|
|
512
|
+
* that gates a blocking hook through that `Boolean()` read, and
|
|
513
|
+
* `tests/config.test.cjs` DERIVES that requirement from the shipped capability
|
|
514
|
+
* registry, so a future blocking gate on a new key reddens until it joins.
|
|
515
|
+
* `vault.required` is the recorded 02-SECURITY UF-01 instance of the same
|
|
516
|
+
* defect: its READ path already refuses a non-boolean (`booleanPolicyValue` in
|
|
517
|
+
* `src/org-profile.cts`), but the CLI door still answered `{"updated": true}` for a
|
|
518
|
+
* value the reader would then discard — the exact "reports success, changes
|
|
519
|
+
* nothing" shape UF-01 was opened for. The last three were already guarded
|
|
520
|
+
* individually and are folded in here unchanged, message included, so there is
|
|
521
|
+
* one mechanism in this file rather than two.
|
|
496
522
|
*
|
|
497
523
|
* REFUSE, do not normalise: `FALSE` plainly means false, but `1`, `enabled` and
|
|
498
524
|
* `on` are guesses, and a config-set that silently rewrites the operator's word
|
|
@@ -501,8 +527,8 @@ function isStringMember(list, value) {
|
|
|
501
527
|
*/
|
|
502
528
|
const BOOLEAN_ONLY_CONFIG_KEYS = Object.freeze([
|
|
503
529
|
'vault.enabled',
|
|
504
|
-
'vault.required',
|
|
505
530
|
'vault.practices_enabled',
|
|
531
|
+
'vault.required',
|
|
506
532
|
'workflow.schema_drift_gate',
|
|
507
533
|
'workflow.nyquist_validation',
|
|
508
534
|
'workflow.security_enforcement',
|
|
@@ -578,10 +604,6 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
578
604
|
if (kp === 'ship.pr_body_sections') {
|
|
579
605
|
validateShipPrBodySections(parsedValue);
|
|
580
606
|
}
|
|
581
|
-
// Phase ceremony tier (pack 9) — enum, src/phase-tier.cts owns the list
|
|
582
|
-
if (kp === 'phase.tier' && !isStringMember(phase_tier_cjs_1.PHASE_TIERS, parsedValue)) {
|
|
583
|
-
error(`Invalid phase.tier '${val}'. Valid values: ${phase_tier_cjs_1.PHASE_TIERS.join(', ')}`, ERROR_REASON.CONFIG_INVALID_VALUE);
|
|
584
|
-
}
|
|
585
607
|
// Human verification checkpoint mode (#3309)
|
|
586
608
|
const VALID_HUMAN_VERIFY_MODES = ['mid-flight', 'end-of-phase'];
|
|
587
609
|
if (kp === 'workflow.human_verify_mode' && !isStringMember(VALID_HUMAN_VERIFY_MODES, parsedValue)) {
|
|
@@ -601,14 +623,27 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
601
623
|
if (kp === 'code_quality.fallow.profile' && !isStringMember(VALID_FALLOW_PROFILES, parsedValue)) {
|
|
602
624
|
error(`Invalid code_quality.fallow.profile '${val}'. Valid values: ${VALID_FALLOW_PROFILES.join(', ')}`);
|
|
603
625
|
}
|
|
626
|
+
// init.probe_status enum validation (07-11 / WR-04). 07-02 registered the
|
|
627
|
+
// four `init.probe_*` keys so config-set would not reject them as unknown;
|
|
628
|
+
// two shipped descriptions then claimed an out-of-vocabulary value "is
|
|
629
|
+
// refused at write time", which was true only of the record writers. This
|
|
630
|
+
// guard makes the claim true on this path too. Membership is tested on the
|
|
631
|
+
// parsed value's TYPE as well (13-REVIEW CR-01): `true`/`false`/numbers are
|
|
632
|
+
// coerced above and a `[`/`{` value is JSON-parsed, so only a plain string
|
|
633
|
+
// can be a member. A VALID value set here is still accepted — that is T-07-09's disclosed
|
|
634
|
+
// accepted residual, not an oversight.
|
|
635
|
+
if (kp === 'init.probe_status' && !isStringMember(VALID_INIT_PROBE_STATUSES, parsedValue)) {
|
|
636
|
+
error(`Invalid init.probe_status '${val}'. Valid values: ${VALID_INIT_PROBE_STATUSES.join(', ')}`, ERROR_REASON.USAGE);
|
|
637
|
+
}
|
|
604
638
|
// plan_review.source_grounding_authority (#22) — enum
|
|
605
639
|
const VALID_SOURCE_GROUNDING_AUTHORITIES = ['grep', 'intel', 'treesitter', 'lsp', 'scip'];
|
|
606
640
|
if (kp === 'plan_review.source_grounding_authority' && !isStringMember(VALID_SOURCE_GROUNDING_AUTHORITIES, parsedValue)) {
|
|
607
641
|
error(`Invalid plan_review.source_grounding_authority '${val}'. Valid values: ${VALID_SOURCE_GROUNDING_AUTHORITIES.join(', ')}`);
|
|
608
642
|
}
|
|
609
|
-
// Capability enum keys, in ONE place (13-05, D-07). Measured
|
|
610
|
-
// `config-set workflow.code_review_depth bogus`
|
|
611
|
-
//
|
|
643
|
+
// Capability enum keys, in ONE place (Vault Assurance 13-05, D-07). Measured
|
|
644
|
+
// 2026-10-09: `config-set workflow.code_review_depth bogus` and
|
|
645
|
+
// `config-set vault.mode whatever` both wrote at exit 0, because no guard
|
|
646
|
+
// consulted the registry's declared `values`. This runs AFTER every
|
|
612
647
|
// hand-coded guard above, so a key that already has its own guard
|
|
613
648
|
// (`workflow.drift_action`) keeps its message byte-identical; the
|
|
614
649
|
// registry, through `capabilityEnumValues`, is the only list consulted
|
|
@@ -618,8 +653,9 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
618
653
|
// The TYPE is checked before membership (13-REVIEW CR-01): a value starting
|
|
619
654
|
// with `[` is JSON-parsed above, and `String(['standards']) === 'standards'`,
|
|
620
655
|
// so a membership-only test let a one-element array through and wrote it —
|
|
621
|
-
// which `
|
|
622
|
-
//
|
|
656
|
+
// which `resolveVaultMode` then refused on every vault command, and which
|
|
657
|
+
// `federated-config` silently reads back as the default for the other enum
|
|
658
|
+
// keys. Every declared capability enum value is a string, so a non-string
|
|
623
659
|
// parse (array, object, number, boolean) can never be a member.
|
|
624
660
|
const enumValues = capabilityEnumValues(kp);
|
|
625
661
|
if (enumValues && !isStringMember(enumValues, parsedValue)) {
|
|
@@ -651,8 +687,7 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
651
687
|
output(maskedResult, raw, `${kp}=${masked}`);
|
|
652
688
|
return;
|
|
653
689
|
}
|
|
654
|
-
//
|
|
655
|
-
// replaced) is display-safe.
|
|
690
|
+
// VAULT-EGRESS-1: the written value stays raw; the echo of it (and of the value it replaced) is display-safe.
|
|
656
691
|
const shownValue = redactConfigCoordinates(kp, parsedValue);
|
|
657
692
|
const shownResult = {
|
|
658
693
|
...setConfigValueResult,
|
|
@@ -662,15 +697,16 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
662
697
|
output(shownResult, raw, `${kp}=${String(shownValue)}`);
|
|
663
698
|
}
|
|
664
699
|
/**
|
|
665
|
-
*
|
|
666
|
-
* for the config
|
|
667
|
-
* `SHARED_REPO_CONFIG_KEY`). `keyPath`
|
|
668
|
-
* at (`''` for the whole config): the
|
|
669
|
-
* carries it, is projected through
|
|
670
|
-
* src/vault.cts); every other value is
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
700
|
+
* VAULT-EGRESS-1 (quick 261005-8py gap closure): the display-safe form of a
|
|
701
|
+
* config value about to be EMITTED, for the one config key that is a remote
|
|
702
|
+
* repo coordinate (the shared-tier repo, `SHARED_REPO_CONFIG_KEY`). `keyPath`
|
|
703
|
+
* is the dotted path the value was read at (`''` for the whole config): the
|
|
704
|
+
* key itself, or any ancestor whose object carries it, is projected through
|
|
705
|
+
* the ONE owner (`redactCloneTarget`, `src/vault.cts`); every other value is
|
|
706
|
+
* returned untouched. The file on disk and every in-process reader keep the
|
|
707
|
+
* raw value — this runs at emission only, the same posture `isSecretKey`
|
|
708
|
+
* masking already takes on this surface. The key is read from its owner
|
|
709
|
+
* module rather than written here (this file names no allowlisted key).
|
|
674
710
|
*/
|
|
675
711
|
function redactConfigCoordinates(keyPath, value) {
|
|
676
712
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -688,7 +724,7 @@ function redactConfigCoordinates(keyPath, value) {
|
|
|
688
724
|
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
689
725
|
return value;
|
|
690
726
|
const copy = JSON.parse(JSON.stringify(value));
|
|
691
|
-
//
|
|
727
|
+
// GATE-05's flat form: the whole dotted key written as one top-level property.
|
|
692
728
|
if (at.length === 0 && typeof copy[SHARED_REPO_CONFIG_KEY] === 'string') {
|
|
693
729
|
copy[SHARED_REPO_CONFIG_KEY] = safe(copy[SHARED_REPO_CONFIG_KEY]);
|
|
694
730
|
}
|
|
@@ -712,23 +748,77 @@ function cmdConfigGet(cwd, keyPath, raw, defaultValue) {
|
|
|
712
748
|
}
|
|
713
749
|
// After the error() guard, keyPath is narrowed to string.
|
|
714
750
|
const kp = keyPath;
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
751
|
+
/**
|
|
752
|
+
* 02-04: the fall-back tail shared by every `config-get` miss.
|
|
753
|
+
*
|
|
754
|
+
* PRECEDENCE — project config > org profile > `--default` > SCHEMA_DEFAULTS >
|
|
755
|
+
* error. This helper is only ever reached AFTER both dotted forms of the
|
|
756
|
+
* project read have missed, so project config outranking the profile is
|
|
757
|
+
* structural here rather than a comparison someone has to keep correct. That
|
|
758
|
+
* ordering is T-02-17's mitigation and is load-bearing: a fleet-wide profile
|
|
759
|
+
* must never be able to overrule what the project itself configured.
|
|
760
|
+
*
|
|
761
|
+
* Wiring this closes the gap `resolveVaultRequired` documents — the vault
|
|
762
|
+
* policy key's workflow line reads it through `config-get`, so before this
|
|
763
|
+
* existed a profile-seeded value reached that resolver's callers but never
|
|
764
|
+
* that workflow. (The key is named in `org-profile.cts`, not here: this file
|
|
765
|
+
* stays free of any allowlisted key literal so the "resolution is generic"
|
|
766
|
+
* check stays a meaningful grep.)
|
|
767
|
+
*
|
|
768
|
+
* NO KEY IS NAMED HERE, deliberately. `readProfileValue` self-gates on
|
|
769
|
+
* ORG_PROFILE_ALLOWLIST and does zero filesystem access for a key that is not
|
|
770
|
+
* on it, so consulting it on every miss is free for the overwhelmingly common
|
|
771
|
+
* non-allowlisted key and the wiring stays generic — adding a key to that
|
|
772
|
+
* allowlist is sufficient to make it readable here.
|
|
773
|
+
*
|
|
774
|
+
* Warnings go to fd 2 only; stdout stays byte-clean so `--raw` still emits a
|
|
775
|
+
* bare scalar (UIBR-02).
|
|
776
|
+
*/
|
|
777
|
+
const missFallback = (missMessage, missReason) => {
|
|
778
|
+
// Call-time lazy require, matching org-profile.cts's own use of the pattern
|
|
779
|
+
// for git-provider.cjs. org-profile.cjs does not import config.cjs, so this
|
|
780
|
+
// adds no cycle — keep it that way.
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
782
|
+
const orgProfile = require('./org-profile.cjs');
|
|
783
|
+
let profileValue;
|
|
784
|
+
try {
|
|
785
|
+
const profile = orgProfile.readProfileValue(cwd, kp);
|
|
786
|
+
profileValue = profile.value;
|
|
787
|
+
for (const w of profile.warnings)
|
|
788
|
+
console.error('Warning: ' + w);
|
|
789
|
+
}
|
|
790
|
+
catch {
|
|
791
|
+
// A malformed or unreadable profile must never take down a config read
|
|
792
|
+
// that would otherwise have resolved a default. Degrade to "no profile".
|
|
793
|
+
profileValue = undefined;
|
|
794
|
+
}
|
|
795
|
+
if (profileValue !== undefined) {
|
|
796
|
+
const shownProfileValue = redactConfigCoordinates(kp, profileValue);
|
|
797
|
+
output(shownProfileValue, raw, String(shownProfileValue));
|
|
798
|
+
return;
|
|
719
799
|
}
|
|
720
|
-
|
|
721
|
-
|
|
800
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
801
|
+
if (hasDefault) {
|
|
722
802
|
output(defaultValue, raw, String(defaultValue));
|
|
723
803
|
return;
|
|
724
804
|
}
|
|
725
|
-
|
|
805
|
+
if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
726
806
|
const def = SCHEMA_DEFAULTS[kp];
|
|
727
807
|
output(def, raw, String(def));
|
|
728
808
|
return;
|
|
729
809
|
}
|
|
810
|
+
error(missMessage, missReason);
|
|
811
|
+
};
|
|
812
|
+
let config = {};
|
|
813
|
+
try {
|
|
814
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
815
|
+
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
816
|
+
}
|
|
730
817
|
else {
|
|
731
|
-
|
|
818
|
+
// No config.json at all: the profile still gets its turn before the
|
|
819
|
+
// defaults, or a config-less project could never see a profile value.
|
|
820
|
+
missFallback('No config.json found at ' + configPath, ERROR_REASON.CONFIG_NO_FILE);
|
|
821
|
+
return;
|
|
732
822
|
}
|
|
733
823
|
}
|
|
734
824
|
catch (err) {
|
|
@@ -736,37 +826,35 @@ function cmdConfigGet(cwd, keyPath, raw, defaultValue) {
|
|
|
736
826
|
throw err;
|
|
737
827
|
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
738
828
|
}
|
|
829
|
+
// GATE-05: the same dotted key may be written flat — one top-level property
|
|
830
|
+
// whose name contains dots — which the nested walk below cannot see. Resolved
|
|
831
|
+
// here as a FALLBACK only: the walk runs first and, when it finds a value,
|
|
832
|
+
// this is never consulted. Nested therefore wins whenever both forms are
|
|
833
|
+
// present, matching capability-activation.cjs's getConfigValueEitherForm so
|
|
834
|
+
// the two read paths a gate uses answer identically. The rejected-segment and
|
|
835
|
+
// hasOwnProperty guards live in that shared helper.
|
|
836
|
+
const flatFallback = _getFlatConfigValue(config, kp);
|
|
739
837
|
// Traverse dot-notation path (e.g., "workflow.auto_advance")
|
|
740
838
|
const keys = kp.split('.');
|
|
741
839
|
let current = config;
|
|
742
840
|
for (const key of keys) {
|
|
743
841
|
if (current === undefined || current === null || typeof current !== 'object') {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
750
|
-
const def = SCHEMA_DEFAULTS[kp];
|
|
751
|
-
output(def, raw, String(def));
|
|
752
|
-
return;
|
|
842
|
+
if (flatFallback.found) {
|
|
843
|
+
current = flatFallback.value;
|
|
844
|
+
break;
|
|
753
845
|
}
|
|
754
|
-
|
|
846
|
+
missFallback(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
847
|
+
return;
|
|
755
848
|
}
|
|
756
849
|
current = current[key];
|
|
757
850
|
}
|
|
851
|
+
// GATE-05: the walk completed but resolved nothing — flat form gets its turn.
|
|
852
|
+
if (current === undefined && flatFallback.found) {
|
|
853
|
+
current = flatFallback.value;
|
|
854
|
+
}
|
|
758
855
|
if (current === undefined) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
output(defaultValue, raw, String(defaultValue));
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
765
|
-
const def = SCHEMA_DEFAULTS[kp];
|
|
766
|
-
output(def, raw, String(def));
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
error(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
856
|
+
missFallback(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
857
|
+
return;
|
|
770
858
|
}
|
|
771
859
|
// Never echo plaintext for sensitive keys via config-get. Plaintext lives
|
|
772
860
|
// in config.json on disk; the CLI surface always shows the masked form.
|
|
@@ -775,7 +863,10 @@ function cmdConfigGet(cwd, keyPath, raw, defaultValue) {
|
|
|
775
863
|
output(masked, raw, masked);
|
|
776
864
|
return;
|
|
777
865
|
}
|
|
778
|
-
//
|
|
866
|
+
// Pre-existing behaviour, now explicit: an object-valued key under --raw
|
|
867
|
+
// stringifies to '[object Object]'. Replacing the trailing `error()` (typed
|
|
868
|
+
// `never`) with the shared miss-tail cost TypeScript the narrowing that used
|
|
869
|
+
// to keep this quiet; the emitted bytes are unchanged.
|
|
779
870
|
const shown = redactConfigCoordinates(kp, current);
|
|
780
871
|
output(shown, raw, String(shown));
|
|
781
872
|
}
|
|
@@ -880,6 +971,7 @@ function cmdMigrateConfig(cwd, raw) {
|
|
|
880
971
|
}
|
|
881
972
|
module.exports = {
|
|
882
973
|
VALID_CONFIG_KEYS,
|
|
974
|
+
VALID_INIT_PROBE_STATUSES,
|
|
883
975
|
BOOLEAN_ONLY_CONFIG_KEYS,
|
|
884
976
|
cmdConfigEnsureSection,
|
|
885
977
|
cmdConfigSet,
|
|
@@ -890,7 +982,7 @@ module.exports = {
|
|
|
890
982
|
cmdMigrateConfig,
|
|
891
983
|
// Exported for programmatic use by capability-writer and tests
|
|
892
984
|
setConfigValue,
|
|
893
|
-
// Pack 6 D-07: `state load` emits the whole config through the same projection.
|
|
894
|
-
redactConfigCoordinates,
|
|
895
985
|
setConfigValues,
|
|
986
|
+
// VAULT-EGRESS-1: `state load` emits the whole config through the same projection.
|
|
987
|
+
redactConfigCoordinates,
|
|
896
988
|
};
|
|
@@ -18,10 +18,10 @@ const DISCRETION_HEADINGS = new Set([
|
|
|
18
18
|
'claude discretion',
|
|
19
19
|
]);
|
|
20
20
|
const NON_TRACKABLE_TAGS = new Set(['informational', 'folded', 'deferred']);
|
|
21
|
-
// ── The decision-bullet grammar
|
|
21
|
+
// ── The decision-bullet grammar (260910-8ea) ────────────────────────────────
|
|
22
22
|
//
|
|
23
23
|
// Why a grammar rather than one regex: this repository writes THREE bullet
|
|
24
|
-
// forms, and
|
|
24
|
+
// forms, and until 260910-8ea only the first was readable. The two others were
|
|
25
25
|
// not exotic — they are what `discuss-phase` emits and what hand-authored
|
|
26
26
|
// CONTEXT.md files use — so a parser that reads only form A hands a blocking
|
|
27
27
|
// coverage gate an empty decision set and lets it report a clean pass. That is
|
|
@@ -111,7 +111,7 @@ function extractDecisionsBlock(content) {
|
|
|
111
111
|
*
|
|
112
112
|
* The second channel exists because "the block was empty" and "the parser
|
|
113
113
|
* could not read any of it" are opposite situations that a coverage gate used
|
|
114
|
-
* to report identically — as a clean pass on a total of zero.
|
|
114
|
+
* to report identically — as a clean pass on a total of zero (260910-8ea).
|
|
115
115
|
*/
|
|
116
116
|
function parseDecisionsReport(content) {
|
|
117
117
|
if (!content || typeof content !== 'string')
|
|
@@ -170,7 +170,7 @@ function parseDecisionsReport(content) {
|
|
|
170
170
|
// be silently dropped — a narrowed trackable set lets a blocking coverage gate
|
|
171
171
|
// report a false pass. Surface it loudly instead.
|
|
172
172
|
//
|
|
173
|
-
//
|
|
173
|
+
// 260910-8ea: the miss is now recorded as DATA as well, so a gate can fail
|
|
174
174
|
// on it rather than warning past it. The `console.warn` stays: the stderr
|
|
175
175
|
// signal is what a human running the command by hand sees, and it is pinned
|
|
176
176
|
// by the #1343 regression tests.
|