Skip to content

fix(agent-org): isolate runtime persistence namespace - #799

Closed
ShiboSheng wants to merge 11 commits into
fix/issue-757-flat-team-capabilitiesfrom
fix/issue-791-agent-org-runtime-namespace
Closed

fix(agent-org): isolate runtime persistence namespace#799
ShiboSheng wants to merge 11 commits into
fix/issue-757-flat-team-capabilitiesfrom
fix/issue-791-agent-org-runtime-namespace

Conversation

@ShiboSheng

Copy link
Copy Markdown
Collaborator

Problem

Fixes #791.

The redesigned Agent Org runtime and published pre-redesign builds shared the same 13 private SQLite table names even though their schemas and lifecycle semantics are incompatible. A downgrade and re-upgrade could therefore make either generation query columns owned by the other generation (completed_at versus activation_generation), causing startup/sidebar retries and preventing ordinary Rust and CLI Sessions from being used safely.

The same collision existed at agent-orgs.json: old recursive definitions and the redesigned versioned flat envelope both treated that path as authoritative. The approved clean-cutover plan supersedes the earlier compatibility-table proposal because isolating only the run envelope would still leave Task, Inbox, approval, intervention, and recovery tables shared across generations.

Solution

  • Move all 13 redesigned private tables, foreign keys, indexes, readers, writers, cleanup paths, sidebar projections, recovery paths, and test/debug probes into one agent_org_runtime_* namespace.
  • Add one schema coordinator at the existing database initialization barrier. In a single immediate transaction it validates an existing canonical namespace, fails closed on partial or unknown canonical state, retires only the exact 13 legacy table names, creates a fresh canonical schema when needed, revalidates the manifest, and commits atomically.
  • Repeat the fixed-size legacy retirement on every redesigned startup so a sequential downgrade may recreate its empty legacy schema and the next upgrade can remove it again. Unknown tables outside the exact registry are preserved and diagnosed.
  • Remove the old four-table fingerprint/reset and incremental Agent Org ALTER TABLE paths. Unit-test, sandbox, production, and HTTP fixture entry points use the same complete coordinator where full application initialization is required.
  • Move redesigned flat Team definitions to agent-org-definitions.json. The redesigned store deletes the exact old live file without parsing or migrating it, never falls back to it, and keeps unknown canonical files fail-closed and byte-preserving.
  • Route prompt assembly and Work Item launch through the canonical definitions store instead of duplicated direct JSON parsing.
  • Preserve the stacked PR feat(agent-org): flatten team capabilities #786 standalone SDE pagination fix and leave ordinary Session/CLI tables, messages, usage, Projects, Work Items, Routines, RPC/wire contracts, frontend code, and dependencies unchanged.

Potential risks

  • This is intentionally destructive for the retired generation: old Agent Org runtime rows and the old agent-orgs.json file are permanently discarded without backup. Ordinary Rust/CLI Sessions and shared product data are outside the cleanup registry.
  • Agent Org data created while downgraded is temporary and is deleted by the next redesigned startup. A stable redesigned Team remains in the isolated canonical namespace, but in-flight Starting, Working, Paused, or crash-recovery downgrade/re-upgrade is unsupported.
  • Concurrent old and redesigned binaries sharing one ORGII_HOME remain unsupported. Same-version initializers are serialized by SQLite and covered by a concurrency test.
  • A partial or structurally unknown canonical namespace deliberately blocks initialization before legacy cleanup instead of guessing or resetting redesigned data. Operators must repair or restore that database before retrying.
  • The packaged compatibility matrix and real-data acceptance were run on macOS. Other platforms are covered by shared Rust tests and compilation, not by a packaged manual run in this verification cycle.
  • This PR is stacked on PR feat(agent-org): flatten team capabilities #786 (fix/issue-757-flat-team-capabilities), which is itself stacked on PR feat(agent-org): add authoritative team lifecycle #781. It must be reviewed and merged in stack order, then retargeted/reverified as earlier layers land.
  • Rollback can restore pre-cutover behavior by reverting this commit or running an old binary, which will recreate an empty legacy Agent Org schema. Reverting cannot recover retired old Agent Org rows or definitions; recovery requires an independent pre-cutover backup. The isolated redesigned tables are not read or modified by old binaries.

