Summary
Lens-core still has direct write/delete/update sites against persist-owned tables that should fold onto Engine API once persist exposes the right primitives. This issue tracks the fold as a unit + names the persist asks needed to complete it.
The PoB §3.1 trajectory says "lens-core folds into agent" — that fold is much cleaner if every persist-substrate touch goes through Engine instead of direct SQL, because Engine survives the move and direct SQL doesn't.
Direct-touch inventory (lens-core only)
Site Disposition Persist API exists?
─────────────────────────────────────────────────────────────────────────────────────
Reads from accord_traces / accord_public_keys Stay on db_pool; n/a
owner relationship —
no security boundary
between lens-core and
persist (same trust)
INSERT accord_traces DROP — legacy n/a
(accord_api.py:2369) fallback ingestion
path; CIRISLENS_USE_
PERSIST_ENGINE has
been on in production
since the v0.2.x
cutover, this is dead
code
INSERT accord_public_keys KEEP for now; Engine.put_public_key
(accord_api.py:2629 document v0.4.0 exists (already mirrored
register_public_key handler) retirement timing via federation_mirror).
Direct INSERT stays as
defense-in-depth until
v0.4.0 retires the
dual-read fallback in
persist's verify path.
DELETE accord_traces NEEDS persist API NO — no Engine
(accord_api.py:2880,2891 primitive for DSAR-
dsar_delete_traces handler) style row deletion
today. See §"Persist
asks" below.
UPDATE accord_traces MOVE to lens-derived NO — and shouldn't
(accord_api.py:3570,3636 schema (cirislens_ exist on persist's
set_trace_public_sample, derived.trace_ side. partner_access
partner_access) visibility, joined and public_sample are
to accord_traces) lens-policy decisions
on persist's data; they
shouldn't co-locate
with substrate columns
INSERT accord_traces_mock dev-only path; not n/a
(accord_api.py:1323) in production
Recommended scope for the actual fold (separate PRs)
-
Drop the legacy fallback ingestion path. The if not _persist_engine_active() branch in receive_accord_events is dead code in production (Phase 2a flag has been on since v0.2.x cutover). Connectivity-event handling and mock-LLM filtering — which are NOT dead — need to be teased out and kept as their own routes/checks rather than living inside the legacy fallback. ~200-300 lines net delete after the connectivity/mock split.
-
Move partner_access + public_sample to a lens-derived schema. New table cirislens_derived.trace_visibility(trace_id, partner_access, public_sample, set_at, set_by) joined to accord_traces at query time. Removes lens's UPDATE on persist's substrate columns. ~150 lines + a migration.
-
Wait for persist's DSAR primitive (see asks below) before migrating DELETE.
-
Wait for persist v0.4.0 (drop dual-read fallback) before stopping accord_public_keys writes.
Asks for persist (file separately if/when they pick this up)
Ask 1: Engine.delete_traces_for_agent_id_hash(agent_id_hash) -> DeleteSummary
GDPR right-to-erasure (DSAR) primitive. Lens currently does direct DELETE on accord_traces; with trace_events post-cutover that path needs an Engine equivalent.
pub struct DeleteSummary {
pub trace_events_deleted: u64,
pub trace_llm_calls_deleted: u64,
pub federation_keys_deleted: u64, // if revoking the agent's key too
pub deleted_at: DateTime<Utc>,
// Audit: lens consumes this for its own DSAR ledger;
// persist returns the row counts, lens records
// the request envelope.
}
Mission-aligned because GDPR Article 17 is a federation-grade ethical requirement, not a lens-side convenience. Persist owns substrate row deletion; lens orchestrates the audit + signature verification of the DSAR request.
Ask 2: v0.4.0 retirement plan
What's the timeline for persist dropping the accord_public_keys dual-read fallback in Backend::lookup_public_key? Lens-core's direct INSERT into accord_public_keys can retire the same release. Coordinate.
Ask 3 (deferred until needed): bulk-iter primitive for analytical reads
Engine.iterate_trace_events(filter, callback) for streaming analytical work that doesn't fit the SQL-via-cirislens_reader pattern. Not blocking; the export script's asyncpg-cursor pattern works fine for now. Mention only if persist team wants to standardize.
What's NOT in this issue's scope
- Reads stay on db_pool. No security boundary between lens-core and persist; routing in-process analytical reads through cirislens_reader is plumbing without benefit. The peer/owner distinction matters: cirislens_reader is for OUT-OF-PROCESS consumers (export script, future federation peers), not lens-core itself.
- Lens-derived tables stay lens-owned.
coherence_ratchet_alerts, pdma_events, wbd_deferrals, creator_ledger, sunset_ledger — these are lens's analytical output, not persist's substrate. They don't fold.
References
- PoB §3.1 — lens-core folds into agent
- v0.3.4 commit (5fcd550) — pin bump for deployment_profile wire-format compatibility
- v0.2.x cutover — Phase 2a flag flip that made the legacy ingestion path dead code
- CIRISPersist v0.4.0 roadmap — accord_public_keys dual-read retirement
Summary
Lens-core still has direct write/delete/update sites against persist-owned tables that should fold onto Engine API once persist exposes the right primitives. This issue tracks the fold as a unit + names the persist asks needed to complete it.
The PoB §3.1 trajectory says "lens-core folds into agent" — that fold is much cleaner if every persist-substrate touch goes through Engine instead of direct SQL, because Engine survives the move and direct SQL doesn't.
Direct-touch inventory (lens-core only)
Recommended scope for the actual fold (separate PRs)
Drop the legacy fallback ingestion path. The
if not _persist_engine_active()branch inreceive_accord_eventsis dead code in production (Phase 2a flag has been on since v0.2.x cutover). Connectivity-event handling and mock-LLM filtering — which are NOT dead — need to be teased out and kept as their own routes/checks rather than living inside the legacy fallback. ~200-300 lines net delete after the connectivity/mock split.Move
partner_access+public_sampleto a lens-derived schema. New tablecirislens_derived.trace_visibility(trace_id, partner_access, public_sample, set_at, set_by)joined to accord_traces at query time. Removes lens's UPDATE on persist's substrate columns. ~150 lines + a migration.Wait for persist's DSAR primitive (see asks below) before migrating DELETE.
Wait for persist v0.4.0 (drop dual-read fallback) before stopping accord_public_keys writes.
Asks for persist (file separately if/when they pick this up)
Ask 1:
Engine.delete_traces_for_agent_id_hash(agent_id_hash) -> DeleteSummaryGDPR right-to-erasure (DSAR) primitive. Lens currently does direct DELETE on accord_traces; with
trace_eventspost-cutover that path needs an Engine equivalent.Mission-aligned because GDPR Article 17 is a federation-grade ethical requirement, not a lens-side convenience. Persist owns substrate row deletion; lens orchestrates the audit + signature verification of the DSAR request.
Ask 2: v0.4.0 retirement plan
What's the timeline for persist dropping the
accord_public_keysdual-read fallback inBackend::lookup_public_key? Lens-core's direct INSERT into accord_public_keys can retire the same release. Coordinate.Ask 3 (deferred until needed): bulk-iter primitive for analytical reads
Engine.iterate_trace_events(filter, callback)for streaming analytical work that doesn't fit the SQL-via-cirislens_reader pattern. Not blocking; the export script's asyncpg-cursor pattern works fine for now. Mention only if persist team wants to standardize.What's NOT in this issue's scope
coherence_ratchet_alerts,pdma_events,wbd_deferrals,creator_ledger,sunset_ledger— these are lens's analytical output, not persist's substrate. They don't fold.References