4790 Commits
Author SHA1 Message Date
jason.woltje 180fb8ab00 fix(row 333 follow-ups R3): a spread before import is a dynamic import (Vikunja #779)
The negative lookbehind in DYNAMIC and in the dynamic branch of IMPORT excluded any dot
before `import`. That also skipped the last dot of the spread punctuator, so
`${({...import('../x.mjs')})}` returned [] for an existing, a missing and an outside
target, and `{...import(name)}` in plain code was neither returned nor refused.

Both patterns now share one fragment: a word character or $ before `import`, or a single
dot (x.import, x?.import), makes it a property call; a dot that follows another dot ends a
spread and does not exclude it. `1..import(` is read as an import too, which errs toward
refusal; the header names it.

Tests: a spread inside a substitution refuses for an existing, a missing and an outside
target and for array spread; a spread in plain code is returned, reaches the target
checks, and is refused when its argument is not a plain string; x.import and x?.import
stay property calls in plain code and in a substitution.
2026-11-02 21:13:56 -06:00
jason.woltje 42c72d781b docs(row 333 follow-ups R2): BUILD-LOG and SESSIONS lines (Vikunja #779) 2026-11-02 21:00:22 -06:00
jason.woltje d3b6ab11da test(row 333 follow-ups R2): refuse a dynamic import inside a template substitution, read division after ++ and -- (Vikunja #779)
The business-imports helper blanked a whole template literal, so a dynamic import inside a
${...} substitution was neither found nor refused and skipped the existence and containment
checks. The scanner now reads a substitution to its closing brace, refuses it with
"unsupported import inside a template substitution" when it holds a dynamic import, and drops
it otherwise, so a nested template and a brace inside a string no longer end it early.

A slash after a postfix ++ or -- was read as the start of a regular expression, so
`n++ / 2` threw "unterminated regular expression". After a ++ or -- pair that follows an
operand the slash is division; after one that follows an operator it still starts a regular
expression. The header names the rare form this still misreads.

Output on the real tree is byte-identical.
2026-11-02 20:59:35 -06:00
jason.woltje a605e44d1e docs(row 332): BUILD-LOG and SESSIONS lines for the proxy first check fix (Vikunja #774) 2026-11-02 20:52:59 -06:00
jason.woltje 85ecdfbde5 fix(row 332): proxy first check runs every key-independent refusal; testProxy test option (Vikunja #774)
F1: with system.proxy set, the first launcher check no longer returns
before the account, sandbox, capacity and limit checks. It skips only the
harness preflight and the session proxy key match, which need the key P2
admits in startLocked. A launch that can never start now refuses before
authorizeLaunch, the workspace and checkouts, and modelPreflight. The first
proxy pass accepts only credential "proxy" for a declared account, tries
the fallback model when the primary refuses, and lets mayLaunch
key-quota-no-model-key through so P2 can issue the first key. The final
check is unchanged and still runs before newRun and spawn.

F4: the proxy test option is renamed testProxy and createLauncher throws
test-proxy-refused when it is given with system.proxy.
launch-account.test.mjs is a second caller, so the option stays.

Tests: proxy-launch-order.test.mjs (6), launcher.test.mjs F4 throw test;
the managed launch fixture gains bwrap, admit and trace.ops.
2026-11-02 20:51:51 -06:00
jason.woltje 2ba22b4cbf docs(row 333 follow-ups): BUILD-LOG and SESSIONS lines (#1831) 2026-11-02 20:41:10 -06:00
jason.woltje 5fbf3d2b51 test(row 333): hold the port a refusal test uses instead of probing and closing it (Vikunja #782)
control-board serve.test: the non-loopback refusal test took a port from a
probe-and-close helper and then called startServer on it. It now binds its
own listener, keeps it for the whole test, and closes it in finally, so the
port cannot be taken by another process in between.

stack-release.test T-I4: the free port goes only to webui-service.sh install
rows that refuse before any listen, so it is now a closed lease
(leaseClosedPort), which refuses connects and blocks other binds.

The 22 other freePort callers hand the number to `stack-release.sh rehearse`,
which probes it and starts a Console later. That needs a script change and
is left as a design, with a comment above freePort naming the window.
2026-11-02 20:22:14 -06:00
jason.woltje 0e7e203db8 test(row 333): harden the business-imports helper and test it on small trees (Vikunja #779)
The helper read imports with a regex that treated a commented import as live, missed
`import (` written with a space, skipped a missing target inside packages/business/src
before checking it existed, and resolved targets lexically. It now blanks comments
(and ignores import forms inside strings and regular expressions) before matching,
counts a spaced or line-broken dynamic import, throws on a dynamic import whose
argument is not a plain string and on a file with an unterminated string, comment,
template or regular expression, checks every target exists and is a regular file
before the inside-own-directory skip, and refuses a target whose real path is outside
the real repository root. The output for the real tree is unchanged.

business-imports.test.mjs builds small trees under a private temp directory.
2026-11-02 20:14:25 -06:00
jason.woltje 38cfa72335 feat(row 333): count dropped proxy usage batches and show them in the Console (Vikunja #778)
The usage queue read drains the proxy. A batch that is then not written (the
client or the bus refuses its shape, an ambiguous key owner, a failed write) is
lost, and the Console showed proxy totals with no sign of it. The reader now
counts these per enabled business since the bus started, the proxy view returns
the count as droppedBatches, and the Console proxy section says so above 0.
A read that failed before the proxy answered or that it refused drained nothing
and is not counted. Telemetry only; no quota reads it. The log line stays
proxy-usage-read-failed.
2026-11-02 20:08:50 -06:00
jason.woltje 3dc824ea12 fix(row 333): a failed lineage lookup forgets orphan ids the document dropped (Vikunja #783)
observeOrphans kept the whole remembered set when the lookup failed, so a leaf
removed and added back during the failure logged no second orphan line. It now
keeps only the ids the refreshed document still holds and adds none. Quota
decisions are unchanged: a failed lookup still refuses.
2026-11-02 20:05:28 -06:00
jason.woltje 1ff45c4ad6 Merge row 333: proxy usage and human model-key quotas, R4 (Gitea #1831) 2026-11-02 19:40:46 -06:00
jason.woltje 9c2f391a90 docs(row 333 R4): BUILD-LOG and SESSIONS lines (#1831) 2026-11-02 19:27:31 -06:00
jason.woltje a13050012a docs(row 333 R4): BUILD.md R4 section (#1831)
Rulings 1 to 4 as built, choices and departures, tests, runs, mutants M1 to M5, and what was not done. The R3 section is unchanged.
2026-11-02 19:27:04 -06:00
jason.woltje 8cf2eda9ff Merge flakes #780 and #781: project inspectGit maintenance race, held closed test ports
Branch t3/flakes-780-781-r2-fix (R1 7e0d3c93f, 1009bbfaf, 9944100ad; R2 1977f4ffc, 728442fce) on 925612922.
GPT review APPROVE on R2 (reviews/flakes-780-781-r2-gpt.md). Probe-then-bind follow-up is Vikunja #782.
2026-11-02 19:03:37 -06:00
jason.woltje f65faf9df8 fix(row 333 R4): a moved key quota keeps its count; orphan set on every refresh (#1831)
Ruling 1: the bus runtime passes launchSettings a key lineage lookup read
from its own credential records (ids only). A new daily key quota takes the
earliest stored since among the old document's daily key quotas in its key's
lineage, and now only when there is none. A reset still sets now, a
caller-supplied since still refuses, and a quota removed and added back in a
later write is new. A failed or malformed lookup refuses proxy-key-refused; a
write that adds no new daily key quota never calls it.

Ruling 2: every limits refresh updates the remembered orphan set, an empty
set when no daily key quota is left, so a quota removed and added back logs
again. In memory only: a restart is a fresh observation.

Ruling 3: pollSettledAfterTick yields on the real setImmediate, captured at
load, inside its deadline.

Tests: p5-key-lineage (writer move counts 160, reset restarts, lookup
failure, orphan remove and re-add), p5-runtime (exhausted on A, moved to B
through the real runtime, refuses), p5-background (barrier with every timer
mocked), token-limit-settings (earliest, no match, re-add, caller since,
malformed answers, default refusal). READMEs: business, plugins, daily-quota,
cliproxy-queue.
2026-11-02 19:02:45 -06:00
jason.woltje 728442fcef Record #780 R2 and correct the R1 entry in BUILD-LOG and SESSIONS (Vikunja #780)
Appends a BUILD-LOG entry that corrects the R1 entry without editing it: the
R1 walk change also dropped the recursive lstat, which R2 restores; the
tester-cli freePort helper has no callers and binds nothing; the stack-release
and control-board probe-then-bind window stays a known risk for a follow-up.
Appends one SESSIONS line.
2026-11-02 18:57:39 -06:00
jason.woltje 1977f4ffc6 Restore the fresh recursive lstat in project inspectGit (Vikunja #780)
R1 passed the parent's stat of a listed directory into the recursive walk,
so a recursive entry no longer took its own lstat. A directory replaced by a
symlink after that stat passed the stale directory check and readdirSync
followed the link; a directory replaced by a regular file leaked a raw
ENOTDIR instead of refusing with project-repository.

The walk now takes a root flag. The .git root keeps its raw lstat and its
own error; every recursive entry takes a fresh lstat through statEntry, so
a vanished entry still maps ENOENT to 'git metadata changed during
inspection' and a replaced one refuses with 'expected local .git directory'.
The mapper is unchanged: ENOENT only.

Two deterministic tests replace a directory under .git/objects inside the
lstat call that stats it as a listed entry, after the real stat is taken:
once with a symlink to a fixture-only directory outside the repo, once with
a regular file. Both refuse with project-repository.
2026-11-02 18:56:28 -06:00
jason.woltje 9944100ade Record the #780 and #781 flake fixes in BUILD-LOG and SESSIONS (Vikunja #780, #781) 2026-11-02 18:28:39 -06:00
jason.woltje 1009bbfaf9 Hold closed fixture ports for the test's life (Vikunja #781)
leasePort({closed:true}) ignored its option: it probed an ephemeral port and
closed it, so a parallel test file could bind it and answer a request the
test expected to fail (preview.test.mjs:209 saw 200 instead of 502).

leaseClosedPort holds a bound, non-listening socket in a perl child until the
test's after hook. Connects still get ECONNREFUSED, bind() from another
process gets EADDRINUSE, and the kernel's ephemeral allocation skips the port.
Lane mode keeps its band and per-port lock, and releases the socket before
the lock. leasePort now refuses a closed option, so a stale call cannot get
the unheld port again.

Unanswerable callers (webui preview, serve, service) move to leaseClosedPort(t).
Callers that listen on the port themselves (stack-release rehearse, control-board
serve, tester-cli) keep leasePort and drop the ignored option.
2026-11-02 18:25:27 -06:00
jason.woltje fe3ef5128d docs(row333 R3): BUILD-LOG and SESSIONS lines (#1831) 2026-11-02 18:12:22 -06:00
jason.woltje 0be9e024cf docs(row333 R3): BUILD.md R3 rotation fix section (#1831) 2026-11-02 18:12:04 -06:00
jason.woltje 7e0d3c93f3 Fix project inspectGit race with git maintenance (Vikunja #780)
Fixture helpers that commit to a repository the product later inspects now pass
maintenance.auto=false and gc.auto=0, as the product's own git calls already do,
so a detached git maintenance run no longer writes pack temp files mid-walk.

inspectGit now refuses a git metadata entry that vanishes between readdir and
lstat (or between listing a directory and reading it) with project-repository,
'git metadata changed during inspection', instead of leaking a raw ENOENT.
It still refuses rather than skipping or retrying. A missing .git keeps its
existing error.

A deterministic test removes the named entry from inside a replaced readdirSync,
for both a file and a directory.
2026-11-02 17:55:40 -06:00
jason.woltje 3692fca522 fix(row333 R3): per-key quota follows the agent's key lineage across rotation and re-issue (#1831)
Ruling 1: model records keep a bounded previous list (64 ids, oldest dropped)
of ids that left by rotation, grace expiry or re-issue; a fire removes the
record. A tokens.keys leaf matches and counts every id in the lineage that
holds it. Lineage ids are unique host-wide. An orphaned leaf limits nothing,
logs one value-free line when it becomes orphaned, and the Console usage
view lists it.
Ruling 2: an agent with no model key under a key quota refuses
key-quota-no-model-key, passed through the broker, launcher and stop reason.
Ruling 3: covered by the lineage; no mayLaunch change.
Ruling 4: the usage module receives a frozen { readUsageQueue } only.
Ruling 5: every telemetry fixture wait races a 5 s real-timer deadline.
2026-11-02 17:54:07 -06:00
jason.woltje 9256129228 PR 1838 review P3s (#725): approved wording in the tasks requirements test header, REBASE-1.19 location correction
GPT review of the 1.19 rebase found the test header still called the PRD a candidate and the rebase record placed lines 793 and 645 in the wrong requirements. Comment and record only; no assertion changes.
2026-11-02 17:29:16 -06:00
jason.woltje 31222d81d7 Merge PR 1838 (#725): disk-space governance PRD as version 1.19
Rebased onto refactor as 1.19 because row 335 landed 1.18 first. Requirement text, IDs, order and the 101
records match the approved fd9ae104d apart from version labels. GPT review APPROVE with two P3s, fixed in the
next commit.
2026-11-02 17:29:03 -06:00
jason.woltje 3d00efe39c Record PR 1838 rebase as PRD 1.19 (PR 1838, #725)
Adds agents/gosalyn/work/disk-prd118/REBASE-1.19.md: the cherry-pick
conflicts and their resolutions, every label moved to 1.19 and every 1.18
kept, the byte check against fd9ae104d with version labels normalized, and
the test runs and version mutant.
2026-11-02 17:00:55 -06:00
jason.woltje 9f817e6961 Renumber disk-space governance to PRD version 1.19 (PR 1838, #725)
Row 335 landed PRD version 1.18 first, for the REQ-INST-2 correction. The
disk-space governance text that Jason approved as 1.18 in Q289 therefore
lands as 1.19. Only version labels move: the status line, the disk version
source bullet, the "since version" labels in the disk sections, the disk
milestone, and requirements.json "version". The requirement text, IDs,
order, defaults, settings and the 101 records are unchanged.

The prior approval history bullet now runs through version 1.18 and carries
the row 335 lines. The two test pins follow the renumbering.
2026-11-02 16:58:30 -06:00
jason.woltje 80c4a37396 gosalyn: record Jason's Q289 approval of PRD 1.18 2026-11-02 16:55:13 -06:00
jason.woltje 259e59d401 docs(prd): propose disk governance in version 1.18 (#725) 2026-11-02 16:52:04 -06:00
jason.woltje b37cc50878 docs(row333): join onto landed row 332 record (#1831)
Records the replayed join of row 333 R2 (0ef269da9) onto landed row 332 (5df0e9d6f) in merge c0c291cda: the two conflict hunks and their resolution, the comparison with the earlier join resolution, and the nine suite results. Adds the docs/SESSIONS.md line.
2026-11-02 16:50:14 -06:00
jason.woltje ea02c061e4 Merge row 227 (C2-14, #1728) R3: Case 2 end-to-end suite that runs alone with a cleanup that cannot touch other processes 2026-11-02 16:46:36 -06:00
jason.woltje dca9bc8691 Row 227 R3 (#1728): restore the executable bit on scripts/test-case2.sh
R3 (abc66a598) changed its mode from 100755 to 100644. Every other scripts/test-*.sh is 100755.
The landing gate runs it through bash, so no run broke; this restores the R2 mode.
2026-11-02 16:35:54 -06:00
jason.woltje 9a9c60d9c0 row 227 R3 (#1728): record the round in BUILD.md, BUILD-LOG and SESSIONS
Appends the R3 section to agents/steelbeak/work/queue-227/BUILD.md: the
R2 red on dummy links, the fixes, how the TMPDIR limit is derived, the
namespace containment and control table, runs, signal probes, the tip
check on 5df0e9d6f and follow-ups. One line each in BUILD-LOG.md and
docs/SESSIONS.md. Earlier entries are unchanged.
2026-11-02 16:34:22 -06:00
jason.woltje c0c291cda2 merge: join row 333 P5 quotas onto landed row 332 (#1831)
Joins row 333 R2 head 0ef269da9 (base 09a847eda: R1 503b91e56, overlay
2627321f6, docs c012b2e0a and ddb3e961f, R2 fixtures 12eba4b59 and
0ef269da9) onto refactor with row 332 landed at 5df0e9d6f. Run with the
union attributes for BUILD-LOG.md and docs/SESSIONS.md. Two files
conflicted; no other path did.

packages/business/src/launch.mjs, import block: 332 changed the errors
import to `{ BusinessError, refuse }` for the proxy route refusals; 333
added `isDailyModelQuota` from plugins/src/model-quota.mjs for the daily
model leaves under limits.tokens.keys. Kept both: the model-quota import
and the `{ BusinessError, refuse }` import. The rest of launch.mjs merged
without conflict (332's checkModels route handling and checkAccount
credential "proxy"; 333's checkTokenLimits keys block).

packages/plugins/README.md, end of file: 332 appended "Per-session proxy
context (P3)" and 333 appended "Proxy usage telemetry (row333 / P5)" at
the same spot. Kept both sections, P3 first, one blank line between.

The resolved files match the earlier 332 R1 join resolution
(join-resolution.patch) line for line apart from hunk offsets.
2026-11-02 16:33:47 -06:00
jason.woltje 0ef269da9b docs(row333 R2): fixtures section in BUILD.md, cause, production check, RED and GREEN counts (#1831) 2026-11-02 16:31:43 -06:00
jason.woltje abc66a5988 row 227 R3 (#1728): guard the test-case2.sh sandbox, sweep cwd links literally, refuse a long TMPDIR
P1: a failed mktemp, mkdir or output redirect now stops the script with
exit 2 before it derives a path, sets a trap or starts anything. The
sandbox root is checked to be absolute, a new empty directory this user
owns and its own physical path, and its device:inode is recorded.
sweep_sandbox and remove_sandbox in scripts/case2/sandbox.sh each refuse
unless the root still matches, so an early, late or empty call gets a
refusal instead of a sweep of the host.

P2: the sweep reads every /proc/<pid>/cwd target with one find and
compares it as a string, root or root plus "/", so *, ? and [ in the
path are literal. A cwd in a deleted directory under the root matches.

P3: a TMPDIR whose tools socket path would pass the 107-byte limit in
packages/seat/src/session.mjs is refused at entry with one line naming
the limit and both lengths. The length comes from the socket path built
on the real sandbox root.

scripts/case2/sandbox-check.sh checks the guard and the match against
dummy symlinks; nothing in it sends a signal.
2026-11-02 16:27:56 -06:00
jason.woltje 12eba4b599 test(row333 R2): release fixtures carry the plug-in files business imports (#1831)
Row 333 made packages/business/src/launch.mjs and organization.mjs import
packages/plugins/src/model-quota.mjs. The stack-release fixtures copy
packages/business/src and added only plugins/src/kinds.mjs, so their copies
failed with 'Cannot find module .../model-quota.mjs'. Whole cli runs on the
row 333 tip gave stack-release 56 pass 168 fail, stack-release-external
4/21 and stack-upgrade 7/14.

business-imports.mjs reads the imports of packages/business/src and returns
the files outside it that they reach, so both fixture builders copy what
business needs instead of a hard-coded name. No product code, schema or
assertion changes.
2026-11-02 16:27:43 -06:00
jason.woltje 5df0e9d6fa Merge row 332 (P3, #1830) R2 with the lead fix: pi harness config from the proxy, hermetic pi launches in tests 2026-11-02 15:48:51 -06:00
jason.woltje bd76d43453 row 227 R2 (#1728): record the standalone round in BUILD.md, BUILD-LOG and SESSIONS
Appends the "R2" section to agents/steelbeak/work/queue-227/BUILD.md: findings and fixes,
no step failures past launchFixture, the environment and signal controls, the mutant table
(m6 on requireCitation survived and is a follow-up), run times and the tip check. One line
each in BUILD-LOG.md and docs/SESSIONS.md. Earlier sections are unchanged.
2026-11-02 15:48:06 -06:00
jason.woltje a8e71be034 Row 332 R2 (#1830): lead correction for the GPT review F1 and F2
F1 fixed in da1f0ceda, with the federation acceptance red and green runs and
the script suite results. F2: the invalid-proxy-context BundleError is raised
at launcher.mjs:569, outside the try at :655, so :686 never sees it;
fromSession maps it at :1199 and launchPm rethrows it at :1215.
2026-11-02 15:47:39 -06:00
jason.woltje da1f0ceda6 Row 332 R2 (#1830): give the federation acceptance hosts a fake pi key
GPT review F1: prepare() built the host env from HOME, PATH, TMPDIR,
MOSAIC_CONFIG and NODE_EXTRA_CA_CERTS only, so the pi credential preflight
refused the assistant launch (no-session-credential) and the RESULT wait
failed. scripts/test-federation-acceptance.sh red on 6e9f3d723, green with
the fake ZAI_API_KEY the cli tests use.
2026-11-02 15:46:57 -06:00
jason.woltje 04e1ce24cc row 227 R2 (#1728): run test-case2.sh standalone, without the slot controller
Remove the R1 slot-controller ties from the C2-14 fixture: the ROW227_CONTROL_ROOT
check, the native permit wait in bindLaunch, the fixture-cleanup.json write, the
priority.mjs preload and the owned-bwrap.sh override. The launcher uses its own bwrap.

The shell runs the driver from an explicit env -i environment (PATH, LANG, a sandbox
HOME, XDG dirs and TMPDIR, MOSAIC_CONFIG, no global git config) as its own process
group. On a signal the driver gets 3 s by the clock, then its group and any process
whose cwd is in the sandbox get SIGKILL, inside the landing gate's 5 s grace.

The fixture credentials are defined only in scripts/case2/fixture-credentials.mjs.
The new scripts/case2/leak-check.mjs reads them from there and never prints them.
2026-11-02 15:41:17 -06:00
jason.woltje 6e9f3d7231 Row 332 R2 (#1830): record R2 evidence in fethry BUILD.md
Append-only R2 section: correction to R1's evidence, the two causes of the
53 cli failures, items 1 to 6 with their tests, RED, hermetic and GREEN
tables, five killed mutants on 39eadf8ba, and the F1, F4 and F2 docs
follow-ups.
2026-11-02 15:24:56 -06:00
jason.woltje 39eadf8ba8 Row 332 R2 (#1830): hermetic pi launches in cli tests, pi bundle default, review F2 and F3
The cli tests that launch pi deleted only the session login names, so pi's
preflight passed or refused by whatever provider keys the host exported. A
shared helper now deletes every credential pi's preflight reads and gives pi
launches one made-up key. The refusal itself is unchanged.

pi bundle() takes a default argument again, so a call with none returns the
non-proxy bundle, as before P3. pi.bundle leaves out a launch.models entry
whose provider has no proxy route instead of failing every pi launch (F2);
malformed proxy entries still refuse. The invalid session proxy context
BundleError gets the code invalid-proxy-context. New tests cover F2, the code,
and the three proxy account branches in the launcher (F3).
2026-11-02 15:22:17 -06:00
jason.woltje fddae79ef6 queue: commit pending rev 4144 (sage, 2026-10-26, row 333 note Mos1961), committed by lead c5c1e8ba 2026-11-02 15:07:37 -06:00
jason.woltje a6acbbe14d SESSIONS: darkwing 10-26 status line and the lead session registration 2026-11-02 15:05:37 -06:00
jason.woltje 46cd8d5835 gitignore: ignore the .turbo build cache
210 untracked cache files at the root (inventory canonical-cleanup-inventory.md). turbo writes them; nothing tracked lives there.
2026-11-02 15:05:19 -06:00
jason.woltje aba5cc505f lead: commit 34 untracked publication-trial review records under docs/plans/reviews as evidence
Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
2026-11-02 15:05:15 -06:00
jason.woltje 8545057275 lead: commit sage's untracked evidence (100 files: prd-1-17 queue-135 queue-176 queue-199 queue-66 vikunja-setup window-repin-4429a104f)
Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
2026-11-02 15:05:15 -06:00
jason.woltje 068d868a99 lead: commit rocko's untracked evidence (4471 files: queue-124 queue-129 queue-171 queue-178 queue-280 queue-31 queue-84 queue-85 queue-94)
Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
2026-11-02 15:05:15 -06:00