@jphutchins/code-review 0.1.0-alpha.47 → 0.1.0-alpha.48
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/index.js +212 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema/VERSIONING.md +2 -1
- package/schema/findings.schema.json +34 -0
- package/schema/prices.example.json +8 -2
- package/schema/prices.schema.json +77 -24
- package/templates/comment.eta +12 -0
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ var LineNumber = t.refinement(
|
|
|
33
33
|
var Confidence = t.refinement(t.number, (n) => n >= 0 && n <= 1, "Confidence");
|
|
34
34
|
var Likelihood = t.refinement(t.number, (n) => n >= 0 && n <= 1, "Likelihood");
|
|
35
35
|
var FiniteNumber = t.refinement(t.number, (n) => Number.isFinite(n), "FiniteNumber");
|
|
36
|
+
var NonNegativePrice = t.refinement(t.number, (n) => n >= 0, "NonNegativePrice");
|
|
36
37
|
var SCHEMA_VERSION_RE = /^(0|[1-9]\d*)\.(\d+)\.(\d+)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
37
38
|
var SchemaVersion = t.refinement(
|
|
38
39
|
t.string,
|
|
@@ -170,6 +171,15 @@ var ConvergenceStrict = t.refinement(
|
|
|
170
171
|
"ConvergenceStrict"
|
|
171
172
|
);
|
|
172
173
|
var ConvergenceCodec = t.exact(ConvergenceStrict);
|
|
174
|
+
var LineDelta = t.refinement(
|
|
175
|
+
t.number,
|
|
176
|
+
(n) => Number.isInteger(n) && n >= 0,
|
|
177
|
+
"LineDelta"
|
|
178
|
+
);
|
|
179
|
+
var ChangeLinesCodec = t.exact(t.type({ added: LineDelta, removed: LineDelta }));
|
|
180
|
+
var ChangeSizeCodec = t.exact(
|
|
181
|
+
t.partial({ code: ChangeLinesCodec, tests: ChangeLinesCodec, docs: ChangeLinesCodec })
|
|
182
|
+
);
|
|
173
183
|
var FindingsCodec = t.exact(
|
|
174
184
|
t.intersection([
|
|
175
185
|
t.type({
|
|
@@ -183,10 +193,20 @@ var FindingsCodec = t.exact(
|
|
|
183
193
|
// Pipeline-stamped only (issue #150); see ScopeMetastasisCodec.
|
|
184
194
|
scope_metastasis: ScopeMetastasisCodec,
|
|
185
195
|
// Pipeline-stamped only (issue #174); see ConvergenceCodec.
|
|
186
|
-
convergence: ConvergenceCodec
|
|
196
|
+
convergence: ConvergenceCodec,
|
|
197
|
+
// Agent-written best-effort (issue #182); see ChangeSizeCodec.
|
|
198
|
+
change_size: ChangeSizeCodec
|
|
187
199
|
})
|
|
188
200
|
])
|
|
189
201
|
);
|
|
202
|
+
var PIPELINE_STAMPED_FIELDS = /* @__PURE__ */ new Set([
|
|
203
|
+
"convergence",
|
|
204
|
+
"scope_metastasis"
|
|
205
|
+
]);
|
|
206
|
+
var RECOVERABLE_OPTIONAL_FIELDS = /* @__PURE__ */ new Set([
|
|
207
|
+
...PIPELINE_STAMPED_FIELDS,
|
|
208
|
+
"change_size"
|
|
209
|
+
]);
|
|
190
210
|
var TriageCodec = t.type({
|
|
191
211
|
safe: t.boolean,
|
|
192
212
|
reasons: t.string
|
|
@@ -219,6 +239,11 @@ var ResultEnvelopeCodec = t.intersection([
|
|
|
219
239
|
vendor_cost_usd: t.union([t.number, t.null]),
|
|
220
240
|
route: t.string,
|
|
221
241
|
effort: t.string,
|
|
242
|
+
// The ISO-8601 UTC instant the run completed (stamped by `adapt` when it built this envelope, issue
|
|
243
|
+
// #170). Cost recomputation prices a time-slotted model at THIS instant, so the same envelope prices
|
|
244
|
+
// to the same slot deterministically wherever it is re-rendered — not at each command's wall clock.
|
|
245
|
+
// Absent on a pre-#170 envelope (cost then falls back to the caller's instant).
|
|
246
|
+
generated_at: t.string,
|
|
222
247
|
// The run produced a notice rather than a completed review (security-gate block, agent kill, no
|
|
223
248
|
// recoverable findings). An empty `findings` array alone can't say this — a genuine clean review
|
|
224
249
|
// is also empty — so the render suppresses "clean review" and the sticky precedence guard refuses
|
|
@@ -226,12 +251,44 @@ var ResultEnvelopeCodec = t.intersection([
|
|
|
226
251
|
incomplete: t.boolean
|
|
227
252
|
})
|
|
228
253
|
]);
|
|
229
|
-
var
|
|
230
|
-
in:
|
|
231
|
-
out:
|
|
232
|
-
cache_read:
|
|
233
|
-
cache_write:
|
|
254
|
+
var FlatModelPricesShape = t.type({
|
|
255
|
+
in: NonNegativePrice,
|
|
256
|
+
out: NonNegativePrice,
|
|
257
|
+
cache_read: NonNegativePrice,
|
|
258
|
+
cache_write: NonNegativePrice
|
|
234
259
|
});
|
|
260
|
+
var FLAT_PRICE_KEYS = new Set(Object.keys(FlatModelPricesShape.props));
|
|
261
|
+
var FlatModelPricesStrict = t.refinement(
|
|
262
|
+
FlatModelPricesShape,
|
|
263
|
+
(p) => Object.keys(p).every((k) => FLAT_PRICE_KEYS.has(k)),
|
|
264
|
+
"FlatModelPricesStrict"
|
|
265
|
+
);
|
|
266
|
+
var FlatModelPricesCodec = t.exact(FlatModelPricesStrict);
|
|
267
|
+
var UTC_HHMM_RE = /^([01][0-9]|2[0-3]):[0-5][0-9]$/;
|
|
268
|
+
var UtcHHMM = t.refinement(t.string, (s) => UTC_HHMM_RE.test(s), "UtcHHMM");
|
|
269
|
+
var PriceSlotShape = t.intersection([
|
|
270
|
+
t.type({ utc_from: UtcHHMM, utc_to: UtcHHMM }),
|
|
271
|
+
FlatModelPricesShape
|
|
272
|
+
]);
|
|
273
|
+
var PRICE_SLOT_KEYS = /* @__PURE__ */ new Set(["utc_from", "utc_to", ...Object.keys(FlatModelPricesShape.props)]);
|
|
274
|
+
var PriceSlotStrict = t.refinement(
|
|
275
|
+
PriceSlotShape,
|
|
276
|
+
(s) => Object.keys(s).every((k) => PRICE_SLOT_KEYS.has(k)),
|
|
277
|
+
"PriceSlotStrict"
|
|
278
|
+
);
|
|
279
|
+
var PriceSlotCodec = t.exact(PriceSlotStrict);
|
|
280
|
+
var NonEmptyPriceSlots = t.refinement(
|
|
281
|
+
t.array(PriceSlotCodec),
|
|
282
|
+
(a) => a.length >= 1,
|
|
283
|
+
"NonEmptyPriceSlots"
|
|
284
|
+
);
|
|
285
|
+
var SlottedModelPricesStrict = t.refinement(
|
|
286
|
+
t.type({ slots: NonEmptyPriceSlots }),
|
|
287
|
+
(s) => Object.keys(s).every((k) => k === "slots"),
|
|
288
|
+
"SlottedModelPricesStrict"
|
|
289
|
+
);
|
|
290
|
+
var SlottedModelPricesCodec = t.exact(SlottedModelPricesStrict);
|
|
291
|
+
var ModelPricesCodec = t.union([FlatModelPricesCodec, SlottedModelPricesCodec]);
|
|
235
292
|
var PriceMapCodec = t.type({
|
|
236
293
|
_updated: t.string,
|
|
237
294
|
_unit: t.string,
|
|
@@ -265,35 +322,64 @@ var defaultWarn = (message) => {
|
|
|
265
322
|
process.stderr.write(`${message}
|
|
266
323
|
`);
|
|
267
324
|
};
|
|
268
|
-
var
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
325
|
+
var parseInstant = (iso) => {
|
|
326
|
+
if (iso === void 0) return void 0;
|
|
327
|
+
if (iso.includes("T") && !/(Z|[+-]\d{2}:?\d{2})$/.test(iso)) return void 0;
|
|
328
|
+
const d = new Date(iso);
|
|
329
|
+
return Number.isNaN(d.getTime()) ? void 0 : d;
|
|
330
|
+
};
|
|
331
|
+
var utcMinuteOfDay = (at) => at.getUTCHours() * 60 + at.getUTCMinutes();
|
|
332
|
+
var hhmmToMinutes = (hhmm) => {
|
|
333
|
+
const [h, m] = hhmm.split(":");
|
|
334
|
+
return Number(h) * 60 + Number(m);
|
|
335
|
+
};
|
|
336
|
+
var hhmmOf = (minute) => `${String(Math.floor(minute / 60)).padStart(2, "0")}:${String(minute % 60).padStart(2, "0")}`;
|
|
337
|
+
var slotCovers = (slot, minute) => {
|
|
338
|
+
const from = hhmmToMinutes(slot.utc_from);
|
|
339
|
+
const to = hhmmToMinutes(slot.utc_to);
|
|
340
|
+
return from < to ? minute >= from && minute < to : minute >= from || minute < to;
|
|
341
|
+
};
|
|
342
|
+
var resolveFlatPrices = (model, p, pricedAt, warn) => {
|
|
343
|
+
if (!("slots" in p)) return p;
|
|
344
|
+
if (pricedAt === void 0) {
|
|
273
345
|
warn(
|
|
274
|
-
`code-review cost:
|
|
346
|
+
`code-review cost: model "${model}" has time-slotted prices but no run instant was supplied to select a slot; cost for this model set to $0`
|
|
275
347
|
);
|
|
276
|
-
return
|
|
277
|
-
model: entry.model,
|
|
278
|
-
inputTokens: entry.input_tokens,
|
|
279
|
-
outputTokens: entry.output_tokens,
|
|
280
|
-
cacheReadTokens: cacheRead,
|
|
281
|
-
cacheWriteTokens: cacheWrite,
|
|
282
|
-
costUSD: 0
|
|
283
|
-
};
|
|
348
|
+
return null;
|
|
284
349
|
}
|
|
285
|
-
const
|
|
286
|
-
|
|
350
|
+
const minute = utcMinuteOfDay(pricedAt);
|
|
351
|
+
const covering = p.slots.filter((s) => slotCovers(s, minute));
|
|
352
|
+
if (covering.length === 1) return covering[0] ?? null;
|
|
353
|
+
warn(
|
|
354
|
+
`code-review cost: model "${model}" \u2014 ${String(covering.length)} price slots cover ${hhmmOf(minute)} UTC (expected exactly 1); a model's slots must partition the 24h day with no gap or overlap; cost for this model set to $0`
|
|
355
|
+
);
|
|
356
|
+
return null;
|
|
357
|
+
};
|
|
358
|
+
var computeModelCost = (entry, prices, pricedAt, warn) => {
|
|
359
|
+
const p = prices.models[entry.model];
|
|
360
|
+
const cacheRead = entry.cache_read_tokens ?? 0;
|
|
361
|
+
const cacheWrite = entry.cache_write_tokens ?? 0;
|
|
362
|
+
const zero = {
|
|
287
363
|
model: entry.model,
|
|
288
364
|
inputTokens: entry.input_tokens,
|
|
289
365
|
outputTokens: entry.output_tokens,
|
|
290
366
|
cacheReadTokens: cacheRead,
|
|
291
367
|
cacheWriteTokens: cacheWrite,
|
|
292
|
-
costUSD
|
|
368
|
+
costUSD: 0
|
|
293
369
|
};
|
|
370
|
+
if (!p) {
|
|
371
|
+
warn(
|
|
372
|
+
`code-review cost: unknown model "${entry.model}" \u2014 no entry in price map; cost for this model set to $0`
|
|
373
|
+
);
|
|
374
|
+
return zero;
|
|
375
|
+
}
|
|
376
|
+
const rate = resolveFlatPrices(entry.model, p, pricedAt, warn);
|
|
377
|
+
if (rate === null) return zero;
|
|
378
|
+
const costUSD = (entry.input_tokens * rate.in + entry.output_tokens * rate.out + cacheRead * rate.cache_read + cacheWrite * rate.cache_write) / 1e6;
|
|
379
|
+
return { ...zero, costUSD };
|
|
294
380
|
};
|
|
295
|
-
var computeCost = (models, prices, warn = defaultWarn) => {
|
|
296
|
-
const lines = models.map((entry) => computeModelCost(entry, prices, warn));
|
|
381
|
+
var computeCost = (models, prices, pricedAt, warn = defaultWarn) => {
|
|
382
|
+
const lines = models.map((entry) => computeModelCost(entry, prices, pricedAt, warn));
|
|
297
383
|
return {
|
|
298
384
|
lines,
|
|
299
385
|
totalInputTokens: lines.reduce((s, l) => s + l.inputTokens, 0),
|
|
@@ -660,6 +746,15 @@ var priorBelowFloorNits = (priorDoc, floor = DEFAULT_NIT_VISIBILITY_FLOOR) => {
|
|
|
660
746
|
};
|
|
661
747
|
var convergenceBadge = (c) => c.converged ? `**Convergence** \u{1F3C1} ${formatScore(c.score)} \u2264 ${String(c.threshold)} \u2014 converged` : `**Convergence** \u{1F504} ${formatScore(c.score)} > ${String(c.threshold)} \u2014 iterating`;
|
|
662
748
|
var convergenceSummary = (doc, threshold = DEFAULT_CONVERGENCE_THRESHOLD) => convergenceBadge(convergenceSignal(doc, threshold));
|
|
749
|
+
var changeSizeSummary = (changeSize) => {
|
|
750
|
+
if (changeSize === void 0) return "";
|
|
751
|
+
const cell = (label, d) => d === void 0 ? null : `+${String(d.added)} / \u2212${String(d.removed)} ${label}`;
|
|
752
|
+
return [
|
|
753
|
+
cell("code", changeSize.code),
|
|
754
|
+
cell("tests", changeSize.tests),
|
|
755
|
+
cell("docs", changeSize.docs)
|
|
756
|
+
].filter((c) => c !== null).join(" \xB7 ");
|
|
757
|
+
};
|
|
663
758
|
var nextRoundNumber = (priorTraj, priorConvRounds) => {
|
|
664
759
|
const last = (rounds) => rounds.length > 0 ? rounds[rounds.length - 1]?.round ?? rounds.length : 0;
|
|
665
760
|
return Math.max(last(priorTraj), last(priorConvRounds)) + 1;
|
|
@@ -1133,7 +1228,8 @@ var render = (input) => {
|
|
|
1133
1228
|
const usageAvailable = input.envelope !== null;
|
|
1134
1229
|
const hasUsage = input.envelope !== null && input.envelope.models.length > 0;
|
|
1135
1230
|
const incomplete = (input.incomplete ?? input.envelope?.incomplete ?? false) || isIncompleteFindings(input.findings);
|
|
1136
|
-
const
|
|
1231
|
+
const pricedAt = parseInstant(input.envelope?.generated_at) ?? input.pricedAt;
|
|
1232
|
+
const costReport = input.envelope ? computeCost(input.envelope.models, input.prices, pricedAt) : null;
|
|
1137
1233
|
const pricesProvided = input.pricesProvided ?? true;
|
|
1138
1234
|
const route = input.route ?? input.envelope?.route ?? null;
|
|
1139
1235
|
const effort = input.effort ?? input.envelope?.effort ?? null;
|
|
@@ -1156,6 +1252,10 @@ var render = (input) => {
|
|
|
1156
1252
|
effort,
|
|
1157
1253
|
modelNames,
|
|
1158
1254
|
testReport: input.testReport ?? null,
|
|
1255
|
+
// The agent's best-effort role split (issue #182) and the pipeline's deterministic cloc table; both
|
|
1256
|
+
// chrome, gated to a completed review by the template's !incomplete block.
|
|
1257
|
+
changeSummary: changeSizeSummary(input.findings.change_size),
|
|
1258
|
+
clocDiff: input.clocDiff !== void 0 ? escapeFence(input.clocDiff) : null,
|
|
1159
1259
|
reviewedSha: input.reviewedSha ?? "0000000000000000000000000000000000000000",
|
|
1160
1260
|
postedAt: input.postedAt ?? "",
|
|
1161
1261
|
severityCounts,
|
|
@@ -1778,11 +1878,24 @@ var triageTable = [
|
|
|
1778
1878
|
];
|
|
1779
1879
|
var pricesTable = [
|
|
1780
1880
|
{
|
|
1881
|
+
// Retained non-latest so `validate`/`print-schema --schema-version 0.1` still resolves (the
|
|
1882
|
+
// keep-old-minors convention the findings table follows). The price map carries no version signal,
|
|
1883
|
+
// and the schema is a single unversioned file, so both entries point at it; the codec's oneOf
|
|
1884
|
+
// accepts a flat 0.1-era map unchanged.
|
|
1781
1885
|
minor: "0.1",
|
|
1782
1886
|
defaultVersion: "0.1.0",
|
|
1783
1887
|
schemaFile: "prices.schema.json",
|
|
1784
1888
|
codec: PriceMapCodec,
|
|
1785
1889
|
normalize: identity,
|
|
1890
|
+
latest: false
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
// v0.2.0 (issue #170): a model's value gained the time-slotted alternative (flat | { slots }).
|
|
1894
|
+
minor: "0.2",
|
|
1895
|
+
defaultVersion: "0.2.0",
|
|
1896
|
+
schemaFile: "prices.schema.json",
|
|
1897
|
+
codec: PriceMapCodec,
|
|
1898
|
+
normalize: identity,
|
|
1786
1899
|
latest: true
|
|
1787
1900
|
}
|
|
1788
1901
|
];
|
|
@@ -1827,7 +1940,8 @@ var resolveFindings = (raw) => {
|
|
|
1827
1940
|
return decoded._tag === "Left" ? { kind: "invalid-shape", errors: formatErrors(decoded.left) } : { kind: "ok", version, value: entry.normalize(decoded.right) };
|
|
1828
1941
|
};
|
|
1829
1942
|
var resolveSingleVersion = (kind, raw) => {
|
|
1830
|
-
const
|
|
1943
|
+
const table = tableFor(kind);
|
|
1944
|
+
const entry = table.find((e) => e.latest) ?? table[0];
|
|
1831
1945
|
if (!entry) throw new Error(`Registry invariant violated \u2014 no entry for "${kind}"`);
|
|
1832
1946
|
const decoded = entry.codec.decode(raw);
|
|
1833
1947
|
return decoded._tag === "Left" ? { kind: "invalid-shape", errors: formatErrors(decoded.left) } : { kind: "ok", version: entry.defaultVersion, value: entry.normalize(decoded.right) };
|
|
@@ -2056,7 +2170,6 @@ var checkLongSuggestions = (comments) => {
|
|
|
2056
2170
|
});
|
|
2057
2171
|
return { comments: adjusted, longFiles };
|
|
2058
2172
|
};
|
|
2059
|
-
var PIPELINE_STAMPED_FIELDS = /* @__PURE__ */ new Set(["convergence", "scope_metastasis"]);
|
|
2060
2173
|
var decodeFindings = (doc) => {
|
|
2061
2174
|
const resolution = resolve("findings", doc);
|
|
2062
2175
|
switch (resolution.kind) {
|
|
@@ -2077,14 +2190,14 @@ var loadFindings = (path) => {
|
|
|
2077
2190
|
return { kind: "corrupt" };
|
|
2078
2191
|
}
|
|
2079
2192
|
const first = decodeFindings(raw);
|
|
2080
|
-
if (first.kind === "invalid-shape" && typeof raw === "object" && raw !== null && !Array.isArray(raw) && Object.keys(raw).some((k) =>
|
|
2193
|
+
if (first.kind === "invalid-shape" && typeof raw === "object" && raw !== null && !Array.isArray(raw) && Object.keys(raw).some((k) => RECOVERABLE_OPTIONAL_FIELDS.has(k))) {
|
|
2081
2194
|
const stripped = Object.fromEntries(
|
|
2082
|
-
Object.entries(raw).filter(([key2]) => !
|
|
2195
|
+
Object.entries(raw).filter(([key2]) => !RECOVERABLE_OPTIONAL_FIELDS.has(key2))
|
|
2083
2196
|
);
|
|
2084
2197
|
const retry = decodeFindings(stripped);
|
|
2085
2198
|
if (retry.kind === "ok") {
|
|
2086
2199
|
process.stderr.write(
|
|
2087
|
-
"Warning: the review draft carried an invalid
|
|
2200
|
+
"Warning: the review draft carried an invalid best-effort field (convergence/scope_metastasis/change_size) \u2014 stripped it and used the rest; the pipeline re-stamps convergence\n"
|
|
2088
2201
|
);
|
|
2089
2202
|
return retry;
|
|
2090
2203
|
}
|
|
@@ -2134,6 +2247,19 @@ var loadTestReport = (path) => {
|
|
|
2134
2247
|
}
|
|
2135
2248
|
return decoded.right;
|
|
2136
2249
|
};
|
|
2250
|
+
var loadClocDiff = (path) => {
|
|
2251
|
+
let raw;
|
|
2252
|
+
try {
|
|
2253
|
+
raw = readFileSync(path, "utf-8");
|
|
2254
|
+
} catch (err) {
|
|
2255
|
+
process.stderr.write(
|
|
2256
|
+
`Warning: could not read cloc diff at ${path}: ${errMsg(err)} \u2014 omitting the cloc collapsible
|
|
2257
|
+
`
|
|
2258
|
+
);
|
|
2259
|
+
return void 0;
|
|
2260
|
+
}
|
|
2261
|
+
return raw.trim() === "" ? void 0 : raw;
|
|
2262
|
+
};
|
|
2137
2263
|
var parseHtmlUrl = (raw) => {
|
|
2138
2264
|
const parsed = tryParseJson(raw);
|
|
2139
2265
|
const htmlUrl = parsed.ok ? asRecord(parsed.value)?.["html_url"] : void 0;
|
|
@@ -2511,6 +2637,7 @@ ${dropNote}` : ""}`,
|
|
|
2511
2637
|
const answeredDropNote = answeredReRaiseNote(verbatimReRaised, droppedCount) + (verbatimReRaised.length > 0 && findings.findings.length === 0 ? "\n> _The stop signal reflects the kept findings \u2014 this round carries none._" : "");
|
|
2512
2638
|
const envelope = loadEnvelope(input.envelopePath);
|
|
2513
2639
|
const testReport = input.testReportPath ? loadTestReport(input.testReportPath) : void 0;
|
|
2640
|
+
const clocDiff = input.clocDiffPath ? loadClocDiff(input.clocDiffPath) : void 0;
|
|
2514
2641
|
const effectiveRoute = input.route ?? envelope?.route;
|
|
2515
2642
|
if (envelope === null) {
|
|
2516
2643
|
const envelopelessIncomplete = isIncompleteFindings(findings);
|
|
@@ -2548,6 +2675,7 @@ ${dropNote}` : ""}`,
|
|
|
2548
2675
|
roundCount: priorRoundCount,
|
|
2549
2676
|
convergenceRound: false,
|
|
2550
2677
|
testReport,
|
|
2678
|
+
clocDiff,
|
|
2551
2679
|
inlineDisposition: { kind: "no-envelope" },
|
|
2552
2680
|
runUrl: input.runUrl,
|
|
2553
2681
|
jsonUrl: input.jsonUrl,
|
|
@@ -2626,6 +2754,7 @@ ${dropNote}` : ""}`,
|
|
|
2626
2754
|
reviewedSha: input.headSha,
|
|
2627
2755
|
effort: input.effort,
|
|
2628
2756
|
testReport,
|
|
2757
|
+
clocDiff,
|
|
2629
2758
|
severityCounts: currentCounts,
|
|
2630
2759
|
sameRootNotes,
|
|
2631
2760
|
answeredNotes: reRaisedNotes,
|
|
@@ -2639,7 +2768,8 @@ ${dropNote}` : ""}`,
|
|
|
2639
2768
|
runUrl: input.runUrl,
|
|
2640
2769
|
jsonUrl: input.jsonUrl,
|
|
2641
2770
|
findingsPointer: findingsMarker,
|
|
2642
|
-
postedAt: input.postedAt
|
|
2771
|
+
postedAt: input.postedAt,
|
|
2772
|
+
pricedAt: input.pricedAt
|
|
2643
2773
|
};
|
|
2644
2774
|
const longFilesNote = longFiles.length > 0 ? `
|
|
2645
2775
|
|
|
@@ -3084,6 +3214,7 @@ var renderOutputs = (result) => {
|
|
|
3084
3214
|
conclusion=${result.conclusion}
|
|
3085
3215
|
diff_size=${String(result.diffSize)}
|
|
3086
3216
|
stacked=${String(result.stacked)}
|
|
3217
|
+
base_sha=${result.baseSha}
|
|
3087
3218
|
`;
|
|
3088
3219
|
}
|
|
3089
3220
|
};
|
|
@@ -3353,7 +3484,8 @@ var gather = async (input, ghApi = runGhApi, gitRun = runGit) => {
|
|
|
3353
3484
|
pr: prNumber,
|
|
3354
3485
|
conclusion: input.conclusion,
|
|
3355
3486
|
diffSize: Buffer.byteLength(prDiff, "utf8"),
|
|
3356
|
-
stacked
|
|
3487
|
+
stacked,
|
|
3488
|
+
baseSha: meta.base_sha
|
|
3357
3489
|
};
|
|
3358
3490
|
};
|
|
3359
3491
|
|
|
@@ -3586,10 +3718,16 @@ var nativeTelemetry = (native, meta) => resolveTelemetry(
|
|
|
3586
3718
|
);
|
|
3587
3719
|
var absentTelemetry = (meta) => resolveTelemetry({ models: [], turns: 0, durationMs: 0, vendorCostUsd: null }, meta);
|
|
3588
3720
|
var buildEnvelope = (telemetry, native, agentFilePath, agentFileFallbackPath, seedUnrevised) => {
|
|
3721
|
+
const generated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
3589
3722
|
const outcome = findingsOutcome(native, agentFilePath, agentFileFallbackPath);
|
|
3590
3723
|
switch (outcome.kind) {
|
|
3591
3724
|
case "ok":
|
|
3592
|
-
return {
|
|
3725
|
+
return {
|
|
3726
|
+
schema_version: outcome.version,
|
|
3727
|
+
findings: outcome.findings,
|
|
3728
|
+
generated_at,
|
|
3729
|
+
...telemetry
|
|
3730
|
+
};
|
|
3593
3731
|
case "telemetry-only": {
|
|
3594
3732
|
const reason = seedUnrevised ? "the review agent did not write a review (its draft is still the pre-seeded sentinel)" : outcome.reason;
|
|
3595
3733
|
return {
|
|
@@ -3598,6 +3736,7 @@ var buildEnvelope = (telemetry, native, agentFilePath, agentFileFallbackPath, se
|
|
|
3598
3736
|
|
|
3599
3737
|
${reason}`),
|
|
3600
3738
|
incomplete: true,
|
|
3739
|
+
generated_at,
|
|
3601
3740
|
...telemetry
|
|
3602
3741
|
};
|
|
3603
3742
|
}
|
|
@@ -3802,6 +3941,7 @@ var resolvePrices = (pricesArg) => {
|
|
|
3802
3941
|
return { kind: "absent", path: bundledPath("schema", "prices.example.json") };
|
|
3803
3942
|
};
|
|
3804
3943
|
var TEST_REPORT_DESCRIPTION = 'Path to a JSON test summary: {"passed": number, "failed": number, "total": number, "failures"?: [{"name": string, "message"?: string}]}';
|
|
3944
|
+
var CLOC_DIFF_DESCRIPTION = "Path to a raw `cloc --git --diff <base> <head>` table, rendered verbatim in the sticky's cloc collapsible. Best-effort: an absent, unreadable, or empty file omits the collapsible.";
|
|
3805
3945
|
var CONVERGENCE_THRESHOLD_DESCRIPTION = "Advisory convergence tolerance: the per-finding convergence score (each finding's severity floor + confidence-and-likelihood-weighted headroom; ceilings critical 4 \xB7 major 2 \xB7 minor 1 \xB7 nit 0) at or below which the sticky reads as converged. The floor values and the systemic-likelihood rule are documented in the README and the findings schema (default: 1)";
|
|
3806
3946
|
var NIT_VISIBILITY_FLOOR_DESCRIPTION = "Nit visibility floor: nits whose confidence \xD7 likelihood falls below this are hidden from humans (no inline comment; a collapsed aside in the sticky) but kept in the machine blob as adjudicated. In [0, 1] (default: 0.25)";
|
|
3807
3947
|
var renderCmd = defineCommand({
|
|
@@ -3844,6 +3984,10 @@ var renderCmd = defineCommand({
|
|
|
3844
3984
|
type: "string",
|
|
3845
3985
|
description: TEST_REPORT_DESCRIPTION
|
|
3846
3986
|
},
|
|
3987
|
+
"cloc-diff": {
|
|
3988
|
+
type: "string",
|
|
3989
|
+
description: CLOC_DIFF_DESCRIPTION
|
|
3990
|
+
},
|
|
3847
3991
|
"convergence-threshold": {
|
|
3848
3992
|
type: "string",
|
|
3849
3993
|
description: CONVERGENCE_THRESHOLD_DESCRIPTION
|
|
@@ -3861,6 +4005,7 @@ var renderCmd = defineCommand({
|
|
|
3861
4005
|
const prices = decode(PriceMapCodec.decode(readJSON(priceResolution.path)), "prices");
|
|
3862
4006
|
const template = readFileSync(templatePath, "utf-8");
|
|
3863
4007
|
const testReport = args["test-report"] ? decode(TestSummaryCodec.decode(readJSON(args["test-report"])), "test report") : void 0;
|
|
4008
|
+
const clocDiff = args["cloc-diff"] ? loadClocDiff(args["cloc-diff"]) : void 0;
|
|
3864
4009
|
const route = args.route || envelope.route || null;
|
|
3865
4010
|
const isRound = isConvergenceRound(route, envelope.incomplete === true || isIncompleteFindings(findings)) && isReviewVerdict(findings.verdict);
|
|
3866
4011
|
const threshold = parseConvergenceThreshold(args["convergence-threshold"]);
|
|
@@ -3878,10 +4023,12 @@ var renderCmd = defineCommand({
|
|
|
3878
4023
|
route: args.route,
|
|
3879
4024
|
effort: args.effort,
|
|
3880
4025
|
testReport,
|
|
4026
|
+
clocDiff,
|
|
3881
4027
|
convergenceThreshold: threshold,
|
|
3882
4028
|
nitVisibilityFloor: parseNitVisibilityFloor(args["nit-visibility-floor"]),
|
|
3883
4029
|
convergenceRound: isRound,
|
|
3884
|
-
postedAt: formatUtc(/* @__PURE__ */ new Date())
|
|
4030
|
+
postedAt: formatUtc(/* @__PURE__ */ new Date()),
|
|
4031
|
+
pricedAt: /* @__PURE__ */ new Date()
|
|
3885
4032
|
});
|
|
3886
4033
|
process.stdout.write(output2);
|
|
3887
4034
|
}
|
|
@@ -3946,7 +4093,11 @@ var costCmd = defineCommand({
|
|
|
3946
4093
|
run: async ({ args }) => {
|
|
3947
4094
|
const envelope = decode(ResultEnvelopeCodec.decode(readJSON(args.envelope)), "envelope");
|
|
3948
4095
|
const prices = decode(PriceMapCodec.decode(readJSON(args.prices)), "prices");
|
|
3949
|
-
const report = computeCost(
|
|
4096
|
+
const report = computeCost(
|
|
4097
|
+
envelope.models,
|
|
4098
|
+
prices,
|
|
4099
|
+
parseInstant(envelope.generated_at) ?? /* @__PURE__ */ new Date()
|
|
4100
|
+
);
|
|
3950
4101
|
process.stdout.write(JSON.stringify(report, null, 2));
|
|
3951
4102
|
}
|
|
3952
4103
|
});
|
|
@@ -3977,7 +4128,11 @@ var checkCostCmd = defineCommand({
|
|
|
3977
4128
|
const usage = sumTranscriptUsage(tree.entries);
|
|
3978
4129
|
const priceResolution = resolvePrices(args.prices);
|
|
3979
4130
|
const prices = decode(PriceMapCodec.decode(readJSON(priceResolution.path)), "prices");
|
|
3980
|
-
const report = computeCost(
|
|
4131
|
+
const report = computeCost(
|
|
4132
|
+
usage.models,
|
|
4133
|
+
prices,
|
|
4134
|
+
usage.lastTsMs !== null ? new Date(usage.lastTsMs) : /* @__PURE__ */ new Date()
|
|
4135
|
+
);
|
|
3981
4136
|
process.stdout.write(
|
|
3982
4137
|
`${JSON.stringify(
|
|
3983
4138
|
{
|
|
@@ -4121,7 +4276,17 @@ var budgetHookCmd = defineCommand({
|
|
|
4121
4276
|
const tree = transcriptPath ? readTranscriptTree(resolve$1(transcriptPath)) : void 0;
|
|
4122
4277
|
const usage = tree ? sumTranscriptUsage(tree.entries) : void 0;
|
|
4123
4278
|
const prices = args.prices ? tryReadPrices(args.prices) : null;
|
|
4124
|
-
const spentUsd = prices !== null && usage ?
|
|
4279
|
+
const spentUsd = prices !== null && usage ? (
|
|
4280
|
+
// Price at the transcript's last activity instant, not the wall clock (issue #170 review
|
|
4281
|
+
// r2). Silent warn: this budget-steering cost is recomputed on EVERY tool event, so a
|
|
4282
|
+
// misconfigured slot map would otherwise flood stderr; the final post's cost render warns.
|
|
4283
|
+
computeCost(
|
|
4284
|
+
usage.models,
|
|
4285
|
+
prices,
|
|
4286
|
+
usage.lastTsMs !== null ? new Date(usage.lastTsMs) : /* @__PURE__ */ new Date(),
|
|
4287
|
+
() => void 0
|
|
4288
|
+
).totalCostUSD
|
|
4289
|
+
) : null;
|
|
4125
4290
|
const wallMs = args.wall ? parseWallMs(args.wall) : null;
|
|
4126
4291
|
const output2 = evaluateBudgetHook(input, {
|
|
4127
4292
|
spentUsd,
|
|
@@ -4322,7 +4487,6 @@ ${printableSchema(schemaPath)}
|
|
|
4322
4487
|
}
|
|
4323
4488
|
});
|
|
4324
4489
|
var isSurfaceStampedDoc = (doc) => typeof doc === "object" && doc !== null && !Array.isArray(doc) && doc["schema_version"] === SURFACE_SCHEMA_VERSION;
|
|
4325
|
-
var PIPELINE_STAMPED_FIELDS2 = /* @__PURE__ */ new Set(["scope_metastasis", "convergence"]);
|
|
4326
4490
|
var withoutScopeMetastasis = (doc) => typeof doc === "object" && doc !== null && !Array.isArray(doc) ? Object.fromEntries(Object.entries(doc).filter(([key2]) => key2 !== "scope_metastasis")) : doc;
|
|
4327
4491
|
var seedDraftCmd = defineCommand({
|
|
4328
4492
|
meta: {
|
|
@@ -4467,12 +4631,12 @@ var seedDraftCmd = defineCommand({
|
|
|
4467
4631
|
const schemaPath = args.schema ? resolve$1(args.schema) : schemaPathFor(kind, args["schema-version"]);
|
|
4468
4632
|
const barePrior = typeof priorFindings === "object" && !Array.isArray(priorFindings) ? Object.fromEntries(
|
|
4469
4633
|
Object.entries(priorFindings).filter(
|
|
4470
|
-
([key2]) => !
|
|
4634
|
+
([key2]) => !RECOVERABLE_OPTIONAL_FIELDS.has(key2)
|
|
4471
4635
|
)
|
|
4472
4636
|
) : priorFindings;
|
|
4473
4637
|
const accepts = (doc) => validateAgainstSchema(doc, schemaPath).valid && resolve("findings", doc).kind === "ok";
|
|
4474
4638
|
const seedDoc = accepts(priorFindings) ? priorFindings : accepts(barePrior) ? (process.stderr.write(
|
|
4475
|
-
`Note: the in-force schema rejects
|
|
4639
|
+
`Note: the in-force schema rejects a carried recoverable field (scope_metastasis/convergence/change_size) \u2014 seeding the prior without it (issue #150 review r2 / #182 review r2)
|
|
4476
4640
|
`
|
|
4477
4641
|
), barePrior) : null;
|
|
4478
4642
|
if (seedDoc === null) return false;
|
|
@@ -4961,6 +5125,10 @@ var postCmd = defineCommand({
|
|
|
4961
5125
|
type: "string",
|
|
4962
5126
|
description: TEST_REPORT_DESCRIPTION
|
|
4963
5127
|
},
|
|
5128
|
+
"cloc-diff": {
|
|
5129
|
+
type: "string",
|
|
5130
|
+
description: CLOC_DIFF_DESCRIPTION
|
|
5131
|
+
},
|
|
4964
5132
|
"run-url": {
|
|
4965
5133
|
type: "string",
|
|
4966
5134
|
description: "Workflow run URL (transcript/traces), rendered as a link in the LLM Disclosure aside"
|
|
@@ -4994,11 +5162,13 @@ var postCmd = defineCommand({
|
|
|
4994
5162
|
headBranch: args["head-branch"],
|
|
4995
5163
|
effort: args.effort,
|
|
4996
5164
|
testReportPath: args["test-report"],
|
|
5165
|
+
clocDiffPath: args["cloc-diff"],
|
|
4997
5166
|
runUrl: args["run-url"],
|
|
4998
5167
|
jsonUrl: args["json-url"],
|
|
4999
5168
|
convergenceThreshold: parseConvergenceThreshold(args["convergence-threshold"]),
|
|
5000
5169
|
nitVisibilityFloor: parseNitVisibilityFloor(args["nit-visibility-floor"]),
|
|
5001
|
-
postedAt: formatUtc(/* @__PURE__ */ new Date())
|
|
5170
|
+
postedAt: formatUtc(/* @__PURE__ */ new Date()),
|
|
5171
|
+
pricedAt: /* @__PURE__ */ new Date()
|
|
5002
5172
|
});
|
|
5003
5173
|
}
|
|
5004
5174
|
});
|