@deftai/directive-core 0.64.0 → 0.66.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 +6 -4
- package/dist/capacity/backfill.js +5 -3
- package/dist/capacity/show.js +5 -3
- package/dist/category-b-namespace/index.d.ts +39 -0
- package/dist/category-b-namespace/index.js +120 -0
- package/dist/codebase/default-extractor.js +2 -1
- package/dist/codebase/map.js +2 -1
- package/dist/codebase/provider.js +4 -2
- package/dist/content-contracts/standards/_helpers.js +3 -1
- package/dist/doctor/checks.js +2 -1
- package/dist/doctor/doctor-state.js +2 -2
- package/dist/encoding/patterns.js +19 -0
- package/dist/encoding/scan.js +3 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/refresh.js +2 -1
- package/dist/init-deposit/scaffold.js +2 -1
- package/dist/install-upgrade/index.d.ts +2 -0
- package/dist/install-upgrade/index.js +31 -2
- package/dist/intake/issue-emit.d.ts +1 -1
- package/dist/intake/issue-emit.js +3 -2
- package/dist/intake/issue-ingest.js +17 -14
- package/dist/intake/markdown-scanners.d.ts +3 -1
- package/dist/intake/markdown-scanners.js +10 -3
- package/dist/intake/reconcile-issues.js +13 -4
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.js +2 -0
- package/dist/layout/resolve.d.ts +87 -0
- package/dist/layout/resolve.js +159 -0
- package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
- package/dist/lifecycle/lifecycle-hygiene.js +8 -5
- package/dist/orchestration/judgment-policy.js +4 -6
- package/dist/orchestration/verify-judgment-gates.d.ts +1 -0
- package/dist/orchestration/verify-judgment-gates.js +4 -2
- package/dist/policy/autonomy.js +4 -6
- package/dist/policy/capacity.js +4 -6
- package/dist/policy/decisions.d.ts +1 -0
- package/dist/policy/decisions.js +4 -2
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +3 -1
- package/dist/policy/plan-extensions.d.ts +35 -0
- package/dist/policy/plan-extensions.js +57 -0
- package/dist/policy/resolve.d.ts +6 -2
- package/dist/policy/resolve.js +18 -8
- package/dist/policy/wip.js +6 -4
- package/dist/preflight-cache/evaluate.js +11 -45
- package/dist/release/native-steps.js +4 -3
- package/dist/render/framework-commands.js +2 -1
- package/dist/render/prd-render.js +2 -2
- package/dist/render/project-render.js +23 -5
- package/dist/render/roadmap-render.js +36 -8
- package/dist/render/scope-outlook.js +3 -2
- package/dist/scope/audit-log.js +3 -2
- package/dist/scope/capacity-stamp.js +2 -1
- package/dist/scope/decompose.js +6 -4
- package/dist/scope/decomposed-refs.js +2 -1
- package/dist/scope/demote.js +5 -4
- package/dist/scope/project-context.js +2 -1
- package/dist/scope/project-definition-sync.js +4 -3
- package/dist/scope/transition.js +6 -2
- package/dist/scope/undo.js +2 -1
- package/dist/scope/vbrief-ref.js +5 -3
- package/dist/session/resume-conditions.js +2 -1
- package/dist/session/ritual-sentinel.js +2 -1
- package/dist/session/staleness.js +2 -1
- package/dist/slice/record.js +4 -4
- package/dist/spec-authority/resolver.js +5 -5
- package/dist/swarm/complete-cohort.js +5 -4
- package/dist/swarm/launch.js +6 -7
- package/dist/swarm/readiness.js +22 -20
- package/dist/swarm/subagent-backend.js +2 -5
- package/dist/swarm/verify-review-clean.js +2 -1
- package/dist/triage/actions/candidates-log.d.ts +3 -0
- package/dist/triage/actions/candidates-log.js +8 -2
- package/dist/triage/bootstrap/gitignore.js +22 -13
- package/dist/triage/bootstrap/index.js +7 -4
- package/dist/triage/bulk/index.js +2 -1
- package/dist/triage/classify/index.js +13 -11
- package/dist/triage/queue/audit.js +3 -3
- package/dist/triage/queue/build-queue.js +6 -0
- package/dist/triage/queue/cache.js +12 -2
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/ranking-labels.js +2 -1
- package/dist/triage/queue/scope-ignores-filter.d.ts +12 -0
- package/dist/triage/queue/scope-ignores-filter.js +50 -0
- package/dist/triage/queue/scope-walk.js +8 -6
- package/dist/triage/queue/types.d.ts +4 -0
- package/dist/triage/reconcile/audit.js +2 -1
- package/dist/triage/reconcile/parse-uri.js +2 -1
- package/dist/triage/reconcile/reconcile.js +8 -7
- package/dist/triage/refresh/extract.js +4 -2
- package/dist/triage/refresh/refresh.js +3 -2
- package/dist/triage/scope/mutations-core.js +13 -9
- package/dist/triage/scope/renderers.js +5 -3
- package/dist/triage/scope/resolve.js +6 -5
- package/dist/triage/scope/validate.js +3 -6
- package/dist/triage/scope-drift/add-ignore.js +4 -2
- package/dist/triage/scope-drift/scope-rules.js +3 -2
- package/dist/triage/subscribe/index.js +14 -8
- package/dist/triage/summary/index.d.ts +33 -0
- package/dist/triage/summary/index.js +180 -6
- package/dist/triage/summary/reconcilable.js +6 -4
- package/dist/triage/summary/scope-drift.js +2 -1
- package/dist/triage/welcome/default-mode.d.ts +1 -0
- package/dist/triage/welcome/default-mode.js +1 -0
- package/dist/triage/welcome/prior-state.js +9 -9
- package/dist/triage/welcome/summary.d.ts +2 -0
- package/dist/triage/welcome/summary.js +15 -8
- package/dist/triage/welcome/writers.js +12 -10
- package/dist/validate-content/capacity-policy.js +5 -4
- package/dist/validate-content/capacity-show.js +5 -3
- package/dist/validate-content/filename.d.ts +1 -1
- package/dist/validate-content/filename.js +5 -4
- package/dist/validate-content/validate-strategy-output.js +14 -5
- package/dist/vbrief-build/build.js +1 -1
- package/dist/vbrief-build/constants.js +7 -0
- package/dist/vbrief-build/speckit.js +3 -2
- package/dist/vbrief-reconcile/graph.js +3 -2
- package/dist/vbrief-reconcile/labels.js +4 -3
- package/dist/vbrief-reconcile/reconciliation.js +5 -1
- package/dist/vbrief-reconcile/swarm-deps.js +6 -5
- package/dist/vbrief-reconcile/umbrellas.d.ts +1 -1
- package/dist/vbrief-reconcile/umbrellas.js +9 -7
- package/dist/vbrief-validate/conformance.d.ts +1 -2
- package/dist/vbrief-validate/conformance.js +29 -14
- package/dist/vbrief-validate/constants.d.ts +18 -1
- package/dist/vbrief-validate/constants.js +32 -3
- package/dist/vbrief-validate/epic-links.js +3 -2
- package/dist/vbrief-validate/filename.js +5 -3
- package/dist/vbrief-validate/index.d.ts +3 -1
- package/dist/vbrief-validate/index.js +2 -1
- package/dist/vbrief-validate/main.js +17 -6
- package/dist/vbrief-validate/origin.js +1 -1
- package/dist/vbrief-validate/paths.js +2 -1
- package/dist/vbrief-validate/plan-hooks.js +4 -3
- package/dist/vbrief-validate/registry-status.d.ts +1 -0
- package/dist/vbrief-validate/registry-status.js +3 -1
- package/dist/vbrief-validate/roundtrip.d.ts +36 -0
- package/dist/vbrief-validate/roundtrip.js +168 -0
- package/dist/vbrief-validate/schema.d.ts +1 -1
- package/dist/vbrief-validate/schema.js +62 -19
- package/dist/vbrief-validate/validate-all.js +12 -4
- package/dist/verify-source/code-structure-validate.js +8 -4
- package/dist/verify-source/contract-drift.d.ts +33 -0
- package/dist/verify-source/contract-drift.js +170 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/xbrief-migrate/constants.d.ts +13 -0
- package/dist/xbrief-migrate/constants.js +14 -0
- package/dist/xbrief-migrate/detect.d.ts +11 -0
- package/dist/xbrief-migrate/detect.js +81 -0
- package/dist/xbrief-migrate/drift-gate.d.ts +56 -0
- package/dist/xbrief-migrate/drift-gate.js +205 -0
- package/dist/xbrief-migrate/index.d.ts +7 -0
- package/dist/xbrief-migrate/index.js +7 -0
- package/dist/xbrief-migrate/migrate-project.d.ts +33 -0
- package/dist/xbrief-migrate/migrate-project.js +189 -0
- package/dist/xbrief-migrate/signpost.d.ts +5 -0
- package/dist/xbrief-migrate/signpost.js +19 -0
- package/dist/xbrief-migrate/transforms.d.ts +46 -0
- package/dist/xbrief-migrate/transforms.js +182 -0
- package/dist/xbrief-migrate/version.d.ts +8 -0
- package/dist/xbrief-migrate/version.js +25 -0
- package/package.json +11 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
4
|
import { DEFAULT_TRIAGE_SCOPE, PROJECT_DEFINITION_REL_PATH } from "./constants.js";
|
|
4
5
|
export function projectDefinitionPath(projectRoot) {
|
|
5
6
|
return join(resolve(projectRoot), PROJECT_DEFINITION_REL_PATH);
|
|
@@ -24,7 +25,7 @@ export function isDefaultApplied(data) {
|
|
|
24
25
|
const plan = data.plan;
|
|
25
26
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
26
27
|
return true;
|
|
27
|
-
const policy = plan
|
|
28
|
+
const policy = readPlanPolicy(plan);
|
|
28
29
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
29
30
|
return true;
|
|
30
31
|
const scope = policy.triageScope;
|
|
@@ -36,7 +37,7 @@ export function getRawScope(data) {
|
|
|
36
37
|
const plan = data.plan;
|
|
37
38
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
38
39
|
return undefined;
|
|
39
|
-
const policy = plan
|
|
40
|
+
const policy = readPlanPolicy(plan);
|
|
40
41
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
41
42
|
return undefined;
|
|
42
43
|
return policy.triageScope;
|
|
@@ -47,7 +48,7 @@ export function getRawIgnores(data) {
|
|
|
47
48
|
const plan = data.plan;
|
|
48
49
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
49
50
|
return [];
|
|
50
|
-
const policy = plan
|
|
51
|
+
const policy = readPlanPolicy(plan);
|
|
51
52
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
52
53
|
return [];
|
|
53
54
|
const raw = policy.triageScopeIgnores;
|
|
@@ -65,7 +66,7 @@ export function resolveScopeRules(projectRoot, projectDefinition) {
|
|
|
65
66
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
66
67
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
67
68
|
}
|
|
68
|
-
const policy = plan
|
|
69
|
+
const policy = readPlanPolicy(plan);
|
|
69
70
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
70
71
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
71
72
|
}
|
|
@@ -91,7 +92,7 @@ export function resolveScopeIgnores(projectRoot, projectDefinition) {
|
|
|
91
92
|
const plan = data.plan;
|
|
92
93
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
93
94
|
return out;
|
|
94
|
-
const policy = plan
|
|
95
|
+
const policy = readPlanPolicy(plan);
|
|
95
96
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
96
97
|
return out;
|
|
97
98
|
const raw = policy.triageScopeIgnores;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
1
2
|
import { REFERENCED_BY_VBRIEF_SCOPES, SLICED_FROM_SCOPES, VALID_IGNORE_KEYS, VALID_IGNORE_RULES, VALID_RULE_TYPES, } from "./constants.js";
|
|
2
3
|
import { parseDurationMs } from "./duration.js";
|
|
3
4
|
import { validateMilestoneRule } from "./milestone.js";
|
|
@@ -238,9 +239,7 @@ function pointerForError(err, rawList) {
|
|
|
238
239
|
}
|
|
239
240
|
export function validateTriageScopeOnPlan(plan, filepath) {
|
|
240
241
|
const out = [];
|
|
241
|
-
const policy =
|
|
242
|
-
? plan.policy
|
|
243
|
-
: undefined;
|
|
242
|
+
const policy = readPlanPolicy(plan);
|
|
244
243
|
const rawScope = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
245
244
|
? policy.triageScope
|
|
246
245
|
: undefined;
|
|
@@ -254,9 +253,7 @@ export function validateTriageScopeOnPlan(plan, filepath) {
|
|
|
254
253
|
}
|
|
255
254
|
export function validateTriageScopeIgnoresOnPlan(plan, filepath) {
|
|
256
255
|
const out = [];
|
|
257
|
-
const policy =
|
|
258
|
-
? plan.policy
|
|
259
|
-
: undefined;
|
|
256
|
+
const policy = readPlanPolicy(plan);
|
|
260
257
|
const raw = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
261
258
|
? policy.triageScopeIgnores
|
|
262
259
|
: undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdtempSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
4
5
|
const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
5
6
|
function projectDefinitionPath(projectRoot) {
|
|
6
7
|
return join(resolve(projectRoot), PROJECT_DEFINITION_REL_PATH);
|
|
@@ -54,10 +55,11 @@ export function addIgnore(projectRoot, options) {
|
|
|
54
55
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan' key`);
|
|
55
56
|
}
|
|
56
57
|
const planRec = plan;
|
|
57
|
-
|
|
58
|
+
migrateLegacyPolicyKey(planRec);
|
|
59
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
58
60
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
59
61
|
policy = {};
|
|
60
|
-
planRec
|
|
62
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
61
63
|
}
|
|
62
64
|
const policyRec = policy;
|
|
63
65
|
const raw = Array.isArray(policyRec.triageScopeIgnores)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
4
|
const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
4
5
|
const DEFAULT_TRIAGE_SCOPE = [{ rule: "all-open" }];
|
|
5
6
|
function loadProjectDefinition(projectRoot) {
|
|
@@ -25,7 +26,7 @@ export function resolveScopeRules(projectRoot, projectDefinition) {
|
|
|
25
26
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
26
27
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
27
28
|
}
|
|
28
|
-
const policy = plan
|
|
29
|
+
const policy = readPlanPolicy(plan);
|
|
29
30
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
30
31
|
return DEFAULT_TRIAGE_SCOPE.map((r) => ({ ...r }));
|
|
31
32
|
}
|
|
@@ -47,7 +48,7 @@ export function resolveScopeIgnores(projectRoot, projectDefinition) {
|
|
|
47
48
|
const plan = data.plan;
|
|
48
49
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan))
|
|
49
50
|
return out;
|
|
50
|
-
const policy = plan
|
|
51
|
+
const policy = readPlanPolicy(plan);
|
|
51
52
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy))
|
|
52
53
|
return out;
|
|
53
54
|
const raw = policy.triageScopeIgnores;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
|
-
import { basename, join
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { resolveEvalPath, resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
5
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
4
6
|
export const SUBSCRIPTION_HISTORY_REL_PATH = "vbrief/.eval/subscription-history.jsonl";
|
|
5
7
|
export const SUBSCRIPTION_HISTORY_SCHEMA = "deft.triage.subscription-change.v1";
|
|
6
8
|
export const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
@@ -15,7 +17,7 @@ export class ProjectDefinitionIOError extends Error {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
function projectDefinitionPath(projectRoot) {
|
|
18
|
-
return
|
|
20
|
+
return resolveProjectDefinitionPath(projectRoot);
|
|
19
21
|
}
|
|
20
22
|
export function loadProjectDefinitionForMutation(projectRoot) {
|
|
21
23
|
const path = projectDefinitionPath(projectRoot);
|
|
@@ -87,7 +89,7 @@ function resolveActor(actor) {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
export function recordSubscriptionChange(projectRoot, options) {
|
|
90
|
-
const historyPath =
|
|
92
|
+
const historyPath = resolveEvalPath(projectRoot, "subscription-history.jsonl");
|
|
91
93
|
const record = {
|
|
92
94
|
schema: SUBSCRIPTION_HISTORY_SCHEMA,
|
|
93
95
|
change_id: randomUUID(),
|
|
@@ -249,15 +251,19 @@ function mutate(projectRoot, options) {
|
|
|
249
251
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan' key`);
|
|
250
252
|
}
|
|
251
253
|
const plan = data.plan;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
migrateLegacyPolicyKey(plan);
|
|
255
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
256
|
+
if (typeof existingPolicy !== "object" ||
|
|
257
|
+
existingPolicy === null ||
|
|
258
|
+
Array.isArray(existingPolicy)) {
|
|
259
|
+
if (existingPolicy === undefined) {
|
|
260
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
255
261
|
}
|
|
256
262
|
else {
|
|
257
263
|
throw new Error(`PROJECT-DEFINITION at ${path} has a non-object 'plan.policy' key`);
|
|
258
264
|
}
|
|
259
265
|
}
|
|
260
|
-
const policy = plan
|
|
266
|
+
const policy = plan[PLAN_POLICY_KEY];
|
|
261
267
|
if (!Array.isArray(policy.triageScope)) {
|
|
262
268
|
if (policy.triageScope === undefined) {
|
|
263
269
|
policy.triageScope = [];
|
|
@@ -298,6 +304,6 @@ export function subscribe(projectRoot, options = {}) {
|
|
|
298
304
|
export function unsubscribe(projectRoot, options = {}) {
|
|
299
305
|
return mutate(projectRoot, { op: "unsubscribe", ...options });
|
|
300
306
|
}
|
|
301
|
-
export const RECONCILE_HINT = " Reconciliation: run `task triage:bootstrap
|
|
307
|
+
export const RECONCILE_HINT = " Reconciliation: run `task triage:bootstrap` to " +
|
|
302
308
|
"backfill / mark out-of-scope cached entries.";
|
|
303
309
|
//# sourceMappingURL=index.js.map
|
|
@@ -8,6 +8,8 @@ export declare const CANDIDATES_LOG_REL_PATH = "vbrief/.eval/candidates.jsonl";
|
|
|
8
8
|
export { latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
9
9
|
export declare const SUMMARY_HISTORY_REL_PATH = "vbrief/.eval/summary-history.jsonl";
|
|
10
10
|
export declare const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
11
|
+
/** D2 repeat-suppression window for session-start triage one-liner (#1122 / #1279). */
|
|
12
|
+
export declare const D2_SUPPRESSION_WINDOW_HOURS = 4;
|
|
11
13
|
export declare const EMPTY_CACHE_LINE = "[triage] cache empty -- run task triage:bootstrap";
|
|
12
14
|
export declare const WIP_LIFECYCLE_DIRS: readonly ["pending", "active"];
|
|
13
15
|
export declare const FILESYSTEM_IN_FLIGHT_FOLDER = "active";
|
|
@@ -45,6 +47,10 @@ export declare function countFilesystemInFlight(projectRoot: string): number;
|
|
|
45
47
|
export declare function isTriageScopeExplicitlyConfigured(projectRoot: string): boolean;
|
|
46
48
|
/** Read `plan.policy.wipCap`; always returns an integer cap. */
|
|
47
49
|
export declare function resolveWipCapInt(projectRoot: string): number;
|
|
50
|
+
/** Read `raw.json` state for a cached github-issue entry; defaults to open when absent. */
|
|
51
|
+
export declare function readCachedIssueState(cacheRoot: string, repo: string, issueNumber: number): string;
|
|
52
|
+
/** True when a cached issue should participate in summary classification counts. */
|
|
53
|
+
export declare function isCachedIssueOpen(cacheRoot: string, repo: string, issueNumber: number): boolean;
|
|
48
54
|
export declare function computeSummary(projectRoot: string, options?: ComputeSummaryOptions): SummaryResult;
|
|
49
55
|
export declare function formatOneLiner(result: SummaryResult, options?: {
|
|
50
56
|
maxChars?: number;
|
|
@@ -58,6 +64,33 @@ export declare function summaryResultToRecord(result: SummaryResult, options: {
|
|
|
58
64
|
emittedAt: string;
|
|
59
65
|
line: string;
|
|
60
66
|
}): Record<string, unknown>;
|
|
67
|
+
/** Structured fields that drive operator-visible triage summary output (#1279). */
|
|
68
|
+
export type SuppressionKeyFields = {
|
|
69
|
+
readonly cache_empty: boolean;
|
|
70
|
+
readonly untriaged: number;
|
|
71
|
+
readonly stale_defer: number;
|
|
72
|
+
readonly in_flight: number;
|
|
73
|
+
readonly in_flight_filesystem: number;
|
|
74
|
+
readonly in_flight_cache_scoped: number;
|
|
75
|
+
readonly triage_scope_configured: boolean;
|
|
76
|
+
readonly wip_count: number;
|
|
77
|
+
readonly wip_cap: number;
|
|
78
|
+
readonly repos: readonly string[];
|
|
79
|
+
readonly scope_drift: number;
|
|
80
|
+
readonly reconcilable: number;
|
|
81
|
+
};
|
|
82
|
+
export declare function suppressionKeyFieldsFromResult(result: SummaryResult): SuppressionKeyFields;
|
|
83
|
+
/** Extract D2 suppression key fields from a summary-history JSONL record. */
|
|
84
|
+
export declare function suppressionKeyFieldsFromRecord(record: Record<string, unknown>): SuppressionKeyFields | null;
|
|
85
|
+
/** Canonical stable hash for D2 suppression comparison (#1279). */
|
|
86
|
+
export declare function suppressionKey(result: SummaryResult): string;
|
|
87
|
+
export declare function suppressionKeyFromRecord(record: Record<string, unknown>): string | null;
|
|
88
|
+
/** Read the most recent JSONL record from summary-history.jsonl. */
|
|
89
|
+
export declare function readLastHistoryRecord(historyPath: string): Record<string, unknown> | null;
|
|
90
|
+
/** True when D2 should skip stdout emission (key unchanged within 4h). */
|
|
91
|
+
export declare function shouldSuppressD2Emission(result: SummaryResult, historyPath: string, options?: {
|
|
92
|
+
now?: Date;
|
|
93
|
+
}): boolean;
|
|
61
94
|
/** Match Python `json.dumps(..., sort_keys=True, ensure_ascii=False)` spacing. */
|
|
62
95
|
export declare function pythonStyleStringify(value: unknown): string;
|
|
63
96
|
/** Append a single JSONL record to summary-history.jsonl. Never throws. */
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
2
3
|
import { join, resolve as pathResolve } from "node:path";
|
|
4
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
5
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
6
|
import { loadProjectDefinition, PROJECT_DEFINITION_REL_PATH } from "../../policy/resolve.js";
|
|
4
7
|
import { countVbriefWip, DEFAULT_WIP_CAP, resolveWipCap } from "../../policy/wip.js";
|
|
5
8
|
import { AUDIT_LOG_REL_PATH, latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
@@ -16,6 +19,8 @@ export const CANDIDATES_LOG_REL_PATH = AUDIT_LOG_REL_PATH;
|
|
|
16
19
|
export { latestDecisions, readAuditLog } from "../actions/candidates-log.js";
|
|
17
20
|
export const SUMMARY_HISTORY_REL_PATH = "vbrief/.eval/summary-history.jsonl";
|
|
18
21
|
export const SUMMARY_HISTORY_SCHEMA = "deft.triage.summary.v1";
|
|
22
|
+
/** D2 repeat-suppression window for session-start triage one-liner (#1122 / #1279). */
|
|
23
|
+
export const D2_SUPPRESSION_WINDOW_HOURS = 4;
|
|
19
24
|
export const EMPTY_CACHE_LINE = "[triage] cache empty -- run task triage:bootstrap";
|
|
20
25
|
export const WIP_LIFECYCLE_DIRS = ["pending", "active"];
|
|
21
26
|
export const FILESYSTEM_IN_FLIGHT_FOLDER = "active";
|
|
@@ -90,13 +95,13 @@ export function iterCachedIssues(cacheRoot) {
|
|
|
90
95
|
// ---------------------------------------------------------------------------
|
|
91
96
|
/** Count filesystem-truth in-flight vBRIEFs (#1270). */
|
|
92
97
|
export function countFilesystemInFlight(projectRoot) {
|
|
93
|
-
const folder =
|
|
98
|
+
const folder = resolveLifecycleFolder(projectRoot, FILESYSTEM_IN_FLIGHT_FOLDER);
|
|
94
99
|
if (!existsSync(folder)) {
|
|
95
100
|
return 0;
|
|
96
101
|
}
|
|
97
102
|
let total = 0;
|
|
98
103
|
for (const entry of readdirSync(folder, { withFileTypes: true })) {
|
|
99
|
-
if (!entry.isFile() || !entry.name
|
|
104
|
+
if (!entry.isFile() || !hasArtifactSuffix(entry.name)) {
|
|
100
105
|
continue;
|
|
101
106
|
}
|
|
102
107
|
try {
|
|
@@ -121,7 +126,7 @@ export function countFilesystemInFlight(projectRoot) {
|
|
|
121
126
|
}
|
|
122
127
|
/** True iff `plan.policy.triageScope` is a non-empty list of dict rules. */
|
|
123
128
|
export function isTriageScopeExplicitlyConfigured(projectRoot) {
|
|
124
|
-
const path =
|
|
129
|
+
const path = resolveProjectDefinitionPath(projectRoot);
|
|
125
130
|
if (!existsSync(path)) {
|
|
126
131
|
return false;
|
|
127
132
|
}
|
|
@@ -134,7 +139,7 @@ export function isTriageScopeExplicitlyConfigured(projectRoot) {
|
|
|
134
139
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
135
140
|
return false;
|
|
136
141
|
}
|
|
137
|
-
const policy = plan
|
|
142
|
+
const policy = readPlanPolicy(plan);
|
|
138
143
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
139
144
|
return false;
|
|
140
145
|
}
|
|
@@ -155,13 +160,36 @@ export function resolveWipCapInt(projectRoot) {
|
|
|
155
160
|
function decisionKey(repo, issueNumber) {
|
|
156
161
|
return `${repo}\0${issueNumber}`;
|
|
157
162
|
}
|
|
163
|
+
/** Read `raw.json` state for a cached github-issue entry; defaults to open when absent. */
|
|
164
|
+
export function readCachedIssueState(cacheRoot, repo, issueNumber) {
|
|
165
|
+
const [owner, name] = repo.split("/", 2);
|
|
166
|
+
const rawPath = join(cacheRoot, CACHE_SOURCE, owner ?? "", name ?? "", String(issueNumber), "raw.json");
|
|
167
|
+
if (!existsSync(rawPath)) {
|
|
168
|
+
return "open";
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const raw = JSON.parse(readFileSync(rawPath, { encoding: "utf8" }));
|
|
172
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
173
|
+
return "open";
|
|
174
|
+
}
|
|
175
|
+
const stateRaw = raw.state ?? "open";
|
|
176
|
+
return typeof stateRaw === "string" ? stateRaw.toLowerCase() : "open";
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return "open";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** True when a cached issue should participate in summary classification counts. */
|
|
183
|
+
export function isCachedIssueOpen(cacheRoot, repo, issueNumber) {
|
|
184
|
+
return readCachedIssueState(cacheRoot, repo, issueNumber) !== "closed";
|
|
185
|
+
}
|
|
158
186
|
// ---------------------------------------------------------------------------
|
|
159
187
|
// compute / format / persist
|
|
160
188
|
// ---------------------------------------------------------------------------
|
|
161
189
|
export function computeSummary(projectRoot, options = {}) {
|
|
162
190
|
const root = pathResolve(projectRoot);
|
|
163
191
|
const resolvedCacheRoot = options.cacheRoot ?? join(root, CACHE_DIR_NAME);
|
|
164
|
-
const resolvedLogPath = options.auditLogPath ??
|
|
192
|
+
const resolvedLogPath = options.auditLogPath ?? resolveEvalPath(root, "candidates.jsonl");
|
|
165
193
|
const cached = iterCachedIssues(resolvedCacheRoot);
|
|
166
194
|
const repos = [...new Set(cached.map(([repo]) => repo))].sort();
|
|
167
195
|
const wipCap = resolveWipCapInt(root);
|
|
@@ -191,6 +219,9 @@ export function computeSummary(projectRoot, options = {}) {
|
|
|
191
219
|
let staleDefer = 0;
|
|
192
220
|
const noDecisionKeys = [];
|
|
193
221
|
for (const [repo, issueNumber] of cached) {
|
|
222
|
+
if (!isCachedIssueOpen(resolvedCacheRoot, repo, issueNumber)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
194
225
|
const decision = decisions.get(decisionKey(repo, issueNumber));
|
|
195
226
|
if (decision === undefined || decision === "reset" || !TRIAGED_DECISIONS.has(decision)) {
|
|
196
227
|
untriaged += 1;
|
|
@@ -319,6 +350,11 @@ export function summaryResultToRecord(result, options) {
|
|
|
319
350
|
schema: SUMMARY_HISTORY_SCHEMA,
|
|
320
351
|
emitted_at: options.emittedAt,
|
|
321
352
|
line: options.line,
|
|
353
|
+
...suppressionKeyFieldsFromResult(result),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export function suppressionKeyFieldsFromResult(result) {
|
|
357
|
+
return {
|
|
322
358
|
cache_empty: result.cacheEmpty,
|
|
323
359
|
untriaged: result.untriaged,
|
|
324
360
|
stale_defer: result.staleDefer,
|
|
@@ -328,11 +364,149 @@ export function summaryResultToRecord(result, options) {
|
|
|
328
364
|
triage_scope_configured: result.triageScopeConfigured,
|
|
329
365
|
wip_count: result.wipCount,
|
|
330
366
|
wip_cap: result.wipCap,
|
|
331
|
-
repos: [...result.repos],
|
|
367
|
+
repos: [...result.repos].sort(),
|
|
332
368
|
scope_drift: result.scopeDrift,
|
|
333
369
|
reconcilable: result.reconcilable,
|
|
334
370
|
};
|
|
335
371
|
}
|
|
372
|
+
function readNumericField(record, key) {
|
|
373
|
+
const value = record[key];
|
|
374
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
375
|
+
}
|
|
376
|
+
function readBooleanField(record, key) {
|
|
377
|
+
const value = record[key];
|
|
378
|
+
return typeof value === "boolean" ? value : null;
|
|
379
|
+
}
|
|
380
|
+
function readStringArrayField(record, key) {
|
|
381
|
+
const value = record[key];
|
|
382
|
+
if (!Array.isArray(value)) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
if (!value.every((entry) => typeof entry === "string")) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
return [...value].sort();
|
|
389
|
+
}
|
|
390
|
+
/** Extract D2 suppression key fields from a summary-history JSONL record. */
|
|
391
|
+
export function suppressionKeyFieldsFromRecord(record) {
|
|
392
|
+
const cacheEmpty = readBooleanField(record, "cache_empty");
|
|
393
|
+
const untriaged = readNumericField(record, "untriaged");
|
|
394
|
+
const staleDefer = readNumericField(record, "stale_defer");
|
|
395
|
+
const inFlight = readNumericField(record, "in_flight");
|
|
396
|
+
const inFlightFilesystem = readNumericField(record, "in_flight_filesystem");
|
|
397
|
+
const inFlightCacheScoped = readNumericField(record, "in_flight_cache_scoped");
|
|
398
|
+
const triageScopeConfigured = readBooleanField(record, "triage_scope_configured");
|
|
399
|
+
const wipCount = readNumericField(record, "wip_count");
|
|
400
|
+
const wipCap = readNumericField(record, "wip_cap");
|
|
401
|
+
const repos = readStringArrayField(record, "repos");
|
|
402
|
+
const scopeDrift = readNumericField(record, "scope_drift");
|
|
403
|
+
const reconcilable = readNumericField(record, "reconcilable");
|
|
404
|
+
if (cacheEmpty === null ||
|
|
405
|
+
untriaged === null ||
|
|
406
|
+
staleDefer === null ||
|
|
407
|
+
inFlight === null ||
|
|
408
|
+
inFlightFilesystem === null ||
|
|
409
|
+
inFlightCacheScoped === null ||
|
|
410
|
+
triageScopeConfigured === null ||
|
|
411
|
+
wipCount === null ||
|
|
412
|
+
wipCap === null ||
|
|
413
|
+
repos === null ||
|
|
414
|
+
scopeDrift === null ||
|
|
415
|
+
reconcilable === null) {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
cache_empty: cacheEmpty,
|
|
420
|
+
untriaged,
|
|
421
|
+
stale_defer: staleDefer,
|
|
422
|
+
in_flight: inFlight,
|
|
423
|
+
in_flight_filesystem: inFlightFilesystem,
|
|
424
|
+
in_flight_cache_scoped: inFlightCacheScoped,
|
|
425
|
+
triage_scope_configured: triageScopeConfigured,
|
|
426
|
+
wip_count: wipCount,
|
|
427
|
+
wip_cap: wipCap,
|
|
428
|
+
repos,
|
|
429
|
+
scope_drift: scopeDrift,
|
|
430
|
+
reconcilable,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/** Canonical stable hash for D2 suppression comparison (#1279). */
|
|
434
|
+
export function suppressionKey(result) {
|
|
435
|
+
return createHash("sha256")
|
|
436
|
+
.update(pythonStyleStringify(suppressionKeyFieldsFromResult(result)))
|
|
437
|
+
.digest("hex");
|
|
438
|
+
}
|
|
439
|
+
export function suppressionKeyFromRecord(record) {
|
|
440
|
+
const fields = suppressionKeyFieldsFromRecord(record);
|
|
441
|
+
if (fields === null) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
return createHash("sha256").update(pythonStyleStringify(fields)).digest("hex");
|
|
445
|
+
}
|
|
446
|
+
function parseHistoryEmittedAt(record) {
|
|
447
|
+
const raw = record.emitted_at;
|
|
448
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
let candidate = raw.trim();
|
|
452
|
+
if (candidate.endsWith("Z")) {
|
|
453
|
+
candidate = `${candidate.slice(0, -1)}+00:00`;
|
|
454
|
+
}
|
|
455
|
+
const parsed = new Date(candidate);
|
|
456
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
457
|
+
}
|
|
458
|
+
/** Read the most recent JSONL record from summary-history.jsonl. */
|
|
459
|
+
export function readLastHistoryRecord(historyPath) {
|
|
460
|
+
if (!existsSync(historyPath)) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
let text;
|
|
464
|
+
try {
|
|
465
|
+
text = readFileSync(historyPath, { encoding: "utf8" });
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const lines = text.split("\n").filter((line) => line.trim().length > 0);
|
|
471
|
+
if (lines.length === 0) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
const last = lines[lines.length - 1];
|
|
475
|
+
if (last === undefined) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
const parsed = JSON.parse(last);
|
|
480
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
481
|
+
return parsed;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
/** True when D2 should skip stdout emission (key unchanged within 4h). */
|
|
490
|
+
export function shouldSuppressD2Emission(result, historyPath, options = {}) {
|
|
491
|
+
const prior = readLastHistoryRecord(historyPath);
|
|
492
|
+
if (prior === null) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
const emittedAt = parseHistoryEmittedAt(prior);
|
|
496
|
+
if (emittedAt === null) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
const now = options.now ?? new Date();
|
|
500
|
+
const ageMs = now.getTime() - emittedAt.getTime();
|
|
501
|
+
if (ageMs < 0 || ageMs >= D2_SUPPRESSION_WINDOW_HOURS * 3_600_000) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
const priorKey = suppressionKeyFromRecord(prior);
|
|
505
|
+
if (priorKey === null) {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
return priorKey === suppressionKey(result);
|
|
509
|
+
}
|
|
336
510
|
function sortKeysDeep(value) {
|
|
337
511
|
if (value === null || typeof value !== "object") {
|
|
338
512
|
return value;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
4
|
+
import { hasArtifactSuffix, resolveEvalPath, resolveLifecycleRoot } from "../../layout/resolve.js";
|
|
3
5
|
import { AUDIT_LOG_REL_PATH, readAuditLog } from "../actions/candidates-log.js";
|
|
4
6
|
/** Lifecycle folders scanned for reconcilable vBRIEFs (#1468). */
|
|
5
7
|
export const BACKFILL_FOLDERS = ["proposed", "pending", "active"];
|
|
@@ -36,7 +38,7 @@ function extractIssueRef(data) {
|
|
|
36
38
|
continue;
|
|
37
39
|
}
|
|
38
40
|
const obj = ref;
|
|
39
|
-
if (obj.type
|
|
41
|
+
if (!referenceTypeMatches(String(obj.type ?? ""), "github-issue")) {
|
|
40
42
|
continue;
|
|
41
43
|
}
|
|
42
44
|
const [repo, number] = parseGithubIssueUri(obj.uri);
|
|
@@ -61,11 +63,11 @@ function auditKey(repo, issueNumber) {
|
|
|
61
63
|
*/
|
|
62
64
|
export function countReconcilable(projectRoot, options = {}) {
|
|
63
65
|
try {
|
|
64
|
-
const auditPath = options.auditLogPath ??
|
|
66
|
+
const auditPath = options.auditLogPath ?? resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
65
67
|
const existing = existingAuditRefs(auditPath);
|
|
66
68
|
const defaultRepo = options.defaultRepo ?? null;
|
|
67
69
|
const keys = new Set();
|
|
68
|
-
const vbriefRoot =
|
|
70
|
+
const vbriefRoot = resolveLifecycleRoot(projectRoot);
|
|
69
71
|
for (const folderName of BACKFILL_FOLDERS) {
|
|
70
72
|
const folder = join(vbriefRoot, folderName);
|
|
71
73
|
if (!existsSync(folder)) {
|
|
@@ -73,7 +75,7 @@ export function countReconcilable(projectRoot, options = {}) {
|
|
|
73
75
|
}
|
|
74
76
|
const entries = readdirSync(folder, { withFileTypes: true });
|
|
75
77
|
for (const entry of entries) {
|
|
76
|
-
if (!entry.isFile() || !entry.name
|
|
78
|
+
if (!entry.isFile() || !hasArtifactSuffix(entry.name)) {
|
|
77
79
|
continue;
|
|
78
80
|
}
|
|
79
81
|
const path = join(folder, entry.name);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
3
4
|
import { loadProjectDefinition } from "../../policy/resolve.js";
|
|
4
5
|
const DRIFT_MIN_ISSUES = 3;
|
|
5
6
|
const CACHE_SOURCE = "github-issue";
|
|
@@ -88,7 +89,7 @@ function resolveScopeRules(projectRoot) {
|
|
|
88
89
|
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
89
90
|
return [{ rule: "all-open" }];
|
|
90
91
|
}
|
|
91
|
-
const policy = plan
|
|
92
|
+
const policy = readPlanPolicy(plan);
|
|
92
93
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
93
94
|
return [{ rule: "all-open" }];
|
|
94
95
|
}
|
|
@@ -16,6 +16,7 @@ export interface DefaultModeOptions {
|
|
|
16
16
|
readonly writeHistory?: boolean;
|
|
17
17
|
readonly taskPrefix?: string | null;
|
|
18
18
|
readonly selfHealFn?: (projectRoot: string) => void;
|
|
19
|
+
readonly now?: Date;
|
|
19
20
|
}
|
|
20
21
|
/** Non-interactive default mode (#1309). */
|
|
21
22
|
export declare function runDefaultMode(projectRoot: string, options?: DefaultModeOptions): WelcomeOutcome;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { hasArtifactSuffix, resolveAuditPath, resolveEvalPath, resolveLifecycleRoot, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
|
+
import { readPlanPolicy } from "../../policy/plan-extensions.js";
|
|
5
|
+
import { CACHE_DIR_NAME, CACHE_SOURCE, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WIP_LIFECYCLE_DIRS, } from "./constants.js";
|
|
4
6
|
function loadProjectDefinition(projectRoot) {
|
|
5
|
-
const path =
|
|
7
|
+
const path = resolveProjectDefinitionPath(projectRoot);
|
|
6
8
|
if (!existsSync(path))
|
|
7
9
|
return null;
|
|
8
10
|
try {
|
|
@@ -37,17 +39,17 @@ function countCacheEntries(projectRoot) {
|
|
|
37
39
|
return count;
|
|
38
40
|
}
|
|
39
41
|
export function candidatesLogPath(projectRoot) {
|
|
40
|
-
return
|
|
42
|
+
return resolveEvalPath(projectRoot, "candidates.jsonl");
|
|
41
43
|
}
|
|
42
44
|
function countWip(projectRoot) {
|
|
43
45
|
let total = 0;
|
|
44
|
-
const root =
|
|
46
|
+
const root = resolveLifecycleRoot(projectRoot);
|
|
45
47
|
for (const sub of WIP_LIFECYCLE_DIRS) {
|
|
46
48
|
const folder = join(root, sub);
|
|
47
49
|
if (!existsSync(folder))
|
|
48
50
|
continue;
|
|
49
51
|
for (const child of readdirSync(folder, { withFileTypes: true })) {
|
|
50
|
-
if (child.isFile() && child.name
|
|
52
|
+
if (child.isFile() && hasArtifactSuffix(child.name))
|
|
51
53
|
total += 1;
|
|
52
54
|
}
|
|
53
55
|
}
|
|
@@ -80,7 +82,7 @@ function summarizeScope(rules) {
|
|
|
80
82
|
return [true, `custom (${rules.length} rule(s))`];
|
|
81
83
|
}
|
|
82
84
|
function countPendingDecisions(projectRoot) {
|
|
83
|
-
const logPath =
|
|
85
|
+
const logPath = resolveAuditPath(projectRoot, "pending-human-decisions.jsonl");
|
|
84
86
|
if (!existsSync(logPath))
|
|
85
87
|
return 0;
|
|
86
88
|
let text;
|
|
@@ -121,9 +123,7 @@ export function pendingDecisionsNudgeLine(count, threshold = 3) {
|
|
|
121
123
|
export function detectPriorState(projectRoot) {
|
|
122
124
|
const data = loadProjectDefinition(projectRoot) ?? {};
|
|
123
125
|
const plan = data.plan;
|
|
124
|
-
const policy =
|
|
125
|
-
? plan.policy
|
|
126
|
-
: null;
|
|
126
|
+
const policy = readPlanPolicy(plan);
|
|
127
127
|
const rawScope = typeof policy === "object" && policy !== null && !Array.isArray(policy)
|
|
128
128
|
? policy.triageScope
|
|
129
129
|
: null;
|
|
@@ -19,5 +19,7 @@ export declare function appendHistory(historyPath: string, result: SummaryResult
|
|
|
19
19
|
export declare function emitOneliner(projectRoot: string, options?: {
|
|
20
20
|
writeHistory?: boolean;
|
|
21
21
|
output?: (line: string) => void;
|
|
22
|
+
now?: Date;
|
|
23
|
+
applyD2Suppression?: boolean;
|
|
22
24
|
}): string;
|
|
23
25
|
//# sourceMappingURL=summary.d.ts.map
|