Skip to content

fix(agent-org): port red-team recovery hardening to develop#533

Open
ShiboSheng wants to merge 1 commit into
developfrom
codex/port-pr-424-red-team-hardening
Open

fix(agent-org): port red-team recovery hardening to develop#533
ShiboSheng wants to merge 1 commit into
developfrom
codex/port-pr-424-red-team-hardening

Conversation

@ShiboSheng

@ShiboSheng ShiboSheng commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Why this PR exists

The main goal of this PR is to close the edge cases raised in follow-up review and red-team feedback on the earlier Agent Org recovery work. It is not a new Agent Org workflow.

The normal path was already working: launch an org, assign work, exchange messages, and finish a run. The feedback focused on the less common paths that can leave a run stuck or make recovery unsafe:

  • What if a message can no longer be delivered because its member/session disappeared?
  • What if two roster members use the same underlying agent definition?
  • What if a task changes ownership while the watchdog is deciding how to recover it?
  • What if the app restarts with old, malformed, or partially upgraded data?
  • What if a late frontend response revives state for a run that the UI has already abandoned?
  • What happens with very large histories, oversized payloads, duplicate mentions, or WebKit's different @ input ordering?

Those fixes were implemented in #424, but #424 was a stacked PR whose base was fix/issue-272-agent-org-recovery-invariants, not develop. GitHub merged it into that stacked branch, so the hardening never became part of develop.

Since then, develop has split the old Agent Org files into smaller Inbox, Plan Approval, Task Store, Session Message, Org Tasks, Watchdog, and Turn Executor modules. This PR therefore ports the behavior from #424 into the current architecture instead of copying the old files back.

Source: #424 / df158006ad76271a0cc60440dc4c611e54774f8a

What we fixed

1. An undeliverable message can now be resolved without rewriting history

Previously, a permanently undeliverable Inbox row could keep waking a member or prevent a Run from becoming final. Marking it as read would hide the evidence and incorrectly imply that the recipient saw it.

This PR adds a coordinator-only org_inbox_repair tool:

  • inspect explains why a delivery is stuck.
  • cancel records that the coordinator intentionally cancelled it.
  • supersede links it to a real replacement delivery.

The original Inbox row is retained and remains unread as audit evidence. A separate durable resolution records the operational decision, and resolved rows no longer block unread processing or Run finality.

2. Work is bound to the exact Run and roster member

Review feedback found several places where reusable agent_id values could be mistaken for roster identity. That is unsafe when two members share the same underlying agent definition.

The repaired paths consistently use the exact (org_run_id, member_id) pair for:

  • Inbox sender and recipient identity
  • task ownership and authorization
  • wake/drain routing
  • member activity and Run View projection

Turn intents also gain a nullable org_run_id. Agent Org turns may bind a previously unbound intent once, but an intent can never be reassigned from one Run to another. Existing ordinary turns remain NULL; we do not guess ownership for old rows.

3. Recovery decisions are rechecked at the moment they are committed

The watchdog and coordinator can observe valid state that becomes stale before the write happens. For example, a task may be reassigned or a member may recover while a recovery action is being prepared.

This PR revalidates the Run, member, task, eligibility, Inbox facts, and work revision inside the writer transaction. Wake-budget consumption, recovery writes, and recovery notices are committed atomically. If the observed facts are stale, the action aborts instead of applying an outdated recovery plan.

Recovery fingerprints are now stable, typed, sorted, and bounded so harmless ordering differences do not consume extra budget and large/corrupt payloads cannot inflate the recovery reason.

4. Old or malformed data fails closed

Historical task and Inbox rows are validated before they are used for operational decisions. Invalid identifiers, timestamps, dependency graphs, statuses, metadata, or payloads are reported as corrupt rather than silently projected as healthy pending work.

The port also adds explicit limits for task counts, identifiers, JSON, Inbox snapshots, history rows/bytes, and UI previews.

Plan Approval paths receive component-wise traversal checks, canonical-root validation, race-bounded artifact installation, and startup reconciliation for missing durable artifacts.

5. The UI no longer trusts stale or unbounded state

The frontend side of the feedback is covered as well:

  • Run View caching is bounded and protected by generations, tombstones, and request ordering.
  • Late IPC responses cannot repopulate a retired Run.
  • Group Chat history can page through overlap/gaps without silently dropping durable rows.
  • Member navigation uses one shared activity predicate, including exact unread Inbox state.
  • Mention targets are deduplicated by their actionable target.
  • WebKit's @ keydown/input ordering is normalized.
  • Run View and Inbox types expose corrupt and deliveryResolution.

