Autonomous session · 2026-09-03 · verification chain, pr-lens assessment, and the gate graph drawn by the tool being assessed.
| command | result | what it said |
|---|---|---|
tools/validate.py | exit 0 | 0 violations · 1 warning · clarity 0.54 · traversal 1.00 · 153 truth rows, 117 proved |
tools/proof.py --summary-only | exit 0 | 70 gates · 57 with primary_file · aligned 50/57 · TABLE_UNCOVERED 10 |
tools/doctrine_census.py --check | exit 1 → 0 | INDEX census block was STALE — synced, now current |
tools/meta-consistency.py | exit 1 | 7 pass · 4 warn · 2 err → REVIEW.md oversize + census (census fixed) |
tools/review-collisions.py | exit 0 | 21 REVIEW ids genuinely reused across eras |
tools/rag/sync.sh | exit 0 | index was ~68h stale — re-ingested |
tools/guard-check.py | exit 2 | requires --app-dir; not runnable bare (CLAUDE.md shows it bare) |
tools/graph-export.py | exit 0 | emits MERMAID, not JSON as CLAUDE.md claims |
Every row was run this session. validate.py is genuinely clean — 0 violations. The two bug/warn rows at the bottom are CLAUDE.md describing tools that do not behave as documented.
73 gates → 4 lanes → 25 propagation edges, rendered through @coldtea/pr-lens-renderer as a library. delta is repurposed to carry build status instead of diff status, which is the one real cost of adopting their IR.
Signal propagation, animated (SMIL, 5 steps):
| part | call | why |
|---|---|---|
Renderer@coldtea/pr-lens-renderer | VENDOR | Hand-rolled layout + SMIL SVG. Zero deps beyond its schema. No network, no fs, no clock, no process.env. Pure data→string. |
Schema@coldtea/pr-lens-schema | VENDOR | Zod IR + referential-integrity pass + patch/apply semantics. Reports every issue at once. |
Agent skillskills/pr-lens/ | REJECT | AGENTS.md tells the agent to fetch and obey a third-party SKILL.md (blader/humanizer). Live second-order injection channel. |
CLI · canvaspr-lens canvas * | REJECT | 1,340 LOC of hosted token/registry/lock machinery against prlens.dev. Newest, least-settled code in the repo. |
CLI · analyzepr-lens analyze | REJECT | --api-key-env reads ANY env var; --base-url ships it to ANY host. One-line exfil primitive in an agent context. |
GitHub Actionpackages/action | REJECT | Runs npx --yes CLI in CI, version-pinned but not integrity-pinned. Not needed for our use. |
Rustify: no. ~6,000 LOC is portable and the golden SVGs would be a free conformance suite, but the work is O(V+E) over documents capped at 256 nodes — there is no hot loop. The only honest argument is a single static binary with no Node in CI.
No hidden Unicode (0 chars in Cf/Co/Cs/Cn), no HTML comments in any .md, no base64 blobs, 49 SVGs free of script/onload/foreignObject, installed skill byte-identical to source.
Directs the agent to run copy through github.com/blader/humanizer SKILL.md. Whoever controls that repo controls instructions this repo hands your agent.
Tells an agent ~/Documents/dev/pr-lens-secrets/ exists, in order to forbid it. One negated commit from bait.
renderer/src/glob.ts:8 builds RegExp from a 256-char attacker selector; ~40 nested .* → catastrophic backtracking. CI DoS.
cli/src/io.ts:45 writes <path>.<pid>.tmp with no flag:'wx' / O_NOFOLLOW; predictable name, 0600 lost on pre-existing target.
comment.ts:23 escapes, collapses to one line, wraps every model string in an element so GitHub won't parse markdown, and inserts U+200B after @/# so nothing autolinks.
The renderer emits global class selectors (.card, .lanebox, .ghost). Inline two SVGs in one HTML document and every stylesheet applies to every SVG; the last one wins. Found the hard way: this page's light diagram was painted with the dark palette until the SVGs were split back into separate documents behind <picture>. Invisible in pr-lens's own <img src> use.
CLAUDE.md declares stub|partial|active|proved. On disk: partial 36, stub 28, active 7, pruned 4, built 2 — 'built' and 'pruned' undeclared, and NOTHING is 'proved'.
Of 77 gate files parsed, 22 have no domain: field — they fall through every domain-keyed tool.
doctrine_census: 154 on disk, 84 named in INDEX.md, 70 never named. They exist but no tool that reads INDEX can see them.
Tried a tight rule: archive only when the entry TITLE names a doctrine that is RATIFIED (L1, no draft marker). 7 titles matched on disk; 4 were still candidate/draft and 1 (REVIEW-228) proposes an extension to an already-ratified doctrine. Only 2 were genuinely answered — archived those. 184,510B vs the 175,000B cap: still over by 9,510. The low yield IS the finding: here the doctrine file is authored as the candidate companion in the SAME session as the REVIEW entry, so existence on disk proves the entry was written, not answered. A looser rule would have destroyed 5 open questions to save 22KB.
Numbering was reused across eras, so a bare REVIEW-NNN citation is ambiguous for 21 numbers.
Oldest 91 days. DEAD_STUB is 0, so these are queue mass, not broken pointers.
| app | last touched |
|---|---|
actionpack | 15d ago |
compliance-ledger | 15d ago |
haven | 15d ago |
feedy | 35d ago |
bigdonkeymailer | 57d ago |
Doctrine census: L0 system 54 · L1 cross-app 51 · L2 app-local 27 · L3 candidate 22 = 154 total, 0 unclassified, 0 orphans — but 70 never named in INDEX.md.