@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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, relative, resolve } from "node:path";
|
|
3
3
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
|
-
import {
|
|
5
|
-
import { CACHE_DIR_NAME, CACHE_SOURCE_GITHUB_ISSUE } from "../triage/queue/constants.js";
|
|
4
|
+
import { ScmStubError } from "../scm/errors.js";
|
|
6
5
|
import { resolveRepo } from "../triage/queue/repo.js";
|
|
6
|
+
import { normalizeScopePath, resolveCandidateScope, } from "./candidate-scope.js";
|
|
7
|
+
import { AGGREGATE_LATENCY_BUDGET_MS, formatAge, makeGateRunner, OpenIssueInventory, resolveIssueStateAggregate, resolveIssueStateScoped, SCOPED_LATENCY_BUDGET_MS, } from "./issue-state.js";
|
|
7
8
|
import { collectGithubRefs } from "./refs.js";
|
|
8
9
|
function readJson(path) {
|
|
9
10
|
try {
|
|
@@ -60,61 +61,62 @@ function listActiveRunningBriefs(projectRoot) {
|
|
|
60
61
|
}
|
|
61
62
|
return out.sort((a, b) => a.path.localeCompare(b.path));
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Records how each verdict was reached so the summary can distinguish a
|
|
66
|
+
* verified pass from an unverified one (#3767).
|
|
67
|
+
*/
|
|
68
|
+
class BasisTally {
|
|
69
|
+
inventory = 0;
|
|
70
|
+
live = 0;
|
|
71
|
+
cache = 0;
|
|
72
|
+
maxCacheAgeMs = 0;
|
|
73
|
+
unverifiedReasons = [];
|
|
74
|
+
record(ref, resolution) {
|
|
75
|
+
switch (resolution.basis) {
|
|
76
|
+
case "inventory":
|
|
77
|
+
this.inventory += 1;
|
|
78
|
+
break;
|
|
79
|
+
case "live":
|
|
80
|
+
this.live += 1;
|
|
81
|
+
break;
|
|
82
|
+
case "cache":
|
|
83
|
+
this.cache += 1;
|
|
84
|
+
this.maxCacheAgeMs = Math.max(this.maxCacheAgeMs, resolution.cacheAgeMs);
|
|
85
|
+
break;
|
|
86
|
+
default: {
|
|
87
|
+
const line = `#${ref.number} (${resolution.detail})`;
|
|
88
|
+
if (!this.unverifiedReasons.includes(line)) {
|
|
89
|
+
this.unverifiedReasons.push(line);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
79
93
|
}
|
|
80
|
-
|
|
81
|
-
return
|
|
94
|
+
get unverified() {
|
|
95
|
+
return this.unverifiedReasons.length;
|
|
82
96
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const result = runGh(["gh", "api", path]);
|
|
88
|
-
if (result.returncode !== 0) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
const payload = JSON.parse(result.stdout);
|
|
93
|
-
if (payload === null || typeof payload !== "object") {
|
|
94
|
-
return null;
|
|
97
|
+
summary() {
|
|
98
|
+
const parts = [];
|
|
99
|
+
if (this.inventory > 0) {
|
|
100
|
+
parts.push(`inventory ${this.inventory}`);
|
|
95
101
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return "closed";
|
|
102
|
+
if (this.live > 0) {
|
|
103
|
+
parts.push(`live ${this.live}`);
|
|
99
104
|
}
|
|
100
|
-
if (
|
|
101
|
-
|
|
105
|
+
if (this.cache > 0) {
|
|
106
|
+
parts.push(`cache ${this.cache} (max age ${formatAge(this.maxCacheAgeMs)})`);
|
|
102
107
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return
|
|
108
|
+
if (this.unverified > 0) {
|
|
109
|
+
parts.push(`unverified ${this.unverified}`);
|
|
110
|
+
}
|
|
111
|
+
return parts.length === 0 ? "no GitHub lookups" : parts.join(", ");
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
|
-
function resolveIssueState(ref,
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
return fetchIssueStateLive(ref, runGh);
|
|
114
|
+
function resolveIssueState(ref, ctx, scoped, tally) {
|
|
115
|
+
const resolution = scoped
|
|
116
|
+
? resolveIssueStateScoped(ref, ctx)
|
|
117
|
+
: resolveIssueStateAggregate(ref, ctx);
|
|
118
|
+
tally.record(ref, resolution);
|
|
119
|
+
return resolution.state;
|
|
118
120
|
}
|
|
119
121
|
function fetchPrMerged(ref, runGh) {
|
|
120
122
|
const path = `repos/${ref.repo}/pulls/${ref.number}`;
|
|
@@ -144,11 +146,18 @@ function shipped(reason) {
|
|
|
144
146
|
function unresolved(reason) {
|
|
145
147
|
return { orphaned: true, reason, kind: "unresolved" };
|
|
146
148
|
}
|
|
147
|
-
function assessOrphanSignature(issueRefs, prRefs,
|
|
149
|
+
function assessOrphanSignature(issueRefs, prRefs, ctx, selectedIssue, tally) {
|
|
150
|
+
const runGh = ctx.runGh;
|
|
151
|
+
const skipGh = ctx.skipGh;
|
|
152
|
+
const scoped = selectedIssue !== null;
|
|
148
153
|
if (selectedIssue !== null) {
|
|
149
|
-
// Confirmed closed origin is shipped even if a linked PR lookup fails.
|
|
150
154
|
const selectedRef = issueRefs.find((ref) => ref.number === selectedIssue);
|
|
151
|
-
|
|
155
|
+
if (selectedRef === undefined) {
|
|
156
|
+
// evaluate() only reaches here for briefs that name the issue.
|
|
157
|
+
return PASS;
|
|
158
|
+
}
|
|
159
|
+
// Confirmed closed origin is shipped even if a linked PR lookup fails.
|
|
160
|
+
const selectedState = resolveIssueState(selectedRef, ctx, scoped, tally);
|
|
152
161
|
if (selectedState === "closed") {
|
|
153
162
|
return shipped(`issue #${selectedIssue} is closed`);
|
|
154
163
|
}
|
|
@@ -165,25 +174,8 @@ function assessOrphanSignature(issueRefs, prRefs, projectRoot, runGh, skipGh, se
|
|
|
165
174
|
}
|
|
166
175
|
}
|
|
167
176
|
// --issue N is one origin: sibling open/unknown must not mask it (#3429).
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
return unresolved(`issue #${selectedIssue} state could not be resolved`);
|
|
171
|
-
}
|
|
172
|
-
return PASS;
|
|
173
|
-
}
|
|
174
|
-
const issueStates = issueRefs.map((ref) => ({
|
|
175
|
-
ref,
|
|
176
|
-
state: resolveIssueState(ref, projectRoot, runGh, skipGh),
|
|
177
|
-
}));
|
|
178
|
-
const unknownIssue = issueStates.find((row) => row.state === null);
|
|
179
|
-
if (unknownIssue !== undefined) {
|
|
180
|
-
return unresolved(`issue #${unknownIssue.ref.number} state could not be resolved`);
|
|
181
|
-
}
|
|
182
|
-
if (issueStates.some((row) => row.state === "closed")) {
|
|
183
|
-
return shipped("all referenced issues are closed");
|
|
184
|
-
}
|
|
185
|
-
if (prRefs.length > 0 && skipGh) {
|
|
186
|
-
return unresolved(`linked PR #${prRefs[0]?.number} state could not be resolved`);
|
|
177
|
+
if (selectedState === null) {
|
|
178
|
+
return unresolved(`issue #${selectedIssue} state could not be resolved`);
|
|
187
179
|
}
|
|
188
180
|
return PASS;
|
|
189
181
|
}
|
|
@@ -199,26 +191,64 @@ function assessOrphanSignature(issueRefs, prRefs, projectRoot, runGh, skipGh, se
|
|
|
199
191
|
if (issueRefs.length === 0) {
|
|
200
192
|
return PASS;
|
|
201
193
|
}
|
|
202
|
-
let resolved = 0;
|
|
203
194
|
for (const ref of issueRefs) {
|
|
204
|
-
const state = resolveIssueState(ref,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
resolved += 1;
|
|
195
|
+
const state = resolveIssueState(ref, ctx, scoped, tally);
|
|
196
|
+
// Unknown keeps the sweep fail-open so offline work is not network-authorized;
|
|
197
|
+
// the unverified basis line stops the pass reading as verified evidence (#3767).
|
|
209
198
|
if (state !== "closed") {
|
|
210
199
|
return PASS;
|
|
211
200
|
}
|
|
212
201
|
}
|
|
213
|
-
|
|
214
|
-
return shipped("all referenced issues are closed");
|
|
215
|
-
}
|
|
216
|
-
return PASS;
|
|
202
|
+
return shipped("all referenced issues are closed");
|
|
217
203
|
}
|
|
218
204
|
function briefReferencesIssue(issues, issue) {
|
|
219
205
|
return issues.some((ref) => ref.number === issue);
|
|
220
206
|
}
|
|
221
|
-
|
|
207
|
+
/** Basis, ghx caveat, and budget lines shared by the pass and refusal messages (#3767). */
|
|
208
|
+
function basisLines(tally, basis) {
|
|
209
|
+
const lines = [` Basis: ${tally.summary()}.`];
|
|
210
|
+
if (tally.unverified > 0) {
|
|
211
|
+
lines.push(" UNVERIFIED: state could not be established for the references below, so this run is", " not evidence that they are unshipped:");
|
|
212
|
+
for (const reason of tally.unverifiedReasons) {
|
|
213
|
+
lines.push(` - ${reason}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (basis.proxied) {
|
|
217
|
+
lines.push(" Note: reads resolved through `ghx`, a cached GET proxy; freshness is bounded by that", " proxy, which this gate cannot inspect (#3737).");
|
|
218
|
+
}
|
|
219
|
+
if (basis.elapsedMs > basis.budgetMs) {
|
|
220
|
+
lines.push(` Budget: took ${formatAge(basis.elapsedMs)} against a ${formatAge(basis.budgetMs)} budget (#3767).`);
|
|
221
|
+
}
|
|
222
|
+
return lines;
|
|
223
|
+
}
|
|
224
|
+
/** Keep an operator-supplied ref (or a reason quoting one) on a single line. */
|
|
225
|
+
function oneLine(text) {
|
|
226
|
+
return text.replace(/\r?\n/g, " ");
|
|
227
|
+
}
|
|
228
|
+
/** One operator-visible line naming what this run was allowed to look at (#3893). */
|
|
229
|
+
function scopeLines(scope, skipped) {
|
|
230
|
+
if (scope === null) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (scope.kind === "sweep") {
|
|
234
|
+
return [` Scope: repo-wide sweep -- ${oneLine(scope.reason)} (#3893).`];
|
|
235
|
+
}
|
|
236
|
+
const noun = skipped === 1 ? "brief" : "briefs";
|
|
237
|
+
const verb = skipped === 1 ? "was" : "were";
|
|
238
|
+
return [
|
|
239
|
+
` Scope: candidate diff against ${oneLine(scope.baseRef)}; ${skipped} running ${noun} outside ` +
|
|
240
|
+
`this branch's diff ${verb} not evaluated (#3893).`,
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
function summarizeScope(scope, skipped) {
|
|
244
|
+
if (scope === null) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return scope.kind === "sweep"
|
|
248
|
+
? { kind: "sweep", baseRef: null, reason: scope.reason, skipped }
|
|
249
|
+
: { kind: "diff", baseRef: scope.baseRef, reason: null, skipped };
|
|
250
|
+
}
|
|
251
|
+
function formatRefusal(orphans, projectRoot, issueFilter, tally, basis, scope, skipped) {
|
|
222
252
|
const shippedOrphans = orphans.filter((orphan) => orphan.kind === "shipped");
|
|
223
253
|
const unresolvedOrphans = orphans.filter((orphan) => orphan.kind === "unresolved");
|
|
224
254
|
const noun = orphans.length === 1 ? "" : "s";
|
|
@@ -241,6 +271,8 @@ function formatRefusal(orphans, projectRoot, issueFilter) {
|
|
|
241
271
|
lines.push(" task scope:cancel -- xbrief/active/<file>.xbrief.json # when abandoning", " For stop-at:pr-open workers the orchestrator owns post-merge complete/cancel;", " for drive-to:merge-ready workers scope:complete is part of the worker unit (#2321 / #3429).", " Or run task swarm:finalize-cohort / task swarm:complete-cohort after cohort merge.");
|
|
242
272
|
}
|
|
243
273
|
if (unresolvedOrphans.length > 0) {
|
|
274
|
+
// Only the scoped `--issue N` arm can report unresolved, so the retry never
|
|
275
|
+
// needs a --changed-only form (the sweep arm fails open on unknown).
|
|
244
276
|
const retry = issueFilter === null
|
|
245
277
|
? "task verify:orphan-active"
|
|
246
278
|
: `task verify:orphan-active -- --issue ${issueFilter}`;
|
|
@@ -250,27 +282,55 @@ function formatRefusal(orphans, projectRoot, issueFilter) {
|
|
|
250
282
|
for (const orphan of orphans) {
|
|
251
283
|
lines.push(` - ${orphan.path} (${orphan.reason})`);
|
|
252
284
|
}
|
|
285
|
+
lines.push(...basisLines(tally, basis), ...scopeLines(scope, skipped));
|
|
253
286
|
return lines.join("\n");
|
|
254
287
|
}
|
|
288
|
+
function emptyBasis(budgetMs) {
|
|
289
|
+
return {
|
|
290
|
+
inventory: 0,
|
|
291
|
+
live: 0,
|
|
292
|
+
cache: 0,
|
|
293
|
+
unverified: 0,
|
|
294
|
+
maxCacheAgeMs: null,
|
|
295
|
+
proxied: false,
|
|
296
|
+
elapsedMs: 0,
|
|
297
|
+
budgetMs,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
255
300
|
/**
|
|
256
301
|
* Pure evaluator for orphaned active/running xBRIEF detection (#2321 / #3429).
|
|
257
302
|
* Fails when active/ briefs with plan.status==running reference only closed
|
|
258
303
|
* issues and/or a merged PR — the stop-at:pr-open orphan signature.
|
|
259
304
|
* Pass `issue` to scan one origin after merge. Confirmed shipped prints
|
|
260
305
|
* scope:complete; unresolved lookup still exits 1 with a retry remediation.
|
|
306
|
+
*
|
|
307
|
+
* Issue state resolves by query shape (#3767): scoped `--issue N` takes an
|
|
308
|
+
* authoritative read and stays fail-closed on unknown; the unscoped sweep uses
|
|
309
|
+
* one complete, fail-closed open-issue inventory and stays fail-open on
|
|
310
|
+
* unknown so offline work is not network-authorized. A cache hit is honoured
|
|
311
|
+
* only inside `ISSUE_CACHE_MAX_AGE_MS`, and every verdict reports its basis.
|
|
312
|
+
*
|
|
313
|
+
* `changedOnly` is the merge-chokepoint form (#3893): only briefs the
|
|
314
|
+
* candidate's own diff touches are evaluated, so a brief stranded by another
|
|
315
|
+
* merge cannot fail this candidate. It falls back to the repo-wide sweep at
|
|
316
|
+
* the delivery tip and whenever the base ref is unresolvable.
|
|
261
317
|
*/
|
|
262
318
|
export function evaluate(projectRoot, options = {}) {
|
|
263
319
|
const root = resolve(projectRoot);
|
|
264
320
|
const quiet = options.quiet ?? false;
|
|
265
321
|
const skipGh = options.skipGh ?? false;
|
|
266
|
-
const
|
|
267
|
-
const
|
|
322
|
+
const issueFilter = options.issue ?? null;
|
|
323
|
+
const budgetMs = issueFilter === null ? AGGREGATE_LATENCY_BUDGET_MS : SCOPED_LATENCY_BUDGET_MS;
|
|
324
|
+
const clock = options.nowMs ?? Date.now;
|
|
325
|
+
const startedMs = clock();
|
|
268
326
|
if (!existsSync(root)) {
|
|
269
327
|
return {
|
|
270
328
|
code: 2,
|
|
271
329
|
message: `verify:orphan-active: project root does not exist: ${root}`,
|
|
272
330
|
stream: "stderr",
|
|
273
331
|
orphans: [],
|
|
332
|
+
basis: emptyBasis(budgetMs),
|
|
333
|
+
scope: null,
|
|
274
334
|
};
|
|
275
335
|
}
|
|
276
336
|
let lifecycleRoot;
|
|
@@ -282,14 +342,13 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
282
342
|
// Consumer/greenfield fixtures may still use legacy vbrief/ only (#2112).
|
|
283
343
|
// Orphan detection applies only to xbrief/active/ — skip cleanly, not config fail.
|
|
284
344
|
if (message.includes("No xbrief/ layout found")) {
|
|
285
|
-
if (quiet) {
|
|
286
|
-
return { code: 0, message: "", stream: "none", orphans: [] };
|
|
287
|
-
}
|
|
288
345
|
return {
|
|
289
346
|
code: 0,
|
|
290
|
-
message: "verify:orphan-active: no xbrief/ lifecycle root; nothing to scan.",
|
|
291
|
-
stream: "stdout",
|
|
347
|
+
message: quiet ? "" : "verify:orphan-active: no xbrief/ lifecycle root; nothing to scan.",
|
|
348
|
+
stream: quiet ? "none" : "stdout",
|
|
292
349
|
orphans: [],
|
|
350
|
+
basis: emptyBasis(budgetMs),
|
|
351
|
+
scope: null,
|
|
293
352
|
};
|
|
294
353
|
}
|
|
295
354
|
return {
|
|
@@ -297,58 +356,121 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
297
356
|
message: `verify:orphan-active: ${message}`,
|
|
298
357
|
stream: "stderr",
|
|
299
358
|
orphans: [],
|
|
359
|
+
basis: emptyBasis(budgetMs),
|
|
360
|
+
scope: null,
|
|
300
361
|
};
|
|
301
362
|
}
|
|
302
363
|
if (!existsSync(lifecycleRoot)) {
|
|
303
|
-
if (quiet) {
|
|
304
|
-
return { code: 0, message: "", stream: "none", orphans: [] };
|
|
305
|
-
}
|
|
306
364
|
return {
|
|
307
365
|
code: 0,
|
|
308
|
-
message: "verify:orphan-active: no xbrief/ lifecycle root; nothing to scan.",
|
|
309
|
-
stream: "stdout",
|
|
366
|
+
message: quiet ? "" : "verify:orphan-active: no xbrief/ lifecycle root; nothing to scan.",
|
|
367
|
+
stream: quiet ? "none" : "stdout",
|
|
310
368
|
orphans: [],
|
|
369
|
+
basis: emptyBasis(budgetMs),
|
|
370
|
+
scope: null,
|
|
311
371
|
};
|
|
312
372
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
for (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
373
|
+
const defaultRepo = resolveRepo(options.repo, root);
|
|
374
|
+
// Resolve the SCM binary lazily: an offline `--skip-gh` run or an empty
|
|
375
|
+
// active/ must not pay for (or fail on) binary resolution it never uses.
|
|
376
|
+
let runner = options.runGh === undefined ? null : { runGh: options.runGh, proxied: false };
|
|
377
|
+
const runGh = (cmd) => {
|
|
378
|
+
runner ??= makeGateRunner();
|
|
379
|
+
return runner.runGh(cmd);
|
|
380
|
+
};
|
|
381
|
+
const tally = new BasisTally();
|
|
382
|
+
const ctx = {
|
|
383
|
+
projectRoot: root,
|
|
384
|
+
runGh,
|
|
385
|
+
skipGh,
|
|
386
|
+
nowMs: clock(),
|
|
387
|
+
inventory: new OpenIssueInventory(runGh),
|
|
388
|
+
};
|
|
389
|
+
try {
|
|
390
|
+
// #3774: missing gh/ghx is config (code 2), not an uncaught throw.
|
|
391
|
+
const briefs = listActiveRunningBriefs(root);
|
|
392
|
+
// Merge-chokepoint scoping (#3893). `--issue N` is already one origin, and
|
|
393
|
+
// an empty active/ has nothing to scope, so neither pays for the git reads.
|
|
394
|
+
const scope = options.changedOnly === true && issueFilter === null && briefs.length > 0
|
|
395
|
+
? resolveCandidateScope(root, join(lifecycleRoot, "active"), {
|
|
396
|
+
baseRef: options.baseRef,
|
|
397
|
+
runGit: options.runGit,
|
|
398
|
+
})
|
|
399
|
+
: null;
|
|
400
|
+
const orphans = [];
|
|
401
|
+
let scanned = 0;
|
|
402
|
+
let skipped = 0;
|
|
403
|
+
for (const brief of briefs) {
|
|
404
|
+
if (scope?.kind === "diff" && !scope.paths.has(normalizeScopePath(brief.path))) {
|
|
405
|
+
skipped += 1;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
scanned += 1;
|
|
409
|
+
const { issues, prs } = collectGithubRefs(brief.plan, defaultRepo);
|
|
410
|
+
// --issue N is one origin: briefs that name that issue. PR-only briefs stay on the unscoped scan (#3429).
|
|
411
|
+
if (issueFilter !== null && !briefReferencesIssue(issues, issueFilter)) {
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (issues.length === 0 && prs.length === 0) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const assessment = assessOrphanSignature(issues, prs, ctx, issueFilter, tally);
|
|
418
|
+
if (assessment.orphaned && assessment.reason !== null && assessment.kind !== null) {
|
|
419
|
+
orphans.push({
|
|
420
|
+
path: relBriefPath(brief.path, root),
|
|
421
|
+
reason: assessment.reason,
|
|
422
|
+
kind: assessment.kind,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
320
425
|
}
|
|
321
|
-
|
|
322
|
-
|
|
426
|
+
const basis = {
|
|
427
|
+
inventory: tally.inventory,
|
|
428
|
+
live: tally.live,
|
|
429
|
+
cache: tally.cache,
|
|
430
|
+
unverified: tally.unverified,
|
|
431
|
+
maxCacheAgeMs: tally.cache > 0 ? tally.maxCacheAgeMs : null,
|
|
432
|
+
proxied: runner?.proxied ?? false,
|
|
433
|
+
elapsedMs: Math.max(0, clock() - startedMs),
|
|
434
|
+
budgetMs,
|
|
435
|
+
};
|
|
436
|
+
const scopeSummary = summarizeScope(scope, skipped);
|
|
437
|
+
if (orphans.length > 0) {
|
|
438
|
+
return {
|
|
439
|
+
code: 1,
|
|
440
|
+
message: formatRefusal(orphans, root, issueFilter, tally, basis, scope, skipped),
|
|
441
|
+
stream: "stderr",
|
|
442
|
+
orphans,
|
|
443
|
+
basis,
|
|
444
|
+
scope: scopeSummary,
|
|
445
|
+
};
|
|
323
446
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
orphans.push({
|
|
327
|
-
path: relBriefPath(brief.path, root),
|
|
328
|
-
reason: assessment.reason,
|
|
329
|
-
kind: assessment.kind,
|
|
330
|
-
});
|
|
447
|
+
if (quiet) {
|
|
448
|
+
return { code: 0, message: "", stream: "none", orphans: [], basis, scope: scopeSummary };
|
|
331
449
|
}
|
|
332
|
-
|
|
333
|
-
|
|
450
|
+
const issueNote = issueFilter === null ? "" : ` for issue #${issueFilter}`;
|
|
451
|
+
const headline = `verify:orphan-active: no orphaned active/running xBRIEFs${issueNote} ` +
|
|
452
|
+
`(scanned ${scanned} running brief${scanned === 1 ? "" : "s"} in active/).`;
|
|
334
453
|
return {
|
|
335
|
-
code:
|
|
336
|
-
message:
|
|
337
|
-
stream: "
|
|
338
|
-
orphans,
|
|
454
|
+
code: 0,
|
|
455
|
+
message: [headline, ...basisLines(tally, basis), ...scopeLines(scope, skipped)].join("\n"),
|
|
456
|
+
stream: "stdout",
|
|
457
|
+
orphans: [],
|
|
458
|
+
basis,
|
|
459
|
+
scope: scopeSummary,
|
|
339
460
|
};
|
|
340
461
|
}
|
|
341
|
-
|
|
342
|
-
|
|
462
|
+
catch (err) {
|
|
463
|
+
if (err instanceof ScmStubError) {
|
|
464
|
+
return {
|
|
465
|
+
code: 2,
|
|
466
|
+
message: `verify:orphan-active: ${err.message}`,
|
|
467
|
+
stream: "stderr",
|
|
468
|
+
orphans: [],
|
|
469
|
+
basis: emptyBasis(budgetMs),
|
|
470
|
+
scope: null,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
throw err;
|
|
343
474
|
}
|
|
344
|
-
const scanned = listActiveRunningBriefs(root).length;
|
|
345
|
-
const issueNote = issueFilter === null ? "" : ` for issue #${issueFilter}`;
|
|
346
|
-
return {
|
|
347
|
-
code: 0,
|
|
348
|
-
message: `verify:orphan-active: no orphaned active/running xBRIEFs${issueNote} ` +
|
|
349
|
-
`(scanned ${scanned} running brief${scanned === 1 ? "" : "s"} in active/).`,
|
|
350
|
-
stream: "stdout",
|
|
351
|
-
orphans: [],
|
|
352
|
-
};
|
|
353
475
|
}
|
|
354
476
|
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { RunGhFn } from "../pr-protected-issues/types.js";
|
|
2
|
+
import type { IssueRef } from "./refs.js";
|
|
3
|
+
export type IssueState = "open" | "closed";
|
|
4
|
+
/**
|
|
5
|
+
* How a verdict was reached (#3767). `unverified` means the gate could not
|
|
6
|
+
* establish state, so a pass on that reference proves nothing.
|
|
7
|
+
*/
|
|
8
|
+
export type StateBasis = "inventory" | "live" | "cache" | "unverified";
|
|
9
|
+
export type StateResolution =
|
|
10
|
+
/** Resolved from the complete open-issue inventory or an authoritative read. */
|
|
11
|
+
{
|
|
12
|
+
readonly state: IssueState;
|
|
13
|
+
readonly basis: "inventory" | "live";
|
|
14
|
+
}
|
|
15
|
+
/** Resolved from a triage-cache entry inside the freshness bound. */
|
|
16
|
+
| {
|
|
17
|
+
readonly state: IssueState;
|
|
18
|
+
readonly basis: "cache";
|
|
19
|
+
readonly cacheAgeMs: number;
|
|
20
|
+
}
|
|
21
|
+
/** State could not be established; a pass on this reference proves nothing. */
|
|
22
|
+
| {
|
|
23
|
+
readonly state: null;
|
|
24
|
+
readonly basis: "unverified";
|
|
25
|
+
readonly detail: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A cache hit older than this is no longer evidence (#3767). The measured
|
|
29
|
+
* defect was a ~12 h old cached `open` suppressing the live read that would
|
|
30
|
+
* have corrected it; 15 min keeps warm-cache offline runs working while making
|
|
31
|
+
* an overnight entry non-authoritative.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ISSUE_CACHE_MAX_AGE_MS: number;
|
|
34
|
+
/**
|
|
35
|
+
* Latency budget for scoped `--issue N`: one authoritative issue read plus at
|
|
36
|
+
* most one linked-PR read. Measured ~0.75 s per `gh api` read; the headroom
|
|
37
|
+
* covers a slow network or a `ghx` proxy hop.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SCOPED_LATENCY_BUDGET_MS = 5000;
|
|
40
|
+
/**
|
|
41
|
+
* Latency budget for the unscoped aggregate sweep. One complete open-issue
|
|
42
|
+
* inventory measured 4.2 s constant on deftai/directive (560 open issues),
|
|
43
|
+
* against 14.2 s and 76.6 s for per-brief reads at the WIP cap of 20 on two
|
|
44
|
+
* hosts. The budget is sized for the inventory plus a few confirming reads and
|
|
45
|
+
* keeps the gate inside the fast-preflight tier.
|
|
46
|
+
*/
|
|
47
|
+
export declare const AGGREGATE_LATENCY_BUDGET_MS = 15000;
|
|
48
|
+
export interface GateRunner {
|
|
49
|
+
readonly runGh: RunGhFn;
|
|
50
|
+
/**
|
|
51
|
+
* True when reads resolve through `ghx`, a cached GET proxy. The freshness
|
|
52
|
+
* guarantee is then bounded by that proxy, which this gate cannot inspect.
|
|
53
|
+
*/
|
|
54
|
+
readonly proxied: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Pin plain `gh` for this gate's authoritative reads (#3767). `resolveBinary`
|
|
58
|
+
* prefers `ghx` — a cached GET proxy — so an unpinned "live" read is itself a
|
|
59
|
+
* cache. When `gh` is absent we still run, and report the proxy caveat rather
|
|
60
|
+
* than claiming freshness we cannot deliver. Whether `ghx` should be preferred
|
|
61
|
+
* at all is #3737.
|
|
62
|
+
*/
|
|
63
|
+
export declare function makeGateRunner(): GateRunner;
|
|
64
|
+
interface CachedIssue {
|
|
65
|
+
readonly state: IssueState | null;
|
|
66
|
+
readonly ageMs: number | null;
|
|
67
|
+
}
|
|
68
|
+
/** Read the triage-cache issue state together with the entry's age (#3767). */
|
|
69
|
+
export declare function readCachedIssue(projectRoot: string, ref: IssueRef, nowMs: number): CachedIssue;
|
|
70
|
+
/** Authoritative single-issue REST read. Returns `null` on any failure. */
|
|
71
|
+
export declare function fetchIssueStateLive(ref: IssueRef, runGh: RunGhFn): IssueState | null;
|
|
72
|
+
type InventoryLookup = {
|
|
73
|
+
numbers: ReadonlySet<number>;
|
|
74
|
+
} | {
|
|
75
|
+
error: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* One complete open-issue inventory per repo per run (#3767, reusing #3752).
|
|
79
|
+
* `restIssueListOpenInventory` fails closed on command failure, non-array JSON,
|
|
80
|
+
* malformed rows, buffer exhaustion, and pagination cap — so absence from a
|
|
81
|
+
* successful inventory really does mean "not open", and a truncated read is an
|
|
82
|
+
* error rather than a silent "closed".
|
|
83
|
+
*/
|
|
84
|
+
export declare class OpenIssueInventory {
|
|
85
|
+
private readonly runGh;
|
|
86
|
+
private readonly byRepo;
|
|
87
|
+
private readonly confirmations;
|
|
88
|
+
constructor(runGh: RunGhFn);
|
|
89
|
+
lookup(repo: string): InventoryLookup;
|
|
90
|
+
/**
|
|
91
|
+
* Confirm an inventory absence with one authoritative read, reused for the
|
|
92
|
+
* rest of this run when several briefs name the same issue (#3767).
|
|
93
|
+
*/
|
|
94
|
+
confirm(ref: IssueRef): IssueState | null;
|
|
95
|
+
private fetch;
|
|
96
|
+
}
|
|
97
|
+
export interface ResolveContext {
|
|
98
|
+
readonly projectRoot: string;
|
|
99
|
+
readonly runGh: RunGhFn;
|
|
100
|
+
readonly skipGh: boolean;
|
|
101
|
+
readonly nowMs: number;
|
|
102
|
+
readonly inventory: OpenIssueInventory;
|
|
103
|
+
}
|
|
104
|
+
/** Human-readable duration for basis reporting: `42s`, `9m`, `12h`, `3d`. */
|
|
105
|
+
export declare function formatAge(ms: number): string;
|
|
106
|
+
/**
|
|
107
|
+
* Scoped `--issue N`: a proof obligation before DONE, so read authoritatively
|
|
108
|
+
* first and accept a cache hit only inside the freshness bound. Unknown stays
|
|
109
|
+
* fail-closed at the call site.
|
|
110
|
+
*/
|
|
111
|
+
export declare function resolveIssueStateScoped(ref: IssueRef, ctx: ResolveContext): StateResolution;
|
|
112
|
+
/**
|
|
113
|
+
* Unscoped aggregate: resolve every reference from one complete open-issue
|
|
114
|
+
* inventory rather than a live read per brief. Membership means open. Absence
|
|
115
|
+
* means "not open", which is the direction that tells an operator to run
|
|
116
|
+
* `scope:complete`, so it is confirmed by one authoritative read before the
|
|
117
|
+
* gate acts on it.
|
|
118
|
+
*/
|
|
119
|
+
export declare function resolveIssueStateAggregate(ref: IssueRef, ctx: ResolveContext): StateResolution;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=issue-state.d.ts.map
|