Verification

Automated checks:

  • cargo test -p agent_core core::coordination::schema::tests -- --nocapture --test-threads=1 — 7 passed, covering historical 5/9/11/13-table retirement, shared-data sentinels, repeated downgrade cleanup, manifest fail-closed behavior, rollback, unknown-object preservation, concurrent initialization, and startup measurements.
  • cargo test -p agent_core core::definitions::orgs::tests -- --nocapture --test-threads=1 — 13 passed, including old/new path isolation, byte-preserving restart, cleanup failure isolation, unknown canonical file fail-closed behavior, stable IDs/grants/links, and the 50-member capability fixture.
  • cargo test -p agent_core agent_org -- --nocapture --test-threads=1 — 224 passed. One earlier parallel aggregate attempt exposed process-global ORGII_HOME/SQLite fixture interference; this required serial rerun passed cleanly.
  • cargo check --workspace --all-targets — passed.
  • cargo clippy -p agent_core -p org2 --all-targets -- -D warnings — passed; the commit hook also passed scoped clippy for agent_core, app_paths, org2, and session_persistence.
  • pnpm run lint — passed.
  • pnpm run check:circular — passed; 6,350 modules checked.
  • pnpm exec vitest run src/util/session/__tests__/sessionDispatch.test.ts — 25 passed for the acceptance fixture's CLI identity path.
  • From src-tauri: cargo fmt --all -- --check — passed.
  • git diff --check — passed.

Packaged compatibility and acceptance:

  • Built the Issue fix(agent-org): preserve safe downgrade across runtime schema redesign #791 acceptance app from the same frozen source tree committed as 185ade293; packaged binary SHA-256: e85d079bfa098e3c2917516a7719bb8492dd75972dd83ad093f529084395dcca.
  • Ran sequential upgrade -> representative old build -> re-upgrade coverage for v1.0.1.11, v1.1.24, v1.2.0, v1.2.5, and 8aa187a70. Each old build recreated its legacy schema/file; the redesigned build removed them again, preserved the canonical Team/runtime and ordinary Session sentinels, avoided schema-column errors, and remained idempotent after another redesigned restart.
  • Ran the acceptance app directly against the authorized real ~/.orgii directory. SQLite integrity remained OK; ordinary counts remained agent_sessions=453, code_sessions=224, and agent_messages=72485; the legacy table count changed from 3 to 0; all 13 canonical tables were present; old related indexes/triggers were absent; the old definitions file was removed; and the canonical definitions envelope remained valid.
  • User manually verified both ordinary Rust and CLI Session visibility and reported the real-data acceptance passed.
  • Rendered WDIO was not rerun for this backend/persistence-only diff. No frontend source or visual state changed, so screenshots are not attached; packaged human acceptance covered app launch, sidebar continuity, and both ordinary Session types.

Architecture audit

All 10 applicable layers were reviewed: compilation; removal of the old fingerprint/incremental migration code; canonical table/index/path naming; durable-runtime ownership; fail-closed default branches; shared Session/CLI boundary isolation; one documented registry/manifest; unchanged RPC/wire shapes; production/sandbox/HTTP initialization parity; and canonical definitions resolution across Settings, launch, prompt, Work Item, Routine, and debug paths. The source-level invariant is that redesigned production SQL owns only agent_org_runtime_*; old names occur only in the exact retirement registry and historical fixtures.

Performance audit

