feat(ledger): Piece E, queue section in the weekly ledger (row 13, #1508)
The ledger prints a queue section above the weekly table. It checks four things: - open issues named by done rows; - owner registrations for active rows; - closed issues for done rows; - the age of required rows. The result is fail, incomplete or reduced pass. It uses its own Gitea budget of the open list plus at most 10 lookups. A full open page counts only while an issue in some row's closes has no known state (lead decision 40). T3 seats are exempt per run with --unsupported-runtime. The weekly routine is in packages/ledger/README.md. Built by Darkwing (build.patch ab1f12ca, manifest 0b20bbca). Filbert reviewed it: round 1 81f26f2e asked for changes (C1, ISO requiredSince never aged); round 2 ce8ce150 approved. Also carries Filbert's plan amendment for decision 40 (68a25ffe). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -3,19 +3,29 @@ 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]';
|
||||
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;
|
||||
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');
|
||||
@@ -27,20 +37,28 @@ export async function main(args, root = path.resolve(path.dirname(fileURLToPath(
|
||||
}
|
||||
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;
|
||||
try { issues = noIssues ? null : readIssues(root, range); }
|
||||
let issues, states = null;
|
||||
try {
|
||||
issues = noIssues ? null : 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);
|
||||
console.log(json ? JSON.stringify(report, null, 2) : formatTable(report));
|
||||
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)) {
|
||||
|
||||
Reference in New Issue
Block a user