The current develop Plan Approval detail cache is already bounded by both entry count and bytes, which is stronger than #424's entry-only version, so that newer implementation is intentionally retained.

What this PR deliberately does not do

  • It does not restore any deleted Agent Org monolith.
  • It does not infer a Run for old turn intents.
  • It does not mark cancelled/superseded Inbox rows as read.
  • It does not replace newer develop behavior with an older implementation when develop is already stronger.
  • It does not publish a release; inclusion still requires this PR to be reviewed, merged, and picked up by a later release.
Complete review-feedback mapping (15 items)
# Feedback contract Result
1 Coordinator-only Inbox inspect, cancel, and supersede Ported
2 Preserve original Inbox evidence while resolving delivery Ported
3 Resolved delivery must not block unread wake/finality Ported
4 Bind a turn intent to at most one Run without guessing legacy ownership Ported
5 Use exact Run/member identity instead of reusable agent_id Ported
6 Revalidate dispatch ownership and eligibility at commit time Ported
7 Commit Wake budget and recovery writes atomically Ported
8 Use stable and bounded recovery fingerprints Ported
9 Fail closed on corrupt historical task/Inbox data Ported
10 Bound task, payload, JSON, page, and preview resources Ported
11 Protect managed Plan paths and reconcile artifacts Ported
12 Bound Run View cache and reject stale generations Ported
13 Bound Plan Approval detail cache Stronger develop implementation retained
14 Preserve paged history and exact shared member activity Ported
15 Deduplicate mentions and handle WebKit inline @ ordering Ported

Schema and API changes

  • Adds agent_inbox_delivery_resolutions without deleting or mutating the original Inbox evidence.
  • Adds nullable session_turn_intents.org_run_id; legacy and ordinary turns remain NULL.
  • Registers coordinator-only org_inbox_repair through tool policy, assembly, metadata, and tool-name contracts.
  • Adds task corrupt and Inbox/history deliveryResolution projections.
  • Covers both fresh database creation and in-place upgrade paths.

Verification

The pushed commit is c51e92625c1a1a825deedd6c4238165f386bb53c, directly based on fetched develop commit 8396874b366e1868d089373ef95a820ffb7c0000.

Passed

  • GitHub CI:
    • Frontend typecheck, lint, and tests
    • Rust clippy
  • cargo check -p agent_core -p session_persistence -p e2e-test -p org2 --all-targets
  • pnpm run typecheck
  • pnpm run lint — 0 errors
  • pnpm run check:circular — 5,819 files, no circular dependency
  • cargo test -p agent_core --lib --no-fail-fast — 3,126 passed, 2 ignored
  • cargo test -p session_persistence --no-fail-fast — 35 passed
  • pnpm test — 691 files / 6,267 tests passed
  • Agent Org runtime E2E — 47/47 passed against the isolated app built from the pushed commit
    • Exercises the production launch, return-to-work wake, and Inbox drain paths
    • Verifies final Run/Task/Inbox state
    • Verifies there is no ownerless in_progress work or hidden tool-error trajectory

Existing repository baseline

  • Workspace-wide cargo fmt --all -- --check reports formatting in files outside this PR.
  • A local workspace-wide cargo clippy --workspace --all-targets -- -D warnings stops in the untouched gateway CLI test layout. The PR's affected crates pass scoped clippy, and the GitHub Rust clippy job is green.

Rendered Tauri E2E baseline

All three requested rendered specs were executed with isolated ORGII homes and independent ports. They are not reported as passing:

  • Group Chat: 3 passed / 3 blocked
  • Pause/resume: 6 passed / 2 blocked
  • Recovery: 0 passed / 2 blocked

The pause/resume and recovery failures reproduce on clean develop. The clean Group Chat run fails earlier and then triggers a tauri-plugin-webdriver-automation 0.1.3 pending-script panic followed by a poisoned lock. The final develop advance did not touch the affected Agent Org UI/E2E paths.

These are recorded as existing rendered-harness/UI baseline blockers rather than hidden or attributed to this port.

Audit reports

  • docs/architecture-audit-2026-07-24/AgentOrgRedTeamPort.md
    • 7 fix / 3 keep-with-reason / 0 open findings
  • docs/frontend-ui-audit-2026-07-24/AgentOrgRedTeamPort.md
    • 6 fix / 2 keep-with-reason / 2 shared abstractions

Semantically ports PR #424 from df15800 onto the current modular develop architecture.
@ShiboSheng
ShiboSheng marked this pull request as ready for review July 25, 2026 09:13
@ShiboSheng
ShiboSheng requested a review from Neonforge98 July 25, 2026 15:51
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.

1 participant