Area Verdict Evidence Change or reason kept Verification
Background work keep Existing startup barrier invokes the coordinator once No polling, timer, worker, listener, retry loop, or hidden/idle work added Call-chain audit and packaged restart matrix
Memory keep Schema manifests and name registries are fixed-size, short-lived initialization values No app-lifetime cache or growing collection added Source audit and repeated-init test
Scope/isolation keep Exact 13-name retirement registry; canonical path/table namespace is separate Unknown objects and all shared Session/CLI tables are preserved Historical fixtures, shared sentinels, real-data acceptance
Rendering/hot path keep No frontend, render, streaming, or per-turn hot path change Startup-only constant-scale work Full Agent Org tests and manual UI acceptance

In the latest 25-sample owning-boundary run, fresh initialization had median 4.23 ms / max 6.21 ms, canonical no-op had median 2.59 ms / max 2.87 ms, and 13-table cleanup had median 3.04 ms / max 3.68 ms.

Performance verdict: pass.

Move the redesigned Agent Org runtime into the canonical agent_org_runtime_* tables and retire the exact legacy table set atomically on startup. Isolate flat Team definitions at agent-org-definitions.json while preserving ordinary Rust and CLI session data across downgrade and re-upgrade.

Pre-commit hook ran. Total eslint: 0, total circular: 0
@ShiboSheng
ShiboSheng requested a review from Neonforge98 August 14, 2026 03:28
@ShiboSheng
ShiboSheng marked this pull request as ready for review August 14, 2026 03:29
@ShiboSheng

Copy link
Copy Markdown
Collaborator Author

orgii://cloud/session/ref?v=1&org=bfa7b134-2486-45fa-81ad-a369441fafb4&owner=776dbd69-ac1d-4f72-a0d4-69cb4f2667dd&session=codexapp-rollout-2026-08-14T00-42-45-019ffc01-371f-7523-8301-13d20a728a28

A failed Agent Org runtime namespace previously ?-propagated out of the
sessions.db init dispatcher, so one dropped canonical table made every
sessions.db connection fail forever and took ordinary chat down.

- new coordination::availability: process-global unavailable state
  (thread-local under cfg(test)), structured
  "agent-org runtime unavailable: <reason>" error, and a gated
  runtime_connection() used by every Agent Org store entry.
- coordination::init_agent_org_schemas_scoped logs the full diagnostic
  at error level, records the reason, and never fails DB init; the
  setup hook now calls it instead of ?-propagating.
- all coordination stores (runs/tasks/inbox/plan approvals/
  interventions/watchdog) acquire connections through the gate.
- test: corrupted namespace leaves the connection usable for ordinary
  DDL, records the reason, gates store entries, and recovers after a
  healthy re-init.
Legacy retirement dropped up to 13 tables without recording what was in
them. Count rows per existing table first and log
agent_org_destructive_table_drop(context, table, rows) at warn level.
The counting only runs when legacy tables actually exist, so canonical
steady-state boots pay nothing. The same helper backs the upcoming
epoch retire-and-recreate path.
The manifest verifier could only fail closed on any DDL drift, leaving
future schema changes no sanctioned path and (pre-scoping) bricking
sessions.db. Add agent_org_runtime_meta(key, value) inside the
canonical namespace carrying schema_epoch = SCHEMA_EPOCH (a const,
currently 1), included in the manifest.

Decision on a non-empty namespace, epoch first:
- epoch < binary (incl. pre-epoch namespaces without the meta table):
  deliberate retire-and-recreate — drop the runtime tables + meta with
  per-table row-count logging, recreate fresh. Destructive by design;
  bumping the const is the sanctioned path for future DDL changes.
- epoch > binary: fail closed with a created-by-a-newer-version
  diagnostic (scoped by availability, so chat keeps working after a
  rollback).
- epoch == binary or unreadable: manifest mismatch is corruption,
  fail closed (scoped).

Tests: epoch upgrade (explicit older epoch + pre-epoch), downgrade
fail-closed with data intact, unreadable-epoch corruption; existing
manifest tests updated for the 14-table registry.
The watchdog test module resolves the connection helper through
'use super::*'; update it for the runtime_connection rename from the
scoped-degradation commit.
retire_legacy_definitions_file deleted the pre-rename agent-orgs.json
unparsed even though old and new files share the same schema_version=2
envelope — destroying user-defined Teams on the rename cutover.

