SoT Fleet Console

Autonomous session · 2026-09-03 · verification chain, pr-lens assessment, and the gate graph drawn by the tool being assessed.

77gates9active36partial28stub 154doctrines0violations1open err72stale pending

1 · Verification chain

commandresultwhat it said
tools/validate.pyexit 00 violations · 1 warning · clarity 0.54 · traversal 1.00 · 153 truth rows, 117 proved
tools/proof.py --summary-onlyexit 070 gates · 57 with primary_file · aligned 50/57 · TABLE_UNCOVERED 10
tools/doctrine_census.py70 → 0 unnamed154 doctrines, all now named in INDEX.md (was 84 of 154)
tools/meta-consistency.py7 pass → 10 pass4 warn → 1 warn · 2 err → 2 err (census fixed, PENDING newly red)
tools/review-collisions.pyexit 021 REVIEW ids genuinely reused across eras
tools/rag/sync.shexit 0RAG was DOWN (docker daemon stopped) + index ~68h stale — restarted, status+pending re-ingested
tools/rag/ingest.py --type snapshot|memoryexit 1FAILED — 15 chunks unembedded. embed() has a 30s timeout and retries by HALVING the text, never raising it; on this loaded box even the 750-char third attempt takes 41.6s. Deterministic failure, not flaky.
tools/guard-check.pyexit 2requires --app-dir; not runnable bare (CLAUDE.md shows it bare)
tools/graph-export.pyexit 0emits 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 / built partial stub — declared, never built
overview diagram

Signal propagation, animated (SMIL, 5 steps):

overview diagram

Entry & auth

lane entry diagram

Money

lane money diagram

3 · pr-lens — what I took, what I refused

partcallwhy
Renderer
@coldtea/pr-lens-renderer
VENDORHand-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
VENDORZod IR + referential-integrity pass + patch/apply semantics. Reports every issue at once.
Agent skill
skills/pr-lens/
REJECTAGENTS.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 *
REJECT1,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
REJECTRuns 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.

BUG

Unscoped <style> — self-contained only in isolation

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.

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 — now 0

Root cause was NOT 70 omissions. 8 were already in the tables but invisible: the parser regex is ^|\s*(DOCTRINE-[A-Z0-9-]+)\s*| and the cell read '| DOCTRINE-MEMBRANE-1 *(draft)* |'. 16 more were listed under abbreviated names that resolve to no file. A parser-shaped hole, not a discipline problem. Closed in ae7ccc2.

SOT

REVIEW.md cap needs a decision, not a drain

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.

SOT

21 REVIEW ids reused

Numbering was reused across eras, so a bare REVIEW-NNN citation is ambiguous for 21 numbers.

SOT

pending-drain.py staleness check is itself stale

It reads a HARDCODED memory/coherence-2026-07-08.md — 57 days old — so the tool that finds stale entries matches against a stale report.

SOT

PENDING.md cap has less headroom than one entry

It sat at 216,129 of an 218,000 cap before this session. Filing two findings overflowed it; staying legal cost 1,650 bytes shaved off my own text. Left 389 over rather than keep deleting evidence to satisfy it.

6 · Fleet heartbeat

applast touched
actionpack15d ago
compliance-ledger15d ago
haven15d ago
feedy35d ago
bigdonkeymailer57d 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.