@duo-director/cli 0.2.1 → 0.3.0-rc.3
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/THIRD_PARTY_NOTICES.md +3 -3
- package/dist/{cli-WTCBCBVN.js → cli-DJGVC2WO.js} +881 -407
- package/dist/duoctl.js +1 -1
- package/dist/runtime-tree.json +12 -12
- package/dist/ui/app.js +6 -6
- package/node_modules/@modelcontextprotocol/client/dist/{index-CEk3qkZM.d.cts → index-D_CiY-T3.d.cts} +3 -1
- package/node_modules/@modelcontextprotocol/client/dist/index-D_CiY-T3.d.cts.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/{index-BCtJi0p5.d.mts → index-D_QDtbiv.d.mts} +3 -1
- package/node_modules/@modelcontextprotocol/client/dist/index-D_QDtbiv.d.mts.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/index.cjs +100 -55
- package/node_modules/@modelcontextprotocol/client/dist/index.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.d.cts +157 -30
- package/node_modules/@modelcontextprotocol/client/dist/index.d.cts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.d.mts +157 -30
- package/node_modules/@modelcontextprotocol/client/dist/index.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.mjs +100 -55
- package/node_modules/@modelcontextprotocol/client/dist/index.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/shimsWorkerd.cjs +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/shimsWorkerd.mjs +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/{src-D0qEN74R.cjs → src-oZBjf3U1.cjs} +21 -4
- package/node_modules/@modelcontextprotocol/client/dist/src-oZBjf3U1.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/{src-DDAAhLnO.mjs → src-xKF0PJN-.mjs} +16 -5
- package/node_modules/@modelcontextprotocol/client/dist/src-xKF0PJN-.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/stdio.cjs +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.mjs +1 -1
- package/node_modules/@modelcontextprotocol/client/package.json +3 -3
- package/node_modules/@modelcontextprotocol/core/dist/{auth--n_Yzytt.d.cts → auth-Anm-lwWi.d.mts} +9 -8
- package/node_modules/@modelcontextprotocol/core/dist/{auth--n_Yzytt.d.cts.map → auth-Anm-lwWi.d.mts.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CGP0BDVq.mjs → auth-BNDyLTqp.mjs} +5 -3
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CGP0BDVq.mjs.map → auth-BNDyLTqp.mjs.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-4-zilcqg.cjs → auth-D1CeV_Jr.cjs} +5 -3
- package/node_modules/@modelcontextprotocol/core/dist/{auth-4-zilcqg.cjs.map → auth-D1CeV_Jr.cjs.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-YUQV3RRv.d.mts → auth-V9XJSCfa.d.cts} +9 -8
- package/node_modules/@modelcontextprotocol/core/dist/{auth-YUQV3RRv.d.mts.map → auth-V9XJSCfa.d.cts.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.cjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.mjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.cjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.mjs +1 -1
- package/node_modules/@modelcontextprotocol/core/package.json +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-Bt6U_Fqb.d.mts → createMcpHandler-CfX6zgs1.d.mts} +3 -1
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-CfX6zgs1.d.mts.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-BLVe3qmU.d.cts → createMcpHandler-LSKhyqpU.d.cts} +3 -1
- package/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-BLVe3qmU.d.cts.map → createMcpHandler-LSKhyqpU.d.cts.map} +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.cjs +5 -3
- package/node_modules/@modelcontextprotocol/server/dist/index.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.mjs +5 -3
- package/node_modules/@modelcontextprotocol/server/dist/index.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{mcp-Dw2OlZ1f.mjs → mcp-DYuW2ZSs.mjs} +6 -2
- package/node_modules/@modelcontextprotocol/server/dist/mcp-DYuW2ZSs.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{mcp-DUcCQSTW.cjs → mcp-TfXCpFsH.cjs} +6 -2
- package/node_modules/@modelcontextprotocol/server/dist/mcp-TfXCpFsH.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.cjs +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.mjs +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{src-D-y6h4N7.mjs → src-BHSMhZ_W.mjs} +8 -4
- package/node_modules/@modelcontextprotocol/server/dist/src-BHSMhZ_W.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{src-CgFsfg3E.cjs → src-BtQ5YSyq.cjs} +8 -4
- package/node_modules/@modelcontextprotocol/server/dist/src-BtQ5YSyq.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/stdio.cjs +2 -2
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.mjs +2 -2
- package/node_modules/@modelcontextprotocol/server/package.json +3 -3
- package/package.json +3 -3
- package/node_modules/@modelcontextprotocol/client/dist/index-BCtJi0p5.d.mts.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/index-CEk3qkZM.d.cts.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/src-D0qEN74R.cjs.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/src-DDAAhLnO.mjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-Bt6U_Fqb.d.mts.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/mcp-DUcCQSTW.cjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/mcp-Dw2OlZ1f.mjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/src-CgFsfg3E.cjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs.map +0 -1
|
@@ -157,8 +157,12 @@ var DIAGNOSTIC_SEVERITY = {
|
|
|
157
157
|
DECISION_SUPERSEDE_TARGET_INVALID: "error",
|
|
158
158
|
/** DecisionService: another process holds the repository decision lock. */
|
|
159
159
|
DECISION_LOCK_BUSY: "error",
|
|
160
|
+
/** DecisionService (T34.2): the candidate changed after the preview a person reviewed; nothing was confirmed. */
|
|
161
|
+
DECISION_CONFIRM_PREVIEW_CHANGED: "error",
|
|
160
162
|
/** A confirmed Decision's lock.digest does not match its content fields (a detection aid, not a signature). */
|
|
161
163
|
DECISION_LOCK_MISMATCH: "warning",
|
|
164
|
+
/** T40 (N1): an authoritative operation refused partial Project Truth; the loader errors follow it. */
|
|
165
|
+
PROJECT_TRUTH_INVALID: "error",
|
|
162
166
|
/** A SourceLocation that does not address its canonical source text (a producer bug, T09.1). */
|
|
163
167
|
SOURCE_LOCATION_INVALID: "error",
|
|
164
168
|
/** Context Compiler: the request cannot be compiled (empty task, budget out of range or below the packet frame, unknown profile). */
|
|
@@ -298,6 +302,8 @@ var DIAGNOSTIC_PERSISTENCE = {
|
|
|
298
302
|
DECISION_TARGET_UNSUPPORTED: T,
|
|
299
303
|
DECISION_SUPERSEDE_TARGET_INVALID: T,
|
|
300
304
|
DECISION_LOCK_BUSY: T,
|
|
305
|
+
DECISION_CONFIRM_PREVIEW_CHANGED: T,
|
|
306
|
+
PROJECT_TRUTH_INVALID: T,
|
|
301
307
|
DECISION_LOCK_MISMATCH: P,
|
|
302
308
|
SOURCE_LOCATION_INVALID: P,
|
|
303
309
|
CONTEXT_REQUEST_INVALID: T,
|
|
@@ -1311,7 +1317,8 @@ var decisionShape = {
|
|
|
1311
1317
|
rationale: text().optional(),
|
|
1312
1318
|
owner: z.literal("human").optional(),
|
|
1313
1319
|
governs: z.strictObject({ requirements: ids().optional(), paths: texts().optional(), symbols: texts().optional() }).optional(),
|
|
1314
|
-
|
|
1320
|
+
/** imported_paths (H-72): repository path patterns; a changed import resolving to a matching file conflicts. */
|
|
1321
|
+
forbids: z.strictObject({ dependencies: texts().optional(), symbols: texts().optional(), paths: texts().optional(), imported_paths: texts().optional() }).optional(),
|
|
1315
1322
|
supersedes: id().nullable().optional(),
|
|
1316
1323
|
evidence: z.array(EvidencePointerSchema).optional(),
|
|
1317
1324
|
source: sources,
|
|
@@ -1592,7 +1599,9 @@ function mapDecisionContent(d, ctx, governsRelation) {
|
|
|
1592
1599
|
forbids: {
|
|
1593
1600
|
dependencies: d.forbids?.dependencies ?? [],
|
|
1594
1601
|
symbols: d.forbids?.symbols ?? [],
|
|
1595
|
-
paths: ctx.patterns(["forbids", "paths"], d.forbids?.paths)
|
|
1602
|
+
paths: ctx.patterns(["forbids", "paths"], d.forbids?.paths),
|
|
1603
|
+
// H-72: only when present and non-empty; absent stays absent (lock digest compatibility).
|
|
1604
|
+
...(d.forbids?.imported_paths?.length ?? 0) > 0 ? { importedPaths: ctx.patterns(["forbids", "imported_paths"], d.forbids?.imported_paths) } : {}
|
|
1596
1605
|
},
|
|
1597
1606
|
supersedes: d.supersedes ?? null,
|
|
1598
1607
|
evidence: ctx.evidence(["evidence"], d.evidence),
|
|
@@ -2183,6 +2192,23 @@ function loadProjectTruth(root, options = {}) {
|
|
|
2183
2192
|
return { value: { truth, trace: trace2.model }, diagnostics: diagnostics.sort(compareDiagnostics) };
|
|
2184
2193
|
}
|
|
2185
2194
|
|
|
2195
|
+
// packages/core/dist/loader/authority.js
|
|
2196
|
+
var NON_BLOCKING_TRUTH_ERRORS = /* @__PURE__ */ new Set(["BROKEN_REFERENCE", "REFERENCE_TYPE_MISMATCH"]);
|
|
2197
|
+
function truthAuthorityErrors(diagnostics) {
|
|
2198
|
+
return diagnostics.filter((d) => d.severity === "error" && !NON_BLOCKING_TRUTH_ERRORS.has(d.code));
|
|
2199
|
+
}
|
|
2200
|
+
function projectTruthInvalid(errors) {
|
|
2201
|
+
const files = [...new Set(errors.map((e) => e.source?.path).filter((p) => p !== void 0))];
|
|
2202
|
+
const where = files.length === 0 ? "" : `: ${files.slice(0, 3).join(", ")}${files.length > 3 ? ` and ${files.length - 3} more` : ""}`;
|
|
2203
|
+
return createDiagnostic("PROJECT_TRUTH_INVALID", `Project Truth could not be read completely (${errors.length} error${errors.length === 1 ? "" : "s"}${where}). DUO does not review, compile context or change Decisions on partial Truth. Fix the file; DUO does not change it.`);
|
|
2204
|
+
}
|
|
2205
|
+
function requireCompleteTruth(loaded) {
|
|
2206
|
+
if (loaded.value === void 0)
|
|
2207
|
+
return loaded;
|
|
2208
|
+
const errors = truthAuthorityErrors(loaded.diagnostics);
|
|
2209
|
+
return errors.length === 0 ? loaded : { diagnostics: [projectTruthInvalid(errors), ...errors] };
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2186
2212
|
// packages/core/dist/decisions/service.js
|
|
2187
2213
|
import os from "node:os";
|
|
2188
2214
|
|
|
@@ -2199,6 +2225,9 @@ function canonical(value) {
|
|
|
2199
2225
|
}
|
|
2200
2226
|
return value;
|
|
2201
2227
|
}
|
|
2228
|
+
function candidateDigest(value) {
|
|
2229
|
+
return sha256(stableJson(value));
|
|
2230
|
+
}
|
|
2202
2231
|
function stableJson(value) {
|
|
2203
2232
|
return JSON.stringify(canonical(value));
|
|
2204
2233
|
}
|
|
@@ -2362,6 +2391,7 @@ var DECISION_PERMISSIONS = {
|
|
|
2362
2391
|
confirm: ["human"],
|
|
2363
2392
|
reject: ["human"]
|
|
2364
2393
|
};
|
|
2394
|
+
var CANDIDATE_FIELDS = ["title", "kind", "question", "answer", "rationale", "governs", "forbids", "enforcement", "supersedes", "evidence", "source", "extensions"];
|
|
2365
2395
|
var SYSTEM = { kind: "system", name: "duo" };
|
|
2366
2396
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2367
2397
|
var idTokens = (message2) => message2.split(/[^A-Za-z0-9-]+/u);
|
|
@@ -2419,8 +2449,9 @@ function createDecisionService(options) {
|
|
|
2419
2449
|
await fs28.remove(file);
|
|
2420
2450
|
}
|
|
2421
2451
|
}
|
|
2422
|
-
function load() {
|
|
2423
|
-
const
|
|
2452
|
+
function load(complete = true) {
|
|
2453
|
+
const raw = loadProjectTruth(root);
|
|
2454
|
+
const loaded = complete ? requireCompleteTruth(raw) : raw;
|
|
2424
2455
|
return loaded.value === void 0 ? failure(loaded.diagnostics) : success(loaded.value.truth, loaded.diagnostics);
|
|
2425
2456
|
}
|
|
2426
2457
|
const isServiceFile = (d) => d.location.path === decisionPath(d.id);
|
|
@@ -2475,13 +2506,13 @@ function createDecisionService(options) {
|
|
|
2475
2506
|
}
|
|
2476
2507
|
return { repaired, diagnostics };
|
|
2477
2508
|
}
|
|
2478
|
-
async function prepare() {
|
|
2479
|
-
let loaded = load();
|
|
2509
|
+
async function prepare(complete = true) {
|
|
2510
|
+
let loaded = load(complete);
|
|
2480
2511
|
if (loaded.value === void 0)
|
|
2481
2512
|
return failure(loaded.diagnostics);
|
|
2482
2513
|
const fixed = await repairWith(loaded.value);
|
|
2483
2514
|
if (fixed.repaired.length > 0) {
|
|
2484
|
-
loaded = load();
|
|
2515
|
+
loaded = load(complete);
|
|
2485
2516
|
if (loaded.value === void 0)
|
|
2486
2517
|
return failure(loaded.diagnostics);
|
|
2487
2518
|
}
|
|
@@ -2541,7 +2572,7 @@ function createDecisionService(options) {
|
|
|
2541
2572
|
if (denied.length > 0)
|
|
2542
2573
|
return failure(denied);
|
|
2543
2574
|
return withLock(async () => {
|
|
2544
|
-
const prepared = await prepare();
|
|
2575
|
+
const prepared = await prepare(false);
|
|
2545
2576
|
if (prepared.value === void 0)
|
|
2546
2577
|
return failure(prepared.diagnostics);
|
|
2547
2578
|
const { truth, repaired } = prepared.value;
|
|
@@ -2588,7 +2619,7 @@ function createDecisionService(options) {
|
|
|
2588
2619
|
return success({ proposalId: created.value.id, path: created.value.path, indexRequired: false, repaired }, prepared.diagnostics);
|
|
2589
2620
|
});
|
|
2590
2621
|
},
|
|
2591
|
-
async confirm(actor, id3) {
|
|
2622
|
+
async confirm(actor, id3, confirmOptions = {}) {
|
|
2592
2623
|
const denied = forbidden(actor, "confirm");
|
|
2593
2624
|
if (denied.length > 0)
|
|
2594
2625
|
return failure(denied);
|
|
@@ -2600,7 +2631,13 @@ function createDecisionService(options) {
|
|
|
2600
2631
|
return failure(prepared.diagnostics);
|
|
2601
2632
|
const { truth, repaired } = prepared.value;
|
|
2602
2633
|
const warnings = [...prepared.diagnostics];
|
|
2603
|
-
|
|
2634
|
+
const c = await candidateOf(truth, id3);
|
|
2635
|
+
if (c.value === void 0)
|
|
2636
|
+
return failure(c.diagnostics);
|
|
2637
|
+
if (confirmOptions.expectedDigest !== void 0 && confirmOptions.expectedDigest !== c.value.digest) {
|
|
2638
|
+
return failure([createDiagnostic("DECISION_CONFIRM_PREVIEW_CHANGED", "The Decision changed after you reviewed it. Review the current contents and confirm again.", { path: c.value.sourcePath })]);
|
|
2639
|
+
}
|
|
2640
|
+
return c.value.sourceKind === "decision" ? confirmDecision(actor, c.value, truth, repaired, warnings) : confirmProposal(actor, c.value, truth, repaired, warnings);
|
|
2604
2641
|
});
|
|
2605
2642
|
},
|
|
2606
2643
|
async reject(actor, proposalId, reason) {
|
|
@@ -2635,39 +2672,70 @@ function createDecisionService(options) {
|
|
|
2635
2672
|
return prepared.value === void 0 ? failure(prepared.diagnostics) : success({ repaired: prepared.value.repaired }, prepared.diagnostics);
|
|
2636
2673
|
});
|
|
2637
2674
|
},
|
|
2638
|
-
async previewConfirm(
|
|
2639
|
-
if (!PROPOSAL_ID_PATTERN.test(
|
|
2640
|
-
return failure([createDiagnostic("INVALID_ID", `"${
|
|
2675
|
+
async previewConfirm(id3) {
|
|
2676
|
+
if (!PROPOSAL_ID_PATTERN.test(id3) && !DECISION_ID.test(id3))
|
|
2677
|
+
return failure([createDiagnostic("INVALID_ID", `"${id3}" is not a proposal or Decision ID`)]);
|
|
2641
2678
|
const loaded = load();
|
|
2642
2679
|
if (loaded.value === void 0)
|
|
2643
2680
|
return failure(loaded.diagnostics);
|
|
2644
2681
|
const truth = loaded.value;
|
|
2645
|
-
const
|
|
2646
|
-
if (
|
|
2647
|
-
return failure(
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
const stale = staleness(truth, entry.proposal);
|
|
2651
|
-
const target = entry.proposal.supersedes === null ? void 0 : truth.decisions.find((d) => d.id === entry.proposal.supersedes);
|
|
2682
|
+
const c = await candidateOf(truth, id3);
|
|
2683
|
+
if (c.value === void 0)
|
|
2684
|
+
return failure(c.diagnostics);
|
|
2685
|
+
const v = c.value;
|
|
2686
|
+
const expected = v.sourceKind === "decision" ? id3 : await allocate("D", truth);
|
|
2652
2687
|
return success({
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2688
|
+
sourceId: id3,
|
|
2689
|
+
sourceKind: v.sourceKind,
|
|
2690
|
+
sourcePath: v.sourcePath,
|
|
2691
|
+
action: v.action,
|
|
2692
|
+
...v.sourceKind === "proposal" ? { proposalId: id3 } : {},
|
|
2693
|
+
expectedDecisionId: expected,
|
|
2694
|
+
nextDecisionId: expected,
|
|
2695
|
+
candidate: v.candidate,
|
|
2696
|
+
...v.proposedBy === void 0 ? {} : { proposedBy: v.proposedBy },
|
|
2697
|
+
...v.proposedByKind === void 0 ? {} : { proposedByKind: v.proposedByKind },
|
|
2698
|
+
...v.stale === void 0 ? {} : { stale: v.stale },
|
|
2699
|
+
...v.target === void 0 ? {} : { supersedes: { id: v.target.id, title: v.target.title, state: v.target.state, path: v.target.location.path } },
|
|
2700
|
+
digest: v.digest
|
|
2657
2701
|
});
|
|
2658
2702
|
},
|
|
2659
|
-
verifyLock(
|
|
2703
|
+
verifyLock(decisionId2) {
|
|
2660
2704
|
const loaded = load();
|
|
2661
2705
|
if (loaded.value === void 0)
|
|
2662
2706
|
return failure(loaded.diagnostics);
|
|
2663
|
-
const d = loaded.value.decisions.find((x) => x.id ===
|
|
2707
|
+
const d = loaded.value.decisions.find((x) => x.id === decisionId2);
|
|
2664
2708
|
if (d === void 0)
|
|
2665
|
-
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `No Decision ${
|
|
2709
|
+
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `No Decision ${decisionId2}`)]);
|
|
2666
2710
|
const r = verifyDecisionLock(d);
|
|
2667
2711
|
return success(r.value, r.diagnostics);
|
|
2668
2712
|
}
|
|
2669
2713
|
};
|
|
2670
|
-
async function
|
|
2714
|
+
async function candidateOf(truth, id3) {
|
|
2715
|
+
if (DECISION_ID.test(id3)) {
|
|
2716
|
+
const d = truth.decisions.find((x) => x.id === id3);
|
|
2717
|
+
if (d === void 0)
|
|
2718
|
+
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `No Decision ${id3}`)]);
|
|
2719
|
+
if (!isServiceFile(d))
|
|
2720
|
+
return failure([createDiagnostic("DECISION_TARGET_UNSUPPORTED", `${id3} is not a decisions/D-###.yaml file; DUO does not rewrite it`, d.location)]);
|
|
2721
|
+
if (d.state === "superseded" || d.state === CONFIRMED && d.lock !== void 0) {
|
|
2722
|
+
return failure([createDiagnostic("DECISION_LOCKED", `${id3} is ${d.state} and locked; supersede it with a new proposal`, d.location)]);
|
|
2723
|
+
}
|
|
2724
|
+
if (d.state === "rejected")
|
|
2725
|
+
return failure([createDiagnostic("PROPOSAL_NOT_PENDING", `${id3} is rejected`, d.location)]);
|
|
2726
|
+
const data2 = await rawData(d.location.path);
|
|
2727
|
+
if (data2 === void 0)
|
|
2728
|
+
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `${d.location.path} is missing`)]);
|
|
2729
|
+
return success(finishCandidate(truth, {
|
|
2730
|
+
sourceId: id3,
|
|
2731
|
+
sourceKind: "decision",
|
|
2732
|
+
sourcePath: d.location.path,
|
|
2733
|
+
action: d.state === CONFIRMED ? "add-lock" : "confirm-in-place",
|
|
2734
|
+
data: data2,
|
|
2735
|
+
supersedesId: d.supersedes,
|
|
2736
|
+
decision: d
|
|
2737
|
+
}));
|
|
2738
|
+
}
|
|
2671
2739
|
const done = truth.decisions.find((d) => d.proposalId === id3);
|
|
2672
2740
|
if (done !== void 0)
|
|
2673
2741
|
return failure([createDiagnostic("PROPOSAL_NOT_PENDING", `${id3} was already confirmed as ${done.id}`)]);
|
|
@@ -2676,16 +2744,54 @@ function createDecisionService(options) {
|
|
|
2676
2744
|
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `No proposal ${id3}`)]);
|
|
2677
2745
|
if (entry.status !== "pending")
|
|
2678
2746
|
return failure([createDiagnostic("PROPOSAL_NOT_PENDING", `${id3} is ${entry.status}`, entry.proposal.location)]);
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2747
|
+
const data = await rawData(entry.proposal.location.path);
|
|
2748
|
+
if (data === void 0)
|
|
2749
|
+
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `${entry.proposal.location.path} is missing`)]);
|
|
2750
|
+
const stale = staleness(truth, entry.proposal);
|
|
2751
|
+
return success(finishCandidate(truth, {
|
|
2752
|
+
sourceId: id3,
|
|
2753
|
+
sourceKind: "proposal",
|
|
2754
|
+
sourcePath: entry.proposal.location.path,
|
|
2755
|
+
action: "create",
|
|
2756
|
+
data,
|
|
2757
|
+
supersedesId: entry.proposal.supersedes,
|
|
2758
|
+
proposal: entry.proposal,
|
|
2759
|
+
...stale === void 0 ? {} : { stale },
|
|
2760
|
+
...typeof data.proposed_by === "string" ? { proposedBy: data.proposed_by } : {},
|
|
2761
|
+
...typeof data.proposed_by_kind === "string" ? { proposedByKind: data.proposed_by_kind } : {}
|
|
2762
|
+
}));
|
|
2763
|
+
}
|
|
2764
|
+
async function rawData(repoPath3) {
|
|
2765
|
+
const text4 = await fs28.readText(`${root}/${repoPath3}`);
|
|
2766
|
+
if (text4 === void 0)
|
|
2767
|
+
return void 0;
|
|
2768
|
+
return parseYaml({ path: repoPath3, text: text4 }).value?.data ?? {};
|
|
2769
|
+
}
|
|
2770
|
+
function finishCandidate(truth, base) {
|
|
2771
|
+
const candidate = defined(Object.fromEntries(CANDIDATE_FIELDS.map((k) => [k, base.data[k]])));
|
|
2772
|
+
const target = base.supersedesId === null ? void 0 : truth.decisions.find((t2) => t2.id === base.supersedesId);
|
|
2773
|
+
const digest = candidateDigest({
|
|
2774
|
+
sourceId: base.sourceId,
|
|
2775
|
+
sourceKind: base.sourceKind,
|
|
2776
|
+
sourcePath: base.sourcePath,
|
|
2777
|
+
action: base.action,
|
|
2778
|
+
candidate,
|
|
2779
|
+
stale: base.stale ?? null,
|
|
2780
|
+
proposedBy: base.proposedBy ?? null,
|
|
2781
|
+
proposedByKind: base.proposedByKind ?? null,
|
|
2782
|
+
supersedes: base.supersedesId === null ? null : target === void 0 ? { id: base.supersedesId, missing: true } : { id: target.id, title: target.title, state: target.state, supersededBy: target.supersededBy, path: target.location.path, content: decisionLockDigest(target) }
|
|
2783
|
+
});
|
|
2784
|
+
return { ...base, candidate, ...target === void 0 ? {} : { target }, digest };
|
|
2785
|
+
}
|
|
2786
|
+
async function confirmProposal(actor, c, truth, repaired, warnings) {
|
|
2787
|
+
const id3 = c.sourceId;
|
|
2788
|
+
const proposal = c.proposal;
|
|
2789
|
+
const stale = c.stale;
|
|
2681
2790
|
if (stale !== void 0) {
|
|
2682
2791
|
warnings.push(createDiagnostic("PROPOSAL_STALE", `${id3} was made on an older Project Truth${stale.changedRefs.length > 0 ? ` (changed: ${stale.changedRefs.join(", ")})` : ""}; confirmed anyway`, proposal.location));
|
|
2683
2792
|
}
|
|
2684
|
-
const
|
|
2685
|
-
|
|
2686
|
-
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `${proposal.location.path} is missing`)]);
|
|
2687
|
-
const data = parseYaml({ path: proposal.location.path, text: text4 }).value?.data ?? {};
|
|
2688
|
-
const target = proposal.supersedes === null ? void 0 : truth.decisions.find((d) => d.id === proposal.supersedes);
|
|
2793
|
+
const data = c.data;
|
|
2794
|
+
const target = c.target;
|
|
2689
2795
|
if (proposal.supersedes !== null) {
|
|
2690
2796
|
if (target === void 0)
|
|
2691
2797
|
return failure([createDiagnostic("BROKEN_REFERENCE", `${id3} supersedes ${proposal.supersedes}, which does not exist`, proposal.location)]);
|
|
@@ -2695,8 +2801,8 @@ function createDecisionService(options) {
|
|
|
2695
2801
|
return failure([createDiagnostic("DECISION_SUPERSEDE_TARGET_INVALID", `${target.id} is ${target.state}; only a confirmed Decision can be superseded`, target.location)]);
|
|
2696
2802
|
}
|
|
2697
2803
|
const confirmedAt = now();
|
|
2698
|
-
const content = (
|
|
2699
|
-
id:
|
|
2804
|
+
const content = (decisionId2, digest) => yaml(defined({
|
|
2805
|
+
id: decisionId2,
|
|
2700
2806
|
title: data.title,
|
|
2701
2807
|
kind: data.kind,
|
|
2702
2808
|
state: CONFIRMED,
|
|
@@ -2719,9 +2825,9 @@ function createDecisionService(options) {
|
|
|
2719
2825
|
confirmed_by: actor.name,
|
|
2720
2826
|
lock: digest === void 0 ? void 0 : { digest }
|
|
2721
2827
|
}));
|
|
2722
|
-
const render = (
|
|
2723
|
-
const draft = parseDecisionFile(decisionPath(
|
|
2724
|
-
return content(
|
|
2828
|
+
const render = (decisionId2) => {
|
|
2829
|
+
const draft = parseDecisionFile(decisionPath(decisionId2), content(decisionId2));
|
|
2830
|
+
return content(decisionId2, draft.value === void 0 ? void 0 : decisionLockDigest(draft.value));
|
|
2725
2831
|
};
|
|
2726
2832
|
const probeId = await allocate("D", truth);
|
|
2727
2833
|
const probe = parseDecisionFile(decisionPath(probeId), render(probeId));
|
|
@@ -2754,17 +2860,9 @@ function createDecisionService(options) {
|
|
|
2754
2860
|
repaired
|
|
2755
2861
|
}, [...warnings, ...after.map((d) => ({ ...d, severity: "warning" }))]);
|
|
2756
2862
|
}
|
|
2757
|
-
async function confirmDecision(actor,
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2760
|
-
return failure([createDiagnostic("PROPOSAL_NOT_FOUND", `No Decision ${id3}`)]);
|
|
2761
|
-
if (!isServiceFile(d))
|
|
2762
|
-
return failure([createDiagnostic("DECISION_TARGET_UNSUPPORTED", `${id3} is not a decisions/D-###.yaml file; DUO does not rewrite it`, d.location)]);
|
|
2763
|
-
if (d.state === "superseded" || d.state === CONFIRMED && d.lock !== void 0) {
|
|
2764
|
-
return failure([createDiagnostic("DECISION_LOCKED", `${id3} is ${d.state} and locked; supersede it with a new proposal`, d.location)]);
|
|
2765
|
-
}
|
|
2766
|
-
if (d.state === "rejected")
|
|
2767
|
-
return failure([createDiagnostic("PROPOSAL_NOT_PENDING", `${id3} is rejected`, d.location)]);
|
|
2863
|
+
async function confirmDecision(actor, c, truth, repaired, warnings) {
|
|
2864
|
+
const id3 = c.sourceId;
|
|
2865
|
+
const d = c.decision;
|
|
2768
2866
|
const digest = decisionLockDigest(d);
|
|
2769
2867
|
const values = d.state === CONFIRMED ? { lock: { digest }, confirmed_at: d.confirmedAt ?? now(), confirmed_by: d.confirmedBy ?? actor.name } : { state: CONFIRMED, owner: "human", confirmed_at: now(), confirmed_by: actor.name, lock: { digest } };
|
|
2770
2868
|
const target = d.supersedes === null ? void 0 : truth.decisions.find((t2) => t2.id === d.supersedes);
|
|
@@ -9533,6 +9631,45 @@ function sameFingerprint(a, b) {
|
|
|
9533
9631
|
return a !== void 0 && b !== void 0 && a.state === b.state && a.fingerprintMode === b.fingerprintMode && a.contentHash === b.contentHash && a.size === b.size && a.gitBlobOid === b.gitBlobOid;
|
|
9534
9632
|
}
|
|
9535
9633
|
|
|
9634
|
+
// packages/graph/dist/incremental/references.js
|
|
9635
|
+
async function readIndexedModuleReferences(root, paths) {
|
|
9636
|
+
const read2 = readIndexState(root);
|
|
9637
|
+
if (read2.state === void 0)
|
|
9638
|
+
return paths.map((path35) => ({ status: "unavailable", path: path35, reason: read2.problem ?? "index state" }));
|
|
9639
|
+
const files = new Map(read2.state.files.map((f) => [f.path, f]));
|
|
9640
|
+
const keys = paths.map((path35) => {
|
|
9641
|
+
const f = files.get(path35);
|
|
9642
|
+
return f?.analysis === void 0 || f.analysis.status !== "ok" || f.resolution === void 0 ? void 0 : { path: path35, contentHash: f.contentHash, analyzer: f.analysis.analyzer, analyzerIdentity: f.analysis.identity };
|
|
9643
|
+
});
|
|
9644
|
+
const present = keys.filter((k) => k !== void 0);
|
|
9645
|
+
const cached = await readCachedAnalyses(root, present);
|
|
9646
|
+
const byPath = new Map(present.map((k, i) => [k.path, cached[i]]));
|
|
9647
|
+
return paths.map((path35, i) => {
|
|
9648
|
+
const f = files.get(path35);
|
|
9649
|
+
if (f === void 0)
|
|
9650
|
+
return { status: "not-indexed", path: path35 };
|
|
9651
|
+
if (keys[i] === void 0)
|
|
9652
|
+
return { status: "not-analyzed", path: path35 };
|
|
9653
|
+
const entry = byPath.get(path35);
|
|
9654
|
+
const modules = f.resolution?.modules ?? [];
|
|
9655
|
+
if (entry === void 0)
|
|
9656
|
+
return { status: "unavailable", path: path35, reason: "analysis cache entry missing" };
|
|
9657
|
+
const refs = entry.analysis.moduleReferences;
|
|
9658
|
+
if (refs.length !== modules.length)
|
|
9659
|
+
return { status: "unavailable", path: path35, reason: "analysis and resolution do not match" };
|
|
9660
|
+
return {
|
|
9661
|
+
status: "ok",
|
|
9662
|
+
path: path35,
|
|
9663
|
+
language: entry.analysis.language,
|
|
9664
|
+
references: refs.map((r, j) => {
|
|
9665
|
+
const resolution = modules[j];
|
|
9666
|
+
const target = resolution.status === "resolved" && resolution.path !== path35 && files.has(resolution.path) ? resolution.path : void 0;
|
|
9667
|
+
return { kind: r.kind, specifier: r.specifier, typeOnly: r.typeOnly, location: r.location, resolution, ...target === void 0 ? {} : { target } };
|
|
9668
|
+
})
|
|
9669
|
+
};
|
|
9670
|
+
});
|
|
9671
|
+
}
|
|
9672
|
+
|
|
9536
9673
|
// packages/graph/dist/query/impact.js
|
|
9537
9674
|
var RANK = { direct: 0, structural: 1, historical: 2 };
|
|
9538
9675
|
var STEPS = {
|
|
@@ -10131,6 +10268,27 @@ function testRunEvidence(store, run2, test) {
|
|
|
10131
10268
|
metadata: { status, ...run2.command === void 0 ? {} : { command: run2.command } }
|
|
10132
10269
|
});
|
|
10133
10270
|
}
|
|
10271
|
+
function moduleReferenceEvidence(store, reader, r, commit) {
|
|
10272
|
+
const slice = reader.slice(r.location);
|
|
10273
|
+
if (slice.value === void 0)
|
|
10274
|
+
return void 0;
|
|
10275
|
+
const contentHash = sha256Text(slice.value);
|
|
10276
|
+
const range = lines(r.location);
|
|
10277
|
+
return store.add({
|
|
10278
|
+
id: evidenceId("repository", "file", `${r.path}
|
|
10279
|
+
module-reference
|
|
10280
|
+
${range?.join("-") ?? ""}
|
|
10281
|
+
${contentHash}`),
|
|
10282
|
+
basis: "repository",
|
|
10283
|
+
kind: "file",
|
|
10284
|
+
entity: { type: "file", path: r.path },
|
|
10285
|
+
source: r.location,
|
|
10286
|
+
contentHash,
|
|
10287
|
+
summary: `${r.path}:${range?.[0] ?? "?"} ${r.kind} "${r.specifier}" \u2192 ${r.target}`,
|
|
10288
|
+
pointer: { kind: "file", path: r.path, ...range === void 0 ? {} : { lines: range }, commit, contentHash },
|
|
10289
|
+
metadata: { moduleReference: r.kind, specifier: r.specifier, typeOnly: r.typeOnly, resolvedTo: r.target }
|
|
10290
|
+
}, slice.value);
|
|
10291
|
+
}
|
|
10134
10292
|
|
|
10135
10293
|
// packages/director/dist/llm/contract/evidence.js
|
|
10136
10294
|
var OBSERVABLE = /* @__PURE__ */ new Set(["repository", "git", "test"]);
|
|
@@ -10154,7 +10312,7 @@ function makeClaim(ctx, input) {
|
|
|
10154
10312
|
const basis = BASIS_ORDER.filter((b) => bases.has(b));
|
|
10155
10313
|
const enforced = input.enforced ?? false;
|
|
10156
10314
|
const v = input.violation;
|
|
10157
|
-
const unverifiable = v?.language !== void 0 && ctx.baselineStructuralLanguages !== void 0 && !ctx.baselineStructuralLanguages.has(v.language);
|
|
10315
|
+
const unverifiable = v?.unverifiable === true || v?.language !== void 0 && ctx.baselineStructuralLanguages !== void 0 && !ctx.baselineStructuralLanguages.has(v.language);
|
|
10158
10316
|
const provenance = v === void 0 || ctx.baselineKeys === void 0 || input.alignment === "ALIGNED" ? void 0 : !ctx.baselineKeys.has(v.key) ? unverifiable ? "unverified-at-adoption" : "introduced" : v.touched ? "pre-existing-touched" : "pre-existing";
|
|
10159
10317
|
const preExisting = provenance === "pre-existing" || provenance === "pre-existing-touched";
|
|
10160
10318
|
return {
|
|
@@ -10303,69 +10461,325 @@ function declaredReferenceParts(message2) {
|
|
|
10303
10461
|
return m === null ? void 0 : { governing: m[1] ?? "", offending: `ref:${m[2] ?? ""}:${m[3] ?? ""}` };
|
|
10304
10462
|
}
|
|
10305
10463
|
|
|
10306
|
-
// packages/director/dist/
|
|
10307
|
-
var
|
|
10308
|
-
var
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10464
|
+
// packages/director/dist/context/policy.js
|
|
10465
|
+
var CONTEXT_POLICY_VERSION = "3";
|
|
10466
|
+
var EDGE_WEIGHTS = {
|
|
10467
|
+
GOVERNS: 1,
|
|
10468
|
+
IMPLEMENTS: 0.9,
|
|
10469
|
+
TRACKED_BY: 0.9,
|
|
10470
|
+
VALIDATED_BY: 0.8,
|
|
10471
|
+
REQUIRES: 0.7,
|
|
10472
|
+
CALLS: 0.7,
|
|
10473
|
+
CONTAINS: 0.6,
|
|
10474
|
+
IMPORTS: 0.5,
|
|
10475
|
+
CHANGED_WITH: 0.3
|
|
10476
|
+
};
|
|
10477
|
+
var SEED_STRENGTH = { id: 1, path: 1, symbol: 1, "symbol-name": 0.9, keyword: 0.6, diff: 1 };
|
|
10478
|
+
var CROSS_LANGUAGE_FACTOR = 0.5;
|
|
10479
|
+
var DEFAULT_LIMITS = {
|
|
10480
|
+
/** Upper bound; project.yaml context.max_depth (1–3, default 2) replaces it. */
|
|
10481
|
+
maxDepth: 2,
|
|
10482
|
+
/** Finalized candidates (05 nodeLimit 200). */
|
|
10483
|
+
nodeLimit: 200,
|
|
10484
|
+
/** Edges read per expanded node. */
|
|
10485
|
+
edgeLimit: 2e3
|
|
10486
|
+
};
|
|
10487
|
+
var MIN_BUDGET = 1e3;
|
|
10488
|
+
var MAX_BUDGET = 1e6;
|
|
10489
|
+
var TASK_TOKEN_LIMIT = 200;
|
|
10490
|
+
var KEYWORD = {
|
|
10491
|
+
/** Keyword seeds kept (05: top 8). */
|
|
10492
|
+
top: 8,
|
|
10493
|
+
/** Keyword matches below this fraction of the best one are not seeds. */
|
|
10494
|
+
minFraction: 0.3,
|
|
10495
|
+
/** BM25 parameters. */
|
|
10496
|
+
k1: 1.2,
|
|
10497
|
+
b: 0.75
|
|
10498
|
+
};
|
|
10499
|
+
var SYMBOL_NAME_MAX_MATCHES = 5;
|
|
10500
|
+
var TIER_ORDER = [
|
|
10501
|
+
"requirement",
|
|
10502
|
+
"decision",
|
|
10503
|
+
"pending",
|
|
10504
|
+
"code-direct",
|
|
10505
|
+
"test",
|
|
10506
|
+
"issue",
|
|
10507
|
+
"code-structural",
|
|
10508
|
+
"code-historical"
|
|
10509
|
+
];
|
|
10510
|
+
var TIER_SHARE = {
|
|
10511
|
+
requirement: 0.25,
|
|
10512
|
+
decision: 0.15,
|
|
10513
|
+
pending: 0.05,
|
|
10514
|
+
"code-direct": 0.3,
|
|
10515
|
+
test: 0.12,
|
|
10516
|
+
issue: 0.05,
|
|
10517
|
+
"code-structural": 0.06,
|
|
10518
|
+
"code-historical": 0.02
|
|
10519
|
+
};
|
|
10520
|
+
var L3_PER_FILE_FIRST_PASS = 2;
|
|
10521
|
+
var LEADING_CONTEXT_LINES = 12;
|
|
10522
|
+
var GENERIC_FILE_WINDOW = { lines: 40, chars: 2e3 };
|
|
10523
|
+
|
|
10524
|
+
// packages/director/dist/context/expand.js
|
|
10525
|
+
var HUBS = /* @__PURE__ */ new Set(["project", "milestone", "decision", "issue"]);
|
|
10526
|
+
function displayRef(id3) {
|
|
10527
|
+
const ref = parseNodeId(id3);
|
|
10528
|
+
if (ref === void 0)
|
|
10529
|
+
return id3;
|
|
10530
|
+
switch (ref.type) {
|
|
10531
|
+
case "project":
|
|
10532
|
+
return "project";
|
|
10533
|
+
case "file":
|
|
10534
|
+
return ref.path;
|
|
10535
|
+
case "symbol":
|
|
10536
|
+
return `${ref.path}#${ref.symbol}`;
|
|
10537
|
+
case "test":
|
|
10538
|
+
return `${ref.path}#${ref.name}`;
|
|
10539
|
+
default:
|
|
10540
|
+
return ref.id;
|
|
10541
|
+
}
|
|
10315
10542
|
}
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
return readSourceFile(ctx.root, file).value;
|
|
10319
|
-
const source = blobSource(side);
|
|
10320
|
-
if (source === void 0)
|
|
10321
|
-
return void 0;
|
|
10322
|
-
const blob = await ctx.git.readBlob(source, file);
|
|
10323
|
-
return blob.value === void 0 ? void 0 : canonicalSourceText(decoder2.decode(blob.value));
|
|
10543
|
+
function better(a, b) {
|
|
10544
|
+
return b.score - a.score || a.depth - b.depth || compareUtf8(a.node.id, b.node.id);
|
|
10324
10545
|
}
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10546
|
+
var Heap = class {
|
|
10547
|
+
items = [];
|
|
10548
|
+
get size() {
|
|
10549
|
+
return this.items.length;
|
|
10550
|
+
}
|
|
10551
|
+
push(c) {
|
|
10552
|
+
const a = this.items;
|
|
10553
|
+
a.push(c);
|
|
10554
|
+
let i = a.length - 1;
|
|
10555
|
+
while (i > 0) {
|
|
10556
|
+
const p = i - 1 >> 1;
|
|
10557
|
+
if (better(a[i], a[p]) >= 0)
|
|
10558
|
+
break;
|
|
10559
|
+
[a[i], a[p]] = [a[p], a[i]];
|
|
10560
|
+
i = p;
|
|
10561
|
+
}
|
|
10562
|
+
}
|
|
10563
|
+
pop() {
|
|
10564
|
+
const a = this.items;
|
|
10565
|
+
const top = a[0];
|
|
10566
|
+
const last = a.pop();
|
|
10567
|
+
if (a.length > 0 && last !== void 0) {
|
|
10568
|
+
a[0] = last;
|
|
10569
|
+
let i = 0;
|
|
10570
|
+
for (; ; ) {
|
|
10571
|
+
const l = 2 * i + 1, r = l + 1;
|
|
10572
|
+
let m = i;
|
|
10573
|
+
if (l < a.length && better(a[l], a[m]) < 0)
|
|
10574
|
+
m = l;
|
|
10575
|
+
if (r < a.length && better(a[r], a[m]) < 0)
|
|
10576
|
+
m = r;
|
|
10577
|
+
if (m === i)
|
|
10578
|
+
break;
|
|
10579
|
+
[a[i], a[m]] = [a[m], a[i]];
|
|
10580
|
+
i = m;
|
|
10581
|
+
}
|
|
10582
|
+
}
|
|
10583
|
+
return top;
|
|
10584
|
+
}
|
|
10585
|
+
};
|
|
10586
|
+
function step(edge) {
|
|
10587
|
+
const provenance = typeof edge.metadata.provenance === "string" ? edge.metadata.provenance : "unknown";
|
|
10588
|
+
return { from: displayRef(edge.from), type: edge.type, to: displayRef(edge.to), provenance };
|
|
10330
10589
|
}
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
const
|
|
10335
|
-
for (const
|
|
10336
|
-
const
|
|
10337
|
-
const
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
if (c2 !== void 0)
|
|
10362
|
-
out.push(c2);
|
|
10363
|
-
}
|
|
10590
|
+
function expandCandidates(store, seeds, options) {
|
|
10591
|
+
const heap = new Heap();
|
|
10592
|
+
const best = /* @__PURE__ */ new Map();
|
|
10593
|
+
const seedIds = new Set(seeds.map((s) => s.id));
|
|
10594
|
+
for (const s of seeds) {
|
|
10595
|
+
const ref = parseNodeId(s.id);
|
|
10596
|
+
const node = ref === void 0 ? void 0 : store.getNode(ref);
|
|
10597
|
+
if (node === void 0)
|
|
10598
|
+
continue;
|
|
10599
|
+
heap.push({ node, score: s.strength, depth: 0, seed: s.id, steps: [] });
|
|
10600
|
+
best.set(s.id, Math.max(best.get(s.id) ?? 0, s.strength));
|
|
10601
|
+
}
|
|
10602
|
+
const done = /* @__PURE__ */ new Map();
|
|
10603
|
+
let truncated = false;
|
|
10604
|
+
while (heap.size > 0) {
|
|
10605
|
+
const c = heap.pop();
|
|
10606
|
+
if (done.has(c.node.id))
|
|
10607
|
+
continue;
|
|
10608
|
+
if (done.size >= options.nodeLimit) {
|
|
10609
|
+
truncated = true;
|
|
10610
|
+
break;
|
|
10611
|
+
}
|
|
10612
|
+
done.set(c.node.id, c);
|
|
10613
|
+
if (c.depth >= options.maxDepth || HUBS.has(c.node.type) && !(c.depth === 0 && seedIds.has(c.node.id)))
|
|
10614
|
+
continue;
|
|
10615
|
+
const adjacent = store.adjacentEdges([c.node.ref], { direction: "both", limit: options.edgeLimit });
|
|
10616
|
+
if (adjacent.truncated)
|
|
10617
|
+
truncated = true;
|
|
10618
|
+
for (const edge of adjacent.edges) {
|
|
10619
|
+
if (edge.type === "SUPERSEDES")
|
|
10364
10620
|
continue;
|
|
10365
|
-
|
|
10366
|
-
if (
|
|
10621
|
+
const other = edge.from === c.node.id ? edge.to : edge.from;
|
|
10622
|
+
if (done.has(other) || other.startsWith("project:"))
|
|
10367
10623
|
continue;
|
|
10368
|
-
const
|
|
10624
|
+
const score = c.score * EDGE_WEIGHTS[edge.type];
|
|
10625
|
+
if (score <= (best.get(other) ?? 0))
|
|
10626
|
+
continue;
|
|
10627
|
+
const ref = parseNodeId(other);
|
|
10628
|
+
const node = ref === void 0 ? void 0 : store.getNode(ref);
|
|
10629
|
+
if (node === void 0)
|
|
10630
|
+
continue;
|
|
10631
|
+
best.set(other, score);
|
|
10632
|
+
heap.push({ node, score, depth: c.depth + 1, seed: c.seed, steps: [...c.steps, step(edge)] });
|
|
10633
|
+
}
|
|
10634
|
+
}
|
|
10635
|
+
return { candidates: [...done.values()].sort(better), truncated };
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10638
|
+
// packages/director/dist/review/seeds.js
|
|
10639
|
+
var PROPOSALS = `${STATE_DIR_NAME}/decisions/proposals/`;
|
|
10640
|
+
function hunkRange(h) {
|
|
10641
|
+
return h.newLines > 0 ? [h.newStart, h.newStart + h.newLines - 1] : [Math.max(1, h.newStart), Math.max(1, h.newStart)];
|
|
10642
|
+
}
|
|
10643
|
+
function overlaps(loc2, range) {
|
|
10644
|
+
if (loc2?.startLine === void 0)
|
|
10645
|
+
return false;
|
|
10646
|
+
const end2 = loc2.endLine ?? loc2.startLine;
|
|
10647
|
+
return range[0] <= end2 && range[1] >= loc2.startLine;
|
|
10648
|
+
}
|
|
10649
|
+
function owned(graph, path35) {
|
|
10650
|
+
const out = [];
|
|
10651
|
+
let afterId;
|
|
10652
|
+
for (; ; ) {
|
|
10653
|
+
const page = graph.listNodes({ ownerFile: path35, limit: 1e3, ...afterId === void 0 ? {} : { afterId } });
|
|
10654
|
+
out.push(...page);
|
|
10655
|
+
if (page.length < 1e3)
|
|
10656
|
+
return out;
|
|
10657
|
+
afterId = page[page.length - 1]?.id;
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
var width = (loc2) => (loc2?.endLine ?? 0) - (loc2?.startLine ?? 0);
|
|
10661
|
+
function truthDefinitions(truth) {
|
|
10662
|
+
return [
|
|
10663
|
+
...truth.requirements.map((d) => ({ ref: definitionRef("requirement", d.id), location: d.location })),
|
|
10664
|
+
...truth.decisions.map((d) => ({ ref: definitionRef("decision", d.id), location: d.location })),
|
|
10665
|
+
...truth.constraints.map((d) => ({ ref: definitionRef("decision", d.id), location: d.location })),
|
|
10666
|
+
...truth.issues.map((d) => ({ ref: definitionRef("issue", d.id), location: d.location })),
|
|
10667
|
+
...truth.milestones.map((d) => ({ ref: definitionRef("milestone", d.id), location: d.location }))
|
|
10668
|
+
];
|
|
10669
|
+
}
|
|
10670
|
+
function diffSeeds(files, graph, truth) {
|
|
10671
|
+
const seeds = /* @__PURE__ */ new Map();
|
|
10672
|
+
const put = (entity, reason, path35, evidenceId2) => {
|
|
10673
|
+
if (graph.getNode(entity) === void 0)
|
|
10674
|
+
return;
|
|
10675
|
+
const id3 = nodeId(entity);
|
|
10676
|
+
const s = seeds.get(id3) ?? { entity, reason, path: path35, evidence: /* @__PURE__ */ new Set() };
|
|
10677
|
+
s.evidence.add(evidenceId2);
|
|
10678
|
+
seeds.set(id3, s);
|
|
10679
|
+
};
|
|
10680
|
+
const defs = truthDefinitions(truth);
|
|
10681
|
+
for (const f of files) {
|
|
10682
|
+
if (f.kind === "deleted")
|
|
10683
|
+
continue;
|
|
10684
|
+
if (f.path.startsWith(`${STATE_DIR_NAME}/`)) {
|
|
10685
|
+
if (f.path.startsWith(PROPOSALS))
|
|
10686
|
+
continue;
|
|
10687
|
+
for (const h of f.hunks) {
|
|
10688
|
+
for (const d of defs)
|
|
10689
|
+
if (d.location.path === f.path && overlaps(d.location, hunkRange(h)))
|
|
10690
|
+
put(d.ref, "truth-changed", f.path, h.evidenceId);
|
|
10691
|
+
}
|
|
10692
|
+
continue;
|
|
10693
|
+
}
|
|
10694
|
+
const nodes = owned(graph, f.path).filter((n) => n.type === "symbol" || n.type === "test");
|
|
10695
|
+
const ranges = new Map(nodes.map((n) => [n.id, nodeLocations(n)]));
|
|
10696
|
+
let touched = false;
|
|
10697
|
+
for (const h of f.hunks) {
|
|
10698
|
+
const range = hunkRange(h);
|
|
10699
|
+
const hits = nodes.map((n) => ({ n, at: (ranges.get(n.id) ?? []).filter((l) => overlaps(l, range)) })).filter((x) => x.at.length > 0);
|
|
10700
|
+
const inner = hits.filter((x) => !hits.some((o) => o !== x && o.at.some((ol) => x.at.some((xl) => width(ol) < width(xl) && overlaps(xl, [ol.startLine ?? 0, ol.endLine ?? 0])))));
|
|
10701
|
+
for (const { n } of inner) {
|
|
10702
|
+
put(n.ref, "hunk-overlap", f.path, h.evidenceId);
|
|
10703
|
+
touched = true;
|
|
10704
|
+
}
|
|
10705
|
+
}
|
|
10706
|
+
if (!touched)
|
|
10707
|
+
for (const e of f.evidenceIds)
|
|
10708
|
+
put(fileRef(f.path), "file-changed", f.path, e);
|
|
10709
|
+
}
|
|
10710
|
+
return [...seeds.entries()].sort(([a], [b]) => compareUtf8(a, b)).map(([id3, s]) => ({
|
|
10711
|
+
id: id3,
|
|
10712
|
+
ref: displayRef(id3),
|
|
10713
|
+
entity: s.entity,
|
|
10714
|
+
reason: s.reason,
|
|
10715
|
+
path: s.path,
|
|
10716
|
+
evidenceIds: [...s.evidence].sort(compareUtf8)
|
|
10717
|
+
}));
|
|
10718
|
+
}
|
|
10719
|
+
|
|
10720
|
+
// packages/director/dist/review/rules.js
|
|
10721
|
+
var DECISIONS = `${STATE_DIR_NAME}/decisions/`;
|
|
10722
|
+
var PROPOSALS2 = `${STATE_DIR_NAME}/decisions/proposals/`;
|
|
10723
|
+
var TEST_FILE3 = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
10724
|
+
var decoder2 = new TextDecoder("utf-8");
|
|
10725
|
+
function blobSource(end2) {
|
|
10726
|
+
if (end2 === "HEAD" || end2 === "INDEX")
|
|
10727
|
+
return end2;
|
|
10728
|
+
return typeof end2 === "string" ? void 0 : { commit: end2.commit };
|
|
10729
|
+
}
|
|
10730
|
+
async function sideText(ctx, side, file) {
|
|
10731
|
+
if (side === "WORKTREE")
|
|
10732
|
+
return readSourceFile(ctx.root, file).value;
|
|
10733
|
+
const source = blobSource(side);
|
|
10734
|
+
if (source === void 0)
|
|
10735
|
+
return void 0;
|
|
10736
|
+
const blob = await ctx.git.readBlob(source, file);
|
|
10737
|
+
return blob.value === void 0 ? void 0 : canonicalSourceText(decoder2.decode(blob.value));
|
|
10738
|
+
}
|
|
10739
|
+
function decisionsOf(file, text4) {
|
|
10740
|
+
if (file.endsWith(".md"))
|
|
10741
|
+
return [...parseDefinitionMarkdown(file, text4).value?.decisions ?? []];
|
|
10742
|
+
const d = parseDecisionFile(file, text4).value;
|
|
10743
|
+
return d === void 0 ? [] : [d];
|
|
10744
|
+
}
|
|
10745
|
+
var decisionFiles = (ctx) => ctx.files.filter((f) => f.path.startsWith(DECISIONS) && !f.path.startsWith(PROPOSALS2));
|
|
10746
|
+
var codeSeeds = (ctx) => ctx.seeds.filter((s) => s.entity.type === "symbol" || s.entity.type === "file");
|
|
10747
|
+
async function decisionIntegrity(ctx) {
|
|
10748
|
+
const out = [];
|
|
10749
|
+
for (const f of decisionFiles(ctx)) {
|
|
10750
|
+
const oldPath = f.oldPath ?? f.path;
|
|
10751
|
+
const oldText = f.kind === "added" || f.kind === "untracked" ? void 0 : await sideText(ctx, ctx.from, oldPath);
|
|
10752
|
+
const before = oldText === void 0 ? [] : decisionsOf(oldPath, oldText);
|
|
10753
|
+
const after = f.kind === "deleted" ? [] : ctx.truth.decisions.filter((d) => d.location.path === f.path);
|
|
10754
|
+
const ids2 = [.../* @__PURE__ */ new Set([...before.map((d) => d.id), ...after.map((d) => d.id)])].sort(compareUtf8);
|
|
10755
|
+
for (const id3 of ids2) {
|
|
10756
|
+
const o = before.find((d) => d.id === id3);
|
|
10757
|
+
const n = after.find((d) => d.id === id3);
|
|
10758
|
+
const oldEvidence = o === void 0 || oldText === void 0 ? void 0 : truthEvidenceFromText(ctx.store, { kind: "decision", id: id3, title: o.title, location: o.location }, oldText, `old:${ctx.fromLabel}`);
|
|
10759
|
+
const newEvidence = n === void 0 ? void 0 : truthEvidence(ctx.store, ctx.reader, { kind: "decision", id: id3, title: n.title, location: n.location });
|
|
10760
|
+
const evidence = [oldEvidence, newEvidence, ...f.evidenceIds];
|
|
10761
|
+
const subject = { kind: "decision", id: id3 };
|
|
10762
|
+
const wasConfirmed = o !== void 0 && (o.state === "confirmed" || o.state === "superseded");
|
|
10763
|
+
if (n === void 0) {
|
|
10764
|
+
if (wasConfirmed) {
|
|
10765
|
+
const c2 = makeClaim(ctx, {
|
|
10766
|
+
rule: "decision-integrity",
|
|
10767
|
+
subject,
|
|
10768
|
+
alignment: "CONFLICT",
|
|
10769
|
+
reason: "confirmed-decision-removed",
|
|
10770
|
+
enforced: true,
|
|
10771
|
+
evidence,
|
|
10772
|
+
expected: `${id3} stays in Project Truth (confirmed Decisions are superseded, not deleted)`,
|
|
10773
|
+
observed: `${id3} was removed`
|
|
10774
|
+
});
|
|
10775
|
+
if (c2 !== void 0)
|
|
10776
|
+
out.push(c2);
|
|
10777
|
+
}
|
|
10778
|
+
continue;
|
|
10779
|
+
}
|
|
10780
|
+
if (n.state !== "confirmed" && n.state !== "superseded")
|
|
10781
|
+
continue;
|
|
10782
|
+
const lock = verifyDecisionLock(n).value;
|
|
10369
10783
|
let c;
|
|
10370
10784
|
if (wasConfirmed && decisionLockDigest(o) !== decisionLockDigest(n)) {
|
|
10371
10785
|
c = makeClaim(ctx, {
|
|
@@ -10792,6 +11206,124 @@ function scopeRelevance(ctx, covered = /* @__PURE__ */ new Set()) {
|
|
|
10792
11206
|
}
|
|
10793
11207
|
return out;
|
|
10794
11208
|
}
|
|
11209
|
+
var overlapsLines = (loc2, [a, b]) => {
|
|
11210
|
+
const start2 = loc2.startLine ?? 1;
|
|
11211
|
+
return start2 <= b && (loc2.endLine ?? start2) >= a;
|
|
11212
|
+
};
|
|
11213
|
+
var preview = (items) => `${items.slice(0, 5).join(", ")}${items.length > 5 ? `, \u2026 (${items.length} in all)` : ""}`;
|
|
11214
|
+
function confirmedAfterBaseline(confirmedAt, recordedAt) {
|
|
11215
|
+
if (confirmedAt === void 0 || recordedAt === void 0)
|
|
11216
|
+
return false;
|
|
11217
|
+
const c = Date.parse(confirmedAt);
|
|
11218
|
+
const r = Date.parse(recordedAt);
|
|
11219
|
+
return Number.isFinite(c) && Number.isFinite(r) && c > r;
|
|
11220
|
+
}
|
|
11221
|
+
async function decisionForbidsImport(ctx) {
|
|
11222
|
+
const decisions = ctx.truth.decisions.filter((d) => isActive(d) && (d.forbids.importedPaths?.length ?? 0) > 0).sort((a, b) => compareUtf8(a.id, b.id));
|
|
11223
|
+
if (decisions.length === 0)
|
|
11224
|
+
return { claims: [], limitations: [] };
|
|
11225
|
+
const matchers = decisions.map((d) => ({ d, match: (d.forbids.importedPaths ?? []).flatMap((p) => compileRepoPattern(p) ?? []) }));
|
|
11226
|
+
const changed = ctx.files.filter((f) => f.kind !== "deleted" && !f.binary && !f.path.startsWith(`${STATE_DIR_NAME}/`) && f.hunks.length > 0);
|
|
11227
|
+
const usable = [];
|
|
11228
|
+
const otherSide = [];
|
|
11229
|
+
for (const f of changed) {
|
|
11230
|
+
if (ctx.to === "WORKTREE") {
|
|
11231
|
+
usable.push(f);
|
|
11232
|
+
continue;
|
|
11233
|
+
}
|
|
11234
|
+
const side = await sideText(ctx, ctx.to, f.path);
|
|
11235
|
+
const current = readSourceFile(ctx.root, f.path).value;
|
|
11236
|
+
if (side !== void 0 && current !== void 0 && side === canonicalSourceText(current))
|
|
11237
|
+
usable.push(f);
|
|
11238
|
+
else
|
|
11239
|
+
otherSide.push(f.path);
|
|
11240
|
+
}
|
|
11241
|
+
const indexed = await readIndexedModuleReferences(ctx.root, usable.map((f) => f.path));
|
|
11242
|
+
const unresolved = [];
|
|
11243
|
+
const ambiguous = [];
|
|
11244
|
+
const unsupported = [];
|
|
11245
|
+
const notIndexedTarget = [];
|
|
11246
|
+
const unavailable2 = [];
|
|
11247
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11248
|
+
usable.forEach((f, i) => {
|
|
11249
|
+
const r = indexed[i];
|
|
11250
|
+
if (r === void 0 || r.status === "unavailable") {
|
|
11251
|
+
unavailable2.push(f.path);
|
|
11252
|
+
return;
|
|
11253
|
+
}
|
|
11254
|
+
if (r.status !== "ok")
|
|
11255
|
+
return;
|
|
11256
|
+
for (const ref of r.references) {
|
|
11257
|
+
const hunks = f.hunks.filter((h) => overlapsLines(ref.location, hunkRange(h))).map((h) => h.evidenceId);
|
|
11258
|
+
if (hunks.length === 0)
|
|
11259
|
+
continue;
|
|
11260
|
+
const where = `${f.path}:${ref.location.startLine ?? "?"}`;
|
|
11261
|
+
const target = ref.target;
|
|
11262
|
+
if (target === void 0) {
|
|
11263
|
+
const s = ref.resolution.status;
|
|
11264
|
+
if (s === "unresolved")
|
|
11265
|
+
unresolved.push(where);
|
|
11266
|
+
else if (s === "ambiguous")
|
|
11267
|
+
ambiguous.push(where);
|
|
11268
|
+
else if (s === "unsupported")
|
|
11269
|
+
unsupported.push(where);
|
|
11270
|
+
else if (s === "resolved")
|
|
11271
|
+
notIndexedTarget.push(where);
|
|
11272
|
+
continue;
|
|
11273
|
+
}
|
|
11274
|
+
for (const { d, match } of matchers) {
|
|
11275
|
+
if (!match.some((m) => m(target)))
|
|
11276
|
+
continue;
|
|
11277
|
+
const key2 = `${d.id}
|
|
11278
|
+
${f.path}
|
|
11279
|
+
${target}`;
|
|
11280
|
+
const g = groups.get(key2) ?? { d, source: f.path, target, sites: [] };
|
|
11281
|
+
g.sites.push({ ref, hunks });
|
|
11282
|
+
groups.set(key2, g);
|
|
11283
|
+
}
|
|
11284
|
+
}
|
|
11285
|
+
});
|
|
11286
|
+
const coverage = ctx.baselineEvaluatedRules?.has("decision-forbids-import") === true;
|
|
11287
|
+
const claims = [];
|
|
11288
|
+
for (const g of [...groups.values()].sort((a, b) => compareUtf8(a.d.id, b.d.id) || compareUtf8(a.source, b.source) || compareUtf8(a.target, b.target))) {
|
|
11289
|
+
const where = g.sites.map((s) => `${g.source}:${s.ref.location.startLine ?? "?"} ${s.ref.kind} "${s.ref.specifier}"${s.ref.typeOnly ? " (type-only)" : ""}`).join("; ");
|
|
11290
|
+
const unverifiable = ctx.baselineKeys !== void 0 && !coverage && !confirmedAfterBaseline(g.d.confirmedAt, ctx.baselineRecordedAt);
|
|
11291
|
+
const c = makeClaim(ctx, {
|
|
11292
|
+
rule: "decision-forbids-import",
|
|
11293
|
+
subject: { kind: "decision", id: g.d.id },
|
|
11294
|
+
key: `import:${g.source}->${g.target}`,
|
|
11295
|
+
alignment: "CONFLICT",
|
|
11296
|
+
reason: "forbidden-import",
|
|
11297
|
+
enforced: g.d.enforcement === "block",
|
|
11298
|
+
expected: `no changed import resolves to ${(g.d.forbids.importedPaths ?? []).join(", ")} (${g.d.id})`,
|
|
11299
|
+
observed: `forbidden import on a changed line: ${where} resolves to ${g.target}`,
|
|
11300
|
+
evidence: [
|
|
11301
|
+
decisionEvidence(ctx, g.d.id),
|
|
11302
|
+
...g.sites.map((s) => moduleReferenceEvidence(ctx.store, ctx.reader, { path: g.source, location: s.ref.location, kind: s.ref.kind, specifier: s.ref.specifier, typeOnly: s.ref.typeOnly, target: g.target }, ctx.toLabel)),
|
|
11303
|
+
...g.sites.flatMap((s) => s.hunks),
|
|
11304
|
+
nodeEvidence(ctx, ctx.graph.getNode(fileRef(g.target)))
|
|
11305
|
+
],
|
|
11306
|
+
violation: { key: violationKey("decision-forbids-import", g.d.id, `import:${nodeId(fileRef(g.source))}->${nodeId(fileRef(g.target))}`), touched: true, ...unverifiable ? { unverifiable: true } : {} }
|
|
11307
|
+
});
|
|
11308
|
+
if (c !== void 0)
|
|
11309
|
+
claims.push(c);
|
|
11310
|
+
}
|
|
11311
|
+
const limitations2 = [];
|
|
11312
|
+
const not = "were not checked against forbids.imported_paths";
|
|
11313
|
+
if (unresolved.length > 0)
|
|
11314
|
+
limitations2.push({ code: "imports-unresolved", message: `${unresolved.length} changed import(s) did not resolve to a repository file and ${not}: ${preview(unresolved)}.` });
|
|
11315
|
+
if (ambiguous.length > 0)
|
|
11316
|
+
limitations2.push({ code: "imports-ambiguous", message: `${ambiguous.length} changed import(s) have more than one candidate file and ${not}: ${preview(ambiguous)}.` });
|
|
11317
|
+
if (unsupported.length > 0)
|
|
11318
|
+
limitations2.push({ code: "imports-resolution-unsupported", message: `${unsupported.length} changed import(s) are in a language without module resolution (imports are recorded as written) and ${not}: ${preview(unsupported)}.` });
|
|
11319
|
+
if (notIndexedTarget.length > 0)
|
|
11320
|
+
limitations2.push({ code: "imports-target-not-indexed", message: `${notIndexedTarget.length} changed import(s) resolve to a file outside the index and ${not}: ${preview(notIndexedTarget)}.` });
|
|
11321
|
+
if (unavailable2.length > 0)
|
|
11322
|
+
limitations2.push({ code: "imports-index-unavailable", message: `The indexed imports of ${unavailable2.length} changed file(s) could not be read and ${not}: ${preview(unavailable2)}.` });
|
|
11323
|
+
if (otherSide.length > 0)
|
|
11324
|
+
limitations2.push({ code: "imports-reviewed-side-not-indexed", message: `${otherSide.length} changed file(s) differ on the reviewed side from the indexed working tree; their imports ${not}: ${preview(otherSide)}.` });
|
|
11325
|
+
return { claims, limitations: limitations2 };
|
|
11326
|
+
}
|
|
10795
11327
|
|
|
10796
11328
|
// packages/director/dist/adoption/head.js
|
|
10797
11329
|
var externals = (s) => s.filter((x) => x.kind === "external");
|
|
@@ -10813,7 +11345,7 @@ async function headBaselineFindings(input) {
|
|
|
10813
11345
|
if (!found.has(key2))
|
|
10814
11346
|
found.set(key2, { key: key2, rule, governing, offending, ...extra });
|
|
10815
11347
|
};
|
|
10816
|
-
const decisions = input.truth.decisions.filter((d) => isActive(d) && d.forbids.paths.length + d.forbids.symbols.length + d.forbids.dependencies.length > 0);
|
|
11348
|
+
const decisions = input.truth.decisions.filter((d) => isActive(d) && d.forbids.paths.length + d.forbids.symbols.length + d.forbids.dependencies.length + (d.forbids.importedPaths?.length ?? 0) > 0);
|
|
10817
11349
|
const declared = input.stateDiagnostics.filter((x) => x.code === "DECLARED_SYMBOL_UNRESOLVED");
|
|
10818
11350
|
const needSymbols = decisions.some((d) => d.forbids.symbols.length > 0) || declared.length > 0 && input.divergent.size > 0;
|
|
10819
11351
|
const symbols = [];
|
|
@@ -10863,6 +11395,20 @@ async function headBaselineFindings(input) {
|
|
|
10863
11395
|
}
|
|
10864
11396
|
}
|
|
10865
11397
|
}
|
|
11398
|
+
const importing = decisions.filter((d) => (d.forbids.importedPaths?.length ?? 0) > 0).map((d) => ({ d, match: (d.forbids.importedPaths ?? []).flatMap((p) => compileRepoPattern(p) ?? []) }));
|
|
11399
|
+
if (importing.length > 0) {
|
|
11400
|
+
for (const f of headFiles.filter((p) => !input.divergent.has(p))) {
|
|
11401
|
+
for (const e of input.graph.adjacentEdges([fileRef(f)], { direction: "outgoing", types: ["IMPORTS"], limit: 1e4 }).edges) {
|
|
11402
|
+
const to = parseNodeId(e.to);
|
|
11403
|
+
if (to?.type !== "file" || !inHead.has(to.path))
|
|
11404
|
+
continue;
|
|
11405
|
+
for (const { d, match } of importing) {
|
|
11406
|
+
if (match.some((m) => m(to.path)))
|
|
11407
|
+
add("decision-forbids-import", d.id, `import:${nodeId(fileRef(f))}->${nodeId(fileRef(to.path))}`, { path: f, enforced: d.enforcement === "block" });
|
|
11408
|
+
}
|
|
11409
|
+
}
|
|
11410
|
+
}
|
|
11411
|
+
}
|
|
10866
11412
|
if (declared.length > 0 && input.divergent.size > 0)
|
|
10867
11413
|
limitations2.add("declared-references-approximated-for-divergent-paths");
|
|
10868
11414
|
for (const diag of declared) {
|
|
@@ -11017,7 +11563,7 @@ async function captureAdoptionBaseline(root, options) {
|
|
|
11017
11563
|
if (options.actor.kind !== "human") {
|
|
11018
11564
|
return failure([createDiagnostic("ADOPTION_FORBIDDEN", `${options.actor.kind} "${options.actor.name}" cannot capture the Adoption Baseline; adoption is a human decision`)]);
|
|
11019
11565
|
}
|
|
11020
|
-
const loaded = loadProjectTruth(root);
|
|
11566
|
+
const loaded = requireCompleteTruth(loadProjectTruth(root));
|
|
11021
11567
|
if (loaded.value === void 0)
|
|
11022
11568
|
return failure(loaded.diagnostics);
|
|
11023
11569
|
const { truth } = loaded.value;
|
|
@@ -11066,7 +11612,12 @@ async function captureAdoptionBaseline(root, options) {
|
|
|
11066
11612
|
own?.value?.dispose();
|
|
11067
11613
|
}
|
|
11068
11614
|
const bootstrapTruth = await bootstrapTruthOf(root, git.value);
|
|
11069
|
-
const
|
|
11615
|
+
const evaluatedRules = ["decision-forbids", "declared-reference", "external-source-drift", ...wt.dirty ? [] : ["decision-forbids-import"]].sort(compareUtf8);
|
|
11616
|
+
const limitations2 = [
|
|
11617
|
+
...evaluated.limitations,
|
|
11618
|
+
...wt.truncated ? ["working-tree-list-truncated"] : [],
|
|
11619
|
+
...wt.dirty ? ["import-relations-not-evaluated-dirty-working-tree"] : []
|
|
11620
|
+
].sort(compareUtf8);
|
|
11070
11621
|
const body = {
|
|
11071
11622
|
format: "duo.adoption-baseline/2",
|
|
11072
11623
|
project: { name: truth.config.name, rootCommits: repo.value.rootCommitOids },
|
|
@@ -11097,7 +11648,8 @@ async function captureAdoptionBaseline(root, options) {
|
|
|
11097
11648
|
findingsAt: "HEAD",
|
|
11098
11649
|
findings: evaluated.findings,
|
|
11099
11650
|
limitations: limitations2,
|
|
11100
|
-
analysis
|
|
11651
|
+
analysis,
|
|
11652
|
+
evaluatedRules
|
|
11101
11653
|
};
|
|
11102
11654
|
const id3 = historyRecordId("adoption", body);
|
|
11103
11655
|
const existing = loadAdoptionBaseline(root);
|
|
@@ -11269,180 +11821,6 @@ function oneGroup(groups, nodes) {
|
|
|
11269
11821
|
return g !== void 0 && nodes.length > 1 && nodes.every((n) => groups.groups.get(n.id) === g);
|
|
11270
11822
|
}
|
|
11271
11823
|
|
|
11272
|
-
// packages/director/dist/context/policy.js
|
|
11273
|
-
var CONTEXT_POLICY_VERSION = "2";
|
|
11274
|
-
var EDGE_WEIGHTS = {
|
|
11275
|
-
GOVERNS: 1,
|
|
11276
|
-
IMPLEMENTS: 0.9,
|
|
11277
|
-
TRACKED_BY: 0.9,
|
|
11278
|
-
VALIDATED_BY: 0.8,
|
|
11279
|
-
REQUIRES: 0.7,
|
|
11280
|
-
CALLS: 0.7,
|
|
11281
|
-
CONTAINS: 0.6,
|
|
11282
|
-
IMPORTS: 0.5,
|
|
11283
|
-
CHANGED_WITH: 0.3
|
|
11284
|
-
};
|
|
11285
|
-
var SEED_STRENGTH = { id: 1, path: 1, symbol: 1, "symbol-name": 0.9, keyword: 0.6, diff: 1 };
|
|
11286
|
-
var CROSS_LANGUAGE_FACTOR = 0.5;
|
|
11287
|
-
var DEFAULT_LIMITS = {
|
|
11288
|
-
/** Upper bound; project.yaml context.max_depth (1–3, default 2) replaces it. */
|
|
11289
|
-
maxDepth: 2,
|
|
11290
|
-
/** Finalized candidates (05 nodeLimit 200). */
|
|
11291
|
-
nodeLimit: 200,
|
|
11292
|
-
/** Edges read per expanded node. */
|
|
11293
|
-
edgeLimit: 2e3
|
|
11294
|
-
};
|
|
11295
|
-
var MIN_BUDGET = 1e3;
|
|
11296
|
-
var MAX_BUDGET = 1e6;
|
|
11297
|
-
var TASK_TOKEN_LIMIT = 200;
|
|
11298
|
-
var KEYWORD = {
|
|
11299
|
-
/** Keyword seeds kept (05: top 8). */
|
|
11300
|
-
top: 8,
|
|
11301
|
-
/** Keyword matches below this fraction of the best one are not seeds. */
|
|
11302
|
-
minFraction: 0.3,
|
|
11303
|
-
/** BM25 parameters. */
|
|
11304
|
-
k1: 1.2,
|
|
11305
|
-
b: 0.75
|
|
11306
|
-
};
|
|
11307
|
-
var SYMBOL_NAME_MAX_MATCHES = 5;
|
|
11308
|
-
var TIER_ORDER = [
|
|
11309
|
-
"requirement",
|
|
11310
|
-
"decision",
|
|
11311
|
-
"pending",
|
|
11312
|
-
"code-direct",
|
|
11313
|
-
"test",
|
|
11314
|
-
"issue",
|
|
11315
|
-
"code-structural",
|
|
11316
|
-
"code-historical"
|
|
11317
|
-
];
|
|
11318
|
-
var TIER_SHARE = {
|
|
11319
|
-
requirement: 0.25,
|
|
11320
|
-
decision: 0.15,
|
|
11321
|
-
pending: 0.05,
|
|
11322
|
-
"code-direct": 0.3,
|
|
11323
|
-
test: 0.12,
|
|
11324
|
-
issue: 0.05,
|
|
11325
|
-
"code-structural": 0.06,
|
|
11326
|
-
"code-historical": 0.02
|
|
11327
|
-
};
|
|
11328
|
-
var L3_PER_FILE_FIRST_PASS = 2;
|
|
11329
|
-
var LEADING_CONTEXT_LINES = 12;
|
|
11330
|
-
var GENERIC_FILE_WINDOW = { lines: 40, chars: 2e3 };
|
|
11331
|
-
|
|
11332
|
-
// packages/director/dist/context/expand.js
|
|
11333
|
-
var HUBS = /* @__PURE__ */ new Set(["project", "milestone", "decision", "issue"]);
|
|
11334
|
-
function displayRef(id3) {
|
|
11335
|
-
const ref = parseNodeId(id3);
|
|
11336
|
-
if (ref === void 0)
|
|
11337
|
-
return id3;
|
|
11338
|
-
switch (ref.type) {
|
|
11339
|
-
case "project":
|
|
11340
|
-
return "project";
|
|
11341
|
-
case "file":
|
|
11342
|
-
return ref.path;
|
|
11343
|
-
case "symbol":
|
|
11344
|
-
return `${ref.path}#${ref.symbol}`;
|
|
11345
|
-
case "test":
|
|
11346
|
-
return `${ref.path}#${ref.name}`;
|
|
11347
|
-
default:
|
|
11348
|
-
return ref.id;
|
|
11349
|
-
}
|
|
11350
|
-
}
|
|
11351
|
-
function better(a, b) {
|
|
11352
|
-
return b.score - a.score || a.depth - b.depth || compareUtf8(a.node.id, b.node.id);
|
|
11353
|
-
}
|
|
11354
|
-
var Heap = class {
|
|
11355
|
-
items = [];
|
|
11356
|
-
get size() {
|
|
11357
|
-
return this.items.length;
|
|
11358
|
-
}
|
|
11359
|
-
push(c) {
|
|
11360
|
-
const a = this.items;
|
|
11361
|
-
a.push(c);
|
|
11362
|
-
let i = a.length - 1;
|
|
11363
|
-
while (i > 0) {
|
|
11364
|
-
const p = i - 1 >> 1;
|
|
11365
|
-
if (better(a[i], a[p]) >= 0)
|
|
11366
|
-
break;
|
|
11367
|
-
[a[i], a[p]] = [a[p], a[i]];
|
|
11368
|
-
i = p;
|
|
11369
|
-
}
|
|
11370
|
-
}
|
|
11371
|
-
pop() {
|
|
11372
|
-
const a = this.items;
|
|
11373
|
-
const top = a[0];
|
|
11374
|
-
const last = a.pop();
|
|
11375
|
-
if (a.length > 0 && last !== void 0) {
|
|
11376
|
-
a[0] = last;
|
|
11377
|
-
let i = 0;
|
|
11378
|
-
for (; ; ) {
|
|
11379
|
-
const l = 2 * i + 1, r = l + 1;
|
|
11380
|
-
let m = i;
|
|
11381
|
-
if (l < a.length && better(a[l], a[m]) < 0)
|
|
11382
|
-
m = l;
|
|
11383
|
-
if (r < a.length && better(a[r], a[m]) < 0)
|
|
11384
|
-
m = r;
|
|
11385
|
-
if (m === i)
|
|
11386
|
-
break;
|
|
11387
|
-
[a[i], a[m]] = [a[m], a[i]];
|
|
11388
|
-
i = m;
|
|
11389
|
-
}
|
|
11390
|
-
}
|
|
11391
|
-
return top;
|
|
11392
|
-
}
|
|
11393
|
-
};
|
|
11394
|
-
function step(edge) {
|
|
11395
|
-
const provenance = typeof edge.metadata.provenance === "string" ? edge.metadata.provenance : "unknown";
|
|
11396
|
-
return { from: displayRef(edge.from), type: edge.type, to: displayRef(edge.to), provenance };
|
|
11397
|
-
}
|
|
11398
|
-
function expandCandidates(store, seeds, options) {
|
|
11399
|
-
const heap = new Heap();
|
|
11400
|
-
const best = /* @__PURE__ */ new Map();
|
|
11401
|
-
const seedIds = new Set(seeds.map((s) => s.id));
|
|
11402
|
-
for (const s of seeds) {
|
|
11403
|
-
const ref = parseNodeId(s.id);
|
|
11404
|
-
const node = ref === void 0 ? void 0 : store.getNode(ref);
|
|
11405
|
-
if (node === void 0)
|
|
11406
|
-
continue;
|
|
11407
|
-
heap.push({ node, score: s.strength, depth: 0, seed: s.id, steps: [] });
|
|
11408
|
-
best.set(s.id, Math.max(best.get(s.id) ?? 0, s.strength));
|
|
11409
|
-
}
|
|
11410
|
-
const done = /* @__PURE__ */ new Map();
|
|
11411
|
-
let truncated = false;
|
|
11412
|
-
while (heap.size > 0) {
|
|
11413
|
-
const c = heap.pop();
|
|
11414
|
-
if (done.has(c.node.id))
|
|
11415
|
-
continue;
|
|
11416
|
-
if (done.size >= options.nodeLimit) {
|
|
11417
|
-
truncated = true;
|
|
11418
|
-
break;
|
|
11419
|
-
}
|
|
11420
|
-
done.set(c.node.id, c);
|
|
11421
|
-
if (c.depth >= options.maxDepth || HUBS.has(c.node.type) && !(c.depth === 0 && seedIds.has(c.node.id)))
|
|
11422
|
-
continue;
|
|
11423
|
-
const adjacent = store.adjacentEdges([c.node.ref], { direction: "both", limit: options.edgeLimit });
|
|
11424
|
-
if (adjacent.truncated)
|
|
11425
|
-
truncated = true;
|
|
11426
|
-
for (const edge of adjacent.edges) {
|
|
11427
|
-
if (edge.type === "SUPERSEDES")
|
|
11428
|
-
continue;
|
|
11429
|
-
const other = edge.from === c.node.id ? edge.to : edge.from;
|
|
11430
|
-
if (done.has(other) || other.startsWith("project:"))
|
|
11431
|
-
continue;
|
|
11432
|
-
const score = c.score * EDGE_WEIGHTS[edge.type];
|
|
11433
|
-
if (score <= (best.get(other) ?? 0))
|
|
11434
|
-
continue;
|
|
11435
|
-
const ref = parseNodeId(other);
|
|
11436
|
-
const node = ref === void 0 ? void 0 : store.getNode(ref);
|
|
11437
|
-
if (node === void 0)
|
|
11438
|
-
continue;
|
|
11439
|
-
best.set(other, score);
|
|
11440
|
-
heap.push({ node, score, depth: c.depth + 1, seed: c.seed, steps: [...c.steps, step(edge)] });
|
|
11441
|
-
}
|
|
11442
|
-
}
|
|
11443
|
-
return { candidates: [...done.values()].sort(better), truncated };
|
|
11444
|
-
}
|
|
11445
|
-
|
|
11446
11824
|
// packages/director/dist/context/types.js
|
|
11447
11825
|
var SEED_PROVENANCE = {
|
|
11448
11826
|
id: "explicit",
|
|
@@ -11612,6 +11990,7 @@ function planContext(input) {
|
|
|
11612
11990
|
const mil = new Map(truth.milestones.map((m) => [m.id, m]));
|
|
11613
11991
|
const seedIds = new Set(input.seeds.seeds.map((s) => s.id));
|
|
11614
11992
|
const exactSeeds = new Set(input.seeds.seeds.filter((s) => s.match !== "keyword").map((s) => s.id));
|
|
11993
|
+
const idSeeds = new Set(input.seeds.seeds.filter((s) => s.match === "id").map((s) => s.id));
|
|
11615
11994
|
const slice = (loc2) => {
|
|
11616
11995
|
const r = reader.slice(loc2);
|
|
11617
11996
|
diagnostics.push(...r.diagnostics);
|
|
@@ -11644,6 +12023,7 @@ function planContext(input) {
|
|
|
11644
12023
|
score: c.score,
|
|
11645
12024
|
depth: c.depth,
|
|
11646
12025
|
mandatory: exactSeeds.has(c.node.id),
|
|
12026
|
+
...c.depth === 0 && idSeeds.has(c.node.id) ? { primary: true } : {},
|
|
11647
12027
|
levels: levels([["L1", head], ["L2", body === "" ? void 0 : `${head}
|
|
11648
12028
|
${body}`], ["L3", exact === void 0 ? void 0 : `${head}
|
|
11649
12029
|
${fence(exact, "md")}`]]),
|
|
@@ -11752,7 +12132,7 @@ ${fence(exact, "yaml")}`]]),
|
|
|
11752
12132
|
case "issue": {
|
|
11753
12133
|
const i = iss.get(ref.id);
|
|
11754
12134
|
if (i !== void 0)
|
|
11755
|
-
put(issueDraft(i, node, c, slice, via(c)));
|
|
12135
|
+
put(issueDraft(i, node, c, slice, via(c), c.depth === 0 && idSeeds.has(node.id)));
|
|
11756
12136
|
break;
|
|
11757
12137
|
}
|
|
11758
12138
|
case "milestone": {
|
|
@@ -11908,6 +12288,7 @@ ${fence(w.text, langOf(ref.path))}`;
|
|
|
11908
12288
|
tier: d.tier,
|
|
11909
12289
|
rank: i + 1,
|
|
11910
12290
|
mandatory: d.mandatory,
|
|
12291
|
+
...d.primary === true ? { primary: true } : {},
|
|
11911
12292
|
levels: d.levels,
|
|
11912
12293
|
via: d.via,
|
|
11913
12294
|
...d.source === void 0 ? {} : { source: d.source },
|
|
@@ -11944,7 +12325,7 @@ function proposalRefs(p) {
|
|
|
11944
12325
|
out.add(e.id);
|
|
11945
12326
|
return [...out].sort(compareUtf8);
|
|
11946
12327
|
}
|
|
11947
|
-
function issueDraft(i, node, c, slice, v) {
|
|
12328
|
+
function issueDraft(i, node, c, slice, v, primary) {
|
|
11948
12329
|
const head = `${i.id} ${i.title} (${[i.status, i.milestone].filter((x) => x !== null).join(", ")})`;
|
|
11949
12330
|
const ac = i.acceptance.map((a) => `- ${a.id} ${a.text}`);
|
|
11950
12331
|
const commits = Array.isArray(node.payload.commits) ? node.payload.commits.slice(0, 3).map((x) => String(x).slice(0, 12)) : [];
|
|
@@ -11957,6 +12338,7 @@ function issueDraft(i, node, c, slice, v) {
|
|
|
11957
12338
|
score: c.score,
|
|
11958
12339
|
depth: c.depth,
|
|
11959
12340
|
mandatory: c.depth === 0,
|
|
12341
|
+
...primary ? { primary: true } : {},
|
|
11960
12342
|
levels: levels([
|
|
11961
12343
|
["L1", head],
|
|
11962
12344
|
["L2", ac.length === 0 ? void 0 : [head, ...ac].join("\n")],
|
|
@@ -12216,8 +12598,14 @@ var STATIC_LIMITATIONS = [
|
|
|
12216
12598
|
{ code: "files-by-path", message: "Files are listed by path; whole files are never included. Symbols and tests show their own source range; a file without a structural analyzer shows at most its first lines." },
|
|
12217
12599
|
{ code: "no-diff", message: "The working-tree diff is not part of this packet." }
|
|
12218
12600
|
];
|
|
12219
|
-
function limitations(plan, taskTruncated, omitted, summarized) {
|
|
12601
|
+
function limitations(plan, taskTruncated, omitted, summarized, budget, truncatedSeeds) {
|
|
12220
12602
|
const out = [...STATIC_LIMITATIONS];
|
|
12603
|
+
for (const t2 of truncatedSeeds) {
|
|
12604
|
+
out.push({
|
|
12605
|
+
code: "explicit-seed-truncated",
|
|
12606
|
+
message: t2.shown === void 0 ? `Explicit seed ${t2.ref} could not fit within the ${budget}-token budget (its full specification is ${t2.fullTokens} tokens); it is not shown.` : `Explicit seed ${t2.ref} could not fit its full specification within the ${budget}-token budget (${t2.fullTokens} tokens); the highest fitting representation (${t2.shown}) is shown.`
|
|
12607
|
+
});
|
|
12608
|
+
}
|
|
12221
12609
|
if (omitted > 0)
|
|
12222
12610
|
out.push({ code: "omitted", message: `omitted: ${omitted} lower-ranked candidates did not fit the budget (ids in omittedCandidates).` });
|
|
12223
12611
|
if (summarized > 0)
|
|
@@ -12240,6 +12628,11 @@ function packContext(input) {
|
|
|
12240
12628
|
const block = (text4) => meter.count(renderItemBlock(text4) + "\n");
|
|
12241
12629
|
const evidence = (i) => meter.count(renderEvidenceLine(i.ref, i.via) + "\n");
|
|
12242
12630
|
const itemTokens = (i, level) => level < 0 ? 0 : block(i.levels[level]?.text ?? "") + evidence(i);
|
|
12631
|
+
const truncatedSeed = (s, level) => ({
|
|
12632
|
+
ref: s.value.ref,
|
|
12633
|
+
fullTokens: itemTokens(s.value, s.value.levels.length - 1),
|
|
12634
|
+
shown: level < 0 ? void 0 : s.value.levels[level]?.level ?? "L1"
|
|
12635
|
+
});
|
|
12243
12636
|
const pendingTokens = (p, level) => block(p.levels[level]?.text ?? "");
|
|
12244
12637
|
const build = () => {
|
|
12245
12638
|
const chosen = items.filter((s) => s.level >= 0);
|
|
@@ -12277,6 +12670,7 @@ function packContext(input) {
|
|
|
12277
12670
|
const omitted = items.filter((s) => s.level < 0);
|
|
12278
12671
|
const summarized = chosen.filter((s) => s.level < s.value.levels.length - 1).length;
|
|
12279
12672
|
const taskTruncated = request.taskTruncated;
|
|
12673
|
+
const truncatedSeeds = items.filter((s) => s.value.primary === true && s.level < s.value.levels.length - 1).map((s) => truncatedSeed(s, s.level));
|
|
12280
12674
|
const packet2 = {
|
|
12281
12675
|
format: "duo.context-packet/1",
|
|
12282
12676
|
request,
|
|
@@ -12288,7 +12682,7 @@ function packContext(input) {
|
|
|
12288
12682
|
issues: of((i) => i.kind === "issue" || i.kind === "milestone"),
|
|
12289
12683
|
pendingDecisions: pend,
|
|
12290
12684
|
evidence: chosen.map((s) => ({ id: s.value.id, ref: s.value.ref, via: s.value.via })),
|
|
12291
|
-
limitations: limitations(plan, taskTruncated, omitted.length, summarized),
|
|
12685
|
+
limitations: limitations(plan, taskTruncated, omitted.length, summarized, request.budget, truncatedSeeds),
|
|
12292
12686
|
signals: plan.signals,
|
|
12293
12687
|
omittedCandidates: omitted.map((s) => ({ id: s.value.id, ref: s.value.ref, rank: s.value.rank, tier: s.value.tier, reason: "budget" })),
|
|
12294
12688
|
truncated: omitted.length > 0 || plan.traversalTruncated || taskTruncated,
|
|
@@ -12299,7 +12693,7 @@ function packContext(input) {
|
|
|
12299
12693
|
return { packet: packet2, markdown: renderContextMarkdown(packet2) };
|
|
12300
12694
|
};
|
|
12301
12695
|
const frame = build();
|
|
12302
|
-
const frameLimits = limitations(plan, request.taskTruncated, Math.max(1, items.length), Math.max(1, items.length));
|
|
12696
|
+
const frameLimits = limitations(plan, request.taskTruncated, Math.max(1, items.length), Math.max(1, items.length), request.budget, items.filter((s) => s.value.primary === true).map((s) => truncatedSeed(s, 0)));
|
|
12303
12697
|
const reserved = meter.count(renderContextMarkdown({ ...frame.packet, limitations: frameLimits }));
|
|
12304
12698
|
if (reserved > request.budget) {
|
|
12305
12699
|
return failure([createDiagnostic("CONTEXT_REQUEST_INVALID", `budget ${request.budget} is smaller than the packet frame (${reserved} tokens: task, headings, pending decisions, limitations)`)]);
|
|
@@ -12334,6 +12728,12 @@ function packContext(input) {
|
|
|
12334
12728
|
for (const s of items)
|
|
12335
12729
|
if (s.value.mandatory)
|
|
12336
12730
|
raise(s, s.value.tier, void 0);
|
|
12731
|
+
for (const s of items) {
|
|
12732
|
+
if (s.value.primary !== true)
|
|
12733
|
+
continue;
|
|
12734
|
+
while (raise(s, s.value.tier, void 0)) {
|
|
12735
|
+
}
|
|
12736
|
+
}
|
|
12337
12737
|
const promotion = available - spent;
|
|
12338
12738
|
const byTier = (tier) => tier === "pending" ? pending : items.filter((s) => s.value.tier === tier);
|
|
12339
12739
|
for (const capped of [true, false]) {
|
|
@@ -12739,7 +13139,7 @@ async function compileContext(root, request, options) {
|
|
|
12739
13139
|
const idle = (status, signals, extra = {}) => success({ status, freshness, signals, cache: { status: "off", written: false }, performance: perf(), diagnostics: canonicalDiagnostics(diagnostics), ...extra }, canonicalDiagnostics(diagnostics));
|
|
12740
13140
|
if (inspected.value.status !== "current")
|
|
12741
13141
|
return idle("index-required", []);
|
|
12742
|
-
const loaded = loadProjectTruth(root);
|
|
13142
|
+
const loaded = requireCompleteTruth(loadProjectTruth(root));
|
|
12743
13143
|
if (loaded.value === void 0)
|
|
12744
13144
|
return failure(loaded.diagnostics);
|
|
12745
13145
|
const { truth } = loaded.value;
|
|
@@ -14445,88 +14845,6 @@ async function externalSourceDrift(ctx) {
|
|
|
14445
14845
|
return { claims, limitations: limitations2 };
|
|
14446
14846
|
}
|
|
14447
14847
|
|
|
14448
|
-
// packages/director/dist/review/seeds.js
|
|
14449
|
-
var PROPOSALS2 = `${STATE_DIR_NAME}/decisions/proposals/`;
|
|
14450
|
-
function hunkRange(h) {
|
|
14451
|
-
return h.newLines > 0 ? [h.newStart, h.newStart + h.newLines - 1] : [Math.max(1, h.newStart), Math.max(1, h.newStart)];
|
|
14452
|
-
}
|
|
14453
|
-
function overlaps(loc2, range) {
|
|
14454
|
-
if (loc2?.startLine === void 0)
|
|
14455
|
-
return false;
|
|
14456
|
-
const end2 = loc2.endLine ?? loc2.startLine;
|
|
14457
|
-
return range[0] <= end2 && range[1] >= loc2.startLine;
|
|
14458
|
-
}
|
|
14459
|
-
function owned(graph, path35) {
|
|
14460
|
-
const out = [];
|
|
14461
|
-
let afterId;
|
|
14462
|
-
for (; ; ) {
|
|
14463
|
-
const page = graph.listNodes({ ownerFile: path35, limit: 1e3, ...afterId === void 0 ? {} : { afterId } });
|
|
14464
|
-
out.push(...page);
|
|
14465
|
-
if (page.length < 1e3)
|
|
14466
|
-
return out;
|
|
14467
|
-
afterId = page[page.length - 1]?.id;
|
|
14468
|
-
}
|
|
14469
|
-
}
|
|
14470
|
-
var width = (loc2) => (loc2?.endLine ?? 0) - (loc2?.startLine ?? 0);
|
|
14471
|
-
function truthDefinitions(truth) {
|
|
14472
|
-
return [
|
|
14473
|
-
...truth.requirements.map((d) => ({ ref: definitionRef("requirement", d.id), location: d.location })),
|
|
14474
|
-
...truth.decisions.map((d) => ({ ref: definitionRef("decision", d.id), location: d.location })),
|
|
14475
|
-
...truth.constraints.map((d) => ({ ref: definitionRef("decision", d.id), location: d.location })),
|
|
14476
|
-
...truth.issues.map((d) => ({ ref: definitionRef("issue", d.id), location: d.location })),
|
|
14477
|
-
...truth.milestones.map((d) => ({ ref: definitionRef("milestone", d.id), location: d.location }))
|
|
14478
|
-
];
|
|
14479
|
-
}
|
|
14480
|
-
function diffSeeds(files, graph, truth) {
|
|
14481
|
-
const seeds = /* @__PURE__ */ new Map();
|
|
14482
|
-
const put = (entity, reason, path35, evidenceId2) => {
|
|
14483
|
-
if (graph.getNode(entity) === void 0)
|
|
14484
|
-
return;
|
|
14485
|
-
const id3 = nodeId(entity);
|
|
14486
|
-
const s = seeds.get(id3) ?? { entity, reason, path: path35, evidence: /* @__PURE__ */ new Set() };
|
|
14487
|
-
s.evidence.add(evidenceId2);
|
|
14488
|
-
seeds.set(id3, s);
|
|
14489
|
-
};
|
|
14490
|
-
const defs = truthDefinitions(truth);
|
|
14491
|
-
for (const f of files) {
|
|
14492
|
-
if (f.kind === "deleted")
|
|
14493
|
-
continue;
|
|
14494
|
-
if (f.path.startsWith(`${STATE_DIR_NAME}/`)) {
|
|
14495
|
-
if (f.path.startsWith(PROPOSALS2))
|
|
14496
|
-
continue;
|
|
14497
|
-
for (const h of f.hunks) {
|
|
14498
|
-
for (const d of defs)
|
|
14499
|
-
if (d.location.path === f.path && overlaps(d.location, hunkRange(h)))
|
|
14500
|
-
put(d.ref, "truth-changed", f.path, h.evidenceId);
|
|
14501
|
-
}
|
|
14502
|
-
continue;
|
|
14503
|
-
}
|
|
14504
|
-
const nodes = owned(graph, f.path).filter((n) => n.type === "symbol" || n.type === "test");
|
|
14505
|
-
const ranges = new Map(nodes.map((n) => [n.id, nodeLocations(n)]));
|
|
14506
|
-
let touched = false;
|
|
14507
|
-
for (const h of f.hunks) {
|
|
14508
|
-
const range = hunkRange(h);
|
|
14509
|
-
const hits = nodes.map((n) => ({ n, at: (ranges.get(n.id) ?? []).filter((l) => overlaps(l, range)) })).filter((x) => x.at.length > 0);
|
|
14510
|
-
const inner = hits.filter((x) => !hits.some((o) => o !== x && o.at.some((ol) => x.at.some((xl) => width(ol) < width(xl) && overlaps(xl, [ol.startLine ?? 0, ol.endLine ?? 0])))));
|
|
14511
|
-
for (const { n } of inner) {
|
|
14512
|
-
put(n.ref, "hunk-overlap", f.path, h.evidenceId);
|
|
14513
|
-
touched = true;
|
|
14514
|
-
}
|
|
14515
|
-
}
|
|
14516
|
-
if (!touched)
|
|
14517
|
-
for (const e of f.evidenceIds)
|
|
14518
|
-
put(fileRef(f.path), "file-changed", f.path, e);
|
|
14519
|
-
}
|
|
14520
|
-
return [...seeds.entries()].sort(([a], [b]) => compareUtf8(a, b)).map(([id3, s]) => ({
|
|
14521
|
-
id: id3,
|
|
14522
|
-
ref: displayRef(id3),
|
|
14523
|
-
entity: s.entity,
|
|
14524
|
-
reason: s.reason,
|
|
14525
|
-
path: s.path,
|
|
14526
|
-
evidenceIds: [...s.evidence].sort(compareUtf8)
|
|
14527
|
-
}));
|
|
14528
|
-
}
|
|
14529
|
-
|
|
14530
14848
|
// packages/director/dist/review/semantic.js
|
|
14531
14849
|
var ALIGNMENTS = ["ALIGNED", "PARTIAL", "CONFLICT", "UNKNOWN"];
|
|
14532
14850
|
function semanticSchema(claimIds) {
|
|
@@ -14773,7 +15091,7 @@ async function reviewChanges(root, request, options) {
|
|
|
14773
15091
|
if (inspected.value.status !== "current") {
|
|
14774
15092
|
return success({ result: { format: "duo.review/1", status: "index-required", request: requestIdentity, baseline, freshness, seeds: [], verdictBasis: { blocking: [], ask: [], warn: [] }, claims: [], evidence: [], limitations: [], semanticAssist: noAssist, metrics: zeroMetrics, diagnostics: [] }, performance: perf() });
|
|
14775
15093
|
}
|
|
14776
|
-
const loaded = loadProjectTruth(root);
|
|
15094
|
+
const loaded = requireCompleteTruth(loadProjectTruth(root));
|
|
14777
15095
|
if (loaded.value === void 0)
|
|
14778
15096
|
return { diagnostics: loaded.diagnostics };
|
|
14779
15097
|
const { truth } = loaded.value;
|
|
@@ -14832,17 +15150,21 @@ async function reviewChanges(root, request, options) {
|
|
|
14832
15150
|
stateDiagnostics: readIndexState(root).state?.diagnostics ?? [],
|
|
14833
15151
|
...request.testResults === void 0 ? {} : { testResults: request.testResults },
|
|
14834
15152
|
...adoption.baseline === void 0 ? {} : { baselineKeys: new Set(adoption.baseline.findings.map((f) => f.key)) },
|
|
14835
|
-
...adoption.baseline === void 0 ? {} : { baselineStructuralLanguages: new Set(adoption.baseline.analysis?.structuralLanguages ?? PRE_T18_STRUCTURAL_LANGUAGES) }
|
|
15153
|
+
...adoption.baseline === void 0 ? {} : { baselineStructuralLanguages: new Set(adoption.baseline.analysis?.structuralLanguages ?? PRE_T18_STRUCTURAL_LANGUAGES) },
|
|
15154
|
+
...adoption.baseline?.evaluatedRules === void 0 ? {} : { baselineEvaluatedRules: new Set(adoption.baseline.evaluatedRules) },
|
|
15155
|
+
...adoption.baseline === void 0 ? {} : { baselineRecordedAt: adoption.baseline.recorded.at }
|
|
14836
15156
|
};
|
|
14837
15157
|
const integrity = await decisionIntegrity(ctx);
|
|
14838
|
-
const
|
|
15158
|
+
const forbids2 = await decisionForbids(ctx);
|
|
15159
|
+
const imports = await decisionForbidsImport(ctx);
|
|
14839
15160
|
const drift = await externalSourceDrift(ctx);
|
|
14840
15161
|
const additions = unlinkedAdditions(ctx);
|
|
14841
|
-
const conflicted = new Set(
|
|
15162
|
+
const conflicted = new Set([...forbids2, ...imports.claims].map((c) => c.subject.id));
|
|
14842
15163
|
const claims = [
|
|
14843
15164
|
...integrity,
|
|
14844
15165
|
...supersedeIntegrity(ctx),
|
|
14845
|
-
...
|
|
15166
|
+
...forbids2,
|
|
15167
|
+
...imports.claims,
|
|
14846
15168
|
...decisionGovernance(ctx, conflicted),
|
|
14847
15169
|
...declaredReferences(ctx),
|
|
14848
15170
|
...requirementImplementation(ctx),
|
|
@@ -14893,6 +15215,7 @@ async function reviewChanges(root, request, options) {
|
|
|
14893
15215
|
...limitationsOf({ identity: diff.value.identity, from: diff.value.from, to: diff.value.to, files }, reviewPacket, task !== "", taskContext?.status === "ready"),
|
|
14894
15216
|
...analysisLimitsOf(files, options.graph),
|
|
14895
15217
|
...drift.limitations,
|
|
15218
|
+
...imports.limitations,
|
|
14896
15219
|
...adoption.status === "incompatible" ? [{ code: "adoption-baseline-unusable", message: "The Adoption Baseline cannot be used (" + (adoption.reason ?? "unreadable") + "); violations are not told apart from pre-existing ones." }] : []
|
|
14897
15220
|
],
|
|
14898
15221
|
semanticAssist: semantic.assist,
|
|
@@ -14913,6 +15236,25 @@ async function reviewChanges(root, request, options) {
|
|
|
14913
15236
|
return success({ result, performance: perf() }, result.diagnostics);
|
|
14914
15237
|
}
|
|
14915
15238
|
|
|
15239
|
+
// packages/director/dist/review/provenance-label.js
|
|
15240
|
+
var PROVENANCE_LABELS = {
|
|
15241
|
+
en: {
|
|
15242
|
+
introduced: "not-in-adoption-baseline",
|
|
15243
|
+
"pre-existing": "in-adoption-baseline",
|
|
15244
|
+
"pre-existing-touched": "in-adoption-baseline, touched",
|
|
15245
|
+
"unverified-at-adoption": "baseline-unverified"
|
|
15246
|
+
},
|
|
15247
|
+
ko: {
|
|
15248
|
+
introduced: "\uCC44\uD0DD \uAE30\uC900\uC120\uC5D0 \uC5C6\uC74C",
|
|
15249
|
+
"pre-existing": "\uCC44\uD0DD \uAE30\uC900\uC120\uC5D0 \uC874\uC7AC",
|
|
15250
|
+
"pre-existing-touched": "\uCC44\uD0DD \uAE30\uC900\uC120\uC5D0 \uC874\uC7AC, \uC774\uBC88 \uBCC0\uACBD\uC5D0\uC11C \uC811\uCD09",
|
|
15251
|
+
"unverified-at-adoption": "\uCC44\uD0DD \uC2DC\uC810 \uAC80\uC99D \uBD88\uAC00"
|
|
15252
|
+
}
|
|
15253
|
+
};
|
|
15254
|
+
function provenanceLabel(value, locale = "en") {
|
|
15255
|
+
return PROVENANCE_LABELS[locale][value];
|
|
15256
|
+
}
|
|
15257
|
+
|
|
14916
15258
|
// packages/director/dist/runtime/metrics.js
|
|
14917
15259
|
import fs23 from "node:fs";
|
|
14918
15260
|
import fsp5 from "node:fs/promises";
|
|
@@ -15410,7 +15752,7 @@ async function withGraphWriter(root, fn) {
|
|
|
15410
15752
|
function project(root) {
|
|
15411
15753
|
const loaded = loadProjectTruth(root);
|
|
15412
15754
|
if (loaded.value !== void 0)
|
|
15413
|
-
return { value: loaded.value };
|
|
15755
|
+
return { value: loaded.value, diagnostics: loaded.diagnostics };
|
|
15414
15756
|
const missing = loaded.diagnostics.some((d) => d.code === "PROJECT_FILE_MISSING");
|
|
15415
15757
|
return { outcome: missing ? { kind: "not-initialized", diagnostics: loaded.diagnostics } : { kind: "failed", diagnostics: loaded.diagnostics } };
|
|
15416
15758
|
}
|
|
@@ -15433,6 +15775,7 @@ function projectStatus(root, options = {}) {
|
|
|
15433
15775
|
if (p.value === void 0)
|
|
15434
15776
|
return p.outcome;
|
|
15435
15777
|
const { truth } = p.value;
|
|
15778
|
+
const truthErrors = truthAuthorityErrors(p.diagnostics ?? []);
|
|
15436
15779
|
const inspection = await withRegistry(options.registry, (registry) => withGraphReader(root, (graph) => inspectIndex(root, { graph, registry })));
|
|
15437
15780
|
const baseline = await getAdoptionBaselineStatus(root);
|
|
15438
15781
|
const pending = listDecisionProposals(truth).filter((e) => e.status === "pending");
|
|
@@ -15441,12 +15784,19 @@ function projectStatus(root, options = {}) {
|
|
|
15441
15784
|
const llm = llmPoolOf(options).forConfig(truth.config.llm);
|
|
15442
15785
|
return {
|
|
15443
15786
|
kind: "ok",
|
|
15444
|
-
diagnostics: errorsOf([...inspection.diagnostics, ...baseline.diagnostics]),
|
|
15787
|
+
diagnostics: errorsOf([...truthErrors, ...inspection.diagnostics, ...baseline.diagnostics]),
|
|
15445
15788
|
payload: {
|
|
15446
15789
|
format: STATUS_FORMAT,
|
|
15447
15790
|
initialized: true,
|
|
15448
15791
|
project: { name: truth.config.name, vision: truth.vision?.status ?? "missing", currentMilestone: truth.config.currentMilestone },
|
|
15449
|
-
truth: {
|
|
15792
|
+
truth: {
|
|
15793
|
+
requirements: truth.requirements.length,
|
|
15794
|
+
decisions: truth.decisions.length,
|
|
15795
|
+
constraints: truth.constraints.length,
|
|
15796
|
+
declaredGaps: truth.gaps.length,
|
|
15797
|
+
// T40 (additive): loader errors that make review, context and Decision confirm refuse this Truth. Empty when Truth loaded completely.
|
|
15798
|
+
errors: truthErrors.map((d) => ({ code: d.code, message: d.message, ...d.source === void 0 ? {} : { path: d.source.path, ...d.source.startLine === void 0 ? {} : { line: d.source.startLine } } }))
|
|
15799
|
+
},
|
|
15450
15800
|
index: i === void 0 ? null : {
|
|
15451
15801
|
status: i.status,
|
|
15452
15802
|
fullRebuildReason: i.fullRebuildReason ?? null,
|
|
@@ -15889,6 +16239,16 @@ var repoPattern = z4.string().min(1).max(4096).refine((p) => normalizeRepoPatter
|
|
|
15889
16239
|
var definitionId = z4.string().regex(DEFINITION_ID_PATTERN, "a definition ID such as AUTH-03 or D-004");
|
|
15890
16240
|
var endpoint = z4.string().min(1).max(256).refine((v) => !v.startsWith("-") && !/[\u0000-\u001f\u007f\s]/u.test(v), "HEAD, INDEX, WORKTREE or a commit / branch name");
|
|
15891
16241
|
var text2 = (max) => z4.string().trim().min(1).max(max);
|
|
16242
|
+
var decisionId = z4.string().regex(/^D-\d+$/u, "a Decision ID such as D-004");
|
|
16243
|
+
var forbidPattern = z4.string().min(1).max(4096).refine((p) => compileRepoPattern(p) !== void 0, "a repository-relative pattern review can match (no absolute or .. pattern)");
|
|
16244
|
+
var FORBIDS_SYMBOL_MESSAGE = "forbids.symbols matches symbol qualified names only. Do not use a DUO path#symbol reference. Use a qualified-name wildcard such as 'LegacyDb' or '*LegacyDb*'.";
|
|
16245
|
+
var forbidSymbol = text2(500).refine((s) => !/[/\\#]/u.test(s), FORBIDS_SYMBOL_MESSAGE);
|
|
16246
|
+
var forbids = z4.strictObject({
|
|
16247
|
+
paths: z4.array(forbidPattern).max(100).optional().describe("Repository path patterns; a change to a matching file conflicts."),
|
|
16248
|
+
symbols: z4.array(forbidSymbol).max(100).optional().describe("Wildcard patterns over the qualified names of changed symbols ('*' any text, '?' one character), e.g. LegacyDb, LegacyDb.run, *LegacyDb*. Not a DUO path#symbol reference. Catches changes to matching symbols, not new calls or references from other symbols."),
|
|
16249
|
+
dependencies: z4.array(text2(500)).max(100).optional().describe("Package names; adding one to a package.json conflicts."),
|
|
16250
|
+
imported_paths: z4.array(forbidPattern).max(100).optional().describe("Repository path patterns (H-72). Matches changed import/module-reference statements (import, export-from, require, dynamic import, type-only included; Python imports and C++ quoted includes where DUO resolves them) that resolve exactly to a matching repository file. It does not prove the import relation is newly introduced; unresolved, ambiguous and Java/C# imports are not checked. External packages belong in dependencies.")
|
|
16251
|
+
}).refine((f) => (f.paths?.length ?? 0) + (f.symbols?.length ?? 0) + (f.dependencies?.length ?? 0) + (f.imported_paths?.length ?? 0) > 0, "forbids needs at least one path, symbol, dependency or imported path");
|
|
15892
16252
|
var INPUT = {
|
|
15893
16253
|
duo_get_status: z4.strictObject({}),
|
|
15894
16254
|
duo_get_context: z4.strictObject({ task: text2(2e4), budget: z4.number().int().min(MIN_BUDGET).max(MAX_BUDGET).optional(), profile: z4.enum(["default", "review"]).optional() }),
|
|
@@ -15911,6 +16271,10 @@ var INPUT = {
|
|
|
15911
16271
|
answer: text2(2e3),
|
|
15912
16272
|
rationale: text2(4e3).optional(),
|
|
15913
16273
|
governs: z4.strictObject({ requirements: z4.array(definitionId).max(100).optional(), paths: z4.array(repoPattern).max(100).optional(), symbols: z4.array(text2(500)).max(100).optional() }).optional(),
|
|
16274
|
+
// H-71: the proposal may carry what a Decision enforces and what it replaces; it gains no authority until a human confirms it.
|
|
16275
|
+
forbids: forbids.optional(),
|
|
16276
|
+
enforcement: z4.enum(["warn", "block"]).optional(),
|
|
16277
|
+
supersedes: decisionId.optional(),
|
|
15914
16278
|
agent: text2(100).optional()
|
|
15915
16279
|
})
|
|
15916
16280
|
};
|
|
@@ -16029,7 +16393,7 @@ ${advice.join("\n")}`);
|
|
|
16029
16393
|
const r = p;
|
|
16030
16394
|
if (r.status === "index-required")
|
|
16031
16395
|
return "INDEX_REQUIRED: the DUO index is not current. Run duoctl index, then review again.";
|
|
16032
|
-
const lines4 = r.claims.filter((c) => c.alignment !== "ALIGNED").map((c) => `- ${c.alignment} ${c.rule} ${c.subject.id}: ${c.reason}${c.provenance === void 0 ? "" : ` (${c.provenance})`}${c.blockEligible ? " [blocking]" : ""}`);
|
|
16396
|
+
const lines4 = r.claims.filter((c) => c.alignment !== "ALIGNED").map((c) => `- ${c.alignment} ${c.rule} ${c.subject.id}: ${c.reason}${c.provenance === void 0 ? "" : ` (${provenanceLabel(c.provenance)})`}${c.blockEligible ? " [blocking]" : ""}`);
|
|
16033
16397
|
const a = r.semanticAssist;
|
|
16034
16398
|
const semantic = a.status === "not-requested" ? [] : [
|
|
16035
16399
|
`Semantic assistance (supplemental, never blocks): ${a.status}${a.failure === void 0 ? "" : ` (${a.failure})`}`,
|
|
@@ -16082,14 +16446,17 @@ ${advice.join("\n")}`);
|
|
|
16082
16446
|
name: "duo_propose_decision",
|
|
16083
16447
|
title: "Propose a Decision",
|
|
16084
16448
|
readOnly: false,
|
|
16085
|
-
description:
|
|
16449
|
+
description: `Creates a Decision proposal only (decisions/proposals/P-*.yaml). Cannot confirm or reject a decision: a human does that in duoctl or the Web UI. A proposal is not confirmed Project Truth and does not change any review verdict. forbids and enforcement: "block" have no authority until a human confirms the proposal. supersedes names a confirmed Decision to replace; that Decision is not modified until a human confirms this proposal. governs describes what the Decision concerns (for tracing and context); it does not scope forbids. Forbids are repository-wide: they apply to matching changes anywhere in the repository. forbids.symbols are wildcard patterns over the qualified names of changed symbols ('*' any text, '?' one character), e.g. LegacyDb, LegacyDb.run, *LegacyDb*; a DUO path#symbol reference such as src/db/legacy-db.ts#LegacyDb (a display handle in context output) is not a pattern and is refused. A symbol pattern catches changes to matching symbols, not new calls or references from other code. forbids.imported_paths are repository path patterns: they match changed import/module-reference statements that resolve exactly to a matching repository file; this does not prove the import relation is newly introduced, and unresolved, ambiguous or Java/C# imports are not checked. The agent name is an audit label, not an authentication.`,
|
|
16086
16450
|
run: async (args, ctx) => {
|
|
16087
16451
|
const op = await proposeDecision(ctx.root, args.agent ?? ctx.agentName, {
|
|
16088
16452
|
title: args.title,
|
|
16089
16453
|
question: args.question,
|
|
16090
16454
|
answer: args.answer,
|
|
16091
16455
|
...args.rationale === void 0 ? {} : { rationale: args.rationale },
|
|
16092
|
-
...args.governs === void 0 ? {} : { governs: args.governs }
|
|
16456
|
+
...args.governs === void 0 ? {} : { governs: args.governs },
|
|
16457
|
+
...args.forbids === void 0 ? {} : { forbids: args.forbids },
|
|
16458
|
+
...args.enforcement === void 0 ? {} : { enforcement: args.enforcement },
|
|
16459
|
+
...args.supersedes === void 0 ? {} : { supersedes: args.supersedes }
|
|
16093
16460
|
});
|
|
16094
16461
|
return { op, metric: { status: op.kind === "ok" ? "proposed" : op.kind } };
|
|
16095
16462
|
},
|
|
@@ -16846,7 +17213,11 @@ async function projectDoctor(rootArg, options) {
|
|
|
16846
17213
|
let project2;
|
|
16847
17214
|
if (runs("truth.project", "git.repository")) {
|
|
16848
17215
|
const loaded = loadProjectTruth(root);
|
|
16849
|
-
|
|
17216
|
+
const authority = loaded.value === void 0 ? [] : truthAuthorityErrors(loaded.diagnostics);
|
|
17217
|
+
if (loaded.value !== void 0 && authority.length > 0) {
|
|
17218
|
+
const problems = authority.slice(0, LIST_LIMIT).map((d) => ({ code: d.code, ...d.source?.path === void 0 ? {} : { path: d.source.path } }));
|
|
17219
|
+
set("truth.project", "error", "invalid", { problems }, [{ id: "fix-truth", commands: [], params: { path: problems.find((p) => p.path !== void 0)?.path ?? ".duo-project/project.yaml" } }]);
|
|
17220
|
+
} else if (loaded.value !== void 0) {
|
|
16850
17221
|
project2 = loaded.value;
|
|
16851
17222
|
const t2 = project2.truth;
|
|
16852
17223
|
set("truth.project", "ok", "valid", { name: t2.config.name, requirements: t2.requirements.length, decisions: t2.decisions.length, constraints: t2.constraints.length });
|
|
@@ -17271,8 +17642,8 @@ function projectProposals(root) {
|
|
|
17271
17642
|
const service = createDecisionService({ root });
|
|
17272
17643
|
const previews = {};
|
|
17273
17644
|
for (const e of d.proposals.filter((x) => x.status === "pending")) {
|
|
17274
|
-
const
|
|
17275
|
-
previews[e.id] =
|
|
17645
|
+
const preview2 = await service.previewConfirm(e.id);
|
|
17646
|
+
previews[e.id] = preview2.value ?? { error: preview2.diagnostics.map((x) => x.code) };
|
|
17276
17647
|
}
|
|
17277
17648
|
return { kind: "ok", diagnostics: [], payload: { proposals: d.proposals, previews } };
|
|
17278
17649
|
});
|
|
@@ -17429,7 +17800,8 @@ var PROPOSAL_ID = /^P-[A-Za-z0-9-]{1,64}$/u;
|
|
|
17429
17800
|
var SCHEMAS = {
|
|
17430
17801
|
context: z5.strictObject({ task: text3(2e4), budget: z5.number().int().min(MIN_BUDGET).max(MAX_BUDGET).optional() }),
|
|
17431
17802
|
review: z5.strictObject({ task: text3(2e4).optional(), from: endpoint2.optional(), to: endpoint2.optional(), includeSemanticAssist: z5.boolean().optional() }),
|
|
17432
|
-
|
|
17803
|
+
// previewDigest is required (T34.3): a UI confirm is always bound to the preview the person reviewed.
|
|
17804
|
+
confirm: z5.strictObject({ confirmId: z5.string().max(80), previewDigest: z5.string().min(1).max(100) }),
|
|
17433
17805
|
reject: z5.strictObject({ reason: z5.string().max(2e3).optional() }),
|
|
17434
17806
|
graph: z5.strictObject({ node: text3(4096), kind: z5.enum(["trace", "impact"]).default("trace"), depth: z5.coerce.number().int().min(1).max(3).default(1) }),
|
|
17435
17807
|
search: z5.strictObject({ q: text3(200), limit: z5.coerce.number().int().min(1).max(50).default(20) }),
|
|
@@ -17486,7 +17858,7 @@ async function startDuoUiServer(options) {
|
|
|
17486
17858
|
const body2 = parse3(SCHEMAS.confirm, await readJson2(req));
|
|
17487
17859
|
if (body2.confirmId.trim() !== id3)
|
|
17488
17860
|
throw new HttpError(400, "UI_CONFIRM_ID_MISMATCH", "type the proposal ID to confirm it");
|
|
17489
|
-
const r2 = await service.confirm(await actor(), id3);
|
|
17861
|
+
const r2 = await service.confirm(await actor(), id3, { expectedDigest: body2.previewDigest });
|
|
17490
17862
|
await meter("decision", r2.value === void 0 ? "failed" : "confirmed", started);
|
|
17491
17863
|
return { name: "decision", body: r2.value === void 0 ? { status: "failed", diagnostics: r2.diagnostics.map((d) => ({ code: d.code, message: d.message })) } : { status: "confirmed", result: r2.value, warnings: r2.diagnostics.map((d) => ({ code: d.code, message: d.message })) } };
|
|
17492
17864
|
}
|
|
@@ -17745,6 +18117,7 @@ var EN = {
|
|
|
17745
18117
|
"status.analysis": "Analysis: {languages}",
|
|
17746
18118
|
"status.baseline": "Adoption baseline: {status}{detail}",
|
|
17747
18119
|
"status.pending": "Pending decisions: {n}",
|
|
18120
|
+
"status.truth-errors": "Truth errors: {n}. These files were not read, so the counts above leave them out. Review, context and Decision confirm refuse to run until they are fixed (DUO does not change them):",
|
|
17748
18121
|
"status.llm": "LLM: {state}",
|
|
17749
18122
|
"index.done": "Indexed ({mode}{reason}) \xB7 {files} files \xB7 {analyzed} parsed \xB7 {changed} changed \xB7 graph {written}",
|
|
17750
18123
|
"index.required": "INDEX_REQUIRED: the index is {status}. Run duoctl index (or pass --refresh).",
|
|
@@ -17768,6 +18141,23 @@ var EN = {
|
|
|
17768
18141
|
"decision.confirmed": "confirmed as {id} \xB7 {path}",
|
|
17769
18142
|
"decision.rejected": "rejected {id}",
|
|
17770
18143
|
"decision.none": "No pending decisions.",
|
|
18144
|
+
"decision.preview.create": "Confirm {id}: this proposal becomes a new confirmed Decision.",
|
|
18145
|
+
"decision.preview.in-place": "Confirm {id}: this proposed Decision becomes confirmed in place.",
|
|
18146
|
+
"decision.preview.lock": "Confirm {id}: this Decision is already confirmed; confirming adds its lock.",
|
|
18147
|
+
"decision.preview.reject": "Reject {id}: the proposal is kept as rejected; nothing becomes a Decision.",
|
|
18148
|
+
"decision.preview.becomes-superseded": "becomes superseded by this Decision",
|
|
18149
|
+
"decision.preview.not-found": "not found; confirm will fail",
|
|
18150
|
+
"decision.preview.stale": "yes, Project Truth changed since this proposal was made{changed}",
|
|
18151
|
+
"decision.preview.fresh": "no",
|
|
18152
|
+
"decision.preview.stale-na": "not applicable (staleness is tracked for proposals only)",
|
|
18153
|
+
"decision.preview.expected": "{id} (expected; the ID in the confirm result is authoritative)",
|
|
18154
|
+
"decision.preview.in-place-id": "{id} (confirmed in place)",
|
|
18155
|
+
"decision.preview.lock-id": "{id} (already confirmed; the lock is added)",
|
|
18156
|
+
"decision.preview.not-set": "(not set)",
|
|
18157
|
+
"decision.preview.none": "(none)",
|
|
18158
|
+
"decision.preview.forbids-scope": "repository-wide (governs does not narrow forbids)",
|
|
18159
|
+
"decision.index-hint": "Run duoctl index so review and context see it.",
|
|
18160
|
+
"decision.truth-errors": "Not listed: {n} Truth error(s). These files could not be read; confirm and reject refuse to run until they are fixed:",
|
|
17771
18161
|
"stats.none": "No metrics yet.",
|
|
17772
18162
|
"not-initialized": "Not a DUO project (no .duo-project/project.yaml). Run duoctl init.",
|
|
17773
18163
|
"not-implemented": "'{command}' is not implemented yet ({task}).",
|
|
@@ -17937,6 +18327,7 @@ var KO = {
|
|
|
17937
18327
|
"status.analysis": "\uBD84\uC11D \uC218\uC900: {languages}",
|
|
17938
18328
|
"status.baseline": "Adoption baseline: {status}{detail}",
|
|
17939
18329
|
"status.pending": "\uB300\uAE30 \uC911 decision: {n}",
|
|
18330
|
+
"status.truth-errors": "Truth \uC624\uB958: {n}. \uC544\uB798 \uD30C\uC77C\uC740 \uC77D\uC9C0 \uBABB\uD574 \uC704 \uAC1C\uC218\uC5D0\uC11C \uBE60\uC84C\uC2B5\uB2C8\uB2E4. \uACE0\uCE60 \uB54C\uAE4C\uC9C0 review, context, Decision \uD655\uC815\uC740 \uC2E4\uD589\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(DUO\uB294 \uC774 \uD30C\uC77C\uC744 \uACE0\uCE58\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4):",
|
|
17940
18331
|
"status.llm": "LLM: {state}",
|
|
17941
18332
|
"index.done": "Index \uC644\uB8CC({mode}{reason}) \xB7 \uD30C\uC77C {files} \xB7 parse {analyzed} \xB7 \uBCC0\uACBD {changed} \xB7 graph {written}",
|
|
17942
18333
|
"index.required": "INDEX_REQUIRED: index\uAC00 {status}\uC785\uB2C8\uB2E4. duoctl index\uB97C \uC2E4\uD589\uD558\uC138\uC694(\uB610\uB294 --refresh).",
|
|
@@ -17960,6 +18351,23 @@ var KO = {
|
|
|
17960
18351
|
"decision.confirmed": "{id}\uB85C \uD655\uC815 \xB7 {path}",
|
|
17961
18352
|
"decision.rejected": "{id} \uAC70\uC808",
|
|
17962
18353
|
"decision.none": "\uB300\uAE30 \uC911 decision\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
18354
|
+
"decision.preview.create": "{id} \uD655\uC815: \uC774 proposal\uC774 \uC0C8 \uD655\uC815 Decision\uC774 \uB429\uB2C8\uB2E4.",
|
|
18355
|
+
"decision.preview.in-place": "{id} \uD655\uC815: \uC774 proposed Decision\uC774 \uADF8 \uC790\uB9AC\uC5D0\uC11C \uD655\uC815\uB429\uB2C8\uB2E4.",
|
|
18356
|
+
"decision.preview.lock": "{id} \uD655\uC815: \uC774\uBBF8 \uD655\uC815\uB41C Decision\uC774\uBA70 lock\uC744 \uCD94\uAC00\uD569\uB2C8\uB2E4.",
|
|
18357
|
+
"decision.preview.reject": "{id} \uAC70\uC808: proposal\uC740 rejected\uB85C \uB0A8\uACE0 Decision\uC774 \uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
18358
|
+
"decision.preview.becomes-superseded": "\uC774 Decision\uC5D0 \uC758\uD574 superseded\uAC00 \uB429\uB2C8\uB2E4",
|
|
18359
|
+
"decision.preview.not-found": "\uC5C6\uC74C, \uD655\uC815\uC774 \uC2E4\uD328\uD569\uB2C8\uB2E4",
|
|
18360
|
+
"decision.preview.stale": "\uC608, proposal \uC774\uD6C4 Project Truth\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4{changed}",
|
|
18361
|
+
"decision.preview.fresh": "\uC544\uB2C8\uC694",
|
|
18362
|
+
"decision.preview.stale-na": "\uD574\uB2F9 \uC5C6\uC74C(staleness\uB294 proposal\uC5D0\uB9CC \uC801\uC6A9)",
|
|
18363
|
+
"decision.preview.expected": "{id} (\uC608\uC0C1\uAC12, \uC2E4\uC81C ID\uB294 \uD655\uC815 \uACB0\uACFC\uB97C \uB530\uB985\uB2C8\uB2E4)",
|
|
18364
|
+
"decision.preview.in-place-id": "{id} (\uADF8 \uC790\uB9AC\uC5D0\uC11C \uD655\uC815)",
|
|
18365
|
+
"decision.preview.lock-id": "{id} (\uC774\uBBF8 \uD655\uC815\uB428, lock \uCD94\uAC00)",
|
|
18366
|
+
"decision.preview.not-set": "(\uC5C6\uC74C)",
|
|
18367
|
+
"decision.preview.none": "(\uBE44\uC5B4 \uC788\uC74C)",
|
|
18368
|
+
"decision.preview.forbids-scope": "\uC800\uC7A5\uC18C \uC804\uCCB4(governs\uB294 forbids\uC758 \uBC94\uC704\uB97C \uC881\uD788\uC9C0 \uC54A\uC74C)",
|
|
18369
|
+
"decision.index-hint": "review\uC640 context\uC5D0 \uBC18\uC601\uD558\uB824\uBA74 duoctl index\uB97C \uC2E4\uD589\uD558\uC138\uC694.",
|
|
18370
|
+
"decision.truth-errors": "\uBAA9\uB85D\uC5D0 \uC5C6\uC74C: Truth \uC624\uB958 {n}\uAC1C. \uC544\uB798 \uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC73C\uBA70, \uACE0\uCE60 \uB54C\uAE4C\uC9C0 \uD655\uC815\uACFC \uAC70\uC808\uC740 \uC2E4\uD589\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4:",
|
|
17963
18371
|
"stats.none": "\uC9C0\uD45C\uAC00 \uC544\uC9C1 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
17964
18372
|
"not-initialized": "DUO project\uAC00 \uC544\uB2D9\uB2C8\uB2E4(.duo-project/project.yaml \uC5C6\uC74C). duoctl init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
|
|
17965
18373
|
"not-implemented": "'{command}'\uB294 \uC544\uC9C1 \uAD6C\uD604\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4({task}).",
|
|
@@ -18221,6 +18629,59 @@ function ambiguityHint(L, a) {
|
|
|
18221
18629
|
}
|
|
18222
18630
|
|
|
18223
18631
|
// apps/cli/dist/commands/decision.js
|
|
18632
|
+
var DECISION_ID2 = /^D-\d+$/u;
|
|
18633
|
+
var LABEL_WIDTH = 13;
|
|
18634
|
+
function show(L, value, nested = false) {
|
|
18635
|
+
if (value === void 0 || value === null)
|
|
18636
|
+
return t(L, "decision.preview.not-set");
|
|
18637
|
+
if (Array.isArray(value))
|
|
18638
|
+
return value.length === 0 ? t(L, "decision.preview.none") : value.map((v) => show(L, v, true)).join(", ");
|
|
18639
|
+
if (typeof value === "object") {
|
|
18640
|
+
const parts = Object.entries(value).filter(([, v]) => v !== void 0 && v !== null && !(Array.isArray(v) && v.length === 0)).map(([k, v]) => `${k}: ${show(L, v, true)}`);
|
|
18641
|
+
if (parts.length === 0)
|
|
18642
|
+
return t(L, "decision.preview.none");
|
|
18643
|
+
return nested ? `{${parts.join("; ")}}` : parts.join("; ");
|
|
18644
|
+
}
|
|
18645
|
+
return String(value).replace(/\r?\n/gu, `
|
|
18646
|
+
${" ".repeat(LABEL_WIDTH + 3)}`);
|
|
18647
|
+
}
|
|
18648
|
+
function hasForbids(forbids2) {
|
|
18649
|
+
return typeof forbids2 === "object" && forbids2 !== null && Object.values(forbids2).some((v) => Array.isArray(v) && v.length > 0);
|
|
18650
|
+
}
|
|
18651
|
+
function previewLines(L, p, op) {
|
|
18652
|
+
const c = p.candidate;
|
|
18653
|
+
const id3 = p.sourceId;
|
|
18654
|
+
const head = op === "reject" ? t(L, "decision.preview.reject", { id: id3 }) : p.action === "create" ? t(L, "decision.preview.create", { id: id3 }) : p.action === "add-lock" ? t(L, "decision.preview.lock", { id: id3 }) : t(L, "decision.preview.in-place", { id: id3 });
|
|
18655
|
+
const row = (label, value) => ` ${label.padEnd(LABEL_WIDTH)} ${value}`;
|
|
18656
|
+
const supersedes = c.supersedes === void 0 ? t(L, "decision.preview.not-set") : p.supersedes === void 0 ? `${show(L, c.supersedes)} (${t(L, "decision.preview.not-found")})` : `${p.supersedes.id} "${p.supersedes.title}" (${p.supersedes.state})${op === "confirm" ? ` \u2192 ${t(L, "decision.preview.becomes-superseded")}` : ""}`;
|
|
18657
|
+
const stale = p.sourceKind === "decision" ? t(L, "decision.preview.stale-na") : p.stale === void 0 ? t(L, "decision.preview.fresh") : t(L, "decision.preview.stale", { changed: p.stale.changedRefs.length > 0 ? ` (changed: ${p.stale.changedRefs.join(", ")})` : "" });
|
|
18658
|
+
const lines4 = [
|
|
18659
|
+
head,
|
|
18660
|
+
row("Title", show(L, c.title)),
|
|
18661
|
+
row("Question", show(L, c.question)),
|
|
18662
|
+
row("Answer", show(L, c.answer)),
|
|
18663
|
+
row("Kind", show(L, c.kind)),
|
|
18664
|
+
row("Rationale", show(L, c.rationale)),
|
|
18665
|
+
row("Governs", show(L, c.governs)),
|
|
18666
|
+
row("Forbids", show(L, c.forbids)),
|
|
18667
|
+
// T40 (N3): governs does not scope forbids; say so wherever forbids are set.
|
|
18668
|
+
...hasForbids(c.forbids) ? [row("Forbids scope", t(L, "decision.preview.forbids-scope"))] : [],
|
|
18669
|
+
row("Enforcement", show(L, c.enforcement)),
|
|
18670
|
+
row("Supersedes", supersedes),
|
|
18671
|
+
// H-71: the proposer as the proposal file records it (an audit label); a YAML Decision has none to show.
|
|
18672
|
+
...p.sourceKind === "proposal" ? [row("Proposed by", p.proposedBy === void 0 ? t(L, "decision.preview.not-set") : `${p.proposedBy}${p.proposedByKind === void 0 ? "" : ` (${p.proposedByKind})`}`)] : [],
|
|
18673
|
+
...c.evidence === void 0 ? [] : [row("Evidence", show(L, c.evidence))],
|
|
18674
|
+
...c.source === void 0 ? [] : [row("Source", show(L, c.source))],
|
|
18675
|
+
...c.extensions === void 0 ? [] : [row("Extensions", show(L, c.extensions))],
|
|
18676
|
+
row("Stale", stale)
|
|
18677
|
+
];
|
|
18678
|
+
if (op === "confirm") {
|
|
18679
|
+
const expected = p.action === "create" ? t(L, "decision.preview.expected", { id: p.expectedDecisionId }) : p.action === "add-lock" ? t(L, "decision.preview.lock-id", { id: p.expectedDecisionId }) : t(L, "decision.preview.in-place-id", { id: p.expectedDecisionId });
|
|
18680
|
+
lines4.push(row("Expected ID", expected));
|
|
18681
|
+
}
|
|
18682
|
+
lines4.push(row("File", p.sourcePath));
|
|
18683
|
+
return lines4;
|
|
18684
|
+
}
|
|
18224
18685
|
async function decisionCommand(env, sub, id3, reason) {
|
|
18225
18686
|
const project2 = requireProject(env, "decision");
|
|
18226
18687
|
if (project2.value === void 0)
|
|
@@ -18230,8 +18691,12 @@ async function decisionCommand(env, sub, id3, reason) {
|
|
|
18230
18691
|
if (sub === "list" || sub === void 0) {
|
|
18231
18692
|
const pending = entries.filter((e) => e.status === "pending");
|
|
18232
18693
|
const proposedDecisions = truth.decisions.filter((d) => d.state === "proposed");
|
|
18233
|
-
const
|
|
18234
|
-
|
|
18694
|
+
const unread = truthAuthorityErrors(project2.diagnostics);
|
|
18695
|
+
const human = [
|
|
18696
|
+
...pending.length + proposedDecisions.length === 0 ? [t(env.locale, "decision.none")] : [...pending.map((e) => `${e.id} ${e.proposal.title} (${e.proposal.question} = ${e.proposal.answer})`), ...proposedDecisions.map((d) => `${d.id} ${d.title} (proposed Decision)`)],
|
|
18697
|
+
...unread.length === 0 ? [] : [t(env.locale, "decision.truth-errors", { n: unread.length }), ...unread.map((d) => ` ${d.code} ${d.source?.path ?? ""}${d.source?.startLine === void 0 ? "" : `:${d.source.startLine}`} ${d.message}`)]
|
|
18698
|
+
];
|
|
18699
|
+
return { command: "decision", exitCode: EXIT.OK, diagnostics: unread, human, result: { pending: pending.map((e) => ({ id: e.id, title: e.proposal.title, question: e.proposal.question, answer: e.proposal.answer })), proposedDecisions: proposedDecisions.map((d) => ({ id: d.id, title: d.title })) } };
|
|
18235
18700
|
}
|
|
18236
18701
|
if (sub !== "confirm" && sub !== "reject")
|
|
18237
18702
|
return usage("decision", "usage: duoctl decision list | confirm <id> | reject <id> [--reason <text>]");
|
|
@@ -18240,27 +18705,31 @@ async function decisionCommand(env, sub, id3, reason) {
|
|
|
18240
18705
|
if (!env.io.isTTY || env.nonInteractive) {
|
|
18241
18706
|
return failed4("decision", EXIT.ERROR, [createDiagnostic("CLI_TTY_REQUIRED", t("en", "decision.tty"))], [t(env.locale, "decision.tty")]);
|
|
18242
18707
|
}
|
|
18243
|
-
const
|
|
18244
|
-
const
|
|
18245
|
-
|
|
18246
|
-
|
|
18247
|
-
|
|
18248
|
-
env.
|
|
18249
|
-
|
|
18250
|
-
if (shown.governs.requirements.length > 0)
|
|
18251
|
-
env.io.err(` governs ${shown.governs.requirements.join(", ")}`);
|
|
18708
|
+
const service = createDecisionService({ root: env.root, clock: () => env.io.now() });
|
|
18709
|
+
const previewed = sub === "reject" && DECISION_ID2.test(id3) ? void 0 : await service.previewConfirm(id3);
|
|
18710
|
+
if (previewed !== void 0) {
|
|
18711
|
+
if (previewed.value === void 0)
|
|
18712
|
+
return failed4("decision", EXIT.ERROR, previewed.diagnostics, diagLines(previewed.diagnostics), null, { status: "failed" });
|
|
18713
|
+
for (const line of previewLines(env.locale, previewed.value, sub))
|
|
18714
|
+
env.io.err(line);
|
|
18252
18715
|
}
|
|
18253
18716
|
const typed = (await env.io.prompt(t(env.locale, "decision.retype", { op: sub })))?.trim();
|
|
18254
18717
|
if (typed !== id3)
|
|
18255
18718
|
return failed4("decision", EXIT.ERROR, [], [t(env.locale, "decision.mismatch")]);
|
|
18256
|
-
const service = createDecisionService({ root: env.root, clock: () => env.io.now() });
|
|
18257
18719
|
const actor = await humanActor(env.root);
|
|
18258
18720
|
if (sub === "confirm") {
|
|
18259
|
-
const r2 = await service.confirm(actor, id3);
|
|
18721
|
+
const r2 = await service.confirm(actor, id3, previewed?.value === void 0 ? {} : { expectedDigest: previewed.value.digest });
|
|
18260
18722
|
if (r2.value === void 0)
|
|
18261
18723
|
return failed4("decision", EXIT.ERROR, r2.diagnostics, diagLines(r2.diagnostics), null, { status: "failed" });
|
|
18262
18724
|
const warnings = r2.diagnostics.filter((d) => d.severity === "warning").map((d) => `${d.code}: ${d.message}`);
|
|
18263
|
-
return {
|
|
18725
|
+
return {
|
|
18726
|
+
command: "decision",
|
|
18727
|
+
exitCode: EXIT.OK,
|
|
18728
|
+
diagnostics: r2.diagnostics,
|
|
18729
|
+
result: r2.value,
|
|
18730
|
+
human: [...warnings, t(env.locale, "decision.confirmed", { id: r2.value.decisionId, path: r2.value.path }), t(env.locale, "decision.index-hint")],
|
|
18731
|
+
metric: { status: "confirmed" }
|
|
18732
|
+
};
|
|
18264
18733
|
}
|
|
18265
18734
|
const r = await service.reject(actor, id3, reason);
|
|
18266
18735
|
if (r.value === void 0)
|
|
@@ -18782,7 +19251,7 @@ function renderReview(env, r) {
|
|
|
18782
19251
|
const ev = new Map(r.evidence.map((e) => [e.id, e]));
|
|
18783
19252
|
const shown = r.claims.filter((c) => env.verbose || c.alignment !== "ALIGNED");
|
|
18784
19253
|
for (const c of shown) {
|
|
18785
|
-
const tags = [c.blockEligible ? "blocking" : "", c.provenance
|
|
19254
|
+
const tags = [c.blockEligible ? "blocking" : "", c.provenance === void 0 ? "" : provenanceLabel(c.provenance, L), c.drift ? "drift" : ""].filter((x) => x !== "").join(", ");
|
|
18786
19255
|
out.push(` ${c.alignment.padEnd(9)} ${c.rule.padEnd(26)} ${c.subject.id} \xB7 ${c.reason}${tags === "" ? "" : ` [${tags}]`}`);
|
|
18787
19256
|
const pointers = c.evidenceIds.map((id3) => ev.get(id3)?.pointer).filter((p) => p?.path !== void 0).slice(0, 3).map((p) => `${p?.path ?? ""}${p?.lines === void 0 ? "" : `:${p.lines[0]}-${p.lines[1]}`}`);
|
|
18788
19257
|
if (pointers.length > 0)
|
|
@@ -18941,6 +19410,11 @@ async function statusCommand(env) {
|
|
|
18941
19410
|
const human = [
|
|
18942
19411
|
t(L, "status.project", { name: s.project.name, goal: s.project.vision, milestone: s.project.currentMilestone ?? "-" }),
|
|
18943
19412
|
t(L, "status.truth", { requirements: s.truth.requirements, decisions: s.truth.decisions, constraints: s.truth.constraints, gaps: s.truth.declaredGaps }),
|
|
19413
|
+
// T40 (N1): what the loader left out, so a lower Decision count is never silent.
|
|
19414
|
+
...(s.truth.errors ?? []).length === 0 ? [] : [
|
|
19415
|
+
t(L, "status.truth-errors", { n: s.truth.errors?.length ?? 0 }),
|
|
19416
|
+
...(s.truth.errors ?? []).map((e) => ` ${e.code} ${e.path ?? ""}${e.line === void 0 ? "" : `:${e.line}`} ${e.message}`)
|
|
19417
|
+
],
|
|
18944
19418
|
t(L, "status.index", { status: s.index?.status ?? "unknown", reason: s.index?.fullRebuildReason == null ? "" : ` (full rebuild: ${s.index.fullRebuildReason})` }),
|
|
18945
19419
|
...indexNeedsRun ? [t(L, "status.index-remediation")] : [],
|
|
18946
19420
|
...c === void 0 ? [] : [t(L, "status.changes", { files: c.files, analysis: c.analysisStale, modules: c.modulesToResolve, calls: c.callsMaybeRecomputed })],
|