Now: if the canonical agent-org-definitions.json does not exist and the
old file parses as a valid v2 envelope, rename (or copy+delete) it into
place, preserving user Teams. An invalid or legacy-shaped old file is
retired as before; an old file next to an existing canonical file is
retired without touching the canonical bytes. I/O failure during
adoption leaves the old file in place for a later retry rather than
destroying it.

Tests: valid-v2 adoption preserves custom Teams across restart; invalid
old file still retired; both-exist keeps the canonical file and deletes
the old one.
normalize_legacy_dependency_rows scanned every agent_org_runtime_tasks
row on every boot to discover unmigrated runs, and re-warned about
permanently-unsafe runs forever.

- discovery now anti-joins agent_org_runtime_runs (one row per run)
  against the per-run markers; a canonical no-op boot costs exactly one
  SELECT regardless of task volume.
- a run whose historical rows cannot be normalized safely is marked
  canonical_blocked_by_v1_skipped once, with a single warn; later boots
  neither rescan nor re-warn. Repair goes through the runtime repair
  surfaces, which normalize on write.

Tests updated for run-driven discovery; new test proves marked runs are
never rescanned and the terminal skip holds after row repair.
measures_constant_scale_startup_paths printed timing medians and
asserted nothing. Replace it with a real guard for the per-boot O(data)
fixes: seed 20 runs / 2000 unread inbox rows / 2000 materialization
receipts / 2000 tasks in a canonical DB and count every SQL statement
during a no-op boot via rusqlite's trace hook (workspace rusqlite gains
the 'trace' feature). The count must equal the empty-DB no-op count —
exact statement-count equality is robust where time thresholds are not.
Directly guards the repair gating and run-driven normalization
discovery.
Dependency-normalization discovery is now driven from
agent_org_runtime_runs, so the two assignment-snapshot tests that
initialized only the task schema need the runs table first — matching
the dependency order the namespace coordinator guarantees in
production.
- drop the three ghost DROP INDEX statements in the inbox DDL
  (…run_task_assignment_v2/_v3 and …causation_once): those names never
  existed in the runtime namespace, and the schema manifest owns
  versioning now. sqlite_master is unchanged, so the expected manifest
  regenerates identically.
- include 'view' in the coordinator's three sqlite_master type filters
  (manifest query, legacy-object count, unknown-object report) so a
  view in the namespace can no longer hide from verification.
- fix stale docs naming retired tables: agent_inbox/schema.rs module
  and init docs, agent_org_tasks module and init docs,
  core_types::tool_names ORG_SEND_MESSAGE, e2e-test member-idle probe.
- doc-comment every module-level pub fn init_schema as tests-only —
  production initialization goes through the namespace coordinator.
orgs.rs resolved to keep both fix sets: PR 786's per-definition quarantine machinery (struct LoadOutcome::Loaded, quarantine loop in load_from_disk, save round-trip, guard fallback) plus PR 799's namespace cutover and legacy-file adoption (strict parse_definitions_content retained as the all-or-nothing adoption gate); the legacy hierarchy sniff (contains_legacy_hierarchy, backup_legacy_file, LoadOutcome::LegacyReset) is fully removed. 786-side hunks in watchdog/runs modules re-pointed at the renamed agent_org_runtime_* tables and runtime_connection().
@ShiboSheng

Copy link
Copy Markdown
Collaborator Author

Superseded by #828: #828

The replacement was rebuilt cleanly as the third layer in #824#825#828. It keeps the original namespace cutover and Neonforge's d3035ef namespace hygiene sweep, excludes the commits rejected in the review notes, and leaves c4190d9 out because its runtime_connection API exists only in the excluded 497ddc1 change. The old branch is intentionally retained.

@ShiboSheng ShiboSheng closed this Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(agent-org): preserve safe downgrade across runtime schema redesign

2 participants