Files
stack/packages/ledger/src/cli.mjs
T
jason.woltjeandClaude Opus 5.5 a5b8a99a18 ledger: skip T3 reasoning summaries and read issue pages up to a ceiling (row 113, #1607)
T3 messages with role reasoning and an id starting "reasoning:" are now
skipped before any other check and counted as reasoningMessages. Any other
unknown role, including reasoning with another id prefix, still refuses.
Issues updated since the start date are read in pages of 50 until a short
page, at most 10 pages. A full tenth page, a duplicate issue number across
pages, a page over 50 rows or any failed page refuses with exit 2, so
partial totals are never reported. The report prints how many pages it
read. The README describes the skip, the page loop and its refusals.

Candidate manifest 4eed1d6405a505e88cde5062c61f768639d899b8f301019b1403d68fb15d9d36
(Darkwing round 1, packet d6ea56831), BASE cc48b124f. Approved by Filbert
(comment 28407). Sage gate rerun at 644ef5310 plus the candidate passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:35:33 -05:00

71 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { dateRange, readCommits, readIssues, readSessions, mergeSources, summarize, formatTable, SourceError } from './ledger.mjs';
import { readT3, defaultT3Path } from './t3.mjs';
import { readQueue, readSeatsDir, issueStates, queueChecks, formatQueue, checkSeatName } from './queue-checks.mjs';
const usage = 'Usage: node packages/ledger/src/cli.mjs --since YYYY-MM-DD [--until YYYY-MM-DD] [--json] [--no-issues] [--no-t3 | --t3-db PATH] [--no-queue | --unsupported-runtime SEAT ...]';
export async function main(args, root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..')) {
let since, until, t3Db, json = false, noIssues = false, noT3 = false, noQueue = false;
const exempt = new Set();
const seen = new Set();
for (let i = 0; i < args.length; i++) {
const flag = args[i];
// A runtime is declared unsupported once per seat, so only that flag repeats.
if (flag === '--unsupported-runtime') {
const seat = checkSeatName(args[++i]);
if (exempt.has(seat)) throw new SourceError(`Duplicate --unsupported-runtime ${seat}`);
exempt.add(seat);
continue;
}
if (seen.has(flag)) throw new SourceError(`Duplicate option: ${flag}`);
seen.add(flag);
if (flag === '--help') { console.log(usage); return; }
if (flag === '--json') json = true;
else if (flag === '--no-issues') noIssues = true;
else if (flag === '--no-t3') noT3 = true;
else if (flag === '--no-queue') noQueue = true;
else if (flag === '--t3-db') {
t3Db = args[++i];
if (!t3Db || t3Db.startsWith('--')) throw new SourceError('--t3-db requires a path');
} else if (flag === '--since' || flag === '--until') {
const value = args[++i];
if (!value || value.startsWith('--')) throw new SourceError(`${flag} requires a date`);
if (flag === '--since') since = value; else until = value;
} else throw new SourceError('Unknown option; ' + usage);
}
if (!since) throw new SourceError(usage);
if (noT3 && t3Db !== undefined) throw new SourceError('--no-t3 and --t3-db cannot be combined');
if (noQueue && exempt.size) throw new SourceError('--no-queue and --unsupported-runtime cannot be combined');
const range = dateRange(since, until);
// The queue loads before any Gitea call, so a bad queue.json costs none.
const queue = noQueue ? null : readQueue(root);
const commits = readCommits(root, range);
// Fixture tools may be placed first on PATH. The repository client is the
// default without requiring installation or reading auth material here.
const priorPath = process.env.PATH;
process.env.PATH = `${priorPath ?? ''}${path.delimiter}${path.join(root, 'scripts')}`;
let issues = null, pages, states = null;
try {
if (!noIssues) ({ issues, pages } = readIssues(root, range));
if (queue && !noIssues) states = issueStates(root, queue.rows, issues);
}
finally { if (priorPath === undefined) delete process.env.PATH; else process.env.PATH = priorPath; }
// T3 is on by default. A missing or unreadable database refuses the report.
const t3 = noT3 ? null : await readT3(root, range, t3Db === undefined ? { dbPath: defaultT3Path(), isDefault: true } : { dbPath: t3Db, isDefault: false });
const sessions = mergeSources(await readSessions(root, range), t3);
const report = summarize(range, commits, issues, sessions);
report.issueSource = noIssues ? { read: false } : { read: true, pages };
report.queue = queue ? queueChecks(queue, { issues: states, seats: readSeatsDir(), exempt, range }) : { checked: false };
// The queue section prints above the weekly table.
console.log(json ? JSON.stringify(report, null, 2) : [...formatQueue(report.queue), formatTable(report)].join('\n'));
return report;
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main(process.argv.slice(2)).catch(error => {
console.error(error instanceof SourceError ? error.message : 'Ledger failed: cannot read source evidence');
process.exitCode = error.exitCode ?? 1;
});
}