Session S304 · Autonomous Discovery Scan · Search-only

124 findings across a 237-star system that no one has ever contributed to.

A full unsupervised walk of AIPass — all 18 agents' code, the logs, error registries, security gates, live processes, ~4,000 tests run, and the newcomer path. Zero system files changed. Every fix here is a proposal, evidence attached.

Date 2026-07-12Agent @devpulseSurfaces walked 18/18Sub-agents 5 read-onlyFiles edited 0 system

124
bug & gap findings
2
live criticals
29
issue drafts, ready to file
9
read-only sub-agents

Fix First

the load-bearing ones
F74Critical · live now · verified Memory rollover is keeping 14 entries, not 15 — on every branch, right now

An off-by-one trims memory at the keep target instead of above it: the trigger fires at len >= 15 and the extractor forces max(excess, 1), so at exactly 15 it removes one it should keep. The proof is on disk — memory's own local.json holds exactly 14 sessions and 14 learnings, and the rollover log shows (15/15 sessions) firing 53 times. Every branch silently loses one extra memory per rollover. Standing hold on rollover fixes respected — documented, not filed.

F5bCritical · root cause corrected Medic error-notification has been switched off for 63 days

The medic log tells the whole story in three lines: at 2026-05-10 20:30:21 — the same minute a 25-second burst of test-fixture errors flooded in — medic was switched off ("Medic DISABLED — error dispatch suppressed") and never switched back on. Since then config.medic_enabled = false makes the dispatch path drop every detected error before anything else runs; 63 days later it's still suppressing live errors (1,667 "Medic OFF" log lines, zero "Circuit breaker OPEN"). A brief annoyance muted the entire error-notification system for two months. Honest note: my first pass blamed a stuck circuit breaker; a later sweep refuted it and I re-verified on disk. That breaker is a real but separate latent bug (F5c) that must be fixed before medic is switched back on, or it re-floods.

F1High PR#696 red CI is one inherently racy test

Every red job fails on the same assertion: test_mtime_cache_avoids_reread passes only when two file writes share an mtime tick — a filesystem coin flip that lands green locally and red on GitHub runners. Two-line deterministic fix (os.utime to pin mtime equality). @prax owns.

F14–F17High aipass doctor cries wolf on a healthy install

Our first-touch trust tool reports 7 errors on a clean repo — all false: it scans .backup/snapshots/ as if they were live agents, compares registry names case-sensitively against 5 legitimately-uppercase entries, prints an unsubstituted {{BRANCHNAME}}, and shows ✓ marks whose text is a warning. A newcomer reads "my install is broken."

F30High spawn repair advises a command that would delete the live @aipass agent

spawn repair flags src/aipass/aipass/ as duplicate "pollution" and prints the remediation --clean-pollution (archive + remove). That directory is the living concierge agent. Following the tool's own advice archives a user-facing citizen. Needs a passport-awareness guard.

F52High · verified in code Commons artifact gifting is broken by name-casing

Gift / trade / mint write an UPPERCASE owner (trade_ops.py:58) while every ownership check compares lowercase. Gift an artifact to @seed and it becomes invisible in their inventory and permanently un-tradeable ("SEED" != "seed"). The self-gift guard never trips. The registry-casing disease, database edition.

F6 · F43High Tests write fixtures into production logs, poisoning the error registry

pytest fixtures ("disk full", "boom", /tmp/pytest-of-patrick/…) land in real branch logs/; the 24/7 log-watcher ingests them into the error registry (664+ occurrences of one fixture line alone). One cut fixes the class: route logging to tmp when PYTEST_CURRENT_TEST is set.

F22 · F23High The human-facing concierge has the worst help in the fleet

aipass --help is a bare module dump — no "what is this," no first steps, internal modules exposed. aipass help "how do I create a new branch" answers with git-PR commands and never mentions @spawn. The one tool built for humans is the least helpful.

F46High 14 feedback messages unread since April — including 4 external bug reports

Precise bug reports from an external project sat NEW for three months; we later rediscovered every one of them the hard way. The owner-to-owner channel works technically and fails operationally — no inbox check in startup, no aging alarm.

F53 · F54High Commons: systemic DB connection leak + unlocked log that self-wipes

~14 handler files close their sqlite connection only on the success path (no finally:), 40+ sites — hottest runs on every post. The shared op-log does unlocked read-modify-write; a torn write is "healed" by silently resetting the log to []. The correct pattern already exists in sibling files.

F59High · verified · integrity The owner model is advisory: git-write trusts an unprotected passport name

verify_git_access grants owner-tier git if the branch_name in the caller's own passport.json reads "devpulse" — no registry cross-check, no signature, and no gate protects that file. The DPLAN-0231 owner-capability model was built to key on the sealed registry_id; the actual check reads the mutable name instead. Scope honestly: agents are same-user and cooperative, so this is "the model isn't enforced," not a remote exploit — but it undoes a guarantee you deliberately built.

F84 · F85High · verified · destructive Two commands can destroy a branch or another branch's plan history

