@deftai/directive-core 0.93.0 → 0.95.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/authz/classify.js +485 -0
- package/dist/cache/fetch.d.ts +29 -0
- package/dist/cache/fetch.js +131 -4
- package/dist/cache/operations.d.ts +8 -0
- package/dist/cache/operations.js +67 -4
- package/dist/check/cached-orchestrator.js +11 -0
- package/dist/check/consumer-gate-integrity.d.ts +68 -0
- package/dist/check/consumer-gate-integrity.js +265 -0
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/orchestrator.js +10 -0
- package/dist/content-contracts/skills/greptile-detector.d.ts +9 -0
- package/dist/content-contracts/skills/greptile-detector.js +9 -2
- package/dist/doctor/main.js +98 -9
- package/dist/doctor/types.d.ts +2 -2
- package/dist/finish-loop/pr-finish-loop.js +1 -0
- package/dist/freshness/bind.d.ts +67 -0
- package/dist/freshness/bind.js +201 -0
- package/dist/freshness/cli.d.ts +30 -0
- package/dist/freshness/cli.js +180 -0
- package/dist/freshness/compare.d.ts +14 -0
- package/dist/freshness/compare.js +134 -0
- package/dist/freshness/generation.d.ts +44 -0
- package/dist/freshness/generation.js +172 -0
- package/dist/freshness/index.d.ts +13 -0
- package/dist/freshness/index.js +13 -0
- package/dist/freshness/report.d.ts +43 -0
- package/dist/freshness/report.js +139 -0
- package/dist/freshness/types.d.ts +70 -0
- package/dist/freshness/types.js +30 -0
- package/dist/handoff-evidence/index.d.ts +8 -0
- package/dist/handoff-evidence/index.js +7 -0
- package/dist/handoff-evidence/validate.d.ts +105 -0
- package/dist/handoff-evidence/validate.js +423 -0
- package/dist/hooks/dispatcher.d.ts +27 -2
- package/dist/hooks/dispatcher.js +134 -14
- package/dist/hooks/readonly.d.ts +14 -0
- package/dist/hooks/readonly.js +126 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +5 -2
- package/dist/init-deposit/init-deposit.d.ts +7 -1
- package/dist/init-deposit/init-deposit.js +24 -4
- package/dist/init-deposit/refresh.d.ts +9 -1
- package/dist/init-deposit/refresh.js +48 -5
- package/dist/intake/issue-ingest.d.ts +20 -0
- package/dist/intake/issue-ingest.js +58 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.js +50 -1
- package/dist/policy/min-greptile-confidence.d.ts +57 -0
- package/dist/policy/min-greptile-confidence.js +123 -0
- package/dist/pr-merge-readiness/compute.d.ts +11 -1
- package/dist/pr-merge-readiness/compute.js +17 -4
- package/dist/pr-merge-readiness/evaluate.d.ts +8 -1
- package/dist/pr-merge-readiness/evaluate.js +7 -4
- package/dist/pr-merge-readiness/mergeability.d.ts +7 -1
- package/dist/pr-merge-readiness/mergeability.js +14 -2
- package/dist/pr-monitor/main.d.ts +2 -0
- package/dist/pr-monitor/main.js +31 -1
- package/dist/pr-monitor/monitor.js +5 -1
- package/dist/pr-monitor/readiness.js +3 -1
- package/dist/pr-monitor/types.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.js +28 -1
- package/dist/pr-wait-mergeable/main.d.ts +2 -0
- package/dist/pr-wait-mergeable/main.js +16 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +3 -0
- package/dist/pr-watch/constants.d.ts +4 -1
- package/dist/pr-watch/constants.js +5 -2
- package/dist/pr-watch/main.js +1 -0
- package/dist/pr-watch/probe.d.ts +1 -1
- package/dist/pr-watch/probe.js +4 -1
- package/dist/pr-watch/types.d.ts +7 -1
- package/dist/pr-watch/watch.js +3 -2
- package/dist/release-e2e/greenfield-python-free-smoke.js +7 -3
- package/dist/review-monitor/index.d.ts +1 -0
- package/dist/review-monitor/index.js +1 -0
- package/dist/review-monitor/l4-owner.d.ts +46 -0
- package/dist/review-monitor/l4-owner.js +262 -0
- package/dist/session/ritual-entrypoint.d.ts +6 -2
- package/dist/session/ritual-entrypoint.js +15 -2
- package/dist/session/session-ready.js +31 -14
- package/dist/session/session-start.d.ts +2 -1
- package/dist/session/session-start.js +66 -3
- package/dist/session/verify-session-ritual.d.ts +4 -2
- package/dist/session/verify-session-ritual.js +11 -11
- package/dist/slash/product-set.js +4 -1
- package/dist/swarm/verify-review-clean.d.ts +4 -3
- package/dist/swarm/verify-review-clean.js +28 -73
- package/dist/tool-events/classify.js +1 -0
- package/dist/triage/classify/index.d.ts +9 -0
- package/dist/triage/classify/index.js +34 -2
- package/dist/triage/classify/label-mirror.d.ts +143 -0
- package/dist/triage/classify/label-mirror.js +684 -0
- package/dist/triage/help/registry-data.d.ts +7 -7
- package/dist/triage/help/registry-data.js +27 -7
- package/dist/triage/queue/show.d.ts +1 -1
- package/dist/triage/queue/show.js +4 -2
- package/dist/triage/welcome/writers.js +16 -7
- package/dist/umbrella-current-shape/index.d.ts +45 -0
- package/dist/umbrella-current-shape/index.js +162 -8
- package/dist/vbrief-reconcile/index.d.ts +2 -2
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +31 -2
- package/dist/vbrief-reconcile/umbrellas.js +231 -19
- package/dist/vbrief-validate/project-definition.js +1 -1
- package/dist/verify-env/agent-hook-readiness.d.ts +42 -0
- package/dist/verify-env/agent-hook-readiness.js +150 -0
- package/dist/verify-env/agent-hooks-live-probe.d.ts +16 -4
- package/dist/verify-env/agent-hooks-live-probe.js +120 -107
- package/dist/verify-env/agent-hooks.js +11 -3
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/package.json +7 -3
|
@@ -168,13 +168,13 @@ export declare const registryData: {
|
|
|
168
168
|
};
|
|
169
169
|
readonly "task triage:classify": {
|
|
170
170
|
readonly name: "task triage:classify";
|
|
171
|
-
readonly summary: "Inspect / validate auto-classification surface";
|
|
172
|
-
readonly refs: "(D10 / #1129)";
|
|
173
|
-
readonly description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify.";
|
|
174
|
-
readonly usage: "task triage:classify -- [--list | --validate]";
|
|
175
|
-
readonly flags: readonly [readonly ["--list", "(default)", "Print effective rules + hold markers."], readonly ["--validate", "(off)", "Validate plan.policy.triageAutoClassify."]];
|
|
176
|
-
readonly examples: readonly ["task triage:classify -- --list", "task triage:classify -- --validate"];
|
|
177
|
-
readonly see_also: readonly ["task triage:bootstrap", "task triage:queue", "#1119 / D10"];
|
|
171
|
+
readonly summary: "Inspect / validate auto-classification surface; Tier-1 SCM label mirror";
|
|
172
|
+
readonly refs: "(D10 / #1129, #1423 Wave 1)";
|
|
173
|
+
readonly description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror runs the Tier-1 deterministic SCM label mirror over the github-issue cache (dry-run default; --apply writes via SCM boundary). Never calls triage:accept / never writes proposed/ xBRIEFs.";
|
|
174
|
+
readonly usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--repo owner/name] [--json] [--allow-cross-repo]]";
|
|
175
|
+
readonly flags: readonly [readonly ["--list", "(default)", "Print effective rules + hold markers."], readonly ["--validate", "(off)", "Validate plan.policy.triageAutoClassify + triageLabelMirror."], readonly ["--mirror", "(off)", "Tier-1 label mirror: classify cache → planned/applied labels (dry-run default)."], readonly ["--apply", "(off)", "With --mirror: write labels via SCM (requires github SCM boundary)."], readonly ["--repo owner/name", "(all cached)", "Limit mirror to one repo."], readonly ["--json", "(off)", "With --mirror: structured JSON outcome."], readonly ["--allow-cross-repo", "(off)", "With --mirror --apply: allow non-project repos (#2601)."]];
|
|
176
|
+
readonly examples: readonly ["task triage:classify -- --list", "task triage:classify -- --validate", "task triage:classify -- --mirror", "task triage:classify -- --mirror --apply --repo owner/name"];
|
|
177
|
+
readonly see_also: readonly ["task triage:bootstrap", "task triage:queue", "task vbrief:reconcile:labels", "#1119 / D10", "#1423"];
|
|
178
178
|
readonly placeholder: false;
|
|
179
179
|
};
|
|
180
180
|
readonly "task triage:bootstrap": {
|
|
@@ -264,16 +264,36 @@ export const registryData = {
|
|
|
264
264
|
},
|
|
265
265
|
"task triage:classify": {
|
|
266
266
|
name: "task triage:classify",
|
|
267
|
-
summary: "Inspect / validate auto-classification surface",
|
|
268
|
-
refs: "(D10 / #1129)",
|
|
269
|
-
description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify.",
|
|
270
|
-
usage: "task triage:classify -- [--list | --validate]",
|
|
267
|
+
summary: "Inspect / validate auto-classification surface; Tier-1 SCM label mirror",
|
|
268
|
+
refs: "(D10 / #1129, #1423 Wave 1)",
|
|
269
|
+
description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror runs the Tier-1 deterministic SCM label mirror over the github-issue cache (dry-run default; --apply writes via SCM boundary). Never calls triage:accept / never writes proposed/ xBRIEFs.",
|
|
270
|
+
usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--repo owner/name] [--json] [--allow-cross-repo]]",
|
|
271
271
|
flags: [
|
|
272
272
|
["--list", "(default)", "Print effective rules + hold markers."],
|
|
273
|
-
["--validate", "(off)", "Validate plan.policy.triageAutoClassify."],
|
|
273
|
+
["--validate", "(off)", "Validate plan.policy.triageAutoClassify + triageLabelMirror."],
|
|
274
|
+
[
|
|
275
|
+
"--mirror",
|
|
276
|
+
"(off)",
|
|
277
|
+
"Tier-1 label mirror: classify cache → planned/applied labels (dry-run default).",
|
|
278
|
+
],
|
|
279
|
+
["--apply", "(off)", "With --mirror: write labels via SCM (requires github SCM boundary)."],
|
|
280
|
+
["--repo owner/name", "(all cached)", "Limit mirror to one repo."],
|
|
281
|
+
["--json", "(off)", "With --mirror: structured JSON outcome."],
|
|
282
|
+
["--allow-cross-repo", "(off)", "With --mirror --apply: allow non-project repos (#2601)."],
|
|
283
|
+
],
|
|
284
|
+
examples: [
|
|
285
|
+
"task triage:classify -- --list",
|
|
286
|
+
"task triage:classify -- --validate",
|
|
287
|
+
"task triage:classify -- --mirror",
|
|
288
|
+
"task triage:classify -- --mirror --apply --repo owner/name",
|
|
289
|
+
],
|
|
290
|
+
see_also: [
|
|
291
|
+
"task triage:bootstrap",
|
|
292
|
+
"task triage:queue",
|
|
293
|
+
"task vbrief:reconcile:labels",
|
|
294
|
+
"#1119 / D10",
|
|
295
|
+
"#1423",
|
|
274
296
|
],
|
|
275
|
-
examples: ["task triage:classify -- --list", "task triage:classify -- --validate"],
|
|
276
|
-
see_also: ["task triage:bootstrap", "task triage:queue", "#1119 / D10"],
|
|
277
297
|
placeholder: false,
|
|
278
298
|
},
|
|
279
299
|
"task triage:bootstrap": {
|
|
@@ -58,7 +58,7 @@ export declare function extractBodySummary(body: string, maxLines?: number): str
|
|
|
58
58
|
* Looks for AC / Acceptance headings and checkbox / bullet lists under them.
|
|
59
59
|
*/
|
|
60
60
|
export declare function extractAcceptanceCriteria(body: string): readonly string[];
|
|
61
|
-
/** Operator-facing pasteable brief backbone for Phase 3 decisions (#2890). */
|
|
61
|
+
/** Operator-facing pasteable brief backbone for Phase 3 decisions (#2890 / #3116). */
|
|
62
62
|
export declare function renderOperatorBrief(options: {
|
|
63
63
|
readonly issue: CachedIssueDetail | null;
|
|
64
64
|
readonly repo: string;
|
|
@@ -245,7 +245,7 @@ export function extractAcceptanceCriteria(body) {
|
|
|
245
245
|
}
|
|
246
246
|
return bullets.slice(0, 12);
|
|
247
247
|
}
|
|
248
|
-
/** Operator-facing pasteable brief backbone for Phase 3 decisions (#2890). */
|
|
248
|
+
/** Operator-facing pasteable brief backbone for Phase 3 decisions (#2890 / #3116). */
|
|
249
249
|
export function renderOperatorBrief(options) {
|
|
250
250
|
const lines = [];
|
|
251
251
|
lines.push(`triage:show --format=operator -- ${options.repo}#${options.number}`);
|
|
@@ -258,9 +258,11 @@ export function renderOperatorBrief(options) {
|
|
|
258
258
|
const issue = options.issue;
|
|
259
259
|
const link = issue.htmlUrl ?? resolveIssueHtmlUrl(options.repo, options.number);
|
|
260
260
|
const labels = issue.labels.length > 0 ? issue.labels.map(oneLine).join(", ") : "<none>";
|
|
261
|
+
// URL-first lead line (#3116): canonical issue URL before title/body fields.
|
|
262
|
+
lines.push(oneLine(link));
|
|
261
263
|
lines.push(`#${options.number} ${oneLine(issue.title)}`);
|
|
262
|
-
lines.push(`link: ${oneLine(link)}`);
|
|
263
264
|
lines.push(`labels: ${labels}`);
|
|
265
|
+
lines.push("validity: (agent-owned — still-open | partial | likely-shipped | needs-re-scope + evidence)");
|
|
264
266
|
lines.push("");
|
|
265
267
|
lines.push("summary:");
|
|
266
268
|
for (const line of extractBodySummary(issue.body).split("\n")) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { containedWrite } from "../../fs/contained-write.js";
|
|
4
|
+
import { assertWriteTargetSafe } from "../../fs/projection-containment.js";
|
|
4
5
|
import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
5
6
|
import { migrateLegacyPolicyKey, PLAN_ONBOARDING_KEY, PLAN_POLICY_KEY, } from "../../policy/plan-extensions.js";
|
|
6
7
|
import { projectDefinitionMutationLock } from "../../vbrief-build/project-definition-io.js";
|
|
@@ -32,21 +33,29 @@ export function appendAuditEntry(projectRoot, entry) {
|
|
|
32
33
|
});
|
|
33
34
|
return logPath;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Atomically write PROJECT-DEFINITION JSON under projectRoot containment (#3077).
|
|
38
|
+
* Containment root is projectRoot (not dirname(path)) so a force-added `xbrief/`
|
|
39
|
+
* directory symlink fails closed before temp+rename — same class as #3042.
|
|
40
|
+
*/
|
|
41
|
+
function atomicWrite(projectRoot, path, data) {
|
|
42
|
+
const root = resolve(projectRoot);
|
|
43
|
+
const targetAbs = resolve(path);
|
|
44
|
+
// Refuse leaf/parent symlinks and out-of-root targets before temp+rename.
|
|
45
|
+
assertWriteTargetSafe(root, targetAbs);
|
|
46
|
+
const dir = dirname(targetAbs);
|
|
38
47
|
const payload = `${JSON.stringify(data, null, 2)}\n`;
|
|
39
48
|
const tmpName = `.${Date.now()}.tmp`;
|
|
40
49
|
const tmp = join(dir, tmpName);
|
|
41
50
|
try {
|
|
42
|
-
// #2980 wave D:
|
|
51
|
+
// #2980 wave D / #3077: product write routes through containedWrite under projectRoot.
|
|
43
52
|
containedWrite({
|
|
44
|
-
root
|
|
45
|
-
target:
|
|
53
|
+
root,
|
|
54
|
+
target: tmp,
|
|
46
55
|
data: payload,
|
|
47
56
|
mode: "create",
|
|
48
57
|
});
|
|
49
|
-
renameSync(tmp,
|
|
58
|
+
renameSync(tmp, targetAbs);
|
|
50
59
|
}
|
|
51
60
|
catch (err) {
|
|
52
61
|
try {
|
|
@@ -66,6 +66,51 @@ export interface RunCurrentShapeOptions {
|
|
|
66
66
|
readonly writeOut?: (text: string) => void;
|
|
67
67
|
readonly writeErr?: (text: string) => void;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Map a GitHub REST issue-comment object (or an already-normalized row) onto
|
|
71
|
+
* the internal `IssueComment` shape. Used by `umbrella:current-shape`, the
|
|
72
|
+
* triage cache put path, and `issue:ingest` so all three share one selector.
|
|
73
|
+
*/
|
|
74
|
+
export declare function mapIssueCommentEntry(entry: unknown): IssueComment | null;
|
|
75
|
+
/**
|
|
76
|
+
* Labels that mark an issue as umbrella/tracker-like for cache comment enrichment
|
|
77
|
+
* (#1870). Mirrors `TRACKER_LABELS` in scope/open-umbrella-warning.ts.
|
|
78
|
+
*/
|
|
79
|
+
export declare const UMBRELLA_TRACKER_LABELS: ReadonlySet<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Key under which `cache:fetch-all` / `cache:put` stash the issue comment thread
|
|
82
|
+
* on the raw.json payload so `content.md` and `issue:ingest` can surface the
|
|
83
|
+
* canonical current-shape comment without a second live fetch (#1870).
|
|
84
|
+
*/
|
|
85
|
+
export declare const RAW_ISSUE_COMMENTS_KEY: "comments";
|
|
86
|
+
/** Sidecar filename next to content.md carrying the selected current-shape payload. */
|
|
87
|
+
export declare const CURRENT_SHAPE_SIDECAR: "current-shape.json";
|
|
88
|
+
export interface CurrentShapeSidecar {
|
|
89
|
+
readonly commentId: number;
|
|
90
|
+
readonly htmlUrl: string;
|
|
91
|
+
readonly pass: number;
|
|
92
|
+
readonly authorLogin: string;
|
|
93
|
+
readonly authorAssociation: string;
|
|
94
|
+
readonly body: string;
|
|
95
|
+
}
|
|
96
|
+
/** True when labels or sub-issue summary mark the issue as umbrella/tracker-like. */
|
|
97
|
+
export declare function isUmbrellaLikeIssue(raw: Record<string, unknown>): boolean;
|
|
98
|
+
/** Normalize a raw issue payload's comment array (REST or pre-mapped). */
|
|
99
|
+
export declare function commentsFromRawPayload(raw: Record<string, unknown>): IssueComment[];
|
|
100
|
+
/**
|
|
101
|
+
* Count maintainer-authored `## Current shape` comments (for the !=1 lint).
|
|
102
|
+
* Non-maintainer forgeries are excluded (#2307).
|
|
103
|
+
*/
|
|
104
|
+
export declare function countMaintainerCurrentShapeComments(comments: readonly IssueComment[]): number;
|
|
105
|
+
/** Markdown section appended to cache content.md when a canonical shape exists. */
|
|
106
|
+
export declare function formatCurrentShapeSection(selected: CurrentShapeComment): string;
|
|
107
|
+
/**
|
|
108
|
+
* Append the canonical current-shape comment (if any) to a rendered cache body.
|
|
109
|
+
* Returns the input unchanged when no maintainer-authored current-shape exists.
|
|
110
|
+
*/
|
|
111
|
+
export declare function appendCurrentShapeSection(baseContent: string, raw: Record<string, unknown>): string;
|
|
112
|
+
/** Build the current-shape.json sidecar payload, or null when none is selectable. */
|
|
113
|
+
export declare function buildCurrentShapeSidecar(raw: Record<string, unknown>): CurrentShapeSidecar | null;
|
|
69
114
|
/** Merge `gh api --paginate` concatenated JSON array pages into comment rows. */
|
|
70
115
|
export declare function parseCommentsFromGhStdout(stdout: string): IssueComment[];
|
|
71
116
|
export declare function extractPassFromBody(body: string): number | null;
|
|
@@ -44,24 +44,178 @@ export const REQUIRED_SECTIONS = [
|
|
|
44
44
|
"readingOrder",
|
|
45
45
|
];
|
|
46
46
|
export const OPTIONAL_SECTIONS = ["openQuestions"];
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Map a GitHub REST issue-comment object (or an already-normalized row) onto
|
|
49
|
+
* the internal `IssueComment` shape. Used by `umbrella:current-shape`, the
|
|
50
|
+
* triage cache put path, and `issue:ingest` so all three share one selector.
|
|
51
|
+
*/
|
|
52
|
+
export function mapIssueCommentEntry(entry) {
|
|
48
53
|
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
49
54
|
return null;
|
|
50
55
|
}
|
|
51
56
|
const rec = entry;
|
|
52
|
-
|
|
57
|
+
// Accept either GitHub REST field names or the already-mapped camelCase shape.
|
|
58
|
+
const id = typeof rec.id === "number" ? rec.id : null;
|
|
59
|
+
const body = typeof rec.body === "string" ? rec.body : null;
|
|
60
|
+
if (id === null || body === null) {
|
|
53
61
|
return null;
|
|
54
62
|
}
|
|
55
63
|
const user = typeof rec.user === "object" && rec.user !== null
|
|
56
64
|
? rec.user
|
|
57
65
|
: null;
|
|
66
|
+
const htmlUrl = typeof rec.html_url === "string"
|
|
67
|
+
? rec.html_url
|
|
68
|
+
: typeof rec.htmlUrl === "string"
|
|
69
|
+
? rec.htmlUrl
|
|
70
|
+
: "";
|
|
71
|
+
const updatedAt = typeof rec.updated_at === "string"
|
|
72
|
+
? rec.updated_at
|
|
73
|
+
: typeof rec.updatedAt === "string"
|
|
74
|
+
? rec.updatedAt
|
|
75
|
+
: "";
|
|
76
|
+
const authorLogin = user !== null && typeof user.login === "string"
|
|
77
|
+
? user.login
|
|
78
|
+
: typeof rec.authorLogin === "string"
|
|
79
|
+
? rec.authorLogin
|
|
80
|
+
: "";
|
|
81
|
+
const authorAssociation = typeof rec.author_association === "string"
|
|
82
|
+
? rec.author_association
|
|
83
|
+
: typeof rec.authorAssociation === "string"
|
|
84
|
+
? rec.authorAssociation
|
|
85
|
+
: "";
|
|
86
|
+
return {
|
|
87
|
+
id,
|
|
88
|
+
body,
|
|
89
|
+
htmlUrl,
|
|
90
|
+
updatedAt,
|
|
91
|
+
authorLogin,
|
|
92
|
+
authorAssociation,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** @deprecated Prefer `mapIssueCommentEntry` — kept as a private alias for call sites. */
|
|
96
|
+
function mapCommentEntry(entry) {
|
|
97
|
+
return mapIssueCommentEntry(entry);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Labels that mark an issue as umbrella/tracker-like for cache comment enrichment
|
|
101
|
+
* (#1870). Mirrors `TRACKER_LABELS` in scope/open-umbrella-warning.ts.
|
|
102
|
+
*/
|
|
103
|
+
export const UMBRELLA_TRACKER_LABELS = new Set([
|
|
104
|
+
"epic",
|
|
105
|
+
"meta",
|
|
106
|
+
"tracker",
|
|
107
|
+
"type:tracker",
|
|
108
|
+
"status:tracker",
|
|
109
|
+
]);
|
|
110
|
+
/**
|
|
111
|
+
* Key under which `cache:fetch-all` / `cache:put` stash the issue comment thread
|
|
112
|
+
* on the raw.json payload so `content.md` and `issue:ingest` can surface the
|
|
113
|
+
* canonical current-shape comment without a second live fetch (#1870).
|
|
114
|
+
*/
|
|
115
|
+
export const RAW_ISSUE_COMMENTS_KEY = "comments";
|
|
116
|
+
/** Sidecar filename next to content.md carrying the selected current-shape payload. */
|
|
117
|
+
export const CURRENT_SHAPE_SIDECAR = "current-shape.json";
|
|
118
|
+
/** True when labels or sub-issue summary mark the issue as umbrella/tracker-like. */
|
|
119
|
+
export function isUmbrellaLikeIssue(raw) {
|
|
120
|
+
const labelsRaw = raw.labels;
|
|
121
|
+
if (Array.isArray(labelsRaw)) {
|
|
122
|
+
for (const label of labelsRaw) {
|
|
123
|
+
let name = "";
|
|
124
|
+
if (typeof label === "string") {
|
|
125
|
+
name = label;
|
|
126
|
+
}
|
|
127
|
+
else if (label !== null && typeof label === "object" && !Array.isArray(label)) {
|
|
128
|
+
const n = label.name;
|
|
129
|
+
if (typeof n === "string")
|
|
130
|
+
name = n;
|
|
131
|
+
}
|
|
132
|
+
if (name.length > 0 && UMBRELLA_TRACKER_LABELS.has(name.toLowerCase())) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const summary = raw.sub_issues_summary;
|
|
138
|
+
if (summary !== null && typeof summary === "object" && !Array.isArray(summary)) {
|
|
139
|
+
const total = summary.total;
|
|
140
|
+
if (typeof total === "number" && Number.isFinite(total) && total > 0) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
/** Normalize a raw issue payload's comment array (REST or pre-mapped). */
|
|
147
|
+
export function commentsFromRawPayload(raw) {
|
|
148
|
+
const rawComments = raw[RAW_ISSUE_COMMENTS_KEY];
|
|
149
|
+
if (!Array.isArray(rawComments)) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const out = [];
|
|
153
|
+
for (const entry of rawComments) {
|
|
154
|
+
const mapped = mapIssueCommentEntry(entry);
|
|
155
|
+
if (mapped !== null) {
|
|
156
|
+
out.push(mapped);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Count maintainer-authored `## Current shape` comments (for the !=1 lint).
|
|
163
|
+
* Non-maintainer forgeries are excluded (#2307).
|
|
164
|
+
*/
|
|
165
|
+
export function countMaintainerCurrentShapeComments(comments) {
|
|
166
|
+
let count = 0;
|
|
167
|
+
for (const comment of comments) {
|
|
168
|
+
if (!isMaintainerAuthored(comment.authorAssociation)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (extractPassFromBody(comment.body) !== null) {
|
|
172
|
+
count += 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return count;
|
|
176
|
+
}
|
|
177
|
+
/** Markdown section appended to cache content.md when a canonical shape exists. */
|
|
178
|
+
export function formatCurrentShapeSection(selected) {
|
|
179
|
+
const permalink = selected.htmlUrl.length > 0 ? selected.htmlUrl : `comment id ${selected.id}`;
|
|
180
|
+
const author = selected.authorLogin.length > 0 ? `@${selected.authorLogin}` : "maintainer";
|
|
181
|
+
return [
|
|
182
|
+
"",
|
|
183
|
+
"---",
|
|
184
|
+
"",
|
|
185
|
+
"## Canonical current shape (#1152 / #1870)",
|
|
186
|
+
"",
|
|
187
|
+
`_Authoritative planning surface — prefer this over the issue body (stale by design). ` +
|
|
188
|
+
`Source: [${permalink}](${permalink}) · pass-${selected.pass} · ${author}. ` +
|
|
189
|
+
`Deterministic read path: \`task umbrella:current-shape <N>\`._`,
|
|
190
|
+
"",
|
|
191
|
+
selected.body.trimEnd(),
|
|
192
|
+
"",
|
|
193
|
+
].join("\n");
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Append the canonical current-shape comment (if any) to a rendered cache body.
|
|
197
|
+
* Returns the input unchanged when no maintainer-authored current-shape exists.
|
|
198
|
+
*/
|
|
199
|
+
export function appendCurrentShapeSection(baseContent, raw) {
|
|
200
|
+
const selected = selectCurrentShapeComment(commentsFromRawPayload(raw));
|
|
201
|
+
if (selected === null) {
|
|
202
|
+
return baseContent;
|
|
203
|
+
}
|
|
204
|
+
return `${baseContent.trimEnd()}\n${formatCurrentShapeSection(selected)}`;
|
|
205
|
+
}
|
|
206
|
+
/** Build the current-shape.json sidecar payload, or null when none is selectable. */
|
|
207
|
+
export function buildCurrentShapeSidecar(raw) {
|
|
208
|
+
const selected = selectCurrentShapeComment(commentsFromRawPayload(raw));
|
|
209
|
+
if (selected === null) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
58
212
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
213
|
+
commentId: selected.id,
|
|
214
|
+
htmlUrl: selected.htmlUrl,
|
|
215
|
+
pass: selected.pass,
|
|
216
|
+
authorLogin: selected.authorLogin,
|
|
217
|
+
authorAssociation: selected.authorAssociation,
|
|
218
|
+
body: selected.body,
|
|
65
219
|
};
|
|
66
220
|
}
|
|
67
221
|
/** Merge `gh api --paginate` concatenated JSON array pages into comment rows. */
|
|
@@ -5,6 +5,6 @@ export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, } from
|
|
|
5
5
|
export { pyRepr } from "./py-repr.js";
|
|
6
6
|
export { buildSpecTaskIndex, detectStatusMarker, folderFromStatus, formatReconciliationMarkdown, hasDisagreement, loadOverrides, normalizeTaskId, OVERRIDES_FILENAME, parseOverridesYaml, reconcileScopeItems, writeReconciliationReport, } from "./reconciliation.js";
|
|
7
7
|
export { allScopeIds, asStrList, candidateDepGraph, candidateFromPath, markCycles, } from "./swarm-deps.js";
|
|
8
|
-
export type { Candidate, Child, ConflictEntry, LabelChange, LabelClient, ReconciledItem, ReconcileGraphOutcome, ReconcileLabelsOutcome, ReconcileUmbrellasOutcome, ReconciliationReport, SpecTaskEntry, UmbrellaChange, UmbrellaClient, } from "./types.js";
|
|
9
|
-
export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, umbrellasOutcomeToJson, } from "./umbrellas.js";
|
|
8
|
+
export type { Candidate, Child, ConflictEntry, ForgeIssueState, LabelChange, LabelClient, ReconciledItem, ReconcileGraphOutcome, ReconcileLabelsOutcome, ReconcileUmbrellasOutcome, ReconciliationReport, SpecTaskEntry, UmbrellaChange, UmbrellaClient, } from "./types.js";
|
|
9
|
+
export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, indexChildrenByIssueNumber, isChildOpen, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileBodyChecklist, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, UNKNOWN_FOLDER, umbrellasOutcomeToJson, } from "./umbrellas.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -5,5 +5,5 @@ export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, } from
|
|
|
5
5
|
export { pyRepr } from "./py-repr.js";
|
|
6
6
|
export { buildSpecTaskIndex, detectStatusMarker, folderFromStatus, formatReconciliationMarkdown, hasDisagreement, loadOverrides, normalizeTaskId, OVERRIDES_FILENAME, parseOverridesYaml, reconcileScopeItems, writeReconciliationReport, } from "./reconciliation.js";
|
|
7
7
|
export { allScopeIds, asStrList, candidateDepGraph, candidateFromPath, markCycles, } from "./swarm-deps.js";
|
|
8
|
-
export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, umbrellasOutcomeToJson, } from "./umbrellas.js";
|
|
8
|
+
export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, indexChildrenByIssueNumber, isChildOpen, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileBodyChecklist, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, UNKNOWN_FOLDER, umbrellasOutcomeToJson, } from "./umbrellas.js";
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -94,7 +94,10 @@ export interface Child {
|
|
|
94
94
|
readonly kind: string;
|
|
95
95
|
readonly folder: string;
|
|
96
96
|
readonly depends_on: string[];
|
|
97
|
+
/** Linked forge issue number when known (#1649). */
|
|
98
|
+
readonly issue_number?: number | null;
|
|
97
99
|
}
|
|
100
|
+
export type ForgeIssueState = "open" | "closed";
|
|
98
101
|
export interface UmbrellaChange {
|
|
99
102
|
readonly story_id: string;
|
|
100
103
|
readonly repo: string;
|
|
@@ -102,6 +105,11 @@ export interface UmbrellaChange {
|
|
|
102
105
|
readonly action: "created" | "edited" | "unchanged";
|
|
103
106
|
readonly pass_n: number;
|
|
104
107
|
readonly body: string;
|
|
108
|
+
/**
|
|
109
|
+
* Issue-body checkbox reconcile (#1649): `edited` when checkboxes flipped,
|
|
110
|
+
* `unchanged` when already correct, `skipped` when body APIs unavailable.
|
|
111
|
+
*/
|
|
112
|
+
readonly checklist_action?: "edited" | "unchanged" | "skipped";
|
|
105
113
|
}
|
|
106
114
|
export interface ReconcileUmbrellasOutcome {
|
|
107
115
|
changed: UmbrellaChange[];
|
|
@@ -124,5 +132,11 @@ export interface UmbrellaClient {
|
|
|
124
132
|
}>;
|
|
125
133
|
editComment(repo: string, commentId: number, body: string): void;
|
|
126
134
|
createComment(repo: string, issueNumber: number, body: string): number | null;
|
|
135
|
+
/** Optional: forge open/closed for child issues (#1649). Folder fallback when absent. */
|
|
136
|
+
fetchIssueStates?(repo: string, issueNumbers: readonly number[]): ReadonlyMap<number, ForgeIssueState>;
|
|
137
|
+
/** Optional: umbrella issue body for checklist reconcile (#1649). */
|
|
138
|
+
fetchIssueBody?(repo: string, issueNumber: number): string;
|
|
139
|
+
/** Optional: write reconciled checklist body (#1649). */
|
|
140
|
+
editIssueBody?(repo: string, issueNumber: number, body: string): void;
|
|
127
141
|
}
|
|
128
142
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1,14 +1,40 @@
|
|
|
1
|
-
import type { Child, ReconcileUmbrellasOutcome, UmbrellaClient } from "./types.js";
|
|
1
|
+
import type { Child, ForgeIssueState, ReconcileUmbrellasOutcome, UmbrellaClient } from "./types.js";
|
|
2
2
|
export declare const OPEN_FOLDERS: readonly ["proposed", "pending", "active"];
|
|
3
3
|
export declare const CLOSED_FOLDERS: readonly ["completed", "cancelled"];
|
|
4
4
|
export declare const LIFECYCLE_FOLDERS: readonly ["proposed", "pending", "active", "completed", "cancelled"];
|
|
5
5
|
export declare const CHILD_REF_TYPE = "x-xbrief/plan";
|
|
6
|
+
/** Synthetic lifecycle folder for issue-only children with no local xBRIEF (#1649). */
|
|
7
|
+
export declare const UNKNOWN_FOLDER: "unknown";
|
|
6
8
|
export declare class UmbrellaScmError extends Error {
|
|
7
9
|
name: string;
|
|
8
10
|
}
|
|
9
11
|
export declare function childFromData(data: Record<string, unknown>, folder: string, fallbackId: string): Child;
|
|
10
12
|
export declare function buildChildIndex(vbriefDir: string): Record<string, Child>;
|
|
11
|
-
|
|
13
|
+
/** Index children by forge issue number for github-issue ref resolution (#1649). */
|
|
14
|
+
export declare function indexChildrenByIssueNumber(index: Record<string, Child>): Map<number, Child>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve epic children from plan refs AND github-issue refs (#1649 defect 2).
|
|
17
|
+
* The epic's own github-issue ref is skipped when `epicIssueNumber` is provided.
|
|
18
|
+
* Issue-only children (no local xBRIEF) are synthesized with folder `unknown`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function computeChildren(epicData: Record<string, unknown>, index: Record<string, Child>, options?: {
|
|
21
|
+
epicIssueNumber?: number | null;
|
|
22
|
+
}): Child[];
|
|
23
|
+
/**
|
|
24
|
+
* Whether a child counts as open for current-shape + checklist (#1649 defect 3).
|
|
25
|
+
* Prefer forge issue state when present; fall back to lifecycle folder.
|
|
26
|
+
* `unknown` (issue-only, no local xBRIEF) counts open without forge confirmation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isChildOpen(child: Child, forgeStates?: ReadonlyMap<number, ForgeIssueState> | null): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Reconcile umbrella issue-body checkboxes `- [ ] #N` / `- [x] #N` to child state (#1649).
|
|
31
|
+
* Only lines that mention a known child issue number are rewritten; other checkboxes untouched.
|
|
32
|
+
* Idempotent: returns `changed: false` when already correct.
|
|
33
|
+
*/
|
|
34
|
+
export declare function reconcileBodyChecklist(body: string, closedIssueNumbers: ReadonlySet<number>, knownIssueNumbers: ReadonlySet<number>): {
|
|
35
|
+
body: string;
|
|
36
|
+
changed: boolean;
|
|
37
|
+
};
|
|
12
38
|
export declare function computeWaves(children: readonly Child[]): string[][];
|
|
13
39
|
export declare function renderBody(options: {
|
|
14
40
|
passN: number;
|
|
@@ -34,6 +60,9 @@ export declare class ScmUmbrellaClient implements UmbrellaClient {
|
|
|
34
60
|
}>;
|
|
35
61
|
editComment(repo: string, commentId: number, body: string): void;
|
|
36
62
|
createComment(repo: string, issueNumber: number, body: string): number | null;
|
|
63
|
+
fetchIssueStates(repo: string, issueNumbers: readonly number[]): ReadonlyMap<number, ForgeIssueState>;
|
|
64
|
+
fetchIssueBody(repo: string, issueNumber: number): string;
|
|
65
|
+
editIssueBody(repo: string, issueNumber: number, body: string): void;
|
|
37
66
|
}
|
|
38
67
|
export declare function nowIso(date?: Date): string;
|
|
39
68
|
export interface ReconcileUmbrellasOptions {
|