@bongos/core 1.19.256 → 1.19.258

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.
@@ -117,17 +117,34 @@ t('materializeClaude: materializes scheduled-tasks — bodies→bongos, companio
117
117
  assert.ok(res.scheduledTasks > 0, 'at least one scheduled-task file materialized');
118
118
  const rt = join(dir, '.claude', 'scheduled-tasks');
119
119
  assert.ok(existsSync(rt), '.claude/scheduled-tasks created in the instance');
120
- // A prose/judgment body's core invocation is bongosified so a consumer-layout model can run it.
121
- const stale = readFileSync(join(rt, 'stale-claim-release', 'SKILL.md'), 'utf8');
122
- assert.ok(stale.includes('bongos exec scripts/gds/pulse.js'), 'body core-invocation rewritten to bongos exec');
123
- assert.ok(!stale.includes('node scripts/gds/pulse.js'), 'no bare relative node scripts/gds left in the body');
124
- // A deterministic routine's `.claude/...` companion-step command is NOT a core invocation → preserved.
125
- const dd = readFileSync(join(rt, 'diagram-drift', 'SKILL.md'), 'utf8');
126
- assert.ok(dd.includes('node .claude/scheduled-tasks/diagram-drift/check.js'), 'companion-path step command preserved');
127
- // Companion .js resolves its core require()s via the materialized _core-resolve.js sibling (task 2139 pattern).
128
- const check = readFileSync(join(rt, 'diagram-drift', 'check.js'), 'utf8');
129
- assert.ok(check.includes("require('../../hooks/_core-resolve.js')"), 'companion resolves core via _core-resolve');
130
- assert.ok(!check.includes("require('../../../scripts/gds/"), 'no bare ../../../scripts/gds require left in the companion');
120
+ // Every materialized routine's core invocation is bongosified — asserted over
121
+ // WHATEVER routines the tree carries (the neutral core carries redteam-weekly;
122
+ // the founder core also carries stale-claim-release/diagram-drift). No routine
123
+ // body may keep a bare `node scripts/gds/…` core invocation.
124
+ let sawBongosified = false;
125
+ for (const name of readdirSync(rt)) {
126
+ const skillPath = join(rt, name, 'SKILL.md');
127
+ if (!existsSync(skillPath)) continue;
128
+ const body = readFileSync(skillPath, 'utf8');
129
+ assert.ok(!/\bnode scripts\/gds\//.test(body), `${name}: bare relative node scripts/gds left in the body`);
130
+ if (body.includes('bongos exec scripts/gds/')) sawBongosified = true;
131
+ }
132
+ assert.ok(sawBongosified, 'at least one routine had its core invocation rewritten to bongos exec');
133
+ // Founder-specific fixtures — assert their finer behavior only when present, so
134
+ // this coverage stays live on the founder core without demanding those routines
135
+ // exist in the neutral core (which carries a different routine set).
136
+ const stalePath = join(rt, 'stale-claim-release', 'SKILL.md');
137
+ if (existsSync(stalePath)) {
138
+ assert.ok(readFileSync(stalePath, 'utf8').includes('bongos exec scripts/gds/pulse.js'), 'body core-invocation rewritten to bongos exec');
139
+ }
140
+ const ddCheck = join(rt, 'diagram-drift', 'check.js');
141
+ if (existsSync(ddCheck)) {
142
+ const dd = readFileSync(join(rt, 'diagram-drift', 'SKILL.md'), 'utf8');
143
+ assert.ok(dd.includes('node .claude/scheduled-tasks/diagram-drift/check.js'), 'companion-path step command preserved');
144
+ const check = readFileSync(ddCheck, 'utf8');
145
+ assert.ok(check.includes("require('../../hooks/_core-resolve.js')"), 'companion resolves core via _core-resolve');
146
+ assert.ok(!check.includes("require('../../../scripts/gds/"), 'no bare ../../../scripts/gds require left in the companion');
147
+ }
131
148
  rmSync(dir, { recursive: true, force: true });
132
149
  });
133
150
 
