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.
2 · The gate graph, drawn by pr-lens
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.
active / builtpartialstub — declared, never built
Signal propagation, animated (SMIL, 5 steps):
Entry & auth
Money
3 · pr-lens — what I took, what I refused
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 skill skills/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 · canvas pr-lens canvas *
REJECT
1,340 LOC of hosted token/registry/lock machinery against prlens.dev. Newest, least-settled code in the repo.
CLI · analyze pr-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 Action packages/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.
4 · Security scan — read-only, nothing executed
CLEAN
Planted prompt injection
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.
RISK
AGENTS.md:9 — third-party instruction fetch
Directs the agent to run copy through github.com/blader/humanizer SKILL.md. Whoever controls that repo controls instructions this repo hands your agent.
RISK
AGENTS.md:7 — names an out-of-repo secrets dir
Tells an agent ~/Documents/dev/pr-lens-secrets/ exists, in order to forbid it. One negated commit from bait.
BUG
ReDoS via .github/pr-lens.yml globs
renderer/src/glob.ts:8 builds RegExp from a 256-char attacker selector; ~40 nested .* → catastrophic backtracking. CI DoS.
BUG
Symlink-following registry write
cli/src/io.ts:45 writes <path>.<pid>.tmp with no flag:'wx' / O_NOFOLLOW; predictable name, 0600 lost on pre-existing target.
GOOD
Untrusted PR text → comment
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.
5 · SoT findings this session
SOT
Gate status enum drift
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'.
SOT
22 gates carry no domain
Of 77 gate files parsed, 22 have no domain: field — they fall through every domain-keyed tool.
SOT
70 doctrines invisible to INDEX
doctrine_census: 154 on disk, 84 named in INDEX.md, 70 never named. They exist but no tool that reads INDEX can see them.
SOT
REVIEW.md drain rule cannot fire
185,492B vs 175,000B cap. Its own header says drain only terminal RESOLVED entries — but 37 say OPEN and 29 carry no status at all.
SOT
21 REVIEW ids reused
Numbering was reused across eras, so a bare REVIEW-NNN citation is ambiguous for 21 numbers.
SOT
72 stale PENDING entries
Oldest 91 days. DEAD_STUB is 0, so these are queue mass, not broken pointers.
6 · Fleet heartbeat
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.