@deftai/directive-core 0.107.0 → 0.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/architecture/sor-preflight.js +2 -0
- package/dist/cache/operations.js +1 -1
- package/dist/check/cached-orchestrator.js +28 -3
- package/dist/check/gate-lists.d.ts +14 -0
- package/dist/check/gate-lists.js +26 -3
- package/dist/check/named-cause.js +18 -3
- package/dist/check/session-completed-ac.d.ts +1 -1
- package/dist/check/session-completed-ac.js +1 -1
- package/dist/codebase/provider.js +7 -1
- package/dist/consumer-check-contract/evaluate.d.ts +47 -0
- package/dist/consumer-check-contract/evaluate.js +181 -15
- package/dist/delivery-attempt/evaluate.d.ts +9 -1
- package/dist/delivery-attempt/evaluate.js +69 -0
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/delivery-attempt/index.d.ts +1 -1
- package/dist/delivery-attempt/index.js +1 -1
- package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
- package/dist/deposit/live-procedure-exclusions.js +110 -0
- package/dist/deposit/live-procedure-targets.d.ts +45 -0
- package/dist/deposit/live-procedure-targets.js +274 -0
- package/dist/deposit/python-free.d.ts +6 -0
- package/dist/deposit/python-free.js +15 -0
- package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
- package/dist/deposit/rewrite-deposit-links.js +148 -0
- package/dist/deposit/run-stage-content-pack.d.ts +2 -0
- package/dist/deposit/run-stage-content-pack.js +3 -0
- package/dist/deposit/stage-content-pack.d.ts +17 -0
- package/dist/deposit/stage-content-pack.js +91 -0
- package/dist/design-critique/citation-grammar.d.ts +60 -0
- package/dist/design-critique/citation-grammar.js +303 -0
- package/dist/design-critique/completed-arc-record.d.ts +62 -0
- package/dist/design-critique/completed-arc-record.js +277 -0
- package/dist/freshness/bind.d.ts +32 -3
- package/dist/freshness/bind.js +58 -12
- package/dist/hooks/classify/host-session-identity.d.ts +89 -0
- package/dist/hooks/classify/host-session-identity.js +467 -0
- package/dist/hooks/classify/index.d.ts +3 -2
- package/dist/hooks/classify/index.js +3 -2
- package/dist/hooks/classify/paths.d.ts +9 -0
- package/dist/hooks/classify/paths.js +39 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +94 -1
- package/dist/hooks/dest-form.d.ts +20 -1
- package/dist/hooks/dest-form.js +158 -21
- package/dist/hooks/dispatcher.d.ts +58 -3
- package/dist/hooks/dispatcher.js +762 -73
- package/dist/hooks/fixtures/cases.d.ts +22 -2
- package/dist/hooks/fixtures/cases.js +228 -0
- package/dist/hooks/git-destructive-log.d.ts +32 -0
- package/dist/hooks/git-destructive-log.js +46 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/hooks/shell-write-targets.d.ts +10 -0
- package/dist/hooks/shell-write-targets.js +274 -0
- package/dist/hooks/tools.d.ts +20 -2
- package/dist/hooks/tools.js +30 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/gitignore.d.ts +7 -0
- package/dist/init-deposit/gitignore.js +24 -0
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/init-deposit/init-deposit.js +3 -0
- package/dist/init-deposit/refresh.js +7 -0
- package/dist/init-deposit/runtime-writers.d.ts +14 -0
- package/dist/init-deposit/runtime-writers.js +33 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/issue-ingest.d.ts +6 -1
- package/dist/intake/issue-ingest.js +83 -4
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/intake/reconcile-issues.js +13 -13
- package/dist/lifecycle/brief-envelope.d.ts +25 -0
- package/dist/lifecycle/brief-envelope.js +42 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/evaluate.js +14 -5
- package/dist/literal-acceptance/index.d.ts +1 -1
- package/dist/literal-acceptance/index.js +1 -1
- package/dist/literal-acceptance/run.d.ts +2 -0
- package/dist/literal-acceptance/run.js +19 -1
- package/dist/orphan-active/candidate-scope.d.ts +53 -0
- package/dist/orphan-active/candidate-scope.js +157 -0
- package/dist/orphan-active/evaluate.d.ts +52 -0
- package/dist/orphan-active/evaluate.js +250 -128
- package/dist/orphan-active/index.d.ts +2 -0
- package/dist/orphan-active/index.js +2 -0
- package/dist/orphan-active/issue-state.d.ts +121 -0
- package/dist/orphan-active/issue-state.js +278 -0
- package/dist/platform/cursor-managed-runtime.d.ts +95 -0
- package/dist/platform/cursor-managed-runtime.js +241 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +23 -1
- package/dist/platform/platform-capabilities.js +55 -15
- package/dist/policy/ceremony-dial.js +5 -9
- package/dist/policy/host-hooks.js +4 -9
- package/dist/policy/merge-approval-head.js +7 -6
- package/dist/policy/org-force-on-migration.js +5 -10
- package/dist/policy/plan-extensions.d.ts +14 -2
- package/dist/policy/plan-extensions.js +24 -3
- package/dist/policy/product-signal.js +5 -10
- package/dist/policy/require-human-merge.js +5 -9
- package/dist/policy/resolve.js +77 -16
- package/dist/policy/value-feedback.js +9 -18
- package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
- package/dist/pr-closeout-attestable/evaluate.js +306 -0
- package/dist/pr-closeout-attestable/index.d.ts +2 -0
- package/dist/pr-closeout-attestable/index.js +2 -0
- package/dist/pr-closing-keywords/gh.js +32 -9
- package/dist/pr-closing-keywords/main.d.ts +1 -0
- package/dist/pr-closing-keywords/main.js +68 -3
- package/dist/pr-closing-keywords/types.d.ts +2 -0
- package/dist/pr-merge-readiness/gh.js +32 -9
- package/dist/pr-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +42 -11
- package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
- package/dist/pr-wait-mergeable/cascade.js +21 -1
- package/dist/pr-wait-mergeable/types.d.ts +2 -0
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +14 -3
- package/dist/preflight/evaluate.d.ts +15 -0
- package/dist/preflight/evaluate.js +29 -3
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
- package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
- package/dist/product-first-done-gate/evaluate.js +23 -9
- package/dist/product-first-done-gate/types.js +2 -0
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/consumer-hard-stops.d.ts +48 -0
- package/dist/release/consumer-hard-stops.js +140 -0
- package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
- package/dist/release/consumer-readiness-disclosure.js +51 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/issue-state-fetch.d.ts +5 -3
- package/dist/release/issue-state-fetch.js +86 -21
- package/dist/release/native-steps.js +1 -0
- package/dist/release/pipeline.js +74 -0
- package/dist/release/run-consumer-readiness.d.ts +15 -0
- package/dist/release/run-consumer-readiness.js +24 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/render/constants.d.ts +4 -0
- package/dist/render/constants.js +11 -8
- package/dist/render/export-spec.js +31 -5
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/prd-render.d.ts +4 -1
- package/dist/render/prd-render.js +82 -23
- package/dist/render/project-render.js +14 -7
- package/dist/render/scope-outlook.d.ts +2 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.js +2 -2
- package/dist/resolution/package-manager.d.ts +33 -4
- package/dist/resolution/package-manager.js +210 -9
- package/dist/review-monitor/constants.d.ts +7 -0
- package/dist/review-monitor/constants.js +7 -0
- package/dist/review-monitor/github-lease.d.ts +98 -1
- package/dist/review-monitor/github-lease.js +186 -4
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/run-summary/types.d.ts +2 -2
- package/dist/scm/build-command.d.ts +2 -2
- package/dist/scm/build-command.js +2 -2
- package/dist/scm/call-shape.d.ts +25 -0
- package/dist/scm/call-shape.js +59 -0
- package/dist/scm/call.d.ts +7 -4
- package/dist/scm/call.js +48 -9
- package/dist/scm/design-critique-chip.d.ts +1 -0
- package/dist/scm/design-critique-chip.js +22 -6
- package/dist/scm/gh-rest.d.ts +34 -13
- package/dist/scm/gh-rest.js +168 -15
- package/dist/scm/index.d.ts +2 -0
- package/dist/scm/index.js +2 -0
- package/dist/scm/readiness.d.ts +2 -0
- package/dist/scm/readiness.js +28 -4
- package/dist/scm/spawn-status.d.ts +33 -0
- package/dist/scm/spawn-status.js +53 -0
- package/dist/scope/acceptance-evidence.d.ts +1 -1
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/scope/transition.js +4 -14
- package/dist/session/ac-pass-banking.d.ts +2 -2
- package/dist/session/ac-pass-banking.js +2 -2
- package/dist/session/git.d.ts +24 -0
- package/dist/session/git.js +138 -2
- package/dist/session/host-session-owner.d.ts +53 -0
- package/dist/session/host-session-owner.js +89 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/occupancy.d.ts +259 -13
- package/dist/session/occupancy.js +877 -33
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +5 -0
- package/dist/session/session-ready.d.ts +5 -1
- package/dist/session/session-ready.js +98 -8
- package/dist/session/session-start.d.ts +5 -1
- package/dist/session/session-start.js +32 -17
- package/dist/session/toolchain-preflight.d.ts +13 -3
- package/dist/session/toolchain-preflight.js +93 -18
- package/dist/session/verify-ac-session-cache.d.ts +2 -2
- package/dist/session/verify-ac-session-cache.js +2 -2
- package/dist/session/verify-session-ritual.d.ts +11 -0
- package/dist/session/verify-session-ritual.js +60 -15
- package/dist/slice/constants.d.ts +1 -1
- package/dist/spec-authority/constants.d.ts +10 -2
- package/dist/spec-authority/constants.js +53 -8
- package/dist/spec-authority/resolver.d.ts +3 -0
- package/dist/spec-authority/resolver.js +55 -7
- package/dist/subprocess/max-buffer.d.ts +14 -0
- package/dist/subprocess/max-buffer.js +15 -0
- package/dist/swarm/complete-cohort.d.ts +2 -0
- package/dist/swarm/complete-cohort.js +21 -13
- package/dist/swarm/index.d.ts +1 -0
- package/dist/swarm/index.js +1 -0
- package/dist/swarm/launch-cli.js +53 -33
- package/dist/swarm/launch.d.ts +8 -0
- package/dist/swarm/launch.js +63 -43
- package/dist/swarm/pre-dispatch-cli.js +2 -1
- package/dist/swarm/pre-dispatch.js +10 -1
- package/dist/swarm/subagent-status-dir.d.ts +27 -0
- package/dist/swarm/subagent-status-dir.js +42 -0
- package/dist/swarm/worktrees.d.ts +17 -3
- package/dist/swarm/worktrees.js +48 -4
- package/dist/triage/evaluate/worktrees.js +153 -6
- package/dist/triage/scope/mutations-core.d.ts +2 -6
- package/dist/triage/scope/mutations-core.js +23 -55
- package/dist/triage/scope-drift/add-ignore.js +5 -39
- package/dist/triage/subscribe/index.d.ts +2 -6
- package/dist/triage/subscribe/index.js +15 -65
- package/dist/triage/welcome/writers.js +15 -74
- package/dist/umbrella-current-shape/index.d.ts +51 -3
- package/dist/umbrella-current-shape/index.js +106 -18
- package/dist/validate-content/deposit-required.d.ts +39 -0
- package/dist/validate-content/deposit-required.js +147 -0
- package/dist/validate-content/index.d.ts +1 -0
- package/dist/validate-content/index.js +1 -0
- package/dist/validate-content/validate-links.d.ts +2 -3
- package/dist/validate-content/validate-links.js +29 -2
- package/dist/vbrief-activate/activate.d.ts +7 -2
- package/dist/vbrief-activate/activate.js +26 -13
- package/dist/vbrief-build/index.d.ts +2 -1
- package/dist/vbrief-build/index.js +2 -1
- package/dist/vbrief-build/parity-scenarios.js +5 -5
- package/dist/vbrief-build/project-definition-io.d.ts +63 -3
- package/dist/vbrief-build/project-definition-io.js +357 -31
- package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
- package/dist/vbrief-build/project-definition-mutation.js +43 -0
- package/dist/vbrief-validate/conformance.d.ts +1 -0
- package/dist/vbrief-validate/conformance.js +85 -8
- package/dist/vbrief-validate/main.js +2 -0
- package/dist/vbrief-validate/precutover.js +5 -10
- package/dist/verify-ac/clauses.d.ts +62 -2
- package/dist/verify-ac/clauses.js +183 -111
- package/dist/verify-ac/evaluate.d.ts +9 -0
- package/dist/verify-ac/evaluate.js +32 -9
- package/dist/verify-ac/index.d.ts +1 -1
- package/dist/verify-ac/index.js +1 -1
- package/dist/verify-env/node-runtime.d.ts +8 -4
- package/dist/verify-env/node-runtime.js +9 -6
- package/dist/verify-env/toolchain-check.d.ts +35 -3
- package/dist/verify-env/toolchain-check.js +155 -36
- package/dist/verify-source/deposit-closure.d.ts +23 -0
- package/dist/verify-source/deposit-closure.js +162 -0
- package/dist/verify-source/index.d.ts +2 -0
- package/dist/verify-source/index.js +2 -0
- package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/dist/verify-source/semantic-single-source.d.ts +36 -0
- package/dist/verify-source/semantic-single-source.js +349 -0
- package/package.json +7 -3
package/dist/release/pipeline.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { assertProjectionContained } from "../fs/projection-containment.js";
|
|
4
|
+
import { restIssueListOpenInventory } from "../scm/gh-rest.js";
|
|
4
5
|
import { readCoverageTotalsFromReport } from "../vitest-runner/coverage-debt.js";
|
|
5
6
|
import { buildCoverageDebtIssueDraft, classifyStep5FailureWithFreshness, evaluateAutoHatch, formatAutoHatchBanner, parseExitCodeFromReason, reasonLooksLikeTimeout, } from "./auto-hatch.js";
|
|
6
7
|
import { prependUpgradeBanner, promoteChangelog, sectionForVersion } from "./changelog.js";
|
|
8
|
+
import { emitCliDriftReportBestEffort, shouldSkipRegistryPoll, } from "./cli-drift-report.js";
|
|
7
9
|
import { assertTagPushClosedVerb } from "./closed-verb-gate.js";
|
|
8
10
|
import { EXIT_CONFIG_ERROR, EXIT_OK, EXIT_VIOLATION, RELEASE_ARTIFACTS, RELEASE_CHECK_TIMEOUT_MINUTES, TOTAL_STEPS, VERIFY_DRAFT_INTERVAL_SECONDS, VERIFY_DRAFT_MAX_ATTEMPTS, } from "./constants.js";
|
|
11
|
+
import { formatConsumerReadinessDisclosure } from "./consumer-readiness-disclosure.js";
|
|
9
12
|
import { createCoverageDebtIssue, probeOpenCoverageDebtLedger } from "./coverage-debt-ledger.js";
|
|
10
13
|
import { checkTagAvailable, createGithubRelease, readTextFile, verifyReleaseDraft } from "./gh.js";
|
|
11
14
|
import { checkGitClean, commitReleaseArtifacts, createTag, currentBranch, pushRelease, releaseCommitSubject, runGit, } from "./git.js";
|
|
12
15
|
import { checkVbriefLifecycleSyncNative, refreshRoadmapNative, runBuildNative, } from "./native-steps.js";
|
|
13
16
|
import { todayIso } from "./paths.js";
|
|
14
17
|
import { runReleaseCheck } from "./preflight.js";
|
|
18
|
+
import { evaluateReleaseConsumerReadiness, issuesFromInventory } from "./run-consumer-readiness.js";
|
|
15
19
|
import { formatSkipCiIncidentWarning } from "./skip-ci-incident.js";
|
|
16
20
|
import { evaluateSuiteStamp, writeSuiteStamp } from "./suite-stamp.js";
|
|
17
21
|
import { isPrereleaseTag } from "./version.js";
|
|
@@ -289,6 +293,58 @@ export function runPipeline(config, seams = {}) {
|
|
|
289
293
|
}
|
|
290
294
|
}
|
|
291
295
|
}
|
|
296
|
+
// #3900: check 4 fail-closed (title/label census); checks 5-6 disclosure never block.
|
|
297
|
+
{
|
|
298
|
+
const crLabel = "Consumer-readiness census";
|
|
299
|
+
let changelogText = "";
|
|
300
|
+
try {
|
|
301
|
+
changelogText = readFile(changelogPath);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
changelogText = "";
|
|
305
|
+
}
|
|
306
|
+
const disclosure = formatConsumerReadinessDisclosure(changelogText);
|
|
307
|
+
if (config.dryRun) {
|
|
308
|
+
emit(5, crLabel, "DRYRUN (would enumerate open privileged hard-stop labels; disclosure does not block)");
|
|
309
|
+
process.stderr.write(disclosure.text + "\n");
|
|
310
|
+
}
|
|
311
|
+
else if (seams.consumerHardStops) {
|
|
312
|
+
const [ok, msg] = seams.consumerHardStops(config.repo, projectRoot);
|
|
313
|
+
if (!ok) {
|
|
314
|
+
emit(5, crLabel, "FAIL (" + msg + ")");
|
|
315
|
+
process.stderr.write(disclosure.text + "\n");
|
|
316
|
+
return EXIT_VIOLATION;
|
|
317
|
+
}
|
|
318
|
+
emit(5, crLabel, "OK (" + msg + ")");
|
|
319
|
+
process.stderr.write(disclosure.text + "\n");
|
|
320
|
+
}
|
|
321
|
+
else if (seams.runCi !== undefined || seams.spawnText !== undefined) {
|
|
322
|
+
emit(5, crLabel, "SKIP (injected seam; run task verify:consumer-hard-stops at a real cut)");
|
|
323
|
+
process.stderr.write(disclosure.text + "\n");
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
try {
|
|
327
|
+
const rows = restIssueListOpenInventory(config.repo);
|
|
328
|
+
const result = evaluateReleaseConsumerReadiness({
|
|
329
|
+
changelogText,
|
|
330
|
+
issues: issuesFromInventory(rows),
|
|
331
|
+
});
|
|
332
|
+
process.stderr.write(result.disclosure.text + "\n");
|
|
333
|
+
if (result.hardStops.code !== 0) {
|
|
334
|
+
emit(5, crLabel, "FAIL (" + result.hardStops.message + ")");
|
|
335
|
+
return result.hardStops.code === 2 ? EXIT_CONFIG_ERROR : EXIT_VIOLATION;
|
|
336
|
+
}
|
|
337
|
+
emit(5, crLabel, "OK (" + result.hardStops.message + ")");
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
341
|
+
emit(5, crLabel, "FAIL (could not list open issues. Recovery: check gh auth and REST quota. " +
|
|
342
|
+
reason +
|
|
343
|
+
")");
|
|
344
|
+
return EXIT_CONFIG_ERROR;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
292
348
|
// Step 6: CHANGELOG promotion.
|
|
293
349
|
label = "CHANGELOG promotion";
|
|
294
350
|
if (!fileExists(changelogPath)) {
|
|
@@ -472,6 +528,24 @@ export function runPipeline(config, seams = {}) {
|
|
|
472
528
|
}
|
|
473
529
|
}
|
|
474
530
|
process.stderr.write(`Release v${version} pipeline complete (dry_run=${config.dryRun ? "True" : "False"}, skip_tag=${config.skipTag ? "True" : "False"}, skip_release=${config.skipRelease ? "True" : "False"}).\n`);
|
|
531
|
+
// #3753 — report-only. Never runs npm i -g. Never fails the cut.
|
|
532
|
+
// Single-shot registry probe (no wait): Step 13 returns before npm-publish.yml
|
|
533
|
+
// is green; Phase 7 of the release skill owns the bounded wait.
|
|
534
|
+
emitCliDriftReportBestEffort(version, {
|
|
535
|
+
skipRegistryPoll: shouldSkipRegistryPoll(config),
|
|
536
|
+
pollTimeoutMs: 0,
|
|
537
|
+
checkActiveCli: seams.checkActiveCli,
|
|
538
|
+
viewPackage: seams.viewWorkspacePackage
|
|
539
|
+
? (name, ver) => {
|
|
540
|
+
const probe = seams.viewWorkspacePackage?.(name, ver);
|
|
541
|
+
return {
|
|
542
|
+
name: name,
|
|
543
|
+
visible: probe?.visible ?? false,
|
|
544
|
+
version: probe?.version ?? null,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
: undefined,
|
|
548
|
+
});
|
|
475
549
|
return EXIT_OK;
|
|
476
550
|
}
|
|
477
551
|
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release pre-flight consumer-readiness: check 4 fail-closed, checks 5-6 disclosure (#3900).
|
|
3
|
+
*/
|
|
4
|
+
import { evaluateConsumerHardStopCensus, type HardStopIssue } from "./consumer-hard-stops.js";
|
|
5
|
+
import { formatConsumerReadinessDisclosure } from "./consumer-readiness-disclosure.js";
|
|
6
|
+
export interface ConsumerReadinessInput {
|
|
7
|
+
readonly changelogText: string;
|
|
8
|
+
readonly issues: readonly HardStopIssue[];
|
|
9
|
+
}
|
|
10
|
+
export declare function evaluateReleaseConsumerReadiness(input: ConsumerReadinessInput): {
|
|
11
|
+
readonly hardStops: ReturnType<typeof evaluateConsumerHardStopCensus>;
|
|
12
|
+
readonly disclosure: ReturnType<typeof formatConsumerReadinessDisclosure>;
|
|
13
|
+
};
|
|
14
|
+
export declare function issuesFromInventory(rows: readonly Record<string, unknown>[]): HardStopIssue[];
|
|
15
|
+
//# sourceMappingURL=run-consumer-readiness.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release pre-flight consumer-readiness: check 4 fail-closed, checks 5-6 disclosure (#3900).
|
|
3
|
+
*/
|
|
4
|
+
import { evaluateConsumerHardStopCensus, issueFromInventoryRow, parseClosesSet, } from "./consumer-hard-stops.js";
|
|
5
|
+
import { formatConsumerReadinessDisclosure } from "./consumer-readiness-disclosure.js";
|
|
6
|
+
export function evaluateReleaseConsumerReadiness(input) {
|
|
7
|
+
return {
|
|
8
|
+
hardStops: evaluateConsumerHardStopCensus({
|
|
9
|
+
issues: input.issues,
|
|
10
|
+
closesSet: parseClosesSet(input.changelogText),
|
|
11
|
+
}),
|
|
12
|
+
disclosure: formatConsumerReadinessDisclosure(input.changelogText),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function issuesFromInventory(rows) {
|
|
16
|
+
const issues = [];
|
|
17
|
+
for (const row of rows) {
|
|
18
|
+
const issue = issueFromInventoryRow(row);
|
|
19
|
+
if (issue !== null)
|
|
20
|
+
issues.push(issue);
|
|
21
|
+
}
|
|
22
|
+
return issues;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=run-consumer-readiness.js.map
|
package/dist/release/types.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ export interface ReleaseSeams {
|
|
|
54
54
|
readonly writeFile?: (path: string, content: string) => void;
|
|
55
55
|
readonly readFile?: (path: string) => string;
|
|
56
56
|
readonly fileExists?: (path: string) => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* #3900 check 4 -- consumer hard-stop census. When omitted, production lists
|
|
59
|
+
* open issues via REST. Tests inject a [ok, message] tuple so they do not
|
|
60
|
+
* hit GitHub.
|
|
61
|
+
*/
|
|
62
|
+
readonly consumerHardStops?: (repo: string, projectRoot: string) => [boolean, string];
|
|
57
63
|
readonly runCi?: (projectRoot: string, allowCoverageDebtIssue: number | null) => [boolean, string];
|
|
58
64
|
readonly refreshRoadmap?: (projectRoot: string) => [boolean, string];
|
|
59
65
|
readonly checkVbriefLifecycleSync?: (projectRoot: string, repo: string) => [boolean, number, string];
|
|
@@ -89,5 +95,19 @@ export interface ReleaseSeams {
|
|
|
89
95
|
* Tests inject `{}` so a host env bypass cannot leak into fail-closed cases.
|
|
90
96
|
*/
|
|
91
97
|
readonly closedVerbEnv?: EnvMap;
|
|
98
|
+
/**
|
|
99
|
+
* #3753 — override the active-CLI probe used by the local-vs-released report.
|
|
100
|
+
* When omitted, production calls `checkActiveCliAgainstTarget`.
|
|
101
|
+
*/
|
|
102
|
+
readonly checkActiveCli?: (targetVersion: string) => import("../session/active-cli.js").ActiveCliCheckResult;
|
|
103
|
+
/**
|
|
104
|
+
* #3753 — override one `npm view <pkg>@<ver> --prefer-online` (tests).
|
|
105
|
+
* When omitted, production spawns npm. Never used to install.
|
|
106
|
+
*/
|
|
107
|
+
readonly viewWorkspacePackage?: (name: string, version: string) => {
|
|
108
|
+
readonly name: string;
|
|
109
|
+
readonly visible: boolean;
|
|
110
|
+
readonly version: string | null;
|
|
111
|
+
};
|
|
92
112
|
}
|
|
93
113
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -13,8 +13,12 @@ export declare const DEFAULT_INCLUDE_SCOPES_MODE: "off";
|
|
|
13
13
|
/** Lifecycle scope aggregation modes for rendered specs (#1566). */
|
|
14
14
|
export type IncludeScopesMode = "off" | "current" | "all";
|
|
15
15
|
export declare const LIFECYCLE_BUCKETS: ReadonlyArray<readonly [string, string]>;
|
|
16
|
+
/** Build a specification banner from the actual specification artifact path. */
|
|
17
|
+
export declare function buildSpecRenderBanner(specPath: string): string;
|
|
16
18
|
export declare const SPEC_RENDER_BANNER: string;
|
|
17
19
|
export declare const PRD_NARRATIVE_KEY_ORDER: readonly ["Overview", "ProblemStatement", "Goals", "UserStories", "Requirements", "SuccessMetrics", "Architecture"];
|
|
20
|
+
/** Build a PRD banner from the actual resolved authority path. */
|
|
21
|
+
export declare function buildPrdBanner(sourcePath: string): string;
|
|
18
22
|
export declare const PRD_BANNER: string;
|
|
19
23
|
export declare const PRD_GENERATED_SENTINEL = "AUTO-GENERATED by task prd:render";
|
|
20
24
|
/** Max completed scopes listed under ## Completed in ROADMAP.md (#2653). */
|
package/dist/render/constants.js
CHANGED
|
@@ -12,6 +12,7 @@ export const VALID_STATUSES = new Set([
|
|
|
12
12
|
]);
|
|
13
13
|
export { VALID_VBRIEF_VERSIONS } from "../vbrief-validate/constants.js";
|
|
14
14
|
export { resolveInfoBlock } from "../vbrief-validate/schema.js";
|
|
15
|
+
import { buildGeneratedArtifactBanner } from "../spec-authority/constants.js";
|
|
15
16
|
export const SPECIFICATION_NARRATIVE_KEY_ORDER = [
|
|
16
17
|
"Overview",
|
|
17
18
|
"ProblemStatement",
|
|
@@ -37,10 +38,11 @@ export const LIFECYCLE_BUCKETS = [
|
|
|
37
38
|
["active", "Active"],
|
|
38
39
|
["completed", "Completed"],
|
|
39
40
|
];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
41
|
+
/** Build a specification banner from the actual specification artifact path. */
|
|
42
|
+
export function buildSpecRenderBanner(specPath) {
|
|
43
|
+
return buildGeneratedArtifactBanner("task spec:render", "rendered specification", specPath);
|
|
44
|
+
}
|
|
45
|
+
export const SPEC_RENDER_BANNER = buildSpecRenderBanner("xbrief/specification.xbrief.json");
|
|
44
46
|
export const PRD_NARRATIVE_KEY_ORDER = [
|
|
45
47
|
"Overview",
|
|
46
48
|
"ProblemStatement",
|
|
@@ -50,10 +52,11 @@ export const PRD_NARRATIVE_KEY_ORDER = [
|
|
|
50
52
|
"SuccessMetrics",
|
|
51
53
|
"Architecture",
|
|
52
54
|
];
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
55
|
+
/** Build a PRD banner from the actual resolved authority path. */
|
|
56
|
+
export function buildPrdBanner(sourcePath) {
|
|
57
|
+
return buildGeneratedArtifactBanner("task prd:render", "rendered PRD", sourcePath);
|
|
58
|
+
}
|
|
59
|
+
export const PRD_BANNER = buildPrdBanner("xbrief/specification.xbrief.json");
|
|
57
60
|
export const PRD_GENERATED_SENTINEL = "AUTO-GENERATED by task prd:render";
|
|
58
61
|
/** Max completed scopes listed under ## Completed in ROADMAP.md (#2653). */
|
|
59
62
|
export const ROADMAP_COMPLETED_CAP = 25;
|
|
@@ -7,6 +7,32 @@ import { buildScopeOutlookSection } from "./scope-outlook.js";
|
|
|
7
7
|
import { normalizeIncludeScopesMode, tryParseIncludeScopesMode, tryParseOnOffFlag, } from "./spec-render.js";
|
|
8
8
|
import { validateSpec } from "./spec-validate.js";
|
|
9
9
|
import { stripTrailingWhitespace } from "./text-utils.js";
|
|
10
|
+
function resolveExportScopePolicy(audience, includeScopes) {
|
|
11
|
+
const includeProposed = audience === "internal";
|
|
12
|
+
if (includeScopes === undefined) {
|
|
13
|
+
return {
|
|
14
|
+
render: includeProposed,
|
|
15
|
+
includeProposed,
|
|
16
|
+
includeCurrent: false,
|
|
17
|
+
includeCompleted: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const mode = normalizeIncludeScopesMode(includeScopes);
|
|
21
|
+
if (mode === "off") {
|
|
22
|
+
return {
|
|
23
|
+
render: false,
|
|
24
|
+
includeProposed: false,
|
|
25
|
+
includeCurrent: false,
|
|
26
|
+
includeCompleted: false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
render: true,
|
|
31
|
+
includeProposed,
|
|
32
|
+
includeCurrent: true,
|
|
33
|
+
includeCompleted: mode === "all",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
10
36
|
function loadPlanTitle(path, fallback) {
|
|
11
37
|
try {
|
|
12
38
|
const doc = JSON.parse(readFileSync(path, "utf8"));
|
|
@@ -36,9 +62,8 @@ export function exportSpec(options = {}) {
|
|
|
36
62
|
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
37
63
|
const outPath = options.outPath ?? join(projectRoot, "SPECIFICATION.md");
|
|
38
64
|
const audience = options.audience ?? "stakeholder";
|
|
39
|
-
const
|
|
65
|
+
const scopePolicy = resolveExportScopePolicy(audience, options.includeScopes);
|
|
40
66
|
const includeLegacyArtifacts = options.includeLegacyArtifacts ?? false;
|
|
41
|
-
const includeProposed = audience === "internal";
|
|
42
67
|
const authority = resolveSpecAuthority(projectRoot);
|
|
43
68
|
if (!authority) {
|
|
44
69
|
return [false, "✗ Missing xbrief/PROJECT-DEFINITION.xbrief.json — cannot export spec."];
|
|
@@ -63,11 +88,12 @@ export function exportSpec(options = {}) {
|
|
|
63
88
|
`# ${title}\n`,
|
|
64
89
|
...renderNarrativeSections(narratives),
|
|
65
90
|
];
|
|
66
|
-
if (
|
|
91
|
+
if (scopePolicy.render) {
|
|
67
92
|
const scopeLines = buildScopeOutlookSection(authority.vbriefDir, {
|
|
68
|
-
includeProposed,
|
|
93
|
+
includeProposed: scopePolicy.includeProposed,
|
|
69
94
|
proposedLimit: options.proposedLimit,
|
|
70
|
-
|
|
95
|
+
includeCurrent: scopePolicy.includeCurrent,
|
|
96
|
+
includeCompleted: scopePolicy.includeCompleted,
|
|
71
97
|
});
|
|
72
98
|
if (scopeLines.length > 0)
|
|
73
99
|
lines.push(...scopeLines);
|
package/dist/render/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { type ExportAudience, type ExportSpecOptions, exportSpec, exportSpecMain
|
|
|
3
3
|
export * as frameworkCommands from "./framework-commands.js";
|
|
4
4
|
export { availableCommands, cmdCoreValidate, formatFrameworkCommand, hasCommand, main as frameworkCommandsMain, normalizeTaskSeparator, runFrameworkCommand, } from "./framework-commands.js";
|
|
5
5
|
export * as prdRender from "./prd-render.js";
|
|
6
|
-
export { main as prdRenderMain, parsePrdArgv, renderPrd } from "./prd-render.js";
|
|
6
|
+
export { main as prdRenderMain, parsePrdArgv, renderPrd, renderProjectPrd, } from "./prd-render.js";
|
|
7
7
|
export * as projectRender from "./project-render.js";
|
|
8
8
|
export { acknowledgeProjectDefinitionStaleness, buildStalenessAcknowledgement, computeStalenessFlags, flagStaleNarratives, main as projectRenderMain, parseStalenessReview, renderProjectDefinition, scanLifecycleFolders, unacknowledgedCompletedItems, } from "./project-render.js";
|
|
9
9
|
export * as roadmapRender from "./roadmap-render.js";
|
package/dist/render/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { exportSpec, exportSpecMain, parseExportSpecArgv, } from "./export-spec.
|
|
|
3
3
|
export * as frameworkCommands from "./framework-commands.js";
|
|
4
4
|
export { availableCommands, cmdCoreValidate, formatFrameworkCommand, hasCommand, main as frameworkCommandsMain, normalizeTaskSeparator, runFrameworkCommand, } from "./framework-commands.js";
|
|
5
5
|
export * as prdRender from "./prd-render.js";
|
|
6
|
-
export { main as prdRenderMain, parsePrdArgv, renderPrd } from "./prd-render.js";
|
|
6
|
+
export { main as prdRenderMain, parsePrdArgv, renderPrd, renderProjectPrd, } from "./prd-render.js";
|
|
7
7
|
export * as projectRender from "./project-render.js";
|
|
8
8
|
export { acknowledgeProjectDefinitionStaleness, buildStalenessAcknowledgement, computeStalenessFlags, flagStaleNarratives, main as projectRenderMain, parseStalenessReview, renderProjectDefinition, scanLifecycleFolders, unacknowledgedCompletedItems, } from "./project-render.js";
|
|
9
9
|
export * as roadmapRender from "./roadmap-render.js";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export interface RenderPrdOptions {
|
|
2
2
|
readonly force?: boolean;
|
|
3
3
|
}
|
|
4
|
-
/** Read specification
|
|
4
|
+
/** Read an explicit specification artifact and write PRD.md. */
|
|
5
5
|
export declare function renderPrd(specPath: string, outputPath: string, options?: RenderPrdOptions): void;
|
|
6
|
+
/** Resolve full-spec or greenfield project authority and write a stakeholder-safe PRD. */
|
|
7
|
+
export declare function renderProjectPrd(projectRoot: string, outputPath: string, options?: RenderPrdOptions): void;
|
|
6
8
|
export interface PrdCliArgs {
|
|
7
9
|
readonly spec?: string;
|
|
10
|
+
readonly projectRoot?: string;
|
|
8
11
|
readonly output?: string;
|
|
9
12
|
readonly force?: boolean;
|
|
10
13
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { resolveSpecArtifactPath } from "../layout/resolve.js";
|
|
4
|
+
import { generatedSourcePath } from "../spec-authority/constants.js";
|
|
5
|
+
import { greenfieldOverviewNonEmpty, resolveExportNarratives, } from "../spec-authority/narratives.js";
|
|
6
|
+
import { resolveSpecAuthority } from "../spec-authority/resolver.js";
|
|
7
|
+
import { buildPrdBanner, PRD_GENERATED_SENTINEL, PRD_NARRATIVE_KEY_ORDER } from "./constants.js";
|
|
8
|
+
import { validateSpec } from "./spec-validate.js";
|
|
3
9
|
function isDeftGenerated(path) {
|
|
4
10
|
if (!existsSync(path))
|
|
5
11
|
return true;
|
|
@@ -12,31 +18,20 @@ function isDeftGenerated(path) {
|
|
|
12
18
|
return false;
|
|
13
19
|
}
|
|
14
20
|
}
|
|
15
|
-
|
|
16
|
-
export function renderPrd(specPath, outputPath, options = {}) {
|
|
17
|
-
const force = options.force ?? false;
|
|
18
|
-
if (!existsSync(specPath)) {
|
|
19
|
-
process.stderr.write(`Error: specification file not found: ${specPath}\n`);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
21
|
+
function writePrd(title, narratives, sourcePath, outputPath, force) {
|
|
22
22
|
if (!force && !isDeftGenerated(outputPath)) {
|
|
23
23
|
process.stderr.write(`Error: refusing to overwrite non-generated PRD at ${outputPath}. ` +
|
|
24
24
|
`This file lacks the "${PRD_GENERATED_SENTINEL}" banner -- it was likely hand-authored. ` +
|
|
25
25
|
"Re-run with --force to overwrite, or point --output at a different file (#539).\n");
|
|
26
26
|
process.exit(2);
|
|
27
27
|
}
|
|
28
|
-
const data = JSON.parse(readFileSync(specPath, "utf8"));
|
|
29
|
-
const plan = (data.plan ?? {});
|
|
30
|
-
const title = String(plan.title ?? "Project");
|
|
31
|
-
const narratives = typeof plan.narratives === "object" &&
|
|
32
|
-
plan.narratives !== null &&
|
|
33
|
-
!Array.isArray(plan.narratives)
|
|
34
|
-
? plan.narratives
|
|
35
|
-
: {};
|
|
36
28
|
if (Object.keys(narratives).length === 0) {
|
|
37
|
-
process.stderr.write(`Warning: no narratives found in ${
|
|
29
|
+
process.stderr.write(`Warning: no narratives found in ${sourcePath}\n`);
|
|
38
30
|
}
|
|
39
|
-
const lines = [
|
|
31
|
+
const lines = [
|
|
32
|
+
buildPrdBanner(sourcePath),
|
|
33
|
+
`# ${title} -- Product Requirements Document\n`,
|
|
34
|
+
];
|
|
40
35
|
const renderedKeys = new Set();
|
|
41
36
|
for (const key of PRD_NARRATIVE_KEY_ORDER) {
|
|
42
37
|
if (key in narratives) {
|
|
@@ -52,16 +47,80 @@ export function renderPrd(specPath, outputPath, options = {}) {
|
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
49
|
lines.push("---\n" +
|
|
55
|
-
|
|
56
|
-
"
|
|
50
|
+
`*This document is auto-generated from \`${generatedSourcePath(sourcePath)}\` ` +
|
|
51
|
+
"via `task prd:render`. Do not edit directly.*\n");
|
|
57
52
|
writeFileSync(outputPath, lines.join("\n"), "utf8");
|
|
58
53
|
process.stdout.write(`PRD.md written to ${outputPath}\n`);
|
|
59
54
|
}
|
|
55
|
+
function loadTitleAndNarratives(sourcePath) {
|
|
56
|
+
const payload = JSON.parse(readFileSync(sourcePath, "utf8"));
|
|
57
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
58
|
+
process.stderr.write(`Error: ${sourcePath} root must be a JSON object\n`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const data = payload;
|
|
62
|
+
const plan = (data.plan ?? {});
|
|
63
|
+
const narratives = typeof plan.narratives === "object" &&
|
|
64
|
+
plan.narratives !== null &&
|
|
65
|
+
!Array.isArray(plan.narratives)
|
|
66
|
+
? plan.narratives
|
|
67
|
+
: {};
|
|
68
|
+
return { title: String(plan.title ?? "Project"), narratives };
|
|
69
|
+
}
|
|
70
|
+
/** Read an explicit specification artifact and write PRD.md. */
|
|
71
|
+
export function renderPrd(specPath, outputPath, options = {}) {
|
|
72
|
+
if (!existsSync(specPath)) {
|
|
73
|
+
process.stderr.write(`Error: specification file not found: ${specPath}\n`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const { title, narratives } = loadTitleAndNarratives(specPath);
|
|
77
|
+
writePrd(title, narratives, specPath, outputPath, options.force ?? false);
|
|
78
|
+
}
|
|
79
|
+
/** Resolve full-spec or greenfield project authority and write a stakeholder-safe PRD. */
|
|
80
|
+
export function renderProjectPrd(projectRoot, outputPath, options = {}) {
|
|
81
|
+
const root = resolve(projectRoot);
|
|
82
|
+
const authority = resolveSpecAuthority(root);
|
|
83
|
+
if (!authority) {
|
|
84
|
+
try {
|
|
85
|
+
const compatibilitySpecPath = resolveSpecArtifactPath(root);
|
|
86
|
+
if (existsSync(compatibilitySpecPath)) {
|
|
87
|
+
renderPrd(compatibilitySpecPath, outputPath, options);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// The stable error below covers projects without a resolvable lifecycle layout.
|
|
93
|
+
}
|
|
94
|
+
process.stderr.write("Error: xbrief/PROJECT-DEFINITION.xbrief.json not found; PRD authority is unavailable.\n");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (authority.kind === "full-spec" && authority.specPath) {
|
|
98
|
+
const [ok, message] = validateSpec(authority.specPath);
|
|
99
|
+
if (!ok) {
|
|
100
|
+
process.stderr.write(`${message}\n`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (!greenfieldOverviewNonEmpty(authority)) {
|
|
105
|
+
process.stderr.write("Error: PROJECT-DEFINITION.xbrief.json Overview narrative is empty; cannot render PRD.\n");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const { title } = loadTitleAndNarratives(authority.sourcePath);
|
|
109
|
+
writePrd(title, resolveExportNarratives(authority), authority.sourcePath, outputPath, options.force ?? false);
|
|
110
|
+
}
|
|
60
111
|
/** CLI entry (mirrors ``scripts/prd_render.main``). */
|
|
61
112
|
export function main(args = {}) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
113
|
+
const outputPath = args.output ?? "PRD.md";
|
|
114
|
+
const options = { force: args.force ?? false };
|
|
115
|
+
if (args.spec !== undefined) {
|
|
116
|
+
renderPrd(args.spec, outputPath, options);
|
|
117
|
+
}
|
|
118
|
+
else if (args.projectRoot !== undefined) {
|
|
119
|
+
renderProjectPrd(args.projectRoot, outputPath, options);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
renderPrd("vbrief/specification.vbrief.json", outputPath, options);
|
|
123
|
+
}
|
|
65
124
|
}
|
|
66
125
|
export function parsePrdArgv(argv) {
|
|
67
126
|
const out = {};
|
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { hasArtifactSuffix, resolveLayoutRootOrCanonical, stripArtifactSuffix, } from "../layout/resolve.js";
|
|
4
4
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
5
|
-
import {
|
|
5
|
+
import { withProjectDefinitionMutation } from "../vbrief-build/project-definition-mutation.js";
|
|
6
6
|
import { deriveRegistryItemStatus, registryMetadataReferencesFromScope, } from "../vbrief-validate/registry-status.js";
|
|
7
7
|
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, VBRIEF_VERSION, } from "../xbrief-migrate/constants.js";
|
|
8
8
|
import { PROJECT_LIFECYCLE_FOLDERS, SKELETON_NARRATIVES } from "./constants.js";
|
|
@@ -192,11 +192,15 @@ export function renderProjectDefinition(vbriefDir, options = {}) {
|
|
|
192
192
|
// Serialise the whole read-modify-write of PROJECT-DEFINITION under the shared
|
|
193
193
|
// mutation lock so a concurrent policy/triage mutator cannot be clobbered by the
|
|
194
194
|
// materialised items/metadata write (or vice versa) (#1260).
|
|
195
|
-
|
|
195
|
+
const projectRoot = resolve(vbriefDir, "..");
|
|
196
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
196
197
|
const nowDate = options.now ?? new Date();
|
|
197
198
|
const now = nowDate.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
198
199
|
const layout = resolveProjectDefinitionLayout(vbriefDir);
|
|
199
|
-
|
|
200
|
+
// Read and write the artifact the lock captured, not a second independent
|
|
201
|
+
// resolution of the same file (#3796). Resolving it again here is how a
|
|
202
|
+
// render could lock one identity and materialise items into another.
|
|
203
|
+
const projectDefPath = mutation.artifactPath;
|
|
200
204
|
const items = scanLifecycleFolders(vbriefDir);
|
|
201
205
|
const createdNew = !existsSync(projectDefPath);
|
|
202
206
|
let projectDef;
|
|
@@ -242,7 +246,7 @@ export function renderProjectDefinition(vbriefDir, options = {}) {
|
|
|
242
246
|
mkdirSync(vbriefDir, { recursive: true });
|
|
243
247
|
// Atomic temp+rename write under the lock so external readers never observe
|
|
244
248
|
// a partially-written PROJECT-DEFINITION (#1260).
|
|
245
|
-
|
|
249
|
+
mutation.persist(projectDef);
|
|
246
250
|
const itemCount = items.length;
|
|
247
251
|
const planMeta = (projectDef.plan?.metadata ?? {});
|
|
248
252
|
const flagCount = Array.isArray(planMeta.staleness_flags) ? planMeta.staleness_flags.length : 0;
|
|
@@ -262,11 +266,14 @@ export function renderProjectDefinition(vbriefDir, options = {}) {
|
|
|
262
266
|
export function acknowledgeProjectDefinitionStaleness(vbriefDir, options = {}) {
|
|
263
267
|
// Serialise the read-modify-write of PROJECT-DEFINITION under the shared
|
|
264
268
|
// mutation lock so a concurrent policy/triage mutator is not clobbered (#1260).
|
|
265
|
-
|
|
269
|
+
const projectRoot = resolve(vbriefDir, "..");
|
|
270
|
+
return withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
266
271
|
const nowDate = options.now ?? new Date();
|
|
267
272
|
const now = isoTimestamp(nowDate);
|
|
268
273
|
const layout = resolveProjectDefinitionLayout(vbriefDir);
|
|
269
|
-
|
|
274
|
+
// Read and write the artifact the lock captured (#3796) -- see the note in
|
|
275
|
+
// `renderProjectDefinition`.
|
|
276
|
+
const projectDefPath = mutation.artifactPath;
|
|
270
277
|
if (!existsSync(projectDefPath)) {
|
|
271
278
|
return [false, `✗ ${projectDefPath} not found — run project:render first`];
|
|
272
279
|
}
|
|
@@ -309,7 +316,7 @@ export function acknowledgeProjectDefinitionStaleness(vbriefDir, options = {}) {
|
|
|
309
316
|
projectDef.plan = plan;
|
|
310
317
|
// Atomic temp+rename write under the lock so external readers never observe
|
|
311
318
|
// a partially-written PROJECT-DEFINITION (#1260).
|
|
312
|
-
|
|
319
|
+
mutation.persist(projectDef);
|
|
313
320
|
const ackCount = completedItems.length;
|
|
314
321
|
return [
|
|
315
322
|
true,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface ScopeOutlookOptions {
|
|
2
2
|
readonly includeProposed?: boolean;
|
|
3
3
|
readonly proposedLimit?: number;
|
|
4
|
+
/** When false, omit pending and active lifecycle buckets. Default true. */
|
|
5
|
+
readonly includeCurrent?: boolean;
|
|
4
6
|
/**
|
|
5
7
|
* When false, omit the completed lifecycle bucket (#1566 compact render).
|
|
6
8
|
* Default true so callers that opt into full aggregation keep prior behavior.
|
|
@@ -203,6 +203,7 @@ function filterProposed(vb) {
|
|
|
203
203
|
export function buildScopeOutlookSection(vbriefDir, options = {}) {
|
|
204
204
|
const includeProposed = options.includeProposed ?? false;
|
|
205
205
|
const proposedLimit = options.proposedLimit ?? 0;
|
|
206
|
+
const includeCurrent = options.includeCurrent ?? true;
|
|
206
207
|
const includeCompleted = options.includeCompleted ?? true;
|
|
207
208
|
const buckets = [];
|
|
208
209
|
if (includeProposed) {
|
|
@@ -221,6 +222,8 @@ export function buildScopeOutlookSection(vbriefDir, options = {}) {
|
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
for (const [folder, heading, filter] of COMMITTED_BUCKETS) {
|
|
225
|
+
if ((folder === "pending" || folder === "active") && !includeCurrent)
|
|
226
|
+
continue;
|
|
224
227
|
if (folder === "completed" && !includeCompleted)
|
|
225
228
|
continue;
|
|
226
229
|
let scopes = loadScopeVbriefs(join(vbriefDir, folder));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { DEFAULT_INCLUDE_SCOPES_MODE, LEGACY_ARTIFACTS_NARRATIVE_KEY, RENDERABLE_SPEC_STATUSES,
|
|
3
|
+
import { buildSpecRenderBanner, DEFAULT_INCLUDE_SCOPES_MODE, LEGACY_ARTIFACTS_NARRATIVE_KEY, RENDERABLE_SPEC_STATUSES, SPECIFICATION_NARRATIVE_KEY_ORDER, } from "./constants.js";
|
|
4
4
|
import { buildScopeOutlookSection } from "./scope-outlook.js";
|
|
5
5
|
import { validateSpec } from "./spec-validate.js";
|
|
6
6
|
import { stripTrailingWhitespace } from "./text-utils.js";
|
|
@@ -100,7 +100,7 @@ export function renderSpec(specPath, outPath, options = {}) {
|
|
|
100
100
|
" Have the user review and set status to one of the renderable statuses before rendering.",
|
|
101
101
|
];
|
|
102
102
|
}
|
|
103
|
-
const lines = [
|
|
103
|
+
const lines = [buildSpecRenderBanner(specPath)];
|
|
104
104
|
let title = "Specification";
|
|
105
105
|
if (typeof plan === "object" && plan !== null && !Array.isArray(plan)) {
|
|
106
106
|
title = String(plan.title ?? "Specification");
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
* gitignored / package-manager-invisible). Only the global, project-local,
|
|
17
17
|
* ephemeral, and upgrade command forms vary by package manager.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Detection from an injected fact-set remains pure. The project resolver is the
|
|
20
|
+
* single filesystem boundary used by consumer gates so package.json and lockfile
|
|
21
|
+
* precedence cannot drift across callers (#3610).
|
|
21
22
|
*/
|
|
22
23
|
export type PackageManager = "npm" | "pnpm";
|
|
23
24
|
export declare const PACKAGE_MANAGERS: readonly PackageManager[];
|
|
@@ -32,10 +33,38 @@ export interface DetectPackageManagerInput {
|
|
|
32
33
|
/** Whether a `pnpm-lock.yaml` is present at the project root. */
|
|
33
34
|
readonly pnpmLockPresent?: boolean;
|
|
34
35
|
}
|
|
36
|
+
export type PackageManagerResolutionSource = "env-override" | "package-manager-field" | "pnpm-lock" | "user-agent" | "default";
|
|
37
|
+
export type PackageManagerResolutionErrorSource = "env-override" | "package-manager-field" | "package-json" | "project-filesystem";
|
|
38
|
+
export type PackageManagerResolution = {
|
|
39
|
+
readonly ok: true;
|
|
40
|
+
readonly packageManager: PackageManager;
|
|
41
|
+
readonly source: PackageManagerResolutionSource;
|
|
42
|
+
} | {
|
|
43
|
+
readonly ok: false;
|
|
44
|
+
readonly source: PackageManagerResolutionErrorSource;
|
|
45
|
+
readonly message: string;
|
|
46
|
+
};
|
|
47
|
+
export interface ResolveProjectPackageManagerOptions {
|
|
48
|
+
/** Consumer project root. Defaults to the current working directory. */
|
|
49
|
+
readonly projectRoot?: string;
|
|
50
|
+
/** Environment map used for override and user-agent precedence. */
|
|
51
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
52
|
+
/** Text-read seam. `null` means the file is absent. */
|
|
53
|
+
readonly readText?: (path: string) => string | null;
|
|
54
|
+
/** File-existence seam used for pnpm-lock.yaml. */
|
|
55
|
+
readonly isFile?: (path: string) => boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Human-readable, non-executable label for a resolution source. */
|
|
58
|
+
export declare function packageManagerSourceLabel(source: PackageManagerResolutionSource): string;
|
|
59
|
+
export declare function resolvePackageManager(input?: DetectPackageManagerInput): PackageManagerResolution;
|
|
60
|
+
/** Render untrusted diagnostic text as one bounded terminal-safe line. */
|
|
61
|
+
export declare function sanitizeSingleLineDiagnostic(text: string): string;
|
|
62
|
+
/** Resolve a consumer project's package manager from package.json and lockfile facts. */
|
|
63
|
+
export declare function resolveProjectPackageManager(options?: ResolveProjectPackageManagerOptions): PackageManagerResolution;
|
|
35
64
|
/**
|
|
36
65
|
* Detect the active package manager. Precedence (first match wins):
|
|
37
|
-
* 1. `
|
|
38
|
-
* 2. `
|
|
66
|
+
* 1. `packageManager` field / Corepack shim (authoritative when present)
|
|
67
|
+
* 2. `DEFT_PACKAGE_MANAGER` env override
|
|
39
68
|
* 3. `pnpm-lock.yaml` present
|
|
40
69
|
* 4. `npm_config_user_agent` (set by the manager that spawned the process)
|
|
41
70
|
* 5. default: npm
|