@deftai/directive-core 0.107.0 → 0.108.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/check/cached-orchestrator.js +28 -3
- package/dist/check/named-cause.js +18 -3
- package/dist/codebase/provider.js +7 -1
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/design-critique/citation-grammar.d.ts +53 -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 +232 -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 +76 -0
- package/dist/hooks/classify/host-session-identity.js +470 -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 +7 -0
- package/dist/hooks/classify/paths.js +35 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +27 -0
- package/dist/hooks/dispatcher.d.ts +52 -1
- package/dist/hooks/dispatcher.js +538 -45
- package/dist/hooks/fixtures/cases.d.ts +16 -2
- package/dist/hooks/fixtures/cases.js +175 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/issue-ingest.js +62 -2
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/orphan-active/evaluate.d.ts +25 -0
- package/dist/orphan-active/evaluate.js +193 -128
- package/dist/orphan-active/index.d.ts +1 -0
- package/dist/orphan-active/index.js +1 -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/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-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +7 -3
- 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 +11 -0
- 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 +17 -7
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -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 +19 -0
- package/dist/release/types.d.ts +14 -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 +182 -1
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/scm/call.js +12 -3
- 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 +9 -1
- package/dist/scm/gh-rest.js +103 -0
- package/dist/scm/readiness.d.ts +2 -0
- package/dist/scm/readiness.js +28 -4
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/session/git.d.ts +24 -0
- package/dist/session/git.js +138 -2
- package/dist/session/occupancy.d.ts +238 -12
- package/dist/session/occupancy.js +815 -29
- 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-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/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/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/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/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/package.json +7 -3
|
@@ -16,6 +16,7 @@ import { spawnSync } from "node:child_process";
|
|
|
16
16
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
17
17
|
import { basename, extname, join, resolve } from "node:path";
|
|
18
18
|
import { hasArtifactSuffix, LIFECYCLE_DIR_NAMES } from "../layout/resolve.js";
|
|
19
|
+
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
20
21
|
// Classification constants
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
@@ -776,6 +777,7 @@ function gitDiff(projectRoot, baseRef) {
|
|
|
776
777
|
cwd: resolve(projectRoot),
|
|
777
778
|
encoding: "utf8",
|
|
778
779
|
timeout: 30_000,
|
|
780
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
779
781
|
});
|
|
780
782
|
if (result.error) {
|
|
781
783
|
return [
|
|
@@ -6,6 +6,7 @@ import { readCorePackageVersion } from "../engine-version.js";
|
|
|
6
6
|
import { applyProductFirstGateMode, EMPTY_AC_CAUSE, EMPTY_AC_REMEDY, isHygieneGate, isProductAcGate, isSoftEmptyAcText, resolveProductFirstCheckMode, } from "../product-first-done-gate/index.js";
|
|
7
7
|
import { RunSummaryEmitter } from "../run-summary/emit.js";
|
|
8
8
|
import { runToolchainPreflight, SKIP_ALL_GATES, } from "../session/toolchain-preflight.js";
|
|
9
|
+
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
9
10
|
import { checkGateCliArgv, cliSpawnPlan, resolveGateDispatch, resolveGlobalCliBin, } from "./cli-native-gates.js";
|
|
10
11
|
import { evaluateConsumerGateIntegrity, formatConsumerGateIntegrityFailure, } from "./consumer-gate-integrity.js";
|
|
11
12
|
import { resolveCheckTarget } from "./context.js";
|
|
@@ -17,6 +18,7 @@ function captureSpawn(taskBin, args, opts) {
|
|
|
17
18
|
cwd: opts.cwd,
|
|
18
19
|
encoding: "utf8",
|
|
19
20
|
env: opts.env ?? process.env,
|
|
21
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
20
22
|
});
|
|
21
23
|
if (result.error !== undefined) {
|
|
22
24
|
return {
|
|
@@ -40,6 +42,27 @@ function writeLines(lines, stream = "stderr") {
|
|
|
40
42
|
write(`${line}\n`);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
45
|
+
function firstImpactingMissingFinding(preflight) {
|
|
46
|
+
const missing = preflight?.findings.filter((finding) => !finding.present) ?? [];
|
|
47
|
+
const impacting = missing.filter((finding) => finding.impact !== "none");
|
|
48
|
+
const priority = [
|
|
49
|
+
"node",
|
|
50
|
+
"task",
|
|
51
|
+
"cli_dist",
|
|
52
|
+
"package_manager",
|
|
53
|
+
preflight?.packageManager,
|
|
54
|
+
"npm",
|
|
55
|
+
"pnpm",
|
|
56
|
+
];
|
|
57
|
+
for (const tool of priority) {
|
|
58
|
+
if (tool === null || tool === undefined)
|
|
59
|
+
continue;
|
|
60
|
+
const finding = impacting.find((candidate) => candidate.tool === tool);
|
|
61
|
+
if (finding !== undefined)
|
|
62
|
+
return finding;
|
|
63
|
+
}
|
|
64
|
+
return impacting[0] ?? missing[0];
|
|
65
|
+
}
|
|
43
66
|
/**
|
|
44
67
|
* Run check gates sequentially with content-hash caching (#1713).
|
|
45
68
|
* Falls back to fail-open execution for undeclared / non-cacheable gates.
|
|
@@ -135,6 +158,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
|
|
|
135
158
|
composedGates: gates,
|
|
136
159
|
consumerDeposit: target === "check:consumer",
|
|
137
160
|
which,
|
|
161
|
+
env: options.env,
|
|
138
162
|
})
|
|
139
163
|
: options.preflight;
|
|
140
164
|
const taskPresent = preflight === null || !preflight.findings.some((f) => f.tool === "task" && !f.present);
|
|
@@ -165,8 +189,9 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
|
|
|
165
189
|
if (allSkipped) {
|
|
166
190
|
const skipped = gates.map((g) => {
|
|
167
191
|
const id = checkGateId(g);
|
|
168
|
-
const
|
|
169
|
-
const
|
|
192
|
+
const missing = firstImpactingMissingFinding(preflight);
|
|
193
|
+
const cause = missing?.cause ?? "toolchain preflight degraded";
|
|
194
|
+
const remedy = missing?.remedy ?? remedyForGate(id, cause);
|
|
170
195
|
gateOutcomes.push({ id, status: "skipped", cause, remedy });
|
|
171
196
|
return { id, cause, remedy };
|
|
172
197
|
});
|
|
@@ -183,7 +208,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
|
|
|
183
208
|
const gateId = checkGateId(gateSpec);
|
|
184
209
|
// #3282: skip gates that require missing tools (e.g. pnpm-only suite).
|
|
185
210
|
if (skipSet.has(gateId)) {
|
|
186
|
-
const missing = preflight
|
|
211
|
+
const missing = firstImpactingMissingFinding(preflight);
|
|
187
212
|
const cause = missing?.cause ?? "toolchain preflight marked gate skippable";
|
|
188
213
|
const remedy = missing?.remedy ?? remedyForGate(gateId, cause);
|
|
189
214
|
process.stderr.write(`check: skipping gate ${gateId} (degraded) — cause: ${cause}; remedy: ${remedy}\n`);
|
|
@@ -13,7 +13,7 @@ const GATE_REMEDIES = {
|
|
|
13
13
|
"verify:wip-cap": "Demote stale pending scopes (task scope:demote) or raise plan.policy.wipCap deliberately",
|
|
14
14
|
doctor: "Run task doctor and follow the named recovery steps",
|
|
15
15
|
"toolchain:check": "Install missing maintainer tools reported by the gate (go, uv, git, gh, node, pnpm)",
|
|
16
|
-
"toolchain:check-consumer": "Install missing consumer
|
|
16
|
+
"toolchain:check-consumer": "Install the missing consumer tool reported by the gate; use npm from Node or enable pnpm with Corepack as declared by package.json",
|
|
17
17
|
"ts:check-lane": "Fix lint/type/test failures; re-run task ts:check-lane",
|
|
18
18
|
"vbrief:validate": "Fix xBRIEF/vBRIEF schema errors reported by the gate",
|
|
19
19
|
"verify-strategy-output": "Re-run strategy output or fix non-conformant scope filenames / PROJECT-DEFINITION",
|
|
@@ -57,6 +57,12 @@ export function extractGateCause(stdout, stderr, exitCode, spawnError, gateId) {
|
|
|
57
57
|
useful.push(line);
|
|
58
58
|
}
|
|
59
59
|
const gateHint = gateId?.trim() ?? "";
|
|
60
|
+
if (gateHint === "toolchain:check" || gateHint === "toolchain:check-consumer") {
|
|
61
|
+
const toolFailure = useful.find((line) => /^(?:package manager|go|uv|git|gh|node|npm|pnpm|task): (?:NOT FOUND|FAILED|ERROR)\b/i.test(line));
|
|
62
|
+
if (toolFailure !== undefined) {
|
|
63
|
+
return sanitizeCauseLine(toolFailure);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
60
66
|
if (gateHint.length > 0) {
|
|
61
67
|
const named = useful.find((line) => line.includes(gateHint));
|
|
62
68
|
if (named !== undefined) {
|
|
@@ -106,14 +112,23 @@ export function remedyForGate(gateId, cause) {
|
|
|
106
112
|
return CLI_SPAWN_ERROR_REMEDY;
|
|
107
113
|
}
|
|
108
114
|
if (/task binary not found|cannot spawn go-task/i.test(cause)) {
|
|
109
|
-
if (gateId.startsWith("verify:") ||
|
|
115
|
+
if (gateId.startsWith("verify:") ||
|
|
116
|
+
gateId.startsWith("verify-") ||
|
|
117
|
+
gateId === "doctor" ||
|
|
118
|
+
gateId === "toolchain:check-consumer") {
|
|
110
119
|
return CLI_SPAWN_ERROR_REMEDY;
|
|
111
120
|
}
|
|
112
121
|
return SPAWN_ERROR_REMEDY;
|
|
113
122
|
}
|
|
114
|
-
if (/
|
|
123
|
+
if (/Unsupported DEFT_PACKAGE_MANAGER value/i.test(cause)) {
|
|
124
|
+
return "Set DEFT_PACKAGE_MANAGER to npm or pnpm, or unset it to use package.json; then re-run the consumer check";
|
|
125
|
+
}
|
|
126
|
+
if (/\bpnpm(?: binary not found|: (?:NOT FOUND|FAILED|ERROR))/i.test(cause)) {
|
|
115
127
|
return "Enable pnpm: corepack enable && corepack prepare pnpm@latest --activate";
|
|
116
128
|
}
|
|
129
|
+
if (/\bnpm(?: binary not found|: (?:NOT FOUND|FAILED|ERROR))/i.test(cause)) {
|
|
130
|
+
return "Install or repair Node 20+ (npm is bundled), then re-run the consumer check";
|
|
131
|
+
}
|
|
117
132
|
return (GATE_REMEDIES[gateId] ??
|
|
118
133
|
`Re-run the gate for details: task ${gateId} (or task check); fix the reported product/process defect`);
|
|
119
134
|
}
|
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { contentRoot } from "../content-root.js";
|
|
7
7
|
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
8
8
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
9
|
+
import { resolveCaptureFailureStderr, SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
9
10
|
import { loadJsonFile } from "../verify-source/code-structure-validate.js";
|
|
10
11
|
import { CODEBASE_MAP_SCHEMA_PATH } from "./constants.js";
|
|
11
12
|
import { buildCodebaseMap, CodeStructureConfigError, configErrorToDict, defaultCodeStructurePath, fileSha256, } from "./default-extractor.js";
|
|
@@ -547,6 +548,7 @@ function runProviderCommand(command, cwd) {
|
|
|
547
548
|
cwd,
|
|
548
549
|
encoding: "utf8",
|
|
549
550
|
timeout: 60_000,
|
|
551
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
550
552
|
stdio: ["ignore", "pipe", "pipe"],
|
|
551
553
|
});
|
|
552
554
|
return { returncode: 0, stdout, stderr: "" };
|
|
@@ -556,7 +558,11 @@ function runProviderCommand(command, cwd) {
|
|
|
556
558
|
return {
|
|
557
559
|
returncode: typeof e.status === "number" ? e.status : 1,
|
|
558
560
|
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
559
|
-
stderr:
|
|
561
|
+
stderr: resolveCaptureFailureStderr({
|
|
562
|
+
captured: typeof e.stderr === "string" ? e.stderr : "",
|
|
563
|
+
status: e.status,
|
|
564
|
+
message: e.message ?? String(err),
|
|
565
|
+
}),
|
|
560
566
|
};
|
|
561
567
|
}
|
|
562
568
|
}
|
|
@@ -8,7 +8,7 @@ import { utcIso } from "./types.js";
|
|
|
8
8
|
export function nextSafeActionFor(decision) {
|
|
9
9
|
switch (decision) {
|
|
10
10
|
case "DENY_DUPLICATE_ACTIVE":
|
|
11
|
-
return "
|
|
11
|
+
return "A running attempt is not proof the worker is alive. Takeover: swarm:pre-dispatch --action cancel, then begin. REDISPATCH_OK does not lift this deny.";
|
|
12
12
|
case "BLOCK_NON_RETRYABLE":
|
|
13
13
|
return "Fix the deterministic configuration/schema/permission failure, record a relevant material delta, then resume.";
|
|
14
14
|
case "BLOCK_NO_MATERIAL_PROGRESS":
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation grammar for the design-critique completed-arc record (#3831).
|
|
3
|
+
*
|
|
4
|
+
* One parser answers both questions the ingest predicate asks: which comment
|
|
5
|
+
* ids a synthesis cites, and whether it claims a verified-claims table. The
|
|
6
|
+
* closed accepted set and the refused positions are published in
|
|
7
|
+
* `content/contracts/design-critique.md` `## Citation grammar`; this module is
|
|
8
|
+
* the only implementation of that grammar.
|
|
9
|
+
*
|
|
10
|
+
* Which code-span convention governs. The intake cross-ref scanners
|
|
11
|
+
* (`intake/markdown-scanners.ts`) delete code spans, so for them a backticked
|
|
12
|
+
* id is an example and never a reference. The citation scan takes the opposite
|
|
13
|
+
* polarity for the id token only: a balanced single-backtick id is an accepted
|
|
14
|
+
* citation, because arc comments are hand-written prose and the mandated lean
|
|
15
|
+
* heading is itself `**Lean:**`. A keyword inside a code span or a fence stays
|
|
16
|
+
* an example in both layers. Positions are classified, never stripped -- the
|
|
17
|
+
* strippers delete a span with its contents, which destroys the digits. The
|
|
18
|
+
* prior art for classify-not-strip is `classifyHit`
|
|
19
|
+
* (`pr-closing-keywords/detect.ts`).
|
|
20
|
+
*
|
|
21
|
+
* The fence scan is local rather than borrowed from that detector because the
|
|
22
|
+
* detector's opener regex is anchored at column zero, so a CommonMark fence
|
|
23
|
+
* indented one to three spaces reads as prose. Relaxing it there would move
|
|
24
|
+
* hits into the #737 false-positive class and change what that gate suppresses,
|
|
25
|
+
* which is a separate decision from this one.
|
|
26
|
+
*/
|
|
27
|
+
export type CitationKind = "lean" | "table" | "comment";
|
|
28
|
+
export type CitationRejectionClass = "code-fence" | "inline-code" | "blockquote" | "strikethrough" | "negation";
|
|
29
|
+
export type Citation = {
|
|
30
|
+
readonly id: number;
|
|
31
|
+
readonly kind: CitationKind;
|
|
32
|
+
};
|
|
33
|
+
export type RejectedCitation = {
|
|
34
|
+
readonly id: number;
|
|
35
|
+
readonly reason: CitationRejectionClass;
|
|
36
|
+
};
|
|
37
|
+
export type CitationScan = {
|
|
38
|
+
/** Accepted citations in document order, deduplicated on (id, kind). */
|
|
39
|
+
readonly citations: readonly Citation[];
|
|
40
|
+
/** Keyword-anchored or permalink occurrences refused by position. */
|
|
41
|
+
readonly rejected: readonly RejectedCitation[];
|
|
42
|
+
/** Every 8-or-more digit run in the body, for deterministic diagnostics. */
|
|
43
|
+
readonly idShapedRuns: readonly number[];
|
|
44
|
+
};
|
|
45
|
+
/** The published closed set, in the order the contract lists it. */
|
|
46
|
+
export declare const ACCEPTED_CITATION_FORMS: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* Parse the closed citation grammar out of one comment body. Refused positions
|
|
49
|
+
* are reported rather than dropped so a block detail can echo the observation
|
|
50
|
+
* instead of guessing at a cause.
|
|
51
|
+
*/
|
|
52
|
+
export declare function scanCitations(body: string): CitationScan;
|
|
53
|
+
//# sourceMappingURL=citation-grammar.d.ts.map
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation grammar for the design-critique completed-arc record (#3831).
|
|
3
|
+
*
|
|
4
|
+
* One parser answers both questions the ingest predicate asks: which comment
|
|
5
|
+
* ids a synthesis cites, and whether it claims a verified-claims table. The
|
|
6
|
+
* closed accepted set and the refused positions are published in
|
|
7
|
+
* `content/contracts/design-critique.md` `## Citation grammar`; this module is
|
|
8
|
+
* the only implementation of that grammar.
|
|
9
|
+
*
|
|
10
|
+
* Which code-span convention governs. The intake cross-ref scanners
|
|
11
|
+
* (`intake/markdown-scanners.ts`) delete code spans, so for them a backticked
|
|
12
|
+
* id is an example and never a reference. The citation scan takes the opposite
|
|
13
|
+
* polarity for the id token only: a balanced single-backtick id is an accepted
|
|
14
|
+
* citation, because arc comments are hand-written prose and the mandated lean
|
|
15
|
+
* heading is itself `**Lean:**`. A keyword inside a code span or a fence stays
|
|
16
|
+
* an example in both layers. Positions are classified, never stripped -- the
|
|
17
|
+
* strippers delete a span with its contents, which destroys the digits. The
|
|
18
|
+
* prior art for classify-not-strip is `classifyHit`
|
|
19
|
+
* (`pr-closing-keywords/detect.ts`).
|
|
20
|
+
*
|
|
21
|
+
* The fence scan is local rather than borrowed from that detector because the
|
|
22
|
+
* detector's opener regex is anchored at column zero, so a CommonMark fence
|
|
23
|
+
* indented one to three spaces reads as prose. Relaxing it there would move
|
|
24
|
+
* hits into the #737 false-positive class and change what that gate suppresses,
|
|
25
|
+
* which is a separate decision from this one.
|
|
26
|
+
*/
|
|
27
|
+
/** The published closed set, in the order the contract lists it. */
|
|
28
|
+
export const ACCEPTED_CITATION_FORMS = [
|
|
29
|
+
"successor lean 12345678",
|
|
30
|
+
"successor lean: 12345678",
|
|
31
|
+
"successor lean `12345678`",
|
|
32
|
+
"**successor lean:** 12345678",
|
|
33
|
+
"#issuecomment-12345678",
|
|
34
|
+
"/issues/comments/12345678",
|
|
35
|
+
];
|
|
36
|
+
const EMPHASIS = "(?:\\*\\*|\\*)";
|
|
37
|
+
const KEYWORD = "(successor[ \\t]+lean|verified-claims[ \\t]+table|lean|comment)";
|
|
38
|
+
/**
|
|
39
|
+
* Colon and horizontal whitespace only. One of the three branches must consume
|
|
40
|
+
* a colon or a space, so `lean12345678` is not a citation.
|
|
41
|
+
*/
|
|
42
|
+
const SEPARATOR = `(?::${EMPHASIS}?[ \\t]*|${EMPHASIS}:?[ \\t]+|[ \\t]+)`;
|
|
43
|
+
/** Bare decimal, or a balanced single-backtick decimal. Nothing else. */
|
|
44
|
+
const ID_TOKEN = "(?:`(\\d{8,})`|(\\d{8,})\\b)";
|
|
45
|
+
const CITATION_RE = new RegExp(`${EMPHASIS}?\\b${KEYWORD}${SEPARATOR}${ID_TOKEN}` +
|
|
46
|
+
"|#issuecomment-(\\d{8,})" +
|
|
47
|
+
"|/issues/comments/(\\d{8,})", "gi");
|
|
48
|
+
const ID_RUN_RE = /\d{8,}/g;
|
|
49
|
+
const BLOCKQUOTE_RE = /^ {0,3}>/;
|
|
50
|
+
/** CommonMark fence delimiter: up to three leading spaces, the run, then the rest. */
|
|
51
|
+
const FENCE_LINE_RE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
52
|
+
/** The last sentence break in a slice, and everything after it. */
|
|
53
|
+
const LAST_SENTENCE_RE = /[.!?;][^.!?;]*$/;
|
|
54
|
+
/** Everything up to the first sentence break. */
|
|
55
|
+
const FIRST_SENTENCE_RE = /^[^.!?;]*/;
|
|
56
|
+
/** The closed set of explicit negation markers the class recognises. */
|
|
57
|
+
const NEGATION_MARKER = "\\b(?:cannot|never|no longer" +
|
|
58
|
+
"|(?:do|does|did|is|are|was|were|has|have|had|will|would|shall|should|must|can|could|may|might)\\s+not" +
|
|
59
|
+
"|(?:do|does|did|is|are|was|were|has|have|had|wo|would|should|must|ca|could|sha)n['\\u2019]t)\\b";
|
|
60
|
+
/**
|
|
61
|
+
* Explicit negation of the citation itself, ending within three plain words of
|
|
62
|
+
* the keyword. A bare marker anywhere in the sentence prefix is not enough: it
|
|
63
|
+
* reads `without a doubt, successor lean N is accepted` and `not only successor
|
|
64
|
+
* lean N but also the table` as refusals, which blocks a valid record.
|
|
65
|
+
*/
|
|
66
|
+
const NEGATED_CITATION_RE = new RegExp(`${NEGATION_MARKER}(?:\\s+[A-Za-z][A-Za-z'\\u2019-]*){0,3}\\s*$`, "i");
|
|
67
|
+
/** The same markers, unanchored, for the complement clause after a citation. */
|
|
68
|
+
const NEGATION_ANYWHERE_RE = new RegExp(NEGATION_MARKER, "i");
|
|
69
|
+
/**
|
|
70
|
+
* A negated verb of denial affirms the citation rather than refusing it, so the
|
|
71
|
+
* negation belongs to the verb and not to the occurrence: `we cannot deny that
|
|
72
|
+
* successor lean N binds` cites N. The verb set is closed and published --
|
|
73
|
+
* negating `deny`, `doubt`, `dispute`, `contest`, or `question` affirms the
|
|
74
|
+
* complement clause those verbs introduce.
|
|
75
|
+
*
|
|
76
|
+
* A trailing `that` alone is not the signal, because `that` is also a
|
|
77
|
+
* determiner. `do not use that successor lean N` and the cleft `the record is
|
|
78
|
+
* not that successor lean N` are genuine refusals, and both keep the negation
|
|
79
|
+
* class. The anchor is the end of the sentence segment, so a second negation
|
|
80
|
+
* before the keyword still binds: `we do not doubt that this does not bind
|
|
81
|
+
* successor lean N` is refused.
|
|
82
|
+
*
|
|
83
|
+
* The carve-out only suspends a prefix rule that already fired; it never
|
|
84
|
+
* refuses on its own, so it cannot widen the refused set. It also does not
|
|
85
|
+
* accept the citation outright. The complement clause is what carries the
|
|
86
|
+
* claim, so a negation anywhere in the rest of that sentence keeps the refusal:
|
|
87
|
+
* `we do not doubt that successor lean N does not bind` says the lean does not
|
|
88
|
+
* bind. That scan runs to the sentence break rather than to a clause boundary,
|
|
89
|
+
* which refuses more than it strictly must inside this one carve-out -- the
|
|
90
|
+
* fail-closed direction, and every such body was already refused before it.
|
|
91
|
+
*/
|
|
92
|
+
const DENIAL_COMPLEMENT_RE = /\b(?:den(?:y|ies|ied|ying)|doubt(?:s|ed|ing)?|disput(?:e|es|ed|ing)|contest(?:s|ed|ing)?|question(?:s|ed|ing)?)\s+that\s*$/i;
|
|
93
|
+
function kindForKeyword(keyword) {
|
|
94
|
+
if (keyword === undefined)
|
|
95
|
+
return "comment";
|
|
96
|
+
const normalized = keyword.toLowerCase().replace(/[ \t]+/g, " ");
|
|
97
|
+
if (normalized.startsWith("verified-claims"))
|
|
98
|
+
return "table";
|
|
99
|
+
if (normalized === "comment")
|
|
100
|
+
return "comment";
|
|
101
|
+
return "lean";
|
|
102
|
+
}
|
|
103
|
+
function lineBounds(text, offset) {
|
|
104
|
+
const start = text.lastIndexOf("\n", offset - 1) + 1;
|
|
105
|
+
const found = text.indexOf("\n", offset);
|
|
106
|
+
return { start, end: found === -1 ? text.length : found };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* CommonMark inline code: a run of N backticks opens a span that only a run of
|
|
110
|
+
* exactly N backticks closes. Counting single backticks would read a
|
|
111
|
+
* double-backtick span as balanced and let documentation prose cite.
|
|
112
|
+
*
|
|
113
|
+
* `text` is the whole span from the enclosing block start, not one line: a code
|
|
114
|
+
* span may carry a newline, so a line-scoped count cannot see the opener.
|
|
115
|
+
*/
|
|
116
|
+
function isInsideInlineCode(text) {
|
|
117
|
+
let openRun = 0;
|
|
118
|
+
let i = 0;
|
|
119
|
+
while (i < text.length) {
|
|
120
|
+
if (text[i] !== "`") {
|
|
121
|
+
i += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
let run = 0;
|
|
125
|
+
while (i + run < text.length && text[i + run] === "`")
|
|
126
|
+
run += 1;
|
|
127
|
+
if (openRun === 0) {
|
|
128
|
+
openRun = run;
|
|
129
|
+
}
|
|
130
|
+
else if (run === openRun) {
|
|
131
|
+
openRun = 0;
|
|
132
|
+
}
|
|
133
|
+
i += run;
|
|
134
|
+
}
|
|
135
|
+
return openRun !== 0;
|
|
136
|
+
}
|
|
137
|
+
function isStruckThrough(text) {
|
|
138
|
+
let runs = 0;
|
|
139
|
+
let i = 0;
|
|
140
|
+
while (i < text.length - 1) {
|
|
141
|
+
if (text[i] === "~" && text[i + 1] === "~") {
|
|
142
|
+
runs += 1;
|
|
143
|
+
i += 2;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
i += 1;
|
|
147
|
+
}
|
|
148
|
+
return runs % 2 === 1;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A negation binds the sentence it sits in, not the whole line. Scoping to the
|
|
152
|
+
* text after the last sentence break keeps `this is not stale. Bound contract
|
|
153
|
+
* is successor lean N` an affirmative citation.
|
|
154
|
+
*/
|
|
155
|
+
function isNegated(line, column) {
|
|
156
|
+
const before = line.slice(0, column);
|
|
157
|
+
const tail = LAST_SENTENCE_RE.exec(before);
|
|
158
|
+
const segment = tail === null ? before : before.slice(tail.index + 1);
|
|
159
|
+
if (!NEGATED_CITATION_RE.test(segment))
|
|
160
|
+
return false;
|
|
161
|
+
if (!DENIAL_COMPLEMENT_RE.test(segment))
|
|
162
|
+
return true;
|
|
163
|
+
const after = line.slice(column);
|
|
164
|
+
return NEGATION_ANYWHERE_RE.test(FIRST_SENTENCE_RE.exec(after)?.[0] ?? after);
|
|
165
|
+
}
|
|
166
|
+
function fenceDelimiter(line) {
|
|
167
|
+
const match = FENCE_LINE_RE.exec(line);
|
|
168
|
+
if (match === null)
|
|
169
|
+
return null;
|
|
170
|
+
const run = match[1] ?? "";
|
|
171
|
+
const info = match[2] ?? "";
|
|
172
|
+
const char = run.startsWith("~") ? "~" : "`";
|
|
173
|
+
// A backtick opener's info string may not contain a backtick.
|
|
174
|
+
if (char === "`" && info.includes("`"))
|
|
175
|
+
return null;
|
|
176
|
+
return { char, len: run.length, info };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Walk the lines up to `offset` once and record both block facts the classifier
|
|
180
|
+
* needs. The fence stack follows CommonMark: opener and closer share the
|
|
181
|
+
* character, the closer is at least as long, and a closer carries no info
|
|
182
|
+
* string, so `` ```ts more text `` inside an open fence is content and not a
|
|
183
|
+
* closer. Inline scanning restarts after a blank line or a fence delimiter
|
|
184
|
+
* because neither a code span nor a strikethrough run crosses one. A blockquote
|
|
185
|
+
* marker carries forward to the blank line that ends the quote, so an unmarked
|
|
186
|
+
* lazy-continuation line is still quoted text.
|
|
187
|
+
*
|
|
188
|
+
* A fence delimiter also ends the quote block, and a line inside an open fence
|
|
189
|
+
* never opens one: a `>` in a fenced example is example text, so the marker must
|
|
190
|
+
* not survive the closing fence and refuse the citation that follows it. Lazy
|
|
191
|
+
* continuation is a paragraph rule, and a fence line is not paragraph text. A
|
|
192
|
+
* fence marked by `>` on every line is not a fence here -- `fenceDelimiter`
|
|
193
|
+
* requires the run at the line start -- so the line-level blockquote test still
|
|
194
|
+
* refuses a citation inside a blockquoted fence.
|
|
195
|
+
*/
|
|
196
|
+
function blockPosition(body, offset) {
|
|
197
|
+
let open = null;
|
|
198
|
+
let inlineStart = 0;
|
|
199
|
+
let quotedBlock = false;
|
|
200
|
+
let lineStart = 0;
|
|
201
|
+
while (lineStart < offset) {
|
|
202
|
+
let lineEnd = body.indexOf("\n", lineStart);
|
|
203
|
+
const partial = lineEnd === -1 || lineEnd > offset;
|
|
204
|
+
if (partial) {
|
|
205
|
+
lineEnd = Math.min(body.length, offset);
|
|
206
|
+
}
|
|
207
|
+
const line = body.slice(lineStart, lineEnd);
|
|
208
|
+
const fence = fenceDelimiter(line);
|
|
209
|
+
if (fence !== null) {
|
|
210
|
+
if (open === null) {
|
|
211
|
+
open = fence;
|
|
212
|
+
}
|
|
213
|
+
else if (fence.char === open.char &&
|
|
214
|
+
fence.len >= open.len &&
|
|
215
|
+
fence.info.trim().length === 0) {
|
|
216
|
+
open = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (!partial) {
|
|
220
|
+
const blank = line.trim().length === 0;
|
|
221
|
+
if (blank || fence !== null) {
|
|
222
|
+
inlineStart = lineEnd + 1;
|
|
223
|
+
quotedBlock = false;
|
|
224
|
+
}
|
|
225
|
+
else if (open === null && BLOCKQUOTE_RE.test(line)) {
|
|
226
|
+
quotedBlock = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (lineEnd >= offset) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
lineStart = lineEnd + 1;
|
|
233
|
+
}
|
|
234
|
+
return { insideFence: open !== null, inlineStart, quotedBlock };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Refused positions are exactly the five published classes. An indented code
|
|
238
|
+
* block and an HTML comment are deliberately not classified: a four-space
|
|
239
|
+
* indent is also ordinary list-continuation content, so refusing it would block
|
|
240
|
+
* valid records more often than it would catch example text.
|
|
241
|
+
*/
|
|
242
|
+
function classifyPosition(body, offset) {
|
|
243
|
+
const block = blockPosition(body, offset);
|
|
244
|
+
if (block.insideFence)
|
|
245
|
+
return "code-fence";
|
|
246
|
+
const { start, end } = lineBounds(body, offset);
|
|
247
|
+
const line = body.slice(start, end);
|
|
248
|
+
if (BLOCKQUOTE_RE.test(line) || block.quotedBlock)
|
|
249
|
+
return "blockquote";
|
|
250
|
+
const preceding = body.slice(block.inlineStart, offset);
|
|
251
|
+
if (isInsideInlineCode(preceding))
|
|
252
|
+
return "inline-code";
|
|
253
|
+
if (isStruckThrough(preceding))
|
|
254
|
+
return "strikethrough";
|
|
255
|
+
if (isNegated(line, offset - start))
|
|
256
|
+
return "negation";
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
function idShapedRuns(body) {
|
|
260
|
+
const runs = [];
|
|
261
|
+
const seen = new Set();
|
|
262
|
+
const re = new RegExp(ID_RUN_RE.source, ID_RUN_RE.flags);
|
|
263
|
+
for (const match of body.matchAll(re)) {
|
|
264
|
+
const id = Number(match[0]);
|
|
265
|
+
if (!Number.isSafeInteger(id) || id <= 0 || seen.has(id))
|
|
266
|
+
continue;
|
|
267
|
+
seen.add(id);
|
|
268
|
+
runs.push(id);
|
|
269
|
+
}
|
|
270
|
+
return runs;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Parse the closed citation grammar out of one comment body. Refused positions
|
|
274
|
+
* are reported rather than dropped so a block detail can echo the observation
|
|
275
|
+
* instead of guessing at a cause.
|
|
276
|
+
*/
|
|
277
|
+
export function scanCitations(body) {
|
|
278
|
+
const citations = [];
|
|
279
|
+
const rejected = [];
|
|
280
|
+
const seen = new Set();
|
|
281
|
+
const re = new RegExp(CITATION_RE.source, CITATION_RE.flags);
|
|
282
|
+
for (const match of body.matchAll(re)) {
|
|
283
|
+
const raw = match[2] ?? match[3] ?? match[4] ?? match[5];
|
|
284
|
+
if (raw === undefined)
|
|
285
|
+
continue;
|
|
286
|
+
const id = Number(raw);
|
|
287
|
+
if (!Number.isSafeInteger(id) || id <= 0)
|
|
288
|
+
continue;
|
|
289
|
+
const reason = classifyPosition(body, match.index ?? 0);
|
|
290
|
+
if (reason !== null) {
|
|
291
|
+
rejected.push({ id, reason });
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const kind = kindForKeyword(match[1]);
|
|
295
|
+
const key = `${id}|${kind}`;
|
|
296
|
+
if (seen.has(key))
|
|
297
|
+
continue;
|
|
298
|
+
seen.add(key);
|
|
299
|
+
citations.push({ id, kind });
|
|
300
|
+
}
|
|
301
|
+
return { citations, rejected, idShapedRuns: idShapedRuns(body) };
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=citation-grammar.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completed-arc record for design-critique ingest (#3806).
|
|
3
|
+
*
|
|
4
|
+
* Catalog chips are list-visible convenience, not clearance. Ingest waits on
|
|
5
|
+
* `design-critique: synthesis accepted, because ...` citing an accepted
|
|
6
|
+
* successor lean (and the verified-claims table when that comment exists).
|
|
7
|
+
*
|
|
8
|
+
* Citations are parsed once, by `scanCitations` (#3831). The accepted forms and
|
|
9
|
+
* the refused positions are published in `content/contracts/design-critique.md`
|
|
10
|
+
* `## Citation grammar`. Clearance is set membership against the latest
|
|
11
|
+
* successor lean, not position in the body, so citing the superseded lean --
|
|
12
|
+
* which `## Successor lean` requires -- cannot block.
|
|
13
|
+
*/
|
|
14
|
+
export type ThreadComment = {
|
|
15
|
+
readonly id: number;
|
|
16
|
+
readonly body: string;
|
|
17
|
+
};
|
|
18
|
+
export type CompletedArcBlockReason = "missing-record" | "lone-shape" | "cite-not-lean" | "missing-table-cite";
|
|
19
|
+
export type CompletedArcVerdict = {
|
|
20
|
+
readonly status: "not-in-arc";
|
|
21
|
+
} | {
|
|
22
|
+
readonly status: "complete";
|
|
23
|
+
readonly synthesisCommentId: number;
|
|
24
|
+
readonly citedLeanId: number;
|
|
25
|
+
readonly citedTableId: number | null;
|
|
26
|
+
} | {
|
|
27
|
+
readonly status: "blocked";
|
|
28
|
+
readonly reason: CompletedArcBlockReason;
|
|
29
|
+
readonly detail: string;
|
|
30
|
+
};
|
|
31
|
+
export declare class DesignCritiqueIngestBlockedError extends Error {
|
|
32
|
+
readonly issueNumber: number;
|
|
33
|
+
readonly reason: CompletedArcBlockReason;
|
|
34
|
+
constructor(issueNumber: number, reason: CompletedArcBlockReason, detail: string);
|
|
35
|
+
}
|
|
36
|
+
export declare function isSynthesisAcceptedShape(body: string): boolean;
|
|
37
|
+
export declare function isSuccessorLeanBody(body: string): boolean;
|
|
38
|
+
export declare function isVerifiedClaimsTableBody(body: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Ids cited by one comment body, in document order. Thin projection of the
|
|
41
|
+
* shared parser -- the grammar itself lives in `citation-grammar.ts`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractCitedCommentIds(body: string): number[];
|
|
44
|
+
export declare function hasDesignCritiqueCatalogChip(labels: readonly string[]): boolean;
|
|
45
|
+
export declare function isPanelDepositBody(body: string): boolean;
|
|
46
|
+
export declare function isInFlightCritiqueThread(comments: readonly ThreadComment[]): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Ingest clearance from thread structure. Labels and author identity are not
|
|
49
|
+
* predicates. A lone synthesis-accepted sentence shape is not the record.
|
|
50
|
+
* Clearance cites the latest successor lean; an older complete record does not
|
|
51
|
+
* clear a later recut. A panel-deposit is in-flight even before critic posts.
|
|
52
|
+
*/
|
|
53
|
+
export declare function evaluateCompletedArcRecord(input: {
|
|
54
|
+
readonly labels?: readonly string[];
|
|
55
|
+
readonly comments: readonly ThreadComment[];
|
|
56
|
+
}): CompletedArcVerdict;
|
|
57
|
+
export declare function assertCompletedArcAllowsIngest(input: {
|
|
58
|
+
readonly issueNumber: number;
|
|
59
|
+
readonly labels?: readonly string[];
|
|
60
|
+
readonly comments: readonly ThreadComment[];
|
|
61
|
+
}): CompletedArcVerdict;
|
|
62
|
+
//# sourceMappingURL=completed-arc-record.d.ts.map
|