Whole-repository · read-only · evidence-first

SmolPaws has good seams. Its identities need fewer owners.

A fourteen-scope architecture audit of the working tree, independently checked for coverage, materiality, overlap, and priority, then revalidated against current origin/main.

Audit snapshot a1c1e5bMain validation f70dc2c2026-08-27No repository edits
01

The short version

The new Message Relay and upstream-shaped agent server are the right architectural direction. The highest-leverage work is to encode their ownership claims in data and construction interfaces before more ingresses depend on them.

Recommendation: fix five narrow, verified seams; fold nine other findings into work that already owns them; delete legacy paths only after the cutover plan declares them unnecessary.
14explicit repository scopes
20provisional opportunities checked
5lean unique filing candidates
independent audit-of-audit passes
02

One event, too many identities

Across WhatsApp, GitHub, email, Slack, Discord, the coordinator, and the agent server, invalid states appear where one real-world operation gains multiple unrelated keys or owners.

Before · ownership is claimed in prose external eventmessage / comment ingress keydelivery / timestamp delivery ownerfresh UUID conversation keylossy slug / duplicate durable state?contradictions allowed side effectretry guesses After · one deep module owns the invariant normalized eventstable operation ID lane directory + work storeunique binding · foreign keysone write seam agent-server lifecyclevalidated lease · serialized stateexplicit construction adapterthin effect
03

Five lean filing candidates

These survived independent schema, overlap, and priority checks and remain present on current main. GitHub creation was attempted as enyst, but credentials could not write; the finished drafts are preserved beside this report.

P0

Encode lane-to-conversation ownership

The lane directory says it owns external-thread → conversation binding. The schema allows duplicate conversation bindings, orphan work, and independent lane/conversation pairs.

  • Unique lanes.conversation_id
  • work.lane_key foreign key
  • One checked store write seam
schema.ts · store.ts · Bead b1r.20
P0

Fail closed on invalid leases

Malformed or unreadable lease files are treated exactly like absent files, allowing generation-one overwrite. Managed app construction also drops apparent lease options.

conversationLease.ts · app.ts · regression of b1r.11
P0

Use the explicit app factory in parity scenarios

The scenario generator guesses modules, factories, and option aliases; it cannot unwrap the real app return shape and has a temporal-dead-zone error path.

scenario generator · parity trust gate
P1

Serialize and atomically persist server state

Concurrent profile, settings, and secret mutations can overwrite one another. Any read/parse error silently resets to defaults; writes replace the target directly.

P1 · security gate

Authorize Discord by immutable account ID

The allowlist uses renameable username/tag while conversation identity already uses the stable Discord snowflake.

adapter.ts · migrate to DISCORD_ALLOWED_USER_IDS
04

Real findings that already have an owner

These should expand existing Beads or cutover acceptance criteria, not become duplicate GitHub issues.

FindingDispositionWhy
WhatsApp monotonic cursor + explicit successful acceptancekxa.2 / kxa.3Current equal-timestamp loss is real; the standalone bridge migration owns the transport cursor and durable intake.
Scope uniqueness + exactly one control scopekxa.2 + 69wNarrow migration acceptance, not a second registry rewrite.
One schedule parser + per-task quarantinekxa.4One malformed recurrence can abort later due tasks; the moved scheduler is the right seam.
Canonical GitHub event identity3iyWebhook and notification intake can represent the same comment with different turn keys.
Email end-to-end idempotencyemail activation / b1r.24Required before dormant email dispatch is enabled; stable emailId should own turn and Resend effects.
Durable Slack thread eligibilityb1r.20 after lane fixCapped in-memory timestamp tracking forgets accepted lanes on restart/eviction.
Context doc classes + private memory path1yd / 750Loader injects heartbeat/reserved docs into every conversation and points at the public memory stub.
Legacy conversation deletion / Discord startup ownershipb1r.24Go/no-go hardening and rollback ownership belong in the cutover plan.
05

Coverage contract

Every tracked surface has an explicit behavior or inventory owner. Generated, vendored, static, private, and task-state paths were named rather than silently ignored.

A01
WhatsApp host + routingincluding live registered-groups config
owned
A02
SQLite + scheduler + mountstask parsing and recurrence
owned
A03
Host runtime + legacy bridgesdead experiments deferred
cleanup
A04
Message coordinatorschema, store, relay, delivery
file
A05
Agent-server state + leasesconversation/profile/settings state
file
A06
Agent-server shell + routesconstruction and OpenAPI
file
A07
Transpile + generated contractsscripts, vendor, package docs, parity workflows
file
A08
Legacy product agent-serverconditional cutover hardening
owned
A09
GitHub ingresswebhook + notification queue
owned
A10
Email ingressSvix, queue, Resend
owned
A11
Slack relaySocket Mode + durable lane
owned
A12
Discord ingressGateway, auth, startup
file
A13
Operations + CIscripts, launchd, general workflows
skip
A14
Docs + identity + skills + governanceBeads, static/reference inventory
owned
06

Dependency order

The order matters more than the volume. Harden the representation before trusting more channels; delete rollback paths last.

1 · ConstructionMake managed/injected ownership explicit enough that lease options are real.
2 · PersistenceFail-closed leases; serialize global server state; encode lane invariants.
3 · ProofMake parity scenarios instantiate the real server and verify the hardened seams.
4 · CutoverMove channel-specific work, run canaries, then delete obsolete paths.
07

Method, skips, and audit corrections

The useful part of an audit is what it refuses to overstate.

Fresh validation passes

One reviewer checked coverage and evidence quality; one checked overlap, authoritative ownership, and materiality; one ranked priority and dependencies. A bounded follow-up assigned 27 initially implicit CI/meta/governance paths.

Revalidated against main

The working branch was four commits behind origin/main. Broad route/OpenAPI drift, dangling manifest hints, and an active CI reference were removed from the filing set because main had already corrected the observed evidence. The five candidates above remain present at f70dc2c.

Explicit skips

Generated lockfiles and OpenAPI bodies were treated as artifacts; vendored/reference skills were inventoried rather than re-audited as product code; static media/games and private/untracked runtime state were excluded from architectural findings. Larger TurnLedger/state-machine rewrites, generic relay abstractions, and policy-dependent guest/DM changes failed the materiality test.

Repository integrity

No source, Bead, workflow, documentation, or generated artifact was edited. Tests were intentionally not run under the requested read-only audit brief. The only worktree entry remained the pre-existing untracked .openhands/.

Issue creation status

The GitHub connector authenticated as enyst but returned HTTP 403 for issue creation. The local Keychain item github.com/enyst returned HTTP 401. No issue was created. Five finished Markdown drafts are saved beside this report and can be filed immediately after the token is refreshed.