spawn delete_branch guards only three hardcoded names — no check for a live process or owner:true — so it will archive-and-remove any other running branch on request. And the flow central aggregator, when it reads a branch's registry during a transient unreadable moment, "heals" it by writing an empty registry back — one branch's routine aggregation can wipe another's entire plan history. Both traced in code.

F114High · verified · adoption-blocking The contribution surface everyone would reach for first has no safety boundary

Running a skill executes its handler.py in-process with full privileges — no sandbox, no confirmation, no review (verified: the only relevant line is a bare exec_module). A built-in skill is also shell=True-injectable, and a skill dropped into ~/.aipass/skills/ can silently impersonate a trusted built-in by name. Skills are the natural first thing an outside contributor would build — so this is the one place the adoption plan and the security posture collide. A community-skills ecosystem needs a trust model (sandbox, review, or a first-run consent gate) before it can open.

The Pattern Underneath

The deepest root: the gauges are decorative, so nothing looks broken

Running the live system surfaced the mechanism behind everything else. The data is sound — the vector store passes an integrity check with 5,012 embeddings, the decision DB is clean, 11,000 tests collect without error, six suites run green. But the system's self-report is wrong almost everywhere it's checked: the dashboard claims 1,274 vectors when there are 5,012; dashboards labeled "live" are up to ten days stale; doctor invents seven errors on a healthy repo; the standards audit reports "100%" while silently ignoring any file it can't parse. When every gauge reads "fine" regardless of reality, a thing can break quietly and stay broken for months — which is exactly what medic, rollover, and the stuck breaker did. The highest-leverage fix isn't any single bug; it's making the instruments measured — real counts, real freshness, invariants that fail loud — so the system can finally see its own state.

We build the write side and never the read side

The same shape recurs across F5 (errors detected, never triaged), F26 (presence records written, never cleaned), F40/F46 (mail delivered, never read), F49 (a health check pointed at a retired schema), F5b (a breaker that opens but can't close), F81 (a scheduler running with zero jobs). Detection, ingestion, and enforcement ship; the closing ritual — the thing that reads, ages, resolves, or recovers — is the half that's consistently missing. It's a culture fix as much as a code fix: every new pipeline needs its janitor shipped with it.

One fix retires a dozen bugs

Across six branches the single most common defect is the same: shared state — registries, passports, inboxes, scheduler runstate, the logger's own data — written with a raw open("w") + json.dump, no temp-and-rename, no lock. A crash mid-write truncates the file; the paired loader "fails open" to empty and writes that empty back, turning a transient read hiccup into permanent data loss. The correct atomic-write helper already exists in every branch — it's just applied inconsistently. One shared primitive plus a seedgo checker that forbids the raw pattern closes roughly a dozen findings at once, including the two that can wipe another branch's plan registry or truncate a passport.

The deepest gap: blindness to silent degradation

I dated the biggest findings by git blame: the rollover bug has been trimming memory since April 22, the seedgo audit has silently dropped crashing checkers since March 23 (its inception), medic has been off since May 10. They survived for months — not because they're hard to find, but because none of them produces a symptom. Rollover archives the entry instead of deleting it, so nothing looks lost. Medic being off just means silence. A dropped checker just makes a number rounder. The whole system is instrumented for visible failure — logs, errors, red CI — and has almost no way to notice when something quietly starts doing the wrong thing. The durable fix isn't per-bug; it's a handful of invariants that assert the invisible — rollover leaves exactly N, medic is on, every registered checker actually ran — and shout when they don't.

The gates fail open, quietly

The security gates are well-built where they run — but they default to allow at four independent layers: a missing config file, a handler that crashes, malformed input, and any parser exception, each logged only as a line of text. And the standards audit does the same — a file that makes a checker throw is dropped from the score rather than failed, so "100%" quietly means "100% of the files we could parse." None of this is a remote exploit — these are cooperative same-user agents — but a guarantee that fails silently isn't a guarantee. The cheap half of the fix is to make every fail-open loud: surface it to the error registry, not just a log.

The Contributor Funnel

DPLAN-0240
~600
unique humans / month
237
stars
1
external human, ever
0
retained / contributing

Attraction works; conversion is broken. GitHub topics are empty, the homepage field is blank, Discussions are dead, and the Commons — a genuinely rich agent-society sandbox with craftable artifacts, time capsules, and a hidden exploration game — has exactly one post.

The April Precedent

This exact play was already run once. In April an external agent created three good first issue tasks and added the README "Need help?" line. Three weeks later our own issue-zero sweep batch-closed all three — same minute, zero comments — and the underlying work was never done (the seedgo readme_currency proof still fails today). We didn't just lose a contributor's momentum; our tidiness reflex deleted the on-ramp.

Deliverables

docs/discovery/S304_discovery_mission.md — 58 findings + 6 adoption notes + 17 ready-to-file issue drafts, evidence inline
DPLAN-0240 — contributor funnel, four tiers, the April Precedent
todo 66 — CI red root-caused to F1; local.json updated
compass #94 / #95 — circuit-breaker lesson + April-Precedent decision recorded
5 read-only sub-agents — newcomer, logs, code sweeps (commons/backup/ai_mail/daemon), security gates