truecourse 0.7.0-next.5 → 0.7.0-next.7
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/cli.mjs +281 -69
- package/package.json +1 -1
- package/public/assets/{index-CI8n7IHP.js → index-D7hFPhQQ.js} +203 -203
- package/public/index.html +1 -1
- package/roslyn-host/csharp-roslyn-host.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.pdb +0 -0
- package/server.mjs +207 -21
package/public/index.html
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
} catch (e) {}
|
|
21
21
|
})();
|
|
22
22
|
</script>
|
|
23
|
-
<script type="module" crossorigin src="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-D7hFPhQQ.js"></script>
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/index-CMCPeAwv.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body class="font-sans">
|
|
Binary file
|
|
Binary file
|
package/server.mjs
CHANGED
|
@@ -28409,6 +28409,153 @@ var init_guard = __esm({
|
|
|
28409
28409
|
}
|
|
28410
28410
|
});
|
|
28411
28411
|
|
|
28412
|
+
// packages/shared/dist/spec/overlap-resolution.js
|
|
28413
|
+
function coveringRelationInAreas(relations, a, b, areas) {
|
|
28414
|
+
return relations.find((r) => {
|
|
28415
|
+
const samePair = r.older === a && r.newer === b || r.older === b && r.newer === a;
|
|
28416
|
+
return samePair && (r.scope === void 0 || areas.includes(r.scope));
|
|
28417
|
+
});
|
|
28418
|
+
}
|
|
28419
|
+
function dedupeCrossAreaOverlaps(entries) {
|
|
28420
|
+
const byPair = /* @__PURE__ */ new Map();
|
|
28421
|
+
for (const e of entries) {
|
|
28422
|
+
const key4 = unorderedPairKey(e.overlap.docs[0], e.overlap.docs[1]);
|
|
28423
|
+
const list = byPair.get(key4);
|
|
28424
|
+
if (list)
|
|
28425
|
+
list.push(e);
|
|
28426
|
+
else
|
|
28427
|
+
byPair.set(key4, [e]);
|
|
28428
|
+
}
|
|
28429
|
+
const merged = [];
|
|
28430
|
+
for (const members of byPair.values()) {
|
|
28431
|
+
const parent = members.map((_, i) => i);
|
|
28432
|
+
const find = (i) => {
|
|
28433
|
+
while (parent[i] !== i) {
|
|
28434
|
+
parent[i] = parent[parent[i]];
|
|
28435
|
+
i = parent[i];
|
|
28436
|
+
}
|
|
28437
|
+
return i;
|
|
28438
|
+
};
|
|
28439
|
+
const union = (i, j) => {
|
|
28440
|
+
const ri = find(i);
|
|
28441
|
+
const rj = find(j);
|
|
28442
|
+
if (ri !== rj)
|
|
28443
|
+
parent[Math.max(ri, rj)] = Math.min(ri, rj);
|
|
28444
|
+
};
|
|
28445
|
+
const ptrOwner = /* @__PURE__ */ new Map();
|
|
28446
|
+
members.forEach((m, i) => {
|
|
28447
|
+
for (const ptr of sectionPointerKeys(m.overlap)) {
|
|
28448
|
+
const owner = ptrOwner.get(ptr);
|
|
28449
|
+
if (owner === void 0)
|
|
28450
|
+
ptrOwner.set(ptr, i);
|
|
28451
|
+
else
|
|
28452
|
+
union(owner, i);
|
|
28453
|
+
}
|
|
28454
|
+
});
|
|
28455
|
+
const components = /* @__PURE__ */ new Map();
|
|
28456
|
+
members.forEach((_, i) => {
|
|
28457
|
+
const root = find(i);
|
|
28458
|
+
const list = components.get(root);
|
|
28459
|
+
if (list)
|
|
28460
|
+
list.push(i);
|
|
28461
|
+
else
|
|
28462
|
+
components.set(root, [i]);
|
|
28463
|
+
});
|
|
28464
|
+
for (const idxs of components.values()) {
|
|
28465
|
+
const group = idxs.map((i) => members[i]);
|
|
28466
|
+
const rep = [...group].sort((x, y) => {
|
|
28467
|
+
const px = preambleCount(x.overlap);
|
|
28468
|
+
const py = preambleCount(y.overlap);
|
|
28469
|
+
if (px !== py)
|
|
28470
|
+
return px - py;
|
|
28471
|
+
if (x.area !== y.area)
|
|
28472
|
+
return x.area < y.area ? -1 : 1;
|
|
28473
|
+
return (x.overlap.note ?? "") < (y.overlap.note ?? "") ? -1 : 1;
|
|
28474
|
+
})[0];
|
|
28475
|
+
const span = /* @__PURE__ */ new Set();
|
|
28476
|
+
for (const g of group) {
|
|
28477
|
+
span.add(g.area);
|
|
28478
|
+
for (const a of g.overlap.areas ?? [])
|
|
28479
|
+
span.add(a);
|
|
28480
|
+
}
|
|
28481
|
+
merged.push({ area: rep.area, areas: [...span].sort(), overlap: rep.overlap });
|
|
28482
|
+
}
|
|
28483
|
+
}
|
|
28484
|
+
merged.sort((x, y) => {
|
|
28485
|
+
if (x.area !== y.area)
|
|
28486
|
+
return x.area < y.area ? -1 : 1;
|
|
28487
|
+
return unorderedPairKey(x.overlap.docs[0], x.overlap.docs[1]) < unorderedPairKey(y.overlap.docs[0], y.overlap.docs[1]) ? -1 : 1;
|
|
28488
|
+
});
|
|
28489
|
+
return merged;
|
|
28490
|
+
}
|
|
28491
|
+
function buildCorpusConflicts(corpus, decisions) {
|
|
28492
|
+
const userRelations = decisions.relations ?? [];
|
|
28493
|
+
const effectiveRels = [...corpus.relations, ...userRelations];
|
|
28494
|
+
const excludes = new Set(decisions.manualExcludes ?? []);
|
|
28495
|
+
const entries = [];
|
|
28496
|
+
for (const area of corpus.areas)
|
|
28497
|
+
for (const ov of area.overlaps)
|
|
28498
|
+
entries.push({ area: area.id, overlap: ov });
|
|
28499
|
+
const mergedOverlaps = dedupeCrossAreaOverlaps(entries);
|
|
28500
|
+
const flagged = [];
|
|
28501
|
+
for (const m of mergedOverlaps) {
|
|
28502
|
+
const [a, b] = m.overlap.docs;
|
|
28503
|
+
const relation = coveringRelationInAreas(effectiveRels, a, b, m.areas);
|
|
28504
|
+
const userRelation = coveringRelationInAreas(userRelations, a, b, m.areas);
|
|
28505
|
+
const excludedRef = excludes.has(a) ? a : excludes.has(b) ? b : void 0;
|
|
28506
|
+
flagged.push({
|
|
28507
|
+
area: m.area,
|
|
28508
|
+
areas: m.areas,
|
|
28509
|
+
a,
|
|
28510
|
+
b,
|
|
28511
|
+
note: m.overlap.note ?? "",
|
|
28512
|
+
resolved: relation !== void 0 || excludedRef !== void 0,
|
|
28513
|
+
synthesized: false,
|
|
28514
|
+
...relation ? { relation } : {},
|
|
28515
|
+
...userRelation ? { userRelation } : {},
|
|
28516
|
+
...excludedRef ? { excludedRef } : {}
|
|
28517
|
+
});
|
|
28518
|
+
}
|
|
28519
|
+
const coversFlagged = (r) => flagged.some((o) => {
|
|
28520
|
+
const samePair = o.a === r.older && o.b === r.newer || o.a === r.newer && o.b === r.older;
|
|
28521
|
+
return samePair && (r.scope === void 0 || o.areas.includes(r.scope));
|
|
28522
|
+
});
|
|
28523
|
+
const synthesized = userRelations.filter((r) => !coversFlagged(r)).map((r) => ({
|
|
28524
|
+
area: r.scope ?? "",
|
|
28525
|
+
areas: r.scope ? [r.scope] : [],
|
|
28526
|
+
a: r.older,
|
|
28527
|
+
b: r.newer,
|
|
28528
|
+
note: r.note ?? "",
|
|
28529
|
+
resolved: true,
|
|
28530
|
+
synthesized: true,
|
|
28531
|
+
relation: r,
|
|
28532
|
+
userRelation: r
|
|
28533
|
+
}));
|
|
28534
|
+
return [...flagged, ...synthesized];
|
|
28535
|
+
}
|
|
28536
|
+
function openConflicts(corpus, decisions) {
|
|
28537
|
+
return buildCorpusConflicts(corpus, decisions).filter((c) => !c.resolved);
|
|
28538
|
+
}
|
|
28539
|
+
var PREAMBLE_PTR, NUL, unorderedPairKey, sectionPointerKeys, preambleCount;
|
|
28540
|
+
var init_overlap_resolution = __esm({
|
|
28541
|
+
"packages/shared/dist/spec/overlap-resolution.js"() {
|
|
28542
|
+
"use strict";
|
|
28543
|
+
PREAMBLE_PTR = "\0preamble";
|
|
28544
|
+
NUL = "\0";
|
|
28545
|
+
unorderedPairKey = (a, b) => a < b ? `${a}${NUL}${b}` : `${b}${NUL}${a}`;
|
|
28546
|
+
sectionPointerKeys = (ov) => (ov.sections ?? []).map((s) => `${s.doc}${NUL}${s.heading ?? PREAMBLE_PTR}`);
|
|
28547
|
+
preambleCount = (ov) => (ov.sections ?? []).filter((s) => s.heading === null || s.heading === void 0).length;
|
|
28548
|
+
}
|
|
28549
|
+
});
|
|
28550
|
+
|
|
28551
|
+
// packages/shared/dist/spec/index.js
|
|
28552
|
+
var init_spec = __esm({
|
|
28553
|
+
"packages/shared/dist/spec/index.js"() {
|
|
28554
|
+
"use strict";
|
|
28555
|
+
init_overlap_resolution();
|
|
28556
|
+
}
|
|
28557
|
+
});
|
|
28558
|
+
|
|
28412
28559
|
// node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
|
|
28413
28560
|
var require_ignore = __commonJS({
|
|
28414
28561
|
"node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js"(exports, module) {
|
|
@@ -28999,6 +29146,7 @@ var init_dist = __esm({
|
|
|
28999
29146
|
init_types2();
|
|
29000
29147
|
init_schemas();
|
|
29001
29148
|
init_guard();
|
|
29149
|
+
init_spec();
|
|
29002
29150
|
init_tcignore();
|
|
29003
29151
|
init_skip_dirs();
|
|
29004
29152
|
init_spec_scope();
|
|
@@ -197014,7 +197162,16 @@ var OverlapSchema = external_exports.object({
|
|
|
197014
197162
|
/** Short note on what may disagree ("auth0_id vs auth0_sub"). */
|
|
197015
197163
|
note: external_exports.string().default(""),
|
|
197016
197164
|
/** The specific conflicting sections per doc (markdown headings), when known. */
|
|
197017
|
-
sections: external_exports.array(OverlapSectionSchema).default([])
|
|
197165
|
+
sections: external_exports.array(OverlapSectionSchema).default([]),
|
|
197166
|
+
/**
|
|
197167
|
+
* The area ids this dispute spans. Detection runs per area, so one disagreement
|
|
197168
|
+
* on a doc pair sharing several areas is flagged in each; the cross-area merge
|
|
197169
|
+
* collapses those to this single record and lists every area it spanned here, so
|
|
197170
|
+
* a resolution scoped to any of them clears the dispute everywhere. Empty for
|
|
197171
|
+
* older corpora written before the merge — the read layer recomputes the span
|
|
197172
|
+
* from the per-area placement of the (then-duplicated) records.
|
|
197173
|
+
*/
|
|
197174
|
+
areas: external_exports.array(external_exports.string()).default([])
|
|
197018
197175
|
});
|
|
197019
197176
|
var AreaSchema = external_exports.object({
|
|
197020
197177
|
/** Canonical area id, `product/concern`. */
|
|
@@ -197789,6 +197946,7 @@ init_zod();
|
|
|
197789
197946
|
import { createHash as createHash6 } from "node:crypto";
|
|
197790
197947
|
import fs28 from "node:fs";
|
|
197791
197948
|
init_transport();
|
|
197949
|
+
init_dist();
|
|
197792
197950
|
var DEFAULT_MAX_PAIRS_PER_AREA = 60;
|
|
197793
197951
|
async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
197794
197952
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -197860,7 +198018,7 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197860
198018
|
examineOne(repoRoot, pair.areaId, pair.a, pair.b, runner2).then((verdict) => {
|
|
197861
198019
|
if (verdict.overlap) {
|
|
197862
198020
|
const list = result.get(pair.areaId) ?? [];
|
|
197863
|
-
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [] });
|
|
198021
|
+
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [], areas: [] });
|
|
197864
198022
|
result.set(pair.areaId, list);
|
|
197865
198023
|
}
|
|
197866
198024
|
}).catch(() => {
|
|
@@ -197878,19 +198036,13 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197878
198036
|
};
|
|
197879
198037
|
launch();
|
|
197880
198038
|
});
|
|
197881
|
-
const
|
|
197882
|
-
|
|
197883
|
-
|
|
197884
|
-
|
|
197885
|
-
|
|
197886
|
-
|
|
197887
|
-
|
|
197888
|
-
return true;
|
|
197889
|
-
});
|
|
197890
|
-
if (kept.length === 0)
|
|
197891
|
-
result.delete(areaId);
|
|
197892
|
-
else
|
|
197893
|
-
result.set(areaId, kept);
|
|
198039
|
+
const entries = [...result].flatMap(([area, list]) => list.map((overlap) => ({ area, overlap })));
|
|
198040
|
+
const merged = dedupeCrossAreaOverlaps(entries);
|
|
198041
|
+
result.clear();
|
|
198042
|
+
for (const m of merged) {
|
|
198043
|
+
const list = result.get(m.area) ?? [];
|
|
198044
|
+
list.push({ ...m.overlap, areas: m.areas });
|
|
198045
|
+
result.set(m.area, list);
|
|
197894
198046
|
}
|
|
197895
198047
|
for (const [areaId, list] of result) {
|
|
197896
198048
|
list.sort((x, y) => x.docs.join() < y.docs.join() ? -1 : 1);
|
|
@@ -197898,11 +198050,6 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197898
198050
|
}
|
|
197899
198051
|
return result;
|
|
197900
198052
|
}
|
|
197901
|
-
function overlapDedupKey(ov) {
|
|
197902
|
-
const docs = [...ov.docs].sort().join("::");
|
|
197903
|
-
const secs = (ov.sections ?? []).map((s) => `${s.doc}|${s.heading}`).sort().join("::");
|
|
197904
|
-
return `${docs}##${secs}`;
|
|
197905
|
-
}
|
|
197906
198053
|
async function examineOne(repoRoot, areaId, a, b, runner2) {
|
|
197907
198054
|
const cacheKey = computeCacheKey4(areaId, a, b);
|
|
197908
198055
|
const cached = await readCache3(repoRoot, cacheKey);
|
|
@@ -232294,7 +232441,7 @@ function readToolVersion() {
|
|
|
232294
232441
|
if (cachedVersion)
|
|
232295
232442
|
return cachedVersion;
|
|
232296
232443
|
if (true) {
|
|
232297
|
-
cachedVersion = "0.7.0-next.
|
|
232444
|
+
cachedVersion = "0.7.0-next.7";
|
|
232298
232445
|
return cachedVersion;
|
|
232299
232446
|
}
|
|
232300
232447
|
try {
|
|
@@ -239986,7 +240133,41 @@ var guard_default = router13;
|
|
|
239986
240133
|
var import_express14 = __toESM(require_express2(), 1);
|
|
239987
240134
|
|
|
239988
240135
|
// packages/core/dist/commands/guard-in-process.js
|
|
240136
|
+
init_dist();
|
|
239989
240137
|
init_transport();
|
|
240138
|
+
var OpenConflictsError = class extends Error {
|
|
240139
|
+
conflicts;
|
|
240140
|
+
constructor(conflicts2) {
|
|
240141
|
+
super(formatOpenConflictsMessage(conflicts2));
|
|
240142
|
+
this.conflicts = conflicts2;
|
|
240143
|
+
this.name = "OpenConflictsError";
|
|
240144
|
+
}
|
|
240145
|
+
};
|
|
240146
|
+
function formatOpenConflictsMessage(conflicts2) {
|
|
240147
|
+
const lines = [
|
|
240148
|
+
`${conflicts2.length} open spec conflict${conflicts2.length === 1 ? "" : "s"} must be resolved before guard generate.`,
|
|
240149
|
+
"Extracting both sides of an unresolved overlap births a red finding that is really the dispute.",
|
|
240150
|
+
""
|
|
240151
|
+
];
|
|
240152
|
+
for (const c of conflicts2) {
|
|
240153
|
+
lines.push(` ${c.area}`);
|
|
240154
|
+
lines.push(` ${c.a} \u2194 ${c.b}`);
|
|
240155
|
+
if (c.note)
|
|
240156
|
+
lines.push(` ${c.note}`);
|
|
240157
|
+
}
|
|
240158
|
+
lines.push("");
|
|
240159
|
+
lines.push("Resolve them with `truecourse spec conflicts list` (or the dashboard Conflicts group), then re-run `truecourse guard generate`.");
|
|
240160
|
+
return lines.join("\n");
|
|
240161
|
+
}
|
|
240162
|
+
async function assertNoOpenConflicts(repoRoot) {
|
|
240163
|
+
const corpus = await getCorpus(repoRoot);
|
|
240164
|
+
if (!corpus)
|
|
240165
|
+
return;
|
|
240166
|
+
const decisions = await getDecisions(repoRoot);
|
|
240167
|
+
const open = openConflicts(corpus, decisions);
|
|
240168
|
+
if (open.length > 0)
|
|
240169
|
+
throw new OpenConflictsError(open);
|
|
240170
|
+
}
|
|
239990
240171
|
var GUARD_GENERATE_STEPS = [
|
|
239991
240172
|
{ key: "index", label: "Indexing sections" },
|
|
239992
240173
|
{ key: "extract", label: "Extracting claims" },
|
|
@@ -240022,6 +240203,7 @@ function resolveTransport2(options) {
|
|
|
240022
240203
|
}
|
|
240023
240204
|
async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
240024
240205
|
const { tracker } = options;
|
|
240206
|
+
await assertNoOpenConflicts(repoRoot);
|
|
240025
240207
|
if (options.onLlmEstimate) {
|
|
240026
240208
|
const prices = await getModelPrices();
|
|
240027
240209
|
const estimate = await estimateGuardTokens(repoRoot, prices);
|
|
@@ -240270,6 +240452,10 @@ router14.post("/:id/guard/generate", async (req, res, next) => {
|
|
|
240270
240452
|
res.json({ cancelled: true });
|
|
240271
240453
|
return;
|
|
240272
240454
|
}
|
|
240455
|
+
if (e instanceof OpenConflictsError) {
|
|
240456
|
+
res.status(422).json({ error: e.message });
|
|
240457
|
+
return;
|
|
240458
|
+
}
|
|
240273
240459
|
emitSpecProgress(repoId, { step: "error", percent: 100, detail: e.message });
|
|
240274
240460
|
next(e);
|
|
240275
240461
|
} finally {
|