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, packetd6ea56831), BASEcc48b124f. Approved by Filbert (comment 28407). Sage gate rerun at644ef5310plus the candidate passed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
71 lines
4.0 KiB
JavaScript
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;
|
|
});
|
|
}
|