Skip to content

Phase A: harness/web pre-work foundation - #100

Merged
ytallo merged 35 commits into
mainfrom
feat/harness-phase-a
May 7, 2026
Merged

Phase A: harness/web pre-work foundation#100
ytallo merged 35 commits into
mainfrom
feat/harness-phase-a

Conversation

@ytallo

@ytallo ytallo commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Multi-crate foundation that unblocks the harness/web Phase B refactor (#TBD). Adds bus surfaces, dual-write semantics, and a UI message reader/reducer that survive WS interleave.

What's in the box

session-tree — exposed surface

  • session-tree::list {limit?, offset?, order?: "asc"|"desc"}{sessions, total} with pagination + ordering. Default desc by updated_at. Backed by existing SessionStore::list().
  • session-tree::messages response now [{entry_id, message}] (was AgentMessage[]). Breaking — only session-tree's own tests called it before.
  • session-tree::ensure {session_id} — caller-supplied idempotent create.
  • session-tree::reconcile {session_id, state_snapshot}{state_count, tree_count_before, tree_count_after, repaired} for repairing state↔tree drift on demand.

turn-orchestrator — dual-write

  • persistence.rs::save_messages writes to state::* (canonical) + best-effort session-tree::append for the delta. Lazy session-tree::ensure on first append; failures log warn and continue (never abort the turn).
  • New state key last_session_tree_len_key tracks how many messages have been mirrored, so re-saves don't re-append.

harness

  • approval-gate added to EXPECTED_WORKERS + iii.worker.yaml so approval_required from the UI actually blocks. Drift test enforces alignment.

harness/web

  • loadMessagesWithEntryIds(sessionId) — UI helper that prefers session-tree::messages and falls back to state::get on empty/error. Drift case (no entry_ids) returns entry_id: null so callers can disable fork buttons with a tooltip.
  • reducer.ts rewritten: entry-id-keyed Map, idempotent under any arrival order, patch-merge for streaming deltas, lastEntryId checkpoint for replay-after-reconnect, unknown event types ignored (forward-compat). Old per-message ad-hoc dedupe removed.
  • visibleMessages(state) consumer helper composes the keyed + unkeyed view.

iii-browser-sdk (separate repo)

Companion PR: iii-hq/iii#1611 — adds addConnectionStateListener on ISdk so consumers can observe WS connection state. Already merged + published as iii-browser-sdk@0.11.7-next.1. Phase B consumes it.

Test plan

  • cargo test --lib clean across harness (4), session-tree (42), turn-orchestrator (63)
  • cargo test --tests clean — incl. new harness/tests/phase_a.rs E2E (dual-write → fork → simulated drift → reconcile)
  • cargo clippy --lib --tests -- -D warnings clean across all changed crates
  • harness/web vitest: 13 passing (reducer + loadMessages)
  • tsc --noEmit clean
  • npm run build clean
  • Branch verified end-to-end against demo.sh stack (16 workers including approval-gate)

Risks & deferred items

  • D-4 forced session-tree failure test deferred — needs a fault-injection harness. The structural guarantee (no ?/unwrap on session-tree calls in mirror_messages_to_session_tree) is enforced by code review.
  • Cross-crate test binary lookup in tests/common/mod.rs walks ../session-tree/target/... with env override. Acceptable as v1; follow-up could use cargo metadata.
  • App.tsx call-site migration to the new helper is intentionally Phase B work.

Commit history

12 atomic commits, TDD throughout. Each commit is independently buildable + testable.

6d7e11d test(harness): Phase A E2E acceptance — dual-write + fork + reconcile
069689a feat(harness/web): rewrite reducer to entry-id-keyed Map; idempotent + replay-safe
3132e19 feat(harness/web): add loadMessagesWithEntryIds helper with state::* fallback
2ceb673 feat(session-tree): add session-tree::reconcile to repair state↔tree drift
371761c test(turn-orchestrator): dual-write delta + lazy-create tests; abort mirror on parent-read failure
181a41d feat(turn-orchestrator): dual-write messages to session-tree (best-effort mirror)
6fc2c5a feat(session-tree): add session-tree::ensure for caller-supplied idempotent create
1bb5382 test(session-tree): serialize bus tests to avoid iii port collision
7d529c9 test(session-tree): bus-level integration tests for list + messages entry_ids
38f7086 feat(session-tree): add entry_id to session-tree::messages response (BREAKING)
387d7a8 feat(session-tree): add session-tree::list bus function with pagination + ordering
8883b1b feat(harness): add approval-gate to EXPECTED_WORKERS + iii.worker.yaml

ytallo added 30 commits May 6, 2026 21:26
- inbox::* -> session-inbox::*
- hooks::publish_collect -> hook-fanout::publish_collect

Updates README, registry/index.json, turn-orchestrator callsites,
and whitelists per-worker config.yaml files in .gitignore.
…nd description

- Introduced constants for skill ID, metadata, and sub-skills in `lib.rs`.
- Updated the description in `start.rs` to remove an unnecessary argument.
- Implemented skill registration with retry logic and graceful shutdown handling in `main.rs`.
Replace Duration::from_secs(180)/(60) and from_secs(3 * 60) with
from_mins(3)/from_mins(1) in skill-register retry loops for
auth-credentials, llm-budget, and session-tree.
- Changed trigger type from "subscribe" to "durable:subscriber" in multiple modules.
- Updated function IDs to use the "iii::durable::publish" format for consistency across the codebase.
Phase A item #3. Includes new test expected_workers_includes_approval_gate
and the existing drift test passes.
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 174 files, which is 24 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c1bd0c49-ce36-4b21-b93f-9f36c8d701bd

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf885a and 4a84cc7.

⛔ Files ignored due to path filters (15)
  • auth-credentials/Cargo.lock is excluded by !**/*.lock
  • harness/Cargo.lock is excluded by !**/*.lock
  • hook-fanout/Cargo.lock is excluded by !**/*.lock
  • llm-budget/Cargo.lock is excluded by !**/*.lock
  • models-catalog/Cargo.lock is excluded by !**/*.lock
  • policy-denylist/Cargo.lock is excluded by !**/*.lock
  • provider-anthropic/Cargo.lock is excluded by !**/*.lock
  • provider-openai/Cargo.lock is excluded by !**/*.lock
  • provider-router/Cargo.lock is excluded by !**/*.lock
  • session-inbox/Cargo.lock is excluded by !**/*.lock
  • session-tree/Cargo.lock is excluded by !**/*.lock
  • shell-bash/Cargo.lock is excluded by !**/*.lock
  • shell-filesystem/Cargo.lock is excluded by !**/*.lock
  • subagent/Cargo.lock is excluded by !**/*.lock
  • turn-orchestrator/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (174)
  • .gitignore
  • README.md
  • approval-gate/src/lib.rs
  • auth-credentials/Cargo.toml
  • auth-credentials/README.md
  • auth-credentials/build.rs
  • auth-credentials/skills/set_token.md
  • auth-credentials/src/config.rs
  • auth-credentials/src/main.rs
  • auth-credentials/src/manifest.rs
  • auth-credentials/tests/manifest.rs
  • harness/Cargo.toml
  • harness/README.md
  • harness/build.rs
  • harness/iii.worker.yaml
  • harness/scripts/demo.sh
  • harness/src/config.rs
  • harness/src/lib.rs
  • harness/src/main.rs
  • harness/src/manifest.rs
  • harness/tests/common/mod.rs
  • harness/tests/manifest.rs
  • harness/tests/phase_a.rs
  • harness/web/src/App.tsx
  • harness/web/src/loadMessages.test.ts
  • harness/web/src/loadMessages.ts
  • harness/web/src/reducer.test.ts
  • harness/web/src/reducer.ts
  • harness/web/src/types.ts
  • hook-fanout/Cargo.toml
  • hook-fanout/README.md
  • hook-fanout/build.rs
  • hook-fanout/iii.worker.yaml
  • hook-fanout/src/config.rs
  • hook-fanout/src/handler.rs
  • hook-fanout/src/lib.rs
  • hook-fanout/src/main.rs
  • hook-fanout/src/manifest.rs
  • hook-fanout/tests/integration.rs
  • hook-fanout/tests/manifest.rs
  • llm-budget/Cargo.toml
  • llm-budget/README.md
  • llm-budget/build.rs
  • llm-budget/config.yaml
  • llm-budget/iii.worker.yaml
  • llm-budget/src/config.rs
  • llm-budget/src/lib.rs
  • llm-budget/src/main.rs
  • llm-budget/src/manifest.rs
  • llm-budget/tests/manifest.rs
  • models-catalog/Cargo.toml
  • models-catalog/README.md
  • models-catalog/build.rs
  • models-catalog/src/config.rs
  • models-catalog/src/lib.rs
  • models-catalog/src/main.rs
  • models-catalog/src/manifest.rs
  • models-catalog/tests/manifest.rs
  • policy-denylist/Cargo.toml
  • policy-denylist/README.md
  • policy-denylist/build.rs
  • policy-denylist/src/config.rs
  • policy-denylist/src/lib.rs
  • policy-denylist/src/main.rs
  • policy-denylist/src/manifest.rs
  • policy-denylist/tests/manifest.rs
  • provider-anthropic/Cargo.toml
  • provider-anthropic/README.md
  • provider-anthropic/build.rs
  • provider-anthropic/config.yaml
  • provider-anthropic/crates/provider-base/src/iii_register.rs
  • provider-anthropic/iii.worker.yaml
  • provider-anthropic/src/config.rs
  • provider-anthropic/src/lib.rs
  • provider-anthropic/src/main.rs
  • provider-anthropic/src/manifest.rs
  • provider-anthropic/tests/manifest.rs
  • provider-openai/Cargo.toml
  • provider-openai/README.md
  • provider-openai/build.rs
  • provider-openai/crates/provider-base/src/iii_register.rs
  • provider-openai/iii.worker.yaml
  • provider-openai/src/config.rs
  • provider-openai/src/lib.rs
  • provider-openai/src/main.rs
  • provider-openai/src/manifest.rs
  • provider-openai/tests/integration.rs
  • provider-openai/tests/manifest.rs
  • provider-router/Cargo.toml
  • provider-router/README.md
  • provider-router/build.rs
  • provider-router/src/config.rs
  • provider-router/src/main.rs
  • provider-router/src/manifest.rs
  • provider-router/src/register.rs
  • provider-router/src/resume.rs
  • provider-router/tests/manifest.rs
  • registry/index.json
  • session-inbox/Cargo.toml
  • session-inbox/README.md
  • session-inbox/build.rs
  • session-inbox/iii.worker.yaml
  • session-inbox/src/config.rs
  • session-inbox/src/handler.rs
  • session-inbox/src/lib.rs
  • session-inbox/src/main.rs
  • session-inbox/src/manifest.rs
  • session-inbox/tests/common/mod.rs
  • session-inbox/tests/integration.rs
  • session-inbox/tests/manifest.rs
  • session-tree/Cargo.toml
  • session-tree/README.md
  • session-tree/build.rs
  • session-tree/iii.worker.yaml
  • session-tree/skill.md
  • session-tree/skills/append.md
  • session-tree/skills/clone.md
  • session-tree/skills/compact.md
  • session-tree/skills/create.md
  • session-tree/skills/export_html.md
  • session-tree/skills/fork.md
  • session-tree/skills/messages.md
  • session-tree/skills/tree.md
  • session-tree/src/config.rs
  • session-tree/src/lib.rs
  • session-tree/src/main.rs
  • session-tree/src/manifest.rs
  • session-tree/src/store_iii_state.rs
  • session-tree/tests/common/mod.rs
  • session-tree/tests/integration.rs
  • session-tree/tests/integration_memory.yaml
  • session-tree/tests/manifest.rs
  • session-tree/tests/restart_e2e.rs
  • shell-bash/Cargo.toml
  • shell-bash/README.md
  • shell-bash/build.rs
  • shell-bash/iii.worker.yaml
  • shell-bash/src/config.rs
  • shell-bash/src/main.rs
  • shell-bash/src/manifest.rs
  • shell-bash/tests/manifest.rs
  • shell-filesystem/Cargo.toml
  • shell-filesystem/README.md
  • shell-filesystem/build.rs
  • shell-filesystem/iii.worker.yaml
  • shell-filesystem/src/config.rs
  • shell-filesystem/src/lib.rs
  • shell-filesystem/src/main.rs
  • shell-filesystem/src/manifest.rs
  • shell-filesystem/tests/manifest.rs
  • subagent/Cargo.toml
  • subagent/README.md
  • subagent/build.rs
  • subagent/iii.worker.yaml
  • subagent/skill.md
  • subagent/skills/start.md
  • subagent/src/config.rs
  • subagent/src/lib.rs
  • subagent/src/main.rs
  • subagent/src/manifest.rs
  • subagent/src/register.rs
  • subagent/src/start.rs
  • subagent/tests/manifest.rs
  • subagent/tests/skill.rs
  • turn-orchestrator/Cargo.toml
  • turn-orchestrator/src/persistence.rs
  • turn-orchestrator/src/register.rs
  • turn-orchestrator/src/run_start.rs
  • turn-orchestrator/src/state.rs
  • turn-orchestrator/src/states/steering.rs
  • turn-orchestrator/src/states/tools.rs
  • turn-orchestrator/src/subscriber.rs
  • turn-orchestrator/tests/common/mod.rs
  • turn-orchestrator/tests/dual_write.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/harness-phase-a

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

ytallo added 3 commits May 7, 2026 16:29
CI runs Rust 1.95 with stricter lints. Replace .map(...).unwrap_or(0)
with .map_or(0, ...) in both harness/tests/common/mod.rs and
turn-orchestrator/tests/common/mod.rs nonce() helpers.
@ytallo
ytallo merged commit c1379c8 into main May 7, 2026
37 checks passed
ytallo added a commit that referenced this pull request May 8, 2026
Resolves squash-merge fake conflicts: Phase A/B content (PR #100, #102)
landed on main as squash commits while feat/iii-native-harness retained
the original commits + the tool->function rename + iii-native-harness work.

Resolutions favor feat side for files where Phase A/B content was the
sole source of conflict; main's unique content (skills filesystem support,
storage worker, etc.) merged automatically.

Removed turn-orchestrator/src/states/tools.rs (renamed to functions.rs).
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.

2 participants