@@ -0,0 +1,60 @@
1
+ // tests/control_manifest.mjs — the assurance-of-assurance check (task 1003131,
2
+ // goal 1000076). Proves the manifest passes on the real tree AND actually
3
+ // CATCHES the two failure modes it exists to catch: a control file that
4
+ // vanishes, and a control that comes unwired. The check is itself a control —
5
+ // so it too must be provably able to fail.
6
+ import assert from 'node:assert/strict';
7
+ import { test } from 'node:test';
8
+ import { createRequire } from 'node:module';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const require = createRequire(import.meta.url);
13
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
14
+ const cm = require(path.join(ROOT, 'scripts', 'gds', 'control-manifest.js'));
15
+
16
+ test('the live tree passes: every goal-1000076 control is present and wired', () => {
17
+ const r = cm.checkControlsExist();
18
+ assert.equal(r.hardFail, false, `unexpected violations:\n ${r.violations.join('\n ')}`);
19
+ assert.ok(cm.CONTROLS.length >= 8, 'the manifest covers the shipped controls');
20
+ });
21
+
22
+ test('a vanished control file is caught', () => {
23
+ const controls = [{ label: 'x', file: '.claude/hooks/secret-gate.js' }];
24
+ const gone = cm.evaluate(controls, { exists: () => false, read: () => '' });
25
+ assert.equal(gone.length, 1);
26
+ assert.match(gone[0], /control file missing/);
27
+ });
28
+
29
+ test('a present-but-UNWIRED control is caught (the settings.json un-hook case)', () => {
30
+ const controls = [{
31
+ label: 'secret-gate hook', file: '.claude/hooks/secret-gate.js',
32
+ wiredIn: { file: '.claude/settings.json', needle: 'secret-gate.js' },
33
+ }];
34
+ // file exists, but the settings no longer reference it
35
+ const unwired = cm.evaluate(controls, {
36
+ exists: (f) => f === '.claude/hooks/secret-gate.js' || f === '.claude/settings.json',
37
+ read: () => '{ "hooks": {} }',
38
+ });
39
+ assert.equal(unwired.length, 1);
40
+ assert.match(unwired[0], /not wired in/);
41
+ });
42
+
43
+ test('a missing companion file (baseline/config) is caught even when the main file exists', () => {
44
+ const controls = [{
45
+ label: 'audit gate', file: 'scripts/gds/audit-gate.js',
46
+ alsoExists: ['config/audit-baseline.json'],
47
+ }];
48
+ const v = cm.evaluate(controls, { exists: (f) => f === 'scripts/gds/audit-gate.js', read: () => '' });
49
+ assert.equal(v.length, 1);
50
+ assert.match(v[0], /companion file missing/);
51
+ });
52
+
53
+ test('all-present + all-wired yields zero violations', () => {
54
+ const controls = [{
55
+ label: 'ok', file: 'a', alsoExists: ['b'],
56
+ wiredIn: { file: 'c', needle: 'NEEDLE' },
57
+ }];
58
+ const v = cm.evaluate(controls, { exists: () => true, read: () => 'has NEEDLE inside' });
59
+ assert.deepEqual(v, []);
60
+ });
@@ -0,0 +1,91 @@
1
+ // tests/fitness_ratchets.mjs — the quality-ratchet mechanics (task 1003129,
2
+ // goal 1000076). Hermetic: exercises the pure metric + compare functions on
3
+ // synthetic inputs; the repo-wide sweep runs via checkQualityRatchets inside
4
+ // tests/fitness.mjs, and the knip half is a CI-only step by design.
5
+ import assert from 'node:assert/strict';
6
+ import { test } from 'node:test';
7
+ import { createRequire } from 'node:module';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const require = createRequire(import.meta.url);
12
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
+ const rat = require(path.join(ROOT, 'scripts', 'gds', 'fitness-ratchets.js'));
14
+
15
+ test('ratchet compare: worse fails, equal passes, better nudges, unknown warns', () => {
16
+ const base = { a: 10, b: 5 };
17
+ assert.equal(rat.compareToBaselines({ a: 10, b: 5 }, base).violations.length, 0, 'equal passes');
18
+ const worse = rat.compareToBaselines({ a: 11, b: 5 }, base);
19
+ assert.equal(worse.violations.length, 1);
20
+ assert.match(worse.violations[0], /a regressed: 11 > baseline 10/);
21
+ const better = rat.compareToBaselines({ a: 9, b: 5 }, base);
22
+ assert.equal(better.violations.length, 0);
23
+ assert.match(better.warnings[0], /ratchet-down available/);
24
+ const unknown = rat.compareToBaselines({ c: 1 }, base);
25
+ assert.equal(unknown.violations.length, 0, 'uninitialized metric warns, never fails');
26
+ assert.match(unknown.warnings[0], /no baseline for "c"/);
27
+ assert.equal(rat.compareToBaselines({ a: null }, base).warnings.length, 0, 'a skipped metric (null) is silent');
28
+ });
29
+
30
+ test('duplication: identical 30-line blocks in TWO files count; one file alone does not', () => {
31
+ const block = Array.from({ length: rat.WINDOW }, (_, i) => `const v${i} = compute(${i});`).join('\n');
32
+ const one = new Map([['a.js', block]]);
33
+ assert.equal(rat.duplicateWindowCount(one), 0, 'same-file windows are not cross-file duplication');
34
+ const two = new Map([['a.js', block], ['b.js', `// header\n${block}`]]);
35
+ assert.ok(rat.duplicateWindowCount(two) >= 1, 'the shared block registers');
36
+ const clean = new Map([['a.js', block], ['b.js', block.replaceAll('compute', 'derive')]]);
37
+ assert.equal(rat.duplicateWindowCount(clean), 0, 'a changed body is not a duplicate');
38
+ });
39
+
40
+ test('normalizeLines drops blanks and comment lines so cosmetic edits cannot dodge the hash', () => {
41
+ const lines = rat.normalizeLines('a();\n\n // comment\n b();\n/* block */\n * cont\n');
42
+ assert.deepEqual(lines, ['a();', 'b();']);
43
+ });
44
+
45
+ test('cycle counting: a 2-node loop is one SCC; a diamond is none; self-requires ignored', () => {
46
+ const loop = new Map([['a.js', ['b.js']], ['b.js', ['a.js']]]);
47
+ assert.equal(rat.countCycles(loop), 1);
48
+ const diamond = new Map([['a.js', ['b.js', 'c.js']], ['b.js', ['d.js']], ['c.js', ['d.js']], ['d.js', []]]);
49
+ assert.equal(rat.countCycles(diamond), 0);
50
+ const tri = new Map([['a.js', ['b.js']], ['b.js', ['c.js']], ['c.js', ['a.js']], ['x.js', []]]);
51
+ assert.equal(rat.countCycles(tri), 1, 'a 3-cycle is one component, not three');
52
+ });
53
+
54
+ test('requireGraph resolves ./x, ./x.js and ./x/index.js and skips unknown targets', () => {
55
+ const contents = new Map([
56
+ ['src/a.js', "const b = require('./b'); const i = require('./lib'); const gone = require('./nope');"],
57
+ ['src/b.js', ''],
58
+ ['src/lib/index.js', ''],
59
+ ]);
60
+ const edges = rat.requireGraph(contents);
61
+ assert.deepEqual(edges.get('src/a.js').sort(), ['src/b.js', 'src/lib/index.js']);
62
+ });
63
+
64
+ test('size metrics: count and excess ratchet independently', () => {
65
+ const files = ['big.js', 'small.js'];
66
+ const read = (f) => (f === 'big.js' ? 'x\n'.repeat(rat.OVERSIZE_LINES + 100) : 'x\n');
67
+ const m = rat.sizeMetrics(files, read);
68
+ assert.equal(m.oversized_file_count, 1);
69
+ assert.ok(m.excess_lines_total >= 100, `excess tracks growth over the threshold (got ${m.excess_lines_total})`);
70
+ });
71
+
72
+ test('console counting only sees server paths', () => {
73
+ assert.equal(rat.isServerPath('src/bongos/auth.js'), true);
74
+ assert.equal(rat.isServerPath('modules/lifecycle/db.js'), true);
75
+ assert.equal(rat.isServerPath('modules/hall-ui/public/builders.js'), false, 'browser surfaces excluded');
76
+ assert.equal(rat.isServerPath('scripts/gds/ship.js'), false, 'CLI stdout is legitimate UX');
77
+ assert.equal(rat.isServerPath('modules/security/tests/floor.mjs'), false);
78
+ const n = rat.consoleCallCount(new Map([
79
+ ['src/x.js', 'console.log(1); console.error(2);'],
80
+ ['scripts/gds/y.js', 'console.log(1);'],
81
+ ]));
82
+ assert.equal(n, 2);
83
+ });
84
+
85
+ test('the committed baseline file parses and covers every hermetic metric', () => {
86
+ const b = require(path.join(ROOT, 'config', 'fitness-baselines.json'));
87
+ for (const key of ['oversized_file_count', 'excess_lines_total', 'duplicate_window_count',
88
+ 'require_cycle_count', 'console_call_count', 'doc_stale_links', 'doc_broken_anchors', 'doc_duplicate_prose']) {
89
+ assert.ok(Number.isInteger(b.metrics[key]), `baseline missing ${key}`);
90
+ }
91
+ });
@@ -0,0 +1,89 @@
1
+ // tests/logger.mjs — the core structured logger (task 1003130, goal 1000076).
2
+ // Proves the three done-when properties without a live server: request-id
3
+ // correlation via AsyncLocalStorage, secret redaction on the logged shapes,
4
+ // and the middleware's adopt-or-mint + x-request-id echo. Hermetic — captures
5
+ // pino's stdout stream, no network.
6
+ import assert from 'node:assert/strict';
7
+ import { test } from 'node:test';
8
+ import { createRequire } from 'node:module';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const require = createRequire(import.meta.url);
13
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
14
+ const log = require(path.join(ROOT, 'src', 'bongos', 'logger.js'));
15
+ const pino = require(path.join(ROOT, 'node_modules', 'pino'));
16
+ const { Writable } = require('node:stream');
17
+
18
+ // pino writes through sonic-boom directly to the fd, so we can't intercept the
19
+ // singleton's stdout. Instead build a logger with the IDENTICAL exported config
20
+ // onto a capture stream — same redact paths + same reqId mixin (the mixin closes
21
+ // over the module's ALS, so runWithRequestId still correlates).
22
+ function capture(fn) {
23
+ const lines = [];
24
+ const stream = new Writable({ write(chunk, _enc, cb) { lines.push(chunk.toString()); cb(); } });
25
+ const testLogger = pino(log.pinoOptions, stream);
26
+ fn(testLogger);
27
+ return lines.filter((l) => l.trim().startsWith('{')).map((l) => JSON.parse(l));
28
+ }
29
+
30
+ test('every line emitted inside runWithRequestId carries the same reqId', () => {
31
+ const out = capture((l) => {
32
+ log.runWithRequestId('req-abc', () => {
33
+ l.info('first');
34
+ l.warn('second');
35
+ assert.equal(log.currentRequestId(), 'req-abc');
36
+ });
37
+ l.info('outside'); // no context → no reqId
38
+ });
39
+ const inside = out.filter((l) => l.msg === 'first' || l.msg === 'second');
40
+ assert.equal(inside.length, 2);
41
+ assert.ok(inside.every((l) => l.reqId === 'req-abc'), 'correlated lines share the id');
42
+ const outside = out.find((l) => l.msg === 'outside');
43
+ assert.equal(outside.reqId, undefined, 'a line with no request context carries no reqId');
44
+ });
45
+
46
+ test('a secret in a logged header/field is redacted, not emitted', () => {
47
+ const out = capture((l) => {
48
+ l.error({
49
+ req: { headers: { authorization: 'Bearer sk-ant-verysecretvalue012345', cookie: 'gds_session=abc' } },
50
+ token: 'ghp_shouldnotappear0000000000000000000000',
51
+ note: 'this stays',
52
+ }, 'boom');
53
+ });
54
+ const line = out.find((l) => l.msg === 'boom');
55
+ const blob = JSON.stringify(line);
56
+ assert.ok(!blob.includes('sk-ant-verysecretvalue'), 'authorization header redacted');
57
+ assert.ok(!blob.includes('ghp_shouldnotappear'), 'token field redacted');
58
+ assert.ok(!blob.includes('gds_session=abc'), 'cookie redacted');
59
+ assert.ok(blob.includes('[Redacted]'), 'redaction marker present');
60
+ assert.ok(blob.includes('this stays'), 'non-secret fields survive');
61
+ });
62
+
63
+ test('REDACT_PATHS covers the ADR 0022 header/field shapes', () => {
64
+ for (const p of ['req.headers.authorization', 'req.headers.cookie', 'token', '*.token', '*.password']) {
65
+ assert.ok(log.REDACT_PATHS.includes(p), `expected redact path ${p}`);
66
+ }
67
+ });
68
+
69
+ test('requestIdMiddleware adopts a valid inbound id and echoes it; mints otherwise', () => {
70
+ const mk = (headers) => {
71
+ const req = { headers };
72
+ const res = { _h: {}, setHeader(k, v) { this._h[k.toLowerCase()] = v; } };
73
+ let ran = false;
74
+ log.requestIdMiddleware(req, res, () => { ran = true; });
75
+ return { req, res, ran };
76
+ };
77
+ const adopted = mk({ 'x-request-id': 'inbound-123' });
78
+ assert.equal(adopted.req.reqId, 'inbound-123');
79
+ assert.equal(adopted.res._h['x-request-id'], 'inbound-123');
80
+ assert.ok(adopted.ran);
81
+
82
+ const minted = mk({});
83
+ assert.match(minted.req.reqId, /^[0-9a-f]{16}$/, 'mints a 16-hex id when none supplied');
84
+ assert.equal(minted.res._h['x-request-id'], minted.req.reqId);
85
+
86
+ // an absurdly long inbound id is rejected in favor of a minted one (header-injection guard)
87
+ const huge = mk({ 'x-request-id': 'x'.repeat(500) });
88
+ assert.match(huge.req.reqId, /^[0-9a-f]{16}$/, 'over-long inbound id is not adopted');
89
+ });
@@ -1547,6 +1547,148 @@ test('1002723 a shipped task appears in the dispositions line as shipped', async
1547
1547
  });
1548
1548
 
1549
1549
  // Drain the microtask queue, then report + exit with the right code.
1550
+
1551
+ // ===========================================================================
1552
+ // task 1001427 — PHANTOM SHIP: `shipped` must mean the code is on main.
1553
+ //
1554
+ // The task described two vectors that left a task status=shipped with its PR
1555
+ // still open:
1556
+ // (1) the reconciler flipping confirmed->shipped on mergeIfGreen alone,
1557
+ // without confirming the commit is an ancestor of origin/main;
1558
+ // (2) under GRADER_BYPASS_ENABLED, shipped_at stamped at CONFIRM time,
1559
+ // before any merge.
1560
+ //
1561
+ // Both were closed by later work — the per-task land proof (1313 / 1547 /
1562
+ // 1002615 / 1002724) and ADR 0120's move of the payout and the shipped_at stamp
1563
+ // to the land. Neither was pinned by a test keyed on THIS invariant, though, so
1564
+ // nothing would catch a regression that reopened either one. These do.
1565
+ //
1566
+ // The invariant, stated once: THE SWEEP CALLS db.shipTask ONLY WHEN THE LAND IS
1567
+ // PROVEN. Not when a merge was attempted, not when a merge was reported, not
1568
+ // when a PR looks green — proven.
1569
+ // ===========================================================================
1570
+
1571
+ // EVERY assertion below reads deps._calls.ship, not out.reconciled.
1572
+ //
1573
+ // That is deliberate and was learned the hard way while writing these: a first
1574
+ // draft used a shipTask stub that THREW on any call, on the theory that a
1575
+ // phantom ship should be impossible to miss. It is exactly backwards — the sweep
1576
+ // isolates per-row failures on purpose (one bad row must not stop the rest), so
1577
+ // the throw was swallowed, counted as an error, and `reconciled` stayed 0. The
1578
+ // test passed while a phantom ship happened. `reconciled` is a summary of what
1579
+ // the sweep BELIEVES it did; the recorded call is what it actually did to the
1580
+ // ledger, and only the second one can answer this question.
1581
+
1582
+ test('vector 1: a green merge is NOT a land — mergeIfGreen alone never ships', async () => {
1583
+ // The exact 1409/1410 shape: the server merges the PR itself (auto-merge
1584
+ // stall), but the status still reports not-landed and no ancestry proof
1585
+ // exists. A merge ATTEMPT is not evidence the commit reached main.
1586
+ const deps = mkDeps({
1587
+ rows: [{ task_id: 4001, builder_id: 7, worktree_name: 'wt-4001', published_head_sha: NOT_ON_MAIN }],
1588
+ statusByBranch: { 'claude/wt-4001': PENDING },
1589
+ mergeByBranch: { 'claude/wt-4001': { merged_now: true, number: 900 } },
1590
+ ancestryBySha: { [NOT_ON_MAIN]: false },
1591
+ });
1592
+ await reconciler.runSweep(deps);
1593
+ assert.deepEqual(deps._calls.ship, [],
1594
+ 'a merged-now PR with no ancestry proof must never reach db.shipTask');
1595
+ assert.ok(deps._calls.merge.includes('claude/wt-4001'),
1596
+ 'the sweep still nudges the merge — it just does not treat the nudge as evidence');
1597
+ });
1598
+
1599
+ test('vector 1: the SAME row ships once the tip is provably on main', async () => {
1600
+ // The control for the test above. Identical inputs except the ancestry answer,
1601
+ // so the assertion isolates exactly the proof — not some unrelated refusal.
1602
+ const deps = mkDeps({
1603
+ rows: [{ task_id: 4002, builder_id: 7, worktree_name: 'wt-4002', published_head_sha: ON_MAIN }],
1604
+ statusByBranch: { 'claude/wt-4002': PENDING },
1605
+ mergeByBranch: { 'claude/wt-4002': { merged_now: true, number: 901 } },
1606
+ ancestryBySha: { [ON_MAIN]: true },
1607
+ });
1608
+ const out = await reconciler.runSweep(deps);
1609
+ assert.equal(out.reconciled, 1);
1610
+ assert.deepEqual(deps._calls.ship, [{ taskId: 4002, builderId: 7 }]);
1611
+ });
1612
+
1613
+ test('vector 1: an OPEN PR with no proof strands rather than ships', async () => {
1614
+ // "shipped while the PR was still open" was the reported symptom. A row the
1615
+ // sweep cannot prove must leave the ledger alone and be counted unprovable.
1616
+ const deps = mkDeps({
1617
+ rows: [{ task_id: 4003, builder_id: 7, worktree_name: 'wt-4003', published_head_sha: NOT_ON_MAIN }],
1618
+ statusByBranch: { 'claude/wt-4003': PENDING },
1619
+ ancestryBySha: { [NOT_ON_MAIN]: false },
1620
+ });
1621
+ const out = await reconciler.runSweep(deps);
1622
+ assert.deepEqual(deps._calls.ship, [], 'an open PR with no proof must never reach db.shipTask');
1623
+ assert.ok((out.unprovable ?? 0) >= 1, 'an unprovable row must be COUNTED, not silently skipped (blocker 47)');
1624
+ });
1625
+
1626
+ test('vector 1: a transient proof error never resolves to "landed"', async () => {
1627
+ // The compare API throwing is indistinguishable from "no proof yet", and the
1628
+ // fail direction must be to wait. An exception that fell through to a ship
1629
+ // would be the phantom-ship bug wearing an error handler.
1630
+ const deps = mkDeps({
1631
+ rows: [{ task_id: 4004, builder_id: 7, worktree_name: 'wt-4004', published_head_sha: ON_MAIN }],
1632
+ statusByBranch: { 'claude/wt-4004': PENDING },
1633
+ ancestryBySha: { [ON_MAIN]: new Error('compare API 502') },
1634
+ });
1635
+ await reconciler.runSweep(deps);
1636
+ assert.deepEqual(deps._calls.ship, [],
1637
+ 'a throwing proof is indistinguishable from no proof — it must wait, never ship');
1638
+ });
1639
+
1640
+ test('vector 2: shipped_at is stamped ONLY by shipTask, and shipTask requires confirmed', async () => {
1641
+ // ADR 0120 moved the payout and the stamp to the land. The structural claim —
1642
+ // that no other write path can stamp shipped_at on a task, and that the one
1643
+ // that can refuses anything not already confirmed — is what closes the
1644
+ // grader-bypass vector, so it is asserted against the SOURCE rather than
1645
+ // re-argued in prose.
1646
+ const fs = await import('node:fs');
1647
+ const url = await import('node:url');
1648
+ const path = await import('node:path');
1649
+ const ROOT = path.dirname(path.dirname(url.fileURLToPath(import.meta.url)));
1650
+ const dbSrc = fs.readFileSync(path.join(ROOT, 'modules/lifecycle/db.js'), 'utf8');
1651
+
1652
+ // Every statement that writes shipped_at, ignoring SELECT projections and comments.
1653
+ const writes = [...dbSrc.matchAll(/shipped_at\s*=\s*now\(\)/g)];
1654
+ assert.equal(writes.length, 1,
1655
+ `shipped_at is written in ${writes.length} places — a second stamp site is how the grader-bypass vector reopens`);
1656
+
1657
+ // …and that one site is guarded on the confirmed state.
1658
+ const stampIdx = dbSrc.indexOf('shipped_at = now()');
1659
+ const window = dbSrc.slice(stampIdx, stampIdx + 500);
1660
+ assert.match(window, /WHERE id = \$1 AND status = 'confirmed'/,
1661
+ 'the stamp must refuse any task not already confirmed — that is what stops a confirm-time ship');
1662
+ });
1663
+
1664
+ test('vector 2: the grader-bypass confirm path lands at CONFIRMED, never shipped', async () => {
1665
+ // The bypass relaxes WHO may confirm and whether a panel ran. It must not
1666
+ // change WHERE the task lands: the route calls confirmTask, and only the
1667
+ // proven-land path may call shipTask.
1668
+ const fs = await import('node:fs');
1669
+ const url = await import('node:url');
1670
+ const path = await import('node:path');
1671
+ const ROOT = path.dirname(path.dirname(url.fileURLToPath(import.meta.url)));
1672
+ const routeSrc = fs.readFileSync(path.join(ROOT, 'modules/lifecycle/routes/tasks.js'), 'utf8');
1673
+
1674
+ // Anchor on the ROUTE, not on `graderBypassEnabled()`. That identifier appears
1675
+ // three times — the function definition, a rank-gate relaxation, and the
1676
+ // handler's log line — so an indexOf anchor measures whichever comes first and
1677
+ // then reads the wrong window. Slice the confirm handler itself: from its
1678
+ // registration to the next route registration.
1679
+ const confirmIdx = routeSrc.indexOf("router.post('/tasks/:id/confirm'");
1680
+ assert.ok(confirmIdx > -1, 'the confirm route must exist — update this test if it moved');
1681
+ const afterIdx = routeSrc.indexOf('router.', confirmIdx + 20);
1682
+ const handler = routeSrc.slice(confirmIdx, afterIdx > -1 ? afterIdx : confirmIdx + 3000);
1683
+
1684
+ assert.match(handler, /db\.confirmTask\(/,
1685
+ 'the confirm route must land the task at CONFIRMED via confirmTask');
1686
+ assert.ok(!/db\.shipTask\(/.test(handler),
1687
+ 'a shipTask call on the confirm path IS the grader-bypass phantom-ship vector, reopened');
1688
+ assert.ok(!/shipped_at/.test(handler),
1689
+ 'the confirm path must never touch shipped_at — that stamp belongs to the proven land');
1690
+ });
1691
+
1550
1692
  setTimeout(() => {
1551
1693
  console.log(`\npublish reconciler (task 1134 + 1915) tests: ${passed} passed, ${failed} failed`);
1552
1694
  process.exit(failed > 0 ? 1 : 0);
@@ -0,0 +1,67 @@
1
+ // tests/redteam_patrol.mjs — the patrol's pure helpers (task 1003128, goal 1000076).
2
+ // Hermetic: parsing, severity mapping, pin detection, report assembly. The live
3
+ // scans (gitleaks/npm/network) and the --canary end-to-end pipe are exercised by
4
+ // hand + the weekly cron, never in the DB-free suite.
5
+ import assert from 'node:assert/strict';
6
+ import { test } from 'node:test';
7
+ import { createRequire } from 'node:module';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const require = createRequire(import.meta.url);
12
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
+ const p = require(path.join(ROOT, 'scripts', 'gds', 'redteam-patrol.js'));
14
+
15
+ test('parseGitleaksReport maps rows to high-severity findings; junk → []', () => {
16
+ const rows = JSON.stringify([{ RuleID: 'aws-key', File: 'x.js', Commit: 'abcdef1234567890' }]);
17
+ const f = p.parseGitleaksReport(rows);
18
+ assert.equal(f.length, 1);
19
+ assert.equal(f[0].severity, 'high');
20
+ assert.match(f[0].item, /aws-key in x\.js @ abcdef12/);
21
+ assert.deepEqual(p.parseGitleaksReport('not json'), []);
22
+ assert.deepEqual(p.parseGitleaksReport('{}'), []);
23
+ });
24
+
25
+ test('severity mapping: npm "moderate" → "medium"; unknowns floor to low; order respected', () => {
26
+ assert.equal(p.mapSeverity('moderate'), 'medium');
27
+ assert.equal(p.mapSeverity('HIGH'), 'high');
28
+ assert.equal(p.mapSeverity('weird'), 'low');
29
+ assert.equal(p.maxSeverity([{ severity: 'low' }, { severity: 'high' }, { severity: 'medium' }]), 'high');
30
+ assert.equal(p.maxSeverity([{ severity: 'low' }]), 'low');
31
+ });
32
+
33
+ test('unpinnedUses flags tags/branches, accepts 40-hex SHAs, skips local + docker refs', () => {
34
+ const yaml = [
35
+ ' - uses: actions/checkout@v4',
36
+ ' - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0',
37
+ ' - uses: ./.github/actions/local',
38
+ ' uses: actions/cache@main',
39
+ ].join('\n');
40
+ const bad = p.unpinnedUses(yaml);
41
+ assert.ok(bad.includes('actions/checkout@v4'));
42
+ assert.ok(bad.includes('actions/cache@main'));
43
+ assert.ok(!bad.some((r) => r.includes('setup-node')), 'a 40-hex SHA pin is accepted');
44
+ assert.ok(!bad.some((r) => r.includes('local')), 'local ./ ref skipped');
45
+ });
46
+
47
+ test('ageDays computes against a fixed now; bad dates → null', () => {
48
+ const now = Date.parse('2026-08-19T00:00:00Z');
49
+ assert.equal(Math.round(p.ageDays('2026-08-12T00:00:00Z', now)), 7);
50
+ assert.equal(p.ageDays('not-a-date', now), null);
51
+ });
52
+
53
+ test('lockedVersion reads the npm v3 lock shape', () => {
54
+ const lock = { packages: { 'node_modules/undici': { version: '6.27.0' } } };
55
+ assert.equal(p.lockedVersion(lock, 'undici'), '6.27.0');
56
+ assert.equal(p.lockedVersion(lock, 'absent'), undefined);
57
+ });
58
+
59
+ test('buildClassReport caps the item list, counts the remainder, and takes max severity', () => {
60
+ const findings = Array.from({ length: 13 }, (_, i) => ({ severity: i === 0 ? 'high' : 'low', item: `f${i}` }));
61
+ const r = p.buildClassReport('dependency-review', findings, ['a note']);
62
+ assert.equal(r.severity, 'high');
63
+ assert.equal(r.target, 'redteam-patrol/dependency-review');
64
+ assert.match(r.title, /13 finding/);
65
+ assert.match(r.repro_steps_md, /…and 3 more/);
66
+ assert.match(r.repro_steps_md, /Notes:/);
67
+ });