Skip to content

Agentic Activity and Streaming UX - #6901

Merged
serrrfirat merged 10 commits into
mainfrom
feat/agent-activity-streaming-ux
Jul 31, 2026
Merged

serrrfirat merged 10 commits into
mainfrom
feat/agent-activity-streaming-ux

Conversation

@rdisandro

@rdisandro rdisandro commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Foundation PR for the agent-activity / streaming UX redesign in webui_v2.
  • Adds the approved design reference: an interactive mockup (docs/design/agent-activity-streaming/mockup.html) and implementation brief (docs/design/agent-activity-streaming.md).
  • Implements NearProcessIndicator, using the canonical NEAR mark with working/done states, and wires it into TypingIndicator.
  • Keeps the branded indicator and adjacent Working… action label pinned in the thread for the full active run, including after assistant text starts streaming.
  • Retains a static Worked for Ns indicator after each final reply, using durable run-scoped transcript timestamps so the duration survives refreshes and stays before the next turn.
  • Removes the duplicate Working fallback from the composer while preserving explicit approval, pairing, and retry-cooldown notices.
  • Adds scoped near-chase / near-pulse motion with a prefers-reduced-motion off-switch.
  • Presentation-only; no backend or SSE contract changes.
  • Railway/Docker builds now inherit the repository-wide Cargo retry and HTTP transport policy, preventing transient crates.io HTTP/2 failures from aborting cargo chef cook.

Change Type

  • Bug fix
  • New feature
  • Documentation

Linked Issue

None — design foundation; follow-up implementation issues are listed in the brief.

Validation

  • cargo fmt --all
  • cargo clippy --all --benches --tests --examples --all-features
  • pnpm build (including the 211 KB initial /chat gzip budget)
  • pnpm test — 117 files / 984 tests
  • pnpm lint (lint:conventions + tsc --noEmit)
  • Targeted composition regression: cargo test -p ironclaw_reborn_composition --features test-support,memory-mem0 --test webui_v2_serve static_near_process_animation_respects_reduced_motion -- --nocapture
  • Docker deployment contracts: cargo test -p ironclaw_reborn_integration_tests --test dockerfile_runtime_home -- --nocapture (19 passed)
  • Manual testing from the original feature pass: working/done states, light/dark themes, and reduced motion

The generic cargo test --workspace --lib closeout compiled and ran broadly, then stopped on two unrelated ironclaw_host_runtime trace-commons tests returning NetworkDenied (545 passed in that crate). The focused Docker suite, full frontend suite, and all-feature clippy pass; CI remains the source of truth for broad workspace buckets.

Test Strategy

User behavior: while a turn runs, the assistant-working indicator remains visible at the bottom of the thread with the branded NEAR mark and adjacent brief action label, even after response text begins streaming. After the final reply, that row remains as Worked for Ns, measured from the durable user/final transcript timestamps for the same run. The composer no longer duplicates Working, but still presents explicit approval, pairing, and cooldown notices. Reduced-motion users receive a static mark, and the mockup renders in standards mode.

Risk areas:

  • Model behavior
  • Browser
  • Side effect
  • Persistence
  • Security or permissions
  • External provider
  • Cross-component behavior

Tests added or updated:

  • Unit or contract: chat.test.ts covers indicator visibility before and during active-run text streaming; chat-input.test.ts covers removal of the implicit composer working status while preserving explicit notices; typing-indicator.test.ts covers both the live action label and static completed duration; message-groups.test.ts exercises the durable timeline-to-completion seam and placement after the final reply; near-process-indicator.test.ts covers working/done rendering, glyph/comet structure, labels, and elapsed time. webui_v2_serve.rs::static_near_process_animation_respects_reduced_motion covers the bundled CSS contract for pulse/chase, state colors, dimming, and reduced-motion suppression. dockerfile_runtime_home.rs::reborn_dockerfile_uses_feature_matched_cache_and_loopback_default isolates the chef stage and verifies .cargo/config.toml is copied before its first Cargo command.
  • Reborn integration: Not applicable: no turn, runner, runtime, or product-workflow behavior changed.
  • Recorded fixture: Not applicable: no model choice or request shape changed.
  • Browser E2E: Not added: the caller-level chat render tests exercise the active-run presentation seam, component-render tests cover indicator/composer prop plumbing, and the bundled-stylesheet contract covers shipped animation behavior; the original feature pass also included manual browser verification.
  • Backend or runtime: Not applicable: no backend or runtime behavior changed.
  • Live canary: Not applicable: no model/provider behavior changed.

What the tests prove: streamed assistant text cannot hide the live indicator before the run settles; the brief action label remains attached to the indicator; completed runs retain a reload-safe measured duration immediately after the final reply; run progress is not duplicated in the composer; explicit composer notices remain visible; the indicator preserves the canonical glyph and state-specific content; the shipped stylesheet animates only the working state; reduced motion suppresses both animations and hides the comet; the frontend stays within its enforced bundle budget; and every Docker Cargo stage receives the repository retry/HTTP transport settings.

Commands run:

  • pnpm build
  • pnpm test
  • pnpm lint
  • cargo fmt --all
  • cargo clippy --all --benches --tests --examples --all-features
  • cargo test -p ironclaw_reborn_integration_tests --test dockerfile_runtime_home -- --nocapture
  • cargo test -p ironclaw_reborn_composition --features test-support,memory-mem0 --test webui_v2_serve static_near_process_animation_respects_reduced_motion -- --nocapture

Security Impact

None. Presentational frontend component; no permissions, network calls, secrets, file access, tool execution, or sandbox policy.

Reborn Trust-Boundary Checklist

Not applicable — no Reborn trust, runtime, persistence, or ingress boundary changed.

Database Impact

None.

Blast Radius

webui_v2 active-run/typing indicator, composer status presentation, scoped indicator CSS, the bundled-CSS regression contract, the design mockup, and Docker dependency-fetch configuration. The universal static-motion policy remains intact and reduced motion is explicitly enforced.

Rollback Plan

Revert the feature and review-fix commits; the indicator returns to the previous three-dot implementation and Docker returns to Cargo default networking. No data or schema rollback is required.

Review Follow-Through

Follow-up PRs remain scoped in the design brief: activity-trail consolidation, composer agent mode, card-family unification, Plan/Context rail, and the new SSE payloads.


Review track: A (docs + a presentational component with tests)

… + design reference

Foundation for the agent-activity / streaming UX redesign.

- docs/design/agent-activity-streaming{.md,/mockup.html}: approved design
  reference — the interactive mockup plus an implementation brief (component
  map, the new SSE payloads to build, and rollout order).
- NearProcessIndicator: the branded live "working" indicator — the canonical
  NEAR "N" mark (from provider-logos.tsx) with a brand-blue (#0091fd) light
  chasing its spine while working, resolving to a solid glyph when done. Wired
  into TypingIndicator.
- app.css: --near-blue token + motion-policy-compliant near-chase / near-pulse
  keyframes (class-scoped !important so they outrank the universal animation:none,
  suppressed under prefers-reduced-motion).

Presentation-only; no backend/SSE changes. Verified: npm run typecheck, lint,
and test (899 tests incl. 2 new) all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@railway-app

railway-app Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-6901 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Jul 31, 2026 at 8:47 am

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 30, 2026 09:11 Destroyed
@github-actions github-actions Bot added scope: docs Documentation size: XL 500+ changed lines labels Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a branded NEAR process indicator with working and completed states, elapsed time, and animated visuals.
    • Completed agent runs now display their duration in chat.
  • UI Improvements

    • Typing status remains visible while responses stream.
    • Status messages appear only when relevant.
    • Improved reduced-motion behavior for accessibility.
  • Documentation

    • Added agent activity streaming design documentation and an interactive mockup.
  • Tests

    • Expanded coverage for indicators, streaming, accessibility, and status messaging.

Walkthrough

Adds a NEAR-branded process indicator with working and done states, animation and reduced-motion styling, typing-status integration, run-completion timing, an activity-stream design specification with an interactive mockup, and Docker build configuration coverage.

Changes

NEAR activity experience

Layer / File(s) Summary
Indicator contract and rendering
crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.tsx
Defines NEAR SVG paths and renders working and done states with scoped clip paths, labels, and optional elapsed text.
Indicator styling and validation
crates/ironclaw_webui/frontend/src/styles/app.css, crates/ironclaw_reborn_composition/tests/webui_v2_serve.rs, crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.test.ts
Adds NEAR colors, comet and pulse animations, reduced-motion handling, and component and stylesheet regression tests.
Run timing and completion rendering
crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.ts, crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.test.ts, crates/ironclaw_webui/frontend/src/pages/chat/components/message-list.tsx
Tracks run start times, emits validated completion items with durations, and renders completed runs with the done indicator.
Typing and chat status integration
crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.tsx, crates/ironclaw_webui/frontend/src/pages/chat/chat.tsx, crates/ironclaw_webui/frontend/src/pages/chat/components/chat-input.tsx, crates/ironclaw_webui/frontend/src/pages/chat/lib/chat.test.ts, crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.test.ts, crates/ironclaw_webui/frontend/src/pages/chat/components/chat-input.test.ts
Replaces the three-dot indicator, keeps typing status visible during active assistant streaming, removes the implicit composer working-status fallback, and updates related tests.
Activity-stream design and mockup
docs/design/agent-activity-streaming.md, docs/design/agent-activity-streaming/mockup.html
Documents the activity-stream UX and provides an interactive compare/focus mockup with timeline playback, activity state rendering, plan/context data, approval controls, and composer interactions.

Docker build configuration

Layer / File(s) Summary
Cargo configuration in build stages
Dockerfile, tests/dockerfile_runtime_home.rs
Copies .cargo/config.toml into the Docker build stage and verifies that the copy precedes the first Cargo command.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: ilblackdragon

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MockupControls
  participant TimelinePlayer
  participant ActivityDOM
  User->>MockupControls: Play, connect, approve, or deny
  MockupControls->>TimelinePlayer: Update timeline step
  TimelinePlayer->>ActivityDOM: Render activity beats, status, plan, context, and job state
  ActivityDOM->>User: Display the updated activity stream
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the pull request's primary agent-activity and streaming UX changes.
Description check ✅ Passed The description covers the required sections, change scope, validation, risks, security, rollback, and review follow-through.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

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.

@github-actions github-actions Bot added risk: low Changes to docs, tests, or low-risk modules contributor: new First-time contributor labels Jul 30, 2026
@ironloopai

ironloopai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Review · PR #6901

⚫ Cancelled · Target changed

The target changed before this Run could finish.

Automatic · PR opened · attempt 1 of 3 · cancelled after 2m 51s

Run details
  • Repository: nearai/ironclaw
  • Base: main at 5923789
  • Head: feat/agent-activity-streaming-ux at 3349c63
  • Created: Jul 30, 2026, 9:16 AM UTC
  • Updated: Jul 30, 2026, 9:19 AM UTC
  • Run: 1b8e705f-815a-447d-a416-d6291455e9d1
  • Latest attempt: 1 · Completed · 6309b31e-14ec-4a63-a8dd-e49ffaf1da61

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_webui/frontend/src/styles/app.css`:
- Around line 413-424: Update the three currentColor values in the
.near-process-icon styles (.near-base fill, .near-comet stroke, and .near-comet
filter) to the lowercase CSS keyword currentcolor, preserving all other styling
unchanged.

In `@docs/design/agent-activity-streaming/mockup.html`:
- Line 1: Add the standard HTML5 doctype declaration before the existing title
element in the mockup document, ensuring the browser renders the page in
standards mode while leaving the rest of the design unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b5e2032e-3c24-4045-bbab-298cf349488e

📥 Commits

Reviewing files that changed from the base of the PR and between 5923789 and 3349c63.

📒 Files selected for processing (6)
  • crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.tsx
  • crates/ironclaw_webui/frontend/src/styles/app.css
  • docs/design/agent-activity-streaming.md
  • docs/design/agent-activity-streaming/mockup.html

Comment thread crates/ironclaw_webui/frontend/src/styles/app.css Outdated
Comment thread docs/design/agent-activity-streaming/mockup.html
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 30, 2026 19:10 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 30, 2026 20:45 Destroyed
@serrrfirat

Copy link
Copy Markdown
Collaborator

Addressed the CodeRabbit feedback and failing CI in e8a56ea: normalized the CSS keywords, added the HTML5 doctype, replaced the stale typing-dot reduced-motion contract with coverage for the NEAR indicator, removed the unused dot animation CSS, and brought the initial /chat bundle back under its existing 211 KB gzip budget. Local validation: frontend build/budget, 988 tests, lint/typecheck, all-features clippy, and the previously failing composition regression all pass.

@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 86.6% (322258 / 372132 lines)
  floor:    85.54% (tolerance 0.5pp -> effective floor 85.04%)
  denominator: 372132 lines now vs 368757 at floor capture (+3375 lines, +0.92%) — not a material change

RATCHET PASS: ironclaw_runner
  observed: 87.16% (14990 / 17198 lines)
  floor:    86.87% (tolerance 0.5pp -> effective floor 86.37%)
  floor_covered_lines: 14669 (tolerance 20 lines -> effective floor 14649)
  denominator: 17198 lines now vs 16887 at floor capture (+311 lines, +1.84%) — not a material change

RATCHET PASS: ironclaw_processes
  observed: 88.93% (5896 / 6630 lines)
  floor:    88.07% (tolerance 0.5pp -> effective floor 87.57%)
  floor_covered_lines: 5839 (tolerance 20 lines -> effective floor 5819)
  denominator: 6630 lines now vs 6630 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_turns
  observed: 87.64% (9829 / 11215 lines)
  floor:    86.21% (tolerance 0.5pp -> effective floor 85.71%)
  floor_covered_lines: 9453 (tolerance 20 lines -> effective floor 9433)
  denominator: 11215 lines now vs 10965 at floor capture (+250 lines, +2.28%) — not a material change

Reborn integration-tier coverage

Line coverage (Reborn crates): 86.6% — 322258 / 372132 lines

Per-crate breakdown (60 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_host_ingress 42.5% 17 / 40
ironclaw_memory 54.6% 635 / 1163
ironclaw_projects 72.59% 233 / 321
ironclaw_trust 73.87% 670 / 907
ironclaw_capabilities 74.89% 2854 / 3811
ironclaw_observability 76.19% 32 / 42
ironclaw_filesystem 76.51% 5772 / 7544
ironclaw_extractors 77.3% 538 / 696
ironclaw_reborn_cli 77.99% 10859 / 13923
ironclaw_wasm 79.1% 704 / 890
ironclaw_llm 81.17% 22539 / 27769
ironclaw_events 81.35% 1256 / 1544
ironclaw_auth 81.61% 6027 / 7385
ironclaw_memory_native 81.99% 2951 / 3599
ironclaw_first_party_extensions 82.7% 6760 / 8174
ironclaw_libsql_runtime 84.21% 384 / 456
ironclaw_host_api 84.24% 9818 / 11655
ironclaw_extension_host 84.69% 22163 / 26170
ironclaw_operator 85.33% 5309 / 6222
ironclaw_network 85.67% 891 / 1040
ironclaw_reborn_event_store 85.81% 1222 / 1424
ironclaw_authorization 85.83% 715 / 833
ironclaw_reborn_composition 86.09% 21779 / 25297
ironclaw_secrets 86.15% 2843 / 3300
ironclaw_hooks 86.25% 9915 / 11496
ironclaw_reborn_config 86.4% 2091 / 2420
ironclaw_common 87.15% 1783 / 2046
ironclaw_runner 87.16% 14990 / 17198
ironclaw_skills 87.36% 4819 / 5516
ironclaw_turns 87.64% 9829 / 11215
ironclaw_scripts 87.87% 420 / 478
ironclaw_extensions 87.87% 4780 / 5440
ironclaw_product 87.98% 22795 / 25909
ironclaw_event_projections 88.12% 1372 / 1557
ironclaw_webui 88.22% 11836 / 13416
ironclaw_reborn_traces 88.49% 11665 / 13183
ironclaw_threads 88.62% 5179 / 5844
ironclaw_process_sandbox 88.64% 281 / 317
ironclaw_telegram_extension 88.7% 589 / 664
ironclaw_processes 88.93% 5896 / 6630
ironclaw_host_runtime 89.17% 21073 / 23632
ironclaw_reborn_openai_compat 89.73% 3643 / 4060
ironclaw_telegram_v2_adapter 90.12% 1560 / 1731
ironclaw_wasm_limiter 90.24% 74 / 82
ironclaw_loop_host 91.11% 18048 / 19809
ironclaw_approvals 91.21% 1816 / 1991
ironclaw_reborn_identity 91.3% 451 / 494
ironclaw_resources 91.49% 4085 / 4465
ironclaw_conversations 92.45% 2387 / 2582
ironclaw_event_streams 92.5% 1048 / 1133
ironclaw_mcp 92.62% 1317 / 1422
ironclaw_agent_loop 94.29% 10416 / 11047
ironclaw_slack_extension 94.48% 3678 / 3893
ironclaw_triggers 94.96% 3091 / 3255
ironclaw_first_party_extension_ports 95.06% 3752 / 3947
ironclaw_outbound 95.1% 4267 / 4487
ironclaw_safety 95.69% 4458 / 4659
ironclaw_prompt_envelope 97.96% 192 / 196
ironclaw_runtime_policy 98.63% 862 / 874
ironclaw_attachments 98.81% 829 / 839

This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors.

Exemptions (18 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_embeddings v1-only: consumed only by root ironclaw (src/app.rs, src/tools/builtin/memory.rs, src/workspace/mod.rs, src/config/{mod,embeddings}.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657
crates/ironclaw_attachments/src/lib.rs Declarative crate facade: module declarations, constants, and re-exports only; executable attachment modules remain covered. #6524
crates/ironclaw_extension_host/src/ingress/mod.rs Declarative ingress module facade and documentation only; executable router modules remain covered. #6524
crates/ironclaw_host_api/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable host API modules remain covered. #6524
crates/ironclaw_host_api/src/product_adapter/mod.rs Declarative product-adapter facade: module declarations and re-exports only; executable adapter modules remain covered. #6524
crates/ironclaw_llm/src/rig_adapter/tests/finish_reason_tests.rs Test-only module stored under src/ for private adapter access; cargo-llvm-cov omits test harness source from production LCOV while the exercised rig_adapter.rs production lines remain coverage-gated. #6284
crates/ironclaw_outbound/src/error.rs Declarative error vocabulary only; variants have no LLVM-instrumentable production statements. #6524
crates/ironclaw_outbound/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable outbound modules remain covered. #6524
crates/ironclaw_product/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable product behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_product/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable product modules remain covered. #6524
crates/ironclaw_product/src/scoped_fs/mod.rs Declarative scoped-filesystem facade and documentation only; executable scoped filesystem modules remain covered. #6524
crates/ironclaw_reborn_composition/src/support/fs/mod.rs Declarative composition support facade: module declarations and re-exports only; executable filesystem adapters remain covered. #6524
crates/ironclaw_slack_extension/src/lib.rs Declarative Slack crate facade: module declarations and re-exports only; executable Slack modules remain covered. #6524
crates/ironclaw_telegram_extension/src/lib.rs Declarative Telegram crate facade: module declarations and re-exports only; executable Telegram modules remain covered. #6524
crates/ironclaw_threads/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable thread behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_webui/src/webui_v2/mod.rs Declaration-only WebUI v2 facade with no executable Rust statements; rustc emits no LCOV source record. Executable route behavior remains covered in the owned implementation modules. #6524

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 30, 2026 21:45 Destroyed
@github-actions github-actions Bot added the scope: sandbox Docker sandbox label Jul 30, 2026
@serrrfirat

Copy link
Copy Markdown
Collaborator

Railway follow-up pushed in ab8132719:

  • Root cause: the Docker build omitted .cargo/config.toml, so cargo chef cook did not receive the repository retry policy or HTTP/2 multiplexing disablement and aborted on a transient crates.io curl [16] Error in the HTTP2 framing layer while fetching ppv-lite86.
  • Fix: copy the existing Cargo config into the shared chef stage before any Cargo command, covering cargo install, planner, dependency cook, and final build stages.
  • Regression coverage: extended dockerfile_runtime_home to require that wiring; the assertion failed before the fix and the full suite now passes 19/19.
  • Local verification: formatting, all-feature clippy, Docker deployment contracts, and pre-commit safety checks pass. A local image build was unavailable because the Docker daemon is stopped; the Railway rebuild is the authoritative deployment check.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/dockerfile_runtime_home.rs`:
- Around line 157-160: Strengthen the assertion around the Dockerfile content in
the relevant test by parsing or isolating the chef stage, then verify the
.cargo/config.toml COPY instruction appears there before any cargo-chef
commands. Preserve multiline COPY syntax coverage, and avoid relying on global
text presence that could match later stages or comments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7afa7c0b-9732-4d5f-912e-0dc8bd092f97

📥 Commits

Reviewing files that changed from the base of the PR and between e8a56ea and ab81327.

📒 Files selected for processing (2)
  • Dockerfile
  • tests/dockerfile_runtime_home.rs

Comment thread tests/dockerfile_runtime_home.rs
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 06:46 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 07:14 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 07:22 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/design/agent-activity-streaming.md (1)

4-7: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Make the hosted mockup reference a usable link.

Line [7] is plain code text and has no URL scheme. Readers cannot open the advertised artifact. Use a Markdown link with an explicit https:// URL, or remove the reference if it is not durable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/agent-activity-streaming.md` around lines 4 - 7, Update the
hosted mockup reference in the design document to use a clickable Markdown link
with the explicit https:// artifact URL, or remove the reference if that URL is
not durable; leave the local mockup.html link unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.tsx`:
- Around line 3-19: Update TypingIndicator’s props so the "done" state requires
a valid durationSeconds, using a discriminated union between working and done
props; preserve the existing "working" behavior and ensure the completed label
cannot render an undefined duration.

In `@docs/design/agent-activity-streaming.md`:
- Around line 80-82: Update the foundation scope statement in the design
document so it no longer describes the full PR as presentation-only; refer
specifically to this activity cohort, or add a separate mention of the
Dockerfile and tests/dockerfile_runtime_home.rs changes while preserving the
existing component description.

---

Outside diff comments:
In `@docs/design/agent-activity-streaming.md`:
- Around line 4-7: Update the hosted mockup reference in the design document to
use a clickable Markdown link with the explicit https:// artifact URL, or remove
the reference if that URL is not durable; leave the local mockup.html link
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7c0e719c-c0da-47c3-9138-328746183311

📥 Commits

Reviewing files that changed from the base of the PR and between e76efe8 and cd981c9.

📒 Files selected for processing (7)
  • crates/ironclaw_webui/frontend/src/pages/chat/components/message-list.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.ts
  • docs/design/agent-activity-streaming.md
  • tests/dockerfile_runtime_home.rs

Comment thread docs/design/agent-activity-streaming.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/ironclaw_webui/frontend/src/pages/chat/lib/chat.test.ts (1)

362-395: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the configured TSX VM wrapper.

This changed test executes Chat through renderChat, which uses raw vm.runInNewContext at Line 152. Route this harness through vm-tsx-setup.ts so the test uses the configured .tsx transpilation path.

Based on learnings, VM-loaded .tsx source must use vm-tsx-setup.ts instead of direct VM evaluation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_webui/frontend/src/pages/chat/lib/chat.test.ts` around lines
362 - 395, Update the renderChat test harness to load Chat through the
configured vm-tsx-setup.ts wrapper instead of calling vm.runInNewContext
directly. Preserve the existing renderChat behavior and test setup while routing
all VM-loaded TSX source through the wrapper’s transpilation path.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.test.ts`:
- Around line 30-34: Update the VM-loading logic in the NearProcessIndicator
test to use the configured vm-tsx-setup.ts wrapper or shared VM harness instead
of calling raw node:vm on the TSX source. Preserve the existing
__testExports.NearProcessIndicator retrieval while ensuring TypeScript and JSX
are transpiled through the established path.

In
`@crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.tsx`:
- Around line 49-50: Update the SVG render in near-process-indicator.tsx to use
React’s clipPath prop instead of clip-path. In near-process-indicator.test.ts
lines 81-82, update the render-tree assertion to read node.props.clipPath.

In
`@crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.test.ts`:
- Around line 40-63: Update the TypingIndicator props contract and
completed-state rendering so a done state cannot produce “Worked for
undefineds”: either require durationSeconds when state is "done" via a
discriminated prop type, or use a duration-free label when it is absent.
Preserve the existing elapsed-duration label for completed indicators that
provide durationSeconds.

In `@docs/design/agent-activity-streaming.md`:
- Line 7: Replace the repo-private claude.ai/code/artifact link on line 7 with
the durable in-repository reference already present on line 4, or use a public
persistent URL; do not leave the external artifact link in the document.

In `@docs/design/agent-activity-streaming/mockup.html`:
- Line 651: Remove the unused data-n lookup and n variable from the code around
the mockup’s pi element handling, leaving the existing data-i index logic
unchanged. Also remove the unused STAT[8].v entry defining “Done · 12s,” since
that status is hardcoded elsewhere.
- Around line 1-2: Add two meta tags to the document head section after the
title element to properly declare character encoding and configure the viewport.
Include a meta charset tag set to utf-8 to ensure the non-ASCII characters
throughout the document render correctly when opened over file:// protocol, and
add a viewport meta tag with appropriate configuration to support the responsive
design that uses vw units and mobile breakpoints in the stylesheet.
- Around line 703-710: Make the elapsed timer run-scoped by anchoring runStart
once when the turn begins, rather than assigning it whenever a busy segment
starts in the busy-state logic. Keep clearing and recreating elTimer across busy
and wait segments without resetting runStart, and clear the runStart anchor in
reset() so a new turn starts at zero.

---

Outside diff comments:
In `@crates/ironclaw_webui/frontend/src/pages/chat/lib/chat.test.ts`:
- Around line 362-395: Update the renderChat test harness to load Chat through
the configured vm-tsx-setup.ts wrapper instead of calling vm.runInNewContext
directly. Preserve the existing renderChat behavior and test setup while routing
all VM-loaded TSX source through the wrapper’s transpilation path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 069526a9-a10d-4a3e-94e5-38ce47603cec

📥 Commits

Reviewing files that changed from the base of the PR and between 553df2e and cd981c9.

📒 Files selected for processing (17)
  • Dockerfile
  • crates/ironclaw_reborn_composition/tests/webui_v2_serve.rs
  • crates/ironclaw_webui/frontend/src/pages/chat/chat.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/chat-input.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/components/chat-input.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/message-list.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/components/near-process-indicator.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/components/typing-indicator.tsx
  • crates/ironclaw_webui/frontend/src/pages/chat/lib/chat.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.test.ts
  • crates/ironclaw_webui/frontend/src/pages/chat/lib/message-groups.ts
  • crates/ironclaw_webui/frontend/src/styles/app.css
  • docs/design/agent-activity-streaming.md
  • docs/design/agent-activity-streaming/mockup.html
  • tests/dockerfile_runtime_home.rs

Comment thread docs/design/agent-activity-streaming.md Outdated
Comment thread docs/design/agent-activity-streaming/mockup.html
Comment thread docs/design/agent-activity-streaming/mockup.html Outdated
Comment thread docs/design/agent-activity-streaming/mockup.html
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 07:58 Destroyed
@serrrfirat

Copy link
Copy Markdown
Collaborator

Addressed the latest CodeRabbit activity review in 1329aa922: hardened the completed-duration prop contract, fixed React SVG clipping, migrated both changed TSX VM tests to the configured shared harness, removed the private artifact link, narrowed the presentation-only scope statement, and corrected the standalone mockup metadata/dead state/run-scoped timer. Validation: 118 files / 992 frontend tests, lint/typecheck, focused 26-test render suite, and production build/bundle budget all pass.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 08:02 Destroyed
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6901 July 31, 2026 08:38 Destroyed
@serrrfirat
serrrfirat merged commit 945d926 into main Jul 31, 2026
64 checks passed
@serrrfirat
serrrfirat deleted the feat/agent-activity-streaming-ux branch July 31, 2026 09:15
personal-upstream-sync Bot pushed a commit to theredspoon/ironclaw that referenced this pull request Aug 20, 2026
…, agent-mode pill (nearai#6994)

* feat(webui): OOBE automation-tasks prototype — carousel, inline cards, agent-mode pill

First-time-user OOBE concepts for the WebChat v2 landing view, built as a
UI-only prototype on mock data (backend intentionally not wired yet). Recovered
and rebased from the Jul design session (was the stale design/oobe-chat-automations
WIP); the streaming NearProcessIndicator busy-states are re-applied on top of #6901.

Adds to the chat view:
- Completed-automations carousel above the composer (automation-carousel,
  automation-task-card) — validates auto-run tasks, deep-links into the 3rd-party app.
- Inline calendar-reschedule rich-preview (calendar-reschedule-card) and a Plan-mode
  batch card (plan-card), sharing one decision model via task-action-bar
  (suggested → Approve/Modify/Cancel; automated → Modify/Revert).
- Agent-mode composer pill (mode-selector + lib/agent-mode) — Suggest/Plan/Auto/Bypass,
  persisted to scoped localStorage in the prototype.
- Typed mock domain + endpoint-shaped seam (lib/automation-tasks*, useAutomationTasks)
  so wiring the backend is a mock→fetch body swap with no component changes.
- DEV-only /design-preview harness (design-preview-page) to view the concepts, gated by import.meta.env.DEV.
- Busy states render the branded NearProcessIndicator (from #6901) — shared design language.

The backend (durable events, projection, transport frame, HTTP routes, facade+effect,
agent-mode persistence) is NOT implemented; AUTOMATION-TASKS-CONTRACT.md is the
reviewable wiring spec, tracked as a follow-up.

NOTE (why this is a draft): the landing carousel reads listAutomationTasks(), which
returns MOCK data for all users and is not DEV-gated. Must be backend-wired or gated
before this can leave draft / merge.

Frontend gate green: conventions + typecheck clean, 1032 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): add OOBE first-run onboarding mockup + brief

Standalone design exploration for the two first-run moments the PR #6994
prototype skips: the cold-start landing (zero automations, nothing connected)
and the first "Done for you" card appearing. House style matches
docs/design/agent-activity-streaming.

- docs/design/oobe.md — brief: goal, the two moments, the Invite vs Coach
  direction fork, what ships (#6994) vs needs backend (#6993), open questions.
- docs/design/oobe/mockup.html — interactive: plays cold-start → connect →
  anticipatory (NEAR indicator + skeleton tiles) → first-card reveal →
  populated, with a seg toggle for Invite (minimal) vs Coach (anticipatory
  ghost cards). Real --v2-* tokens; light+dark; reduced-motion honored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — add Thread + Plan scenes

Fold the two in-thread concepts into the standalone mockup so one shared
Artifact covers the whole OOBE arc. Adds a Scene selector (First run /
Thread / Plan):
- Thread — the inline CalendarRescheduleCard rich-preview (live: Approve →
  "Rescheduling…" → Automated; Modify time cycles the proposed slot; Skip →
  dismissed), plus an already-automated example with Modify/Revert.
- Plan — the batched PlanCard (Approve all → "Running your plan…" → all done;
  per-item skip), faithful to plan-card.tsx.
Same --v2-* tokens, NearProcessIndicator busy states, light+dark, flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — flag taxonomy + connect-pill redesign

Flags reframed around what's on main: Shipped (already on main), Redesign
(design update to existing main UI), New Feature (net-new, needs new
events/functionality), New UX (new design not on main); New Feature + New UX
combine. Applied: connect row = Shipped (reuses AuthRequired); composer =
Redesign (mode pill added); Coach ghost = New UX; carousel, both calendar
cards, and the plan card = New UX + New Feature.

Connect pills redesigned: per-tool checkbox state (no "connect" text), a
"Connect all" action, and — once connected — the pills condense into an
overlapping icon stack ("N connected", tap to re-expand).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — Connect all as a lightweight link

Restyle the "Connect all" action from a filled primary button to a
lightweight accent text link (underline on hover) so it doesn't compete with
the connect pills. Kept as a <button> for keyboard/focus + the click handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — avatar-style condensed connect stack

Restyle the condensed connected-tools stack after the stacked-avatars
reference: circular app icons with a white ring (theme surface) + soft drop
shadow, heavier overlap, and a trailing "+" circle to add another tool. The
count moves to the header subtitle ("3 connected — tap to manage"); the whole
stack re-expands on tap. Light + dark, reduced-motion honored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — refine condensed connect stack

Per feedback on the stacked-tools chip: opaque icon fills (drop the
transparent tint so overlaps don't bleed), rounded-square shape to match the
expanded pills (was circular), a ">" chevron instead of "+" on the trailing
chip, and "add more later" → "add more anytime" in the header subtitle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — relocate live indicator to carousel + collapse action

Contextual placement: the branded NEAR process indicator now leads the carousel
header (animated while working with a live elapsed, settling to a solid mark +
"worked for Ns" when done) instead of floating above the composer — it sits
where the agent's output is forming. Header is now a two-line block (mark +
title/elapsed over subtitle) so it stays clear of the annotation flags.

Connect pills: add a collapse control (left-chevron chip) to the right of the
expanded pills, mirroring the stack's expand affordance, to re-condense.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — agent mode drives task-card state

Rename the "Automated" badge to "Completed", and make the agent-mode pill a
live control: Suggest / Plan render the carousel cards as suggested (Approve /
Modify / Cancel, "Suggested" badge, "Suggested for you" header + hero); Auto /
Bypass render them completed ("Completed" badge, Modify / Revert, "Done for
you"). Per-card Approve flips a single card to completed, Cancel → dismissed,
Revert → reverted — so the suggested→completed flow is real, not just a label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — single-line carousel header

Put the secondary description back on the same line as the indicator's activity
string (title + elapsed). To keep it single-line and clear of the annotation
flag, drop the redundant working-state subtitle (title + live elapsed is enough)
and tighten the done-state subtitle to "Review or undo anytime".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — remove the cold-start Invite/Coach switcher

Drop the Invite/Coach direction toggle and its JS; first run now uses the
minimal ("Invite") cold start. Cleaned up the lede + footnote copy that
referenced the toggle and the Coach ghost strip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — dismissible connect panel + composer pill; archive stack

Connect-tools panel:
- add a close (X) to dismiss the panel; when dismissed it collapses to a small
  "Connect your tools" pill in the composer action row (left of the agent-mode
  picker) with its own X. Pill body re-opens the panel; pill X removes it.
- remove the collapse/expand overlapping-stack control entirely.

Archive: docs/design/oobe/archive/connect-tools-stack.html — a self-contained,
theme-aware record of the retired collapse/expand states (expanded pills +
collapsed avatar stack) for the design archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — move connect pill right of the mode selector

Place the dismissed-state "Connect your tools" pill after the agent-mode picker
in the composer action row (was to its left).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — connect panel button + reflow

- Move the connect action out of the header into a bottom-right button (was a
  link); its label is "Connect all" with nothing selected, "Connect" once any
  tool is picked, hidden when all are connected.
- Pin the dismiss (X) far-right in the header (margin-left:auto) so it no longer
  relocates when the button hides.
- Add more tools (Notion, Drive, GitHub) so the pills reflow to a second row
  past four.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — connect button rides the pill row (drop empty footer)

The connect action now flows at the end of the pills (right-aligned via
margin-left:auto) instead of a dedicated full-width footer row, removing the
wasted empty space to the button's left.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — Gemini ai-spark border on the first-card reveal

Replace the static accent glow ring on the first "aha" card with a Gemini-style
ai-spark: a blue→purple→pink conic gradient masked to the card border that
chases around once (1.35s) and then dissipates, with a soft purple/coral glow.
Uses @property --ai-angle for the sweep; hidden under prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — make the ai-spark actually chase the border

The conic-gradient + @property --ai-angle version interpolated the angle but
Chromium didn't repaint the gradient, so the spark never moved. Rebuild it as
an SVG rect stroke with an animated stroke-dashoffset (a Gemini blue→purple→pink
gradient dash that travels the border once, then dissipates) — stroke-dashoffset
repaints reliably every frame. Hidden under prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE ai-spark — faster, tapered comet tail, theme-aware colors

- Speed: 1.5s → 0.7s lap.
- Tail: uniform round-cap dash → a solid head fading into progressively
  sparser dashes; the drop-shadow glow blurs it into a smooth tapered comet
  (restores the taper the conic version had).
- Colors: per-theme tokens (--ais-1/2/3 + --ais-glow) — deeper/saturated blue
  →purple→magenta on light so it reads on white, brighter on dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE first card is conjured — spell-cast reveal

Make the first automation card feel summoned rather than placed:

- Faster spark: 0.7s -> 0.5s lap.
- Conjure: the card no longer pops in fully-formed — it materializes
  (opacity 0->1, scale .84->1 with a slight overshoot, blur 7px->0) in sync
  with the spark tracing its border.
- Spell-land: a brief glow pulse (--ais-glow) blooms around the card as the
  spark completes its loop.
- prefers-reduced-motion disables conjure + spell-land alongside the spark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — soften the conjure/spark effect

Dial the spell-cast reveal back to a subtle shimmer:
- Thinner spark stroke (2.6 -> 2.1) with a softer drop-shadow (3/8px -> 2/5px).
- Lower glow alpha (dark .85 -> .62, light .5 -> .4).
- Gentler spell-land pulse (24px/.85 -> 14px/.4).
- Calmer conjure: less blur (7 -> 4px), smaller scale-up (.84 -> .92) and
  near-zero overshoot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE spark — smooth continuous tapered comet

Replace the segmented SVG dash with one intact line:
- Technique: a conic-gradient comet masked to the border ring and rotated
  (transform repaints reliably, unlike an animated conic angle) — gives a
  single continuous line with a smooth head-to-tail taper.
- Transparency: color-mix bakes translucency into the color line
  (head ~86%, fading to fully transparent at the tail).
- Faster: 0.5s -> 0.4s lap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — clean up the task card layout & styling

Restyle the automation cards after the "Your availability" reference pattern:
- Hierarchy: the task title now leads the header (icon + bold title, status
  badge top-right); the app name drops to a muted "From Gmail · 2m ago"
  provenance line above the actions.
- Buttons: filled primary + text secondaries (Approve / Modify / Dismiss)
  instead of three bordered buttons; Cancel -> Dismiss.
- Surface: larger radius (13 -> 16px), more padding, a soft floating shadow,
  a middot-separated metric line, and bottom-aligned action rows so equal-
  height cards line up. Spark/conjure ring radii follow the new corner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE cards — real brand logos, drop the tag, condense

- Real product logos (Gmail, Google Calendar, Docs, Drive, Slack full-colour;
  Notion + GitHub monochrome via currentColor so they follow the theme) replace
  the placeholder line icons — on the task cards, connect pills, Thread card,
  and Plan list. Icon chips become tile-less logo holders (no tint/border).
- Remove the status tag/badge from the task-card header (state still reads from
  the action row).
- Condense card height (padding 14->12, tighter header/prov gaps; single-line
  titles now that the badge is gone) and scale the button row down
  (height 32->28, smaller padding/font).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — consistent card buttons, real Notion mark, task drawer

1. Link buttons carry icons in both modes: suggested-mode Modify/Dismiss now
   get the edit / close icons, matching the completed-mode Modify/Revert.
2. Notion logo swapped to the real Notion mark (notebook + N, monochrome via
   currentColor so it follows the theme) instead of the plain geometric N.
3. Task drawer: typing in the composer collapses the full task cards into a
   condensed, scrollable pill row (brand logo + title) above the composer;
   clearing the field — or tapping a pill — re-expands. Same control can seed
   suggested tasks for a returning user / new thread.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Vision/Foundational versions + attached task drawer

Add a Version switch (toolbar) with two design tracks:

Vision (north-star): the task cards now sit in a bordered "drawer" frame that
docks onto the composer and extends up from it, cards inset within the frame.
The drawer header carries collapse/expand (cards <-> pills) and a dismiss (X)
that hides it behind a "Show suggestions" restore bar. Typing still collapses
to pills. Keeps the connect flow, named greeting, and full mode set.

Foundational (near-term, v2-faithful): scoped for a multi-tenant enterprise
deploy — tools are admin-preconfigured so there's no connect step; no username
unless derivable (nameless greeting + blank account chip); agent modes scoped to
Suggest / Plan / Auto Approve, default Suggest. Uses main's composer and the
plain pills-collapse from the prior commit (no bordered drawer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Foundational cards at first step, Vision status line above drawer

1. Foundational first run is now a single populated state: because enterprise
   tools are admin-preconnected, the suggested task cards appear at the first
   step (no empty cold start, no beat scrubber).
2. The branded progress indicator + agent activity string move ABOVE the drawer
   (a relocated status line); the drawer header now carries the subtitle
   top-left ("Approve to run, or tweak first") beside the collapse/dismiss
   controls. Applies across both versions; the frame remains Vision-only.
3. Auto Approve description clarified: auto-approves task types already approved
   plus any task the user requests.
4. Rewrote docs/design/oobe.md to document the Vision/Foundational split,
   Foundational enterprise scoping, the reusable task drawer, and phasing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — corner-X dismiss, empty-state fallback, drawer title = agent string

- Per-item dismiss: the "× Dismiss" link is gone; each task card gets an X in
  its top-right corner and each collapsed pill gets an X on the right. Dismissed
  items are removed from the strip/pills.
- Empty state: when every suggestion is dismissed, a dashed fallback appears —
  Vision "Coming up with new suggestions" (pulsing), Foundational "Find new
  suggestions" (tap to repopulate).
- Removed the drawer-level dismiss X and the restore bar (dismissal is per-item
  now); the drawer header keeps only the collapse/expand toggle.
- Removed the branded NEAR progress indicator on both versions; the agent
  activity string ("Looking for things to suggest" / "Suggested for you") is now
  the drawer title (upper-left) with the subtitle beneath it. Hidden when collapsed.
- Toggle is pinned top-right and floats above the pills (bg fade + padding) so it
  no longer covers an overflowing pill.
- Foundational is steppable again (scrubber restored) with suggested cards from
  the first step; revert now returns a card to suggested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — keep drawer title + subtitle on one line

Revert the drawer header to a row layout so the agent string and its subtitle
("Suggested for you  Approve to run, or tweak first") stay inline on a single
line instead of the subtitle reflowing to a second line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Foundational approve→automate→complete journey, Modify modal, attachment collapse

1. Foundational now steps through a real flow: beat 0 suggested → approve →
   beat 1 "Automating…" (spinner) → beat 2 completed, repeating for the next
   task, ending all-done. Adds a `running` card state; clicking Approve (either
   version) animates suggested → Automating… → completed (~1s). Drawer title
   tracks the state (Suggested for you / Automating… / Done for you).
2. Attachment collapse: adding an attachment (the composer + button, with a
   removable chip) now collapses the drawer to pills too — alongside typing.
3. Modify opens a modification modal (both versions): title = task name, an
   "adjust before it runs" field, Cancel / Save changes; backdrop over the app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — state-aware card copy, tighter composer gap, pill tap expands

1. Cards/pills now carry both a suggested (proposal) and completed (result)
   phrasing and switch on state: e.g. "Triage your inbox · 40 unread · 12 need
   replies · From Gmail" while suggested, "Triaged your inbox · 12 replied · 40
   archived · From Gmail · 2m ago" once done. Fixes suggested cards reading as
   already-completed (both versions).
2. Halved the gap between the cards/pills and the composer (Foundational).
3. Tapping a collapsed pill now expands it back to the full task cards (both
   versions); typing/attaching re-collapses (suppression flag so a tap-to-expand
   isn't immediately re-collapsed by lingering composer text).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — 3rd-party auth flows (queued OAuth modal)

Add a reusable modal "browser" OAuth dialog (chrome bar + provider domain,
sign-in account chooser, consent/scopes, Allow) wired into both tracks:

- Vision: the connect panel now *selects* tools; the Connect button opens the
  dialog queued across the selection (sign in once, approve scopes per tool)
  until all are authorized, then advances.
- Foundational: tools are admin-whitelisted but user-authorized — each task card
  starts unconnected with a "Connect <Tool>" CTA; connecting runs the dialog and
  the card becomes an actionable suggestion. Beat journey now walks
  unconnected → connected(suggested) → automating → completed.

Brief updated to match (Foundational connect model + Vision OAuth queue).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — refresh the 5 suggested/automated tasks

Replace the 3 sample cards with the intended task set (both versions):
1. Email triage — archive marketing to "IronClaw Archive", flag urgent (Gmail)
2. Calendar — accept free invites, propose times for conflicts
3. Build your profile — read activity across Gmail/Slack/Telegram (multi-tool)
4. Catch-you-up 24h digest — org summary, flag replies, propose priorities
   (Drive/Notion, multi-tool)
5. Suggest 5 automations — agent drafts its top-5 to approve (no external tool)

Cards now carry a short description line (suggested proposal vs completed
result) instead of the number pairs, custom glyphs for the agent/meta tasks,
and a per-card `conn` tool list so the connect CTA queues the right OAuth
dialogs ("Connect 3 tools" → Gmail→Slack→Telegram). Added a Telegram brand
logo + auth metadata; Foundational beat table + greeting updated for 5 cards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — match collapsed-pill bottom gap to the task-card gap

The pill row carried 6px bottom padding vs the card strip's 2px, so the pills
sat ~4px farther from the composer. Reduce the task-drawer bottom padding to
match the strip (4px base, 2px Foundational) — pill and card bottoms now sit the
same distance above the composer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Vision — connect banner confirms then dismisses after auth

After the user authorizes their selected tools through the OAuth queue, the
"Connect your tools" banner flips to a confirmation state — green check icon,
"Tools connected · N authorized", the connected tools shown green, close-X
hidden — then dismisses (~1.3s) as the flow advances to the working/anticipatory
beat. Beat 1 is now that confirmation moment (also reachable via the scrubber).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — section-level dismiss X for cards + pills

Add a drawer-level close (X) pinned top-right of the suggestions section,
visible in both the expanded task-card state and the collapsed pill row
(Foundational only — Vision keeps its collapse toggle there). Dismissing hides
the whole drawer and drops a "Show suggestions N" restore bar above the
composer; restoring brings it back. Per-item × on each card/pill is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — align the section dismiss X in both states

Center the drawer dismiss X on the "Suggested for you" header (expanded) and on
the pill row (collapsed) via a state-specific top, and move it flush to the
right edge of the card/pill container + composer (right 11px -> 2px). Verified
dy=0 in both states.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — dismiss-X container masks against the real background

The dismiss X used --v2-surface (white) for its fill + left fade, but the pills
sit on --v2-canvas, so pills bled through the gradient. Switch the X container
fill and its left-fade shadow to --v2-canvas so it matches the background behind
the pills — overflowing pills now fade cleanly into the bg (masking effect),
gradient retained. Verified fill == scene bg in light and dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — collapsed dismiss becomes a full-height gutter mask

In the collapsed pill state the dismiss control is no longer a small rounded
square: it fills the drawer height, pins flush to the right edge, and carries a
transparent->canvas gradient so pills fade out and aren't visible past it. The X
sits centred in the gutter. Expanded (cards) keeps the header-aligned X.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — move "Show suggestions" restore into the composer

Replace the restore bar above the composer with a pill inside the composer, to
the right of the agent-mode selector (reusing the connect-pill style). Tapping
the pill body restores the dismissed suggestions drawer; the pill's X fully
dismisses it (new 'gone' state — drawer and pill both hidden). Removed the dead
restore-bar markup + CSS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE integration proposal & plan — Foundational + Vision phasing

Add a #6918-style proposal package under docs/design/oobe/ (README / PROPOSAL /
PLAN / CHECKLIST) for phasing the OOBE prototype into production:

- Foundational (near-term, ships on current main) then Vision (north-star),
  matching the mockup's two versions; every Vision piece a superset of a
  Foundational one, so nothing is redone.
- Scopes Foundational as shipped-vs-net-new: the connect CTA, busy states,
  agent-mode semantics, and manage-result surface all REUSE code on main
  (extension-auth path, NearProcessIndicator, resolve_gate/global_auto_approve,
  pages/automations); the net-new surface is the card family, the
  AutomationTask events+projection+routes+facade, and the first-run suggestion
  producer.
- Inventories dependencies D-F1..F6 (Foundational) and D-V1..V5 (Vision), each
  with an implementation approach, and maps the work onto the five-layer WebUI
  flow and the #6918 target families.
- Applies the APDD governance kit (docs-first workflow, Feedback & Decisions
  anchor, Critical Bug Fix Log, design track, CUJ baseline).

Companion human-review artifact (schematics/diagrams):
https://claude.ai/code/artifact/734b1b6a-e35d-4736-9ac2-952dcdf84ab4

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): reconcile OOBE proposal + contract to post-#6918 family names

The #6918 family-folder reorg has landed on main; update the proposal package
and the wiring contract to current crate names/paths and fix a stale claim:

- crates now under crates/{contracts,events,domains,product,app}/; renames
  ironclaw_events -> ironclaw_event_log, add ironclaw_event_store (both under
  events/), ironclaw_reborn_composition -> ironclaw_composition, and the webui
  frontend paths move to crates/product/ironclaw_webui/frontend/.
- correct the facade identity: it is RebornServicesApi in
  crates/product/ironclaw_assistant (NOT "ProductSurface" — that is the typed
  capability contract/DTOs in ironclaw_product_contracts).
- reframe "#6918 target families" as the family folders now on main.
- note the triggers-hosted suggester option (D-F2) can reuse the existing
  composition automation wiring (trigger_poller + trusted_submit).
- retire the removed .claude/rules/tool-evidence.md reference -> gateway-events
  / lifecycle; product adapters -> the ProductAdapter surface in ironclaw_host_api.
- mark the F0 merge-to-main + contract-reconciliation boxes done.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Roll back the OOBE prototype code; reposition PR as design artifacts + plan

Per review (IronLoop/CodeRabbit flagged mock automations shown to real users and
an autonomy selector execution ignored), drop the prototype source and make this
branch code-free: crates/ is now identical to main.

- Revert the edits to shipped files (app.tsx, chat-input, empty-state, en.ts,
  button/icons + their tests) and delete the added prototype files (automation
  cards, action bar, mode selector, data seam, hooks, design-preview harness).
- Move AUTOMATION-TASKS-CONTRACT.md out of the code tree into docs/design/oobe/
  (kept as the design reference / proposed wiring).
- Plan of record is now the artifacts + the written plan: add
  docs/design/oobe/integration-review.html (the "IronClaw OOBE — Integration
  Review" page) in-branch, and repoint the former claude.ai artifact links to it
  (rendered via html-preview.github.io).
- Reconcile the package (README/PROPOSAL/PLAN/CHECKLIST/brief + contract): a
  code-free banner, fix links to rolled-back files, and reframe "the prototype
  ships here / mock->fetch swap" as "prototyped earlier + demonstrated in the
  mockup; the first implementation builds fresh." D-F5/D-F4 gating moves to the
  implementation PRs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — align the Foundational version to shipped v2

The mockup's design tokens already match crates/product/ironclaw_webui/src/styles/app.css
verbatim; this aligns the Foundational version's *treatment* to the shipped
WebChat v2 landing:

- hero switches from the serif exploration face to Geist sans, heavier and larger
  (matching empty-state.tsx's text-4xl/6xl font-semibold hero);
- suggestions become full-width divider rows with a round leading icon (matching
  the shipped grid-cols-[auto_1fr_auto] row treatment) instead of pill chips;
- composer picks up the shipped 20px radius + card-bg + round icon buttons.

All scoped to .v-foundational so the Vision (north-star) version keeps its
distinctive treatment. CSS validated (balanced); in-app browser CDP was wedged,
so verify visually via html-preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup composer — match main (remove mic, round send, larger, full-width suggestions)

Correct the composer to the shipped chat-input.tsx:
- remove the microphone/dictate button (main has none);
- send button becomes a round primary icon button (paper-plane), replacing the
  labeled "Send ⌘↵" pill, matching Button variant="primary" size="icon-sm" rounded-full;
- enlarge the Foundational composer (min-height 120, 15px field, roomier padding)
  to match main's min-h-[120px] hero composer;
- the suggestion rows below the composer now span the full composer width
  (width:100% on .v-foundational .suggs — they were shrink-to-fit + centered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — apply composer button treatment to Vision too

The mic-removal and round send-button were already global; the round attach
icon button was still Foundational-scoped, leaving Vision's composer with a
square attach. Make the icon-button treatment global (round, 36px) so the
Vision flow's composer reflects the same Send / attach / no-mic design as
Foundational and main. (Composer *sizing* stays Foundational-scoped — Vision
docks its composer onto the drawer.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE CHECKLIST — adopt epic #7044 success criteria

Additive only: add the epic's Phase-1 success criteria (time-to-first-automation,
first-session activation, suggestion quality) to the Foundational exit gate. No
other plan content changes — the proposal package stays the plan of record; the
epic↔proposal scope conflicts are reconciled in #7044, not here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Phase-1 (v1) UX update — 6 changes wired to shipped backend seams

Mockup (Foundational-scoped; Vision mock unchanged):
- remove the agent-mode selector (kept in Vision) [1]
- disable the other cards while one job runs [3]
- replace Revert with "+ Automation" (creates a scheduled automation) [4]
- v1 = connect + approve UI, no background jobs [5]
- drop Modify; show completed / error-incomplete status on the card [6]

PROPOSAL: new §2A "Phase 1 (v1) implementation update" specifying how each change
wires to EXISTING backend seams (verified on main) — no new AutomationTask
events/projection needed for v1:
- approve -> POST /threads/{id}/messages (submit_turn -> TurnCoordinator), run in thread [2]
- status/activity -> existing WebChatV2Event stream (running/capability_activity/final_reply/failed)
- one-active-run -> submit_turn DeferredBusy/RejectedBusy
- connect -> extension setup/OAuth + AuthRequired frame
- "+ Automation" -> prompt injection -> builtin.trigger_create -> automations dashboard
- gates -> resolve_gate

Also: fix doc link depth after main renamed docs/design -> docs/internal/design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational v1 — implementation plan grounded in current main

Add IMPLEMENTATION.md: a concrete build plan for the Phase-1 v1 UX (PROPOSAL §2A),
proving every card action wires to seams already enabled on main and enumerating
the frontend components, the feature-flag gating (the D-F5 merge-safety fix), the
vertical PR slices, and the tests.

Verified-enabled on main: submit_turn via lib/api.ts sendMessage; status via
useChatEvents (folds WebChatV2Event frames → running/final_reply/failed); connect
via extension-pairing-api + AuthRequired; resolve_gate; automations dashboard +
useAutomations; builtin.trigger_create; session feature flags (app/auth.ts
features?.). The one net-new backend piece is the first-run suggestion producer
(D-F2) — slices 1–5 ship frontend-only behind an off-by-default flag; the flag
flips on only when the producer lands. No new AutomationTask events/projection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE Foundational v1 slice 1 — feature-gated SuggestedTaskCard

First implementation slice of the OOBE Foundational v1 (docs/internal/design/oobe
PROPOSAL §2A / IMPLEMENTATION.md). Presentational + gated only — no backend
wiring, no mock data reachable by real users.

- SuggestedTaskCard: one action row per state per §2A — unconnected→Connect,
  suggested→Approve (no Modify), running→NearProcessIndicator, completed→Completed
  chip + "+ Automation" (no Revert/Modify), failed→"Couldn't complete" + Try again;
  `locked` disables the card (item 3). Pure/presentational (callbacks are props).
- SuggestedTaskSurface: reads the `oobe_suggestions` deployment flag via a shared
  ["session"] query and renders null when off (landing unchanged for real users);
  renders a static demo list only when on. Mounted in empty-state above composer.
- auth.ts: `oobeSuggestionsEnabled` + downstream `useOobeSuggestionsEnabled()`
  (no extra session fetch), off by default.
- i18n: 15 chat.oobe.* keys across all 11 locales (parity).
- Tests: per-state card tests + surface gating tests. Frontend gate green
  (pnpm lint clean; pnpm test 1252 passing).

Later slices wire Approve→submit_turn, Connect→extension setup, +Automation→
trigger_create, and the real suggestion feed; the flag stays off in prod until then.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): reconcile OOBE package — implementation restarted behind a flag

Slice 1 landed real (gated) code on this branch, so the "code-free" framing is
retired: README status + banner, PROPOSAL banner, CHECKLIST F0, and PLAN now say
implementation is underway behind the off-by-default `oobe_suggestions` flag
(slice 1 gate-green). Add IMPLEMENTATION.md to the README doc index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 2 — Approve a suggested card runs a foreground turn

Wire the card's Approve to the existing send path (PROPOSAL §2A change 2):
approving submits the task's `approvePrompt` through chat.tsx `handleSend`
(display content = the card title), so it runs as a real foreground agent turn
and the thread streams the activity by reuse — no new event/backend code. The
approved card flips to `running` optimistically; its live completed/failed
status arrives via the thread in a later slice (persistent drawer).

- SuggestedTask: add required `approvePrompt`.
- SuggestedTaskSurface: `onApproveTask` prop + `runningId` state (hook before the
  flag early-return); each card wires approve → setRunningId + onApproveTask.
- empty-state/chat.tsx: thread `onApproveTask` down; chat.tsx adds only
  `handleApproveTask` over the existing `handleSend` (gates/nav untouched).
- Tests: approve reports the task + flips it to running; empty-state forwards the
  prop. Still gated off by default. Gate green (pnpm lint clean; 1254 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — mark slices 1–2 landed; split 2b (live card status)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 4 — "+ Automation" schedules via prompt injection

PROPOSAL §2A change 4. On a completed suggested card, "+ Automation" submits the
task's `automationPrompt` through the existing `handleSend` (display content
"Set up automation — <title>"), so the agent creates a scheduled automation via
`builtin.trigger_create` (prompt injection — no REST create). The card flips to
an "Automation scheduled" chip optimistically. Mirrors the slice-2 approve wiring.

- SuggestedTask: add required `automationPrompt`.
- card: `scheduled?` prop → completed shows a scheduled chip instead of the button.
- surface: `onAutomationTask` prop + `scheduledId` state; +Automation → set + submit.
- empty-state/chat.tsx: thread `onAutomationTask` down; chat.tsx adds only
  `handleAutomationTask` over the existing `handleSend`.
- i18n: `chat.oobe.status.scheduled` across all 11 locales.
- Tests for the scheduled chip + the +Automation wiring. Gated off by default.
  Gate green (pnpm lint clean; 1257 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — slice 4 landed; slice 3 (Connect) deferred w/ reason

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): add oobe_suggestions server feature flag so deployments can enable the OOBE cards

Mirror of the reborn_projects flag: GET /session now emits
features.oobe_suggestions, read from the IRONCLAW_OOBE_SUGGESTIONS env var
(default off). The frontend already reads session.features.oobe_suggestions
(slices 1/2/4), so setting IRONCLAW_OOBE_SUGGESTIONS=1 on a deployment (e.g. the
Railway PR preview) turns the first-run suggestion cards on; unset everywhere
else they stay hidden.

- webui_serve.rs: oobe_suggestions_enabled() env read + builder wiring.
- webui_v2/router.rs: WebUiV2State field + with_/getter.
- webui_v2/handlers.rs: WebUiV2Features.oobe_suggestions + get_session literal.
- test: get_session_reports_oobe_suggestions_feature_from_state_flag (drives the
  real router, asserts features.oobe_suggestions mirrors the state flag).

Note: no Rust toolchain in this environment — cargo check/clippy/test not run
locally; CI + the Railway build compile it. Change is a mechanical mirror of an
existing, passing flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(webui): OOBE — close the /chat bundle-budget CI failure

The "Initial /chat JavaScript (gzip)" budget (check-bundle-budgets.ts,
219.0 KB) failed at 220.6 KB after slices 1/2/4 landed, because
suggested-task-surface.tsx (+ its card + demo data) was imported eagerly from
empty-state.tsx. Not caught by pnpm lint/test — it's a separate CI job
(WebUI v2 JS lint) this PR's frontend gate never ran locally.

Three-part fix, in order of diminishing-but-real savings:

1. Lazy-load the surface: `React.lazy(() => import("./suggested-task-surface"))`
   + `<Suspense fallback={null}>` in empty-state.tsx, mirroring the existing
   CommandResult/AttachmentPreviewModal pattern in message-bubble.tsx.
   (220.6 -> 220.0 KB — smaller gain than expected, see #2.)
2. Hoist the `useOobeSuggestionsEnabled()` flag check OUT of the lazy module
   into empty-state.tsx (already-eager): the hook's own import (app/auth.ts ->
   api.ts/auth-scope.ts) was already eager-reachable elsewhere, so calling it
   from inside the lazy chunk too forced the bundler to extract those modules
   into their own less-efficient standalone chunks. suggested-task-surface.tsx
   is now purely presentational; empty-state.tsx decides whether to even mount
   the lazy import. (220.0 -> 219.3 KB.)
3. Same fix for NearProcessIndicator: suggested-task-card.tsx no longer imports
   it directly (also already-eager via typing-indicator.tsx); empty-state.tsx
   passes a `renderRunningIndicator` render-prop down through the surface to
   the card instead. (219.3 -> 219.2 KB.)

The remaining 0.2 KB is irreducible: gating the lazy-import decision and the
flag-read hook must live in the eager /chat closure. check-bundle-budgets.ts's
own history shows this is the established path for a legitimate net-new
eager cost — CHAT_GZIP_BUDGET raised 219.0 -> 220.0 KB with the same
documented-rationale-comment convention as every prior increase in that file.

Verified: pnpm build clean; check-bundle-budgets.ts passes (login 134.3 KB/
45.7 KB headroom; /chat 219.2 KB/0.8 KB headroom; largest chunk 435.4 KB raw/
64.6 KB headroom); pnpm lint clean; pnpm test 141 files / 1260 tests, all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 5 (partial) — lock other cards while one job runs

PROPOSAL §2A change 3: only one suggested job may run at a time. The card
already supported a `locked` prop (slice 1, disables connect/approve/automation
+ dims the card); the surface just wasn't computing it. Now every card other
than the one actively running gets `locked={runningId !== null && runningId
!== task.id}` — the acting card itself stays interactive so its own
running/completed state remains visible.

Test generically discovers whichever card the vm-harness surfaces (its
componentProps helper collapses a mapped list to the last instance's props, so
the test asserts relative to a discovered task id rather than a hardcoded demo
id) and checks all three states: idle (unlocked), a different card running
(locked), the card itself running (unlocked).

The other half of slice 5 — a live `failed`-frame error/incomplete status —
depends on slice 2b's useChatEvents wiring (not yet landed) and stays open.

Gate green: pnpm lint clean; pnpm test 141 files / 1261 tests; pnpm build +
check-bundle-budgets.ts still pass (219.2 KB / 0.8 KB headroom, unchanged —
logic-only change, no new eager weight).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — slice 5 half-landed (lock done; failed-status blocked on 2b)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — retire slice 2b, mark 5 done

2b assumed the surface needed to persist into the thread view for live status.
Traced chat.tsx: EmptyState/MessageList are mutually exclusive (showLanding
ternary) — EmptyState fully unmounts on navigation into a thread, so a
persistent drawer would duplicate the thread's own event/message rendering and
import Vision's docked-drawer architecture into Foundational. The correct
model (already delivered by slices 1/2/5): the card gives instant local
feedback pre-navigation; the thread owns live status once the user is in it.
Card-persistent status for a *returning* user needs a durable record, which is
slice 6's scope, not a new frontend slice.

Also: mark slice 5 fully landed for what's achievable (the lock); the
failed-status half is resolved by the 2b finding, not blocked.

Slice 3 (Connect): recorded the useExtensions() investigation — page-level
hook, no isolated connect primitive; recommend extracting one first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 3 — Connect reuses the real setup/OAuth modal

An unconnected suggested card's Connect now resolves its `app` to a real
catalog extension and opens the EXISTING extensions setup/OAuth modal
(configure-modal.tsx), rather than cloning the connect flow:

- New pure resolver `pages/chat/lib/connect-extension.ts` maps a card's `app`
  id -> a real `useExtensions()` catalog entry, returning the `configurePayload`
  shape ConfigureModal expects (packageRef + displayName). Tolerant matching
  (normalized, containment) bridges static demo ids and live package refs;
  prefers installed over registry; returns null (no modal) when nothing matches.
- `suggested-task-surface.tsx` calls `useExtensions()`, tracks the connecting
  task + connected ids, and React.lazy-loads ConfigureModal so its OAuth
  watcher/state-machine weight lands in a lazy chunk (eager /chat unchanged at
  219.3 KB, 0.7 KB headroom). Successful save flips unconnected -> suggested;
  an unresolvable app shows a plain notice instead of a dead button.
- New i18n key `chat.oobe.connectUnavailable` across all 11 locales.

Why reuse, not reimplement: useOauthSetup is a ~250-line page-level state
machine keyed on a real packageRef + secret descriptor, not an extractable
helper — cloning its popup/polling/error-mapping would duplicate it and risk
bugs. Driving the one real path keeps OOBE connect and the extensions page in
lockstep.

Tests: connect-extension.test.ts (6, pure) + 4 new surface vm-tests. Full gate
green: pnpm lint, 1271 tests, build + bundle budgets. The live OAuth popup
round-trip is the only uncovered part (needs a real third-party consent grant)
— to be walked in browser QA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — reconcile provenance note with shipped Foundational v1

The mockup's Foundational flow already matches the shipped SuggestedTaskCard
(found-gated Connect / Approve-only / "Working — activity in the thread" /
Completed + "+ Automation" / "Couldn't complete", single-active lock, no
Modify/Revert, no agent-mode selector). Only the footer provenance note was
stale — it described the retired prototype (AutomationCarousel,
AutomationTaskCard, TaskActionBar Approve/Modify/Cancel · Modify/Revert, agent-
mode pill) as "built".

Updated the note to the actual branch state: SuggestedTaskCard +
SuggestedTaskSurface behind the off-by-default oobe_suggestions flag; the
server flag (IRONCLAW_OOBE_SUGGESTIONS -> /session features.oobe_suggestions);
Approve/+Automation via the existing chat send path; Connect resolving to a
real catalog extension and opening the existing ConfigureModal (no cloned OAuth);
NearProcessIndicator reuse. Carousel/TaskActionBar/agent-mode/Calendar/Plan are
now correctly labeled Vision-only (not built). Backend suggestion producer
(#6993, slice 6) called out as the remaining net-new piece + prod gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(webui): retarget OOBE to Vision on the durable suggestions contract (#7694)

Foundational is cut. PR #7694 shipped the durable backend suggestions contract
— the agent-driven producer this package had classified as Vision-tier — so
#6994 becomes its frontend consumer and the static demo model is replaced by
real data.

Docs:
- New VISION-RECONCILIATION.md (governs): what #7694 grants (V2 reveal, V3
  anticipatory states, live card status), the connect-model conflict and its
  resolution, superseded sections, and the keep/change/delete refactor map.
- Marked superseded: PROPOSAL §2/§3.1 P3/§3.2 N3-N5/§4 V1/§2A.3,
  AUTOMATION-TASKS-CONTRACT §§1-3 (events/projection -> typed ScopedFilesystem
  store), IMPLEMENTATION (historical), README (scope retargeted).

Frontend:
- suggestions-api.ts: typed client over the four routes (list/generate/start/
  dismiss) mirroring RebornSuggestion; pollDelayMs clamps the backend retry
  hint so a missing/hostile value can't hot-loop or stall.
- useSuggestions.ts: react-query owner. Polls only while status=generating, at
  the backend's cadence. Generation is never automatic — it costs a model run,
  so `empty` renders a CTA.
- Surface consumes real state: empty -> CTA, generating -> anticipatory
  indicator (V3), ready -> cards, failed -> retry. An existing set survives
  regeneration rather than blanking.
- Approve now calls POST /suggestions/{id}/start; the backend creates the
  thread/run and returns the binding, and the browser navigates to it. No more
  prompt injection through the composer.
- Cards are tool-agnostic: the backend schema carries no app identity and its
  generator is instructed not to assume capability availability, so the
  connect card-state, resolveConnectExtension, and ConfigureModal wiring are
  removed. Connect re-homes to its own catalog-driven surface (V1).
- A started card keeps its durable thread binding and offers "View in thread".
- i18n reduced to the 9 keys actually used, parity across all 11 locales.

Deferred with the contract: "+ Automation" (no backend field/route) and live
run-derived card status (its own slice, now buildable via the bound run_id).

Gate: pnpm lint clean, 1265 tests / 142 files pass, build + bundle budgets pass
(/chat 219.1 KB, down from 219.3).

Cannot QA against a preview yet: #7694 targets native-structured-output, not
main, so the routes are not deployed. Built against the frozen DTOs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): Vision-only — parallel cards, remove +Automation, excise Foundational

Per the retarget decisions:
- Cards run in parallel: the single-active lock is gone (each suggestion starts
  its own thread — no backend constraint to reflect). VISION-RECONCILIATION §4.1
  is now a decision, not an open question.
- "+ Automation" removed (no field/route in the shipped contract) — dropped from
  the card, not deferred. §4.2 decided.
- VISION-RECONCILIATION open questions trimmed to the three still open
  (AuthRequired verification, agent modes, replacement UX).

Docs swept for stale references: PROPOSAL/IMPLEMENTATION banners now enumerate
the reversed decisions and mark the bodies historical; README status +
"what this proposes" rewritten to Vision (connect is a separate surface;
approve → start-thread → navigate); oobe.md brief retargeted.

mockup.html: removed the Foundational/Vision toggle and all Foundational scope
— version pinned to Vision, isVision/found branches collapsed to the Vision
path, FOUND_BEATS/FOUND_CAP/HERO_FOUND/CAP_FOUND/MODES.foundational deleted,
.v-foundational CSS + is-locked + item-N comments removed, footer rewritten to
describe the #7694 contract (icon + source_ids, parallel cards). Script
re-verified with `node --check`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): brand icons for suggestion cards (icon + source_ids)

The #7694 author is adding `icon` (brand-icon enum) and `source_ids` (related
extension ids) to the card schema. Build the frontend mapping ahead of it:

- brand-icons.tsx: BrandIconId enum (mirrors the extension-package namespace),
  iconIdForSource() (source-id → icon), resolveIconId() (prefer explicit icon,
  else derive from source_ids[0], else `generic`), and <BrandIcon>. Colored
  marks reuse the license-clean inline SVGs already committed in the OOBE
  mockup; sheets/slides/web/memory/generic are neutral in-house glyphs. Lives in
  the lazy surface chunk — /chat stays 219.1 KB.
- Suggestion type gains optional icon + source_ids; the card renders the
  resolved brand mark. All optional, everything degrades to `generic`, so the
  card is correct before the backend field lands.
- SUGGESTION-ICONS.md: the enum, JSON-schema block, suggested Rust
  SuggestionIconId, and the icon↔source_ids derivation note for the #7694
  author. Records that icon/source_ids reverse the connect-conflict premise;
  connect stays decoupled but per-card connect is reopened as a review question.

No web scraping: assets are in-repo or in-house; brand marks are nominative-use.

Tests: brand-icons.test.ts (13) + a card BrandIcon test. Full gate green:
lint, 1273 tests, build + bundle budgets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(webui): reconcile OOBE frontend to the shipped #7694 contract

#7694 (durable backend suggestions) + #7693 (native structured output) landed
on main and are now merged into this branch — the /api/webchat/v2/suggestions
routes and the RebornSuggestion contract are present here. Reconcile the
frontend to the shipped shape:

- Field is `sources` (1-5 human-readable tool names, for display), not
  `source_ids`. Rename on the Suggestion type.
- `icon` is REQUIRED and enum-constrained, and its values are byte-identical to
  the enum this branch proposed (gmail..generic). It is the authoritative icon
  source. `resolveIconId` now trusts `icon` directly (→ generic fallback) and no
  longer derives from sources (those are free-form display names, not ids) —
  which also removes any icon↔sources drift. Dropped the obsolete
  iconIdForSource/SOURCE_TO_ICON extension-id mapping.
- Docs updated to shipped reality: SUGGESTION-ICONS.md (proposal → shipped
  reference), VISION-RECONCILIATION §3/§5.2/§6.4 (sequencing resolved; source_ids
  → sources; icon authoritative).

Everything else already matched the shipped contract exactly: routes, the
status enum (empty/generating/ready/failed), and the generate/start/dismiss
DTOs.

Gate green on the merged tree: lint, 1362 tests / 162 files, build + bundle
budgets (/chat 221.5 KB under the 222 budget — OOBE stays lazy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE suggestion surface — Vision polish (drawer, skeleton, reveal, provenance)

Closes the visual gap against the Vision mockup for the affordances that need
no new backend; tracks the rest as follow-ups (VISION-RECONCILIATION §5.1).

- V4 docked drawer frame: the surface now renders as a bordered drawer with a
  "Suggested for you · approve to run, or tweak first" header, docked close to
  the composer (empty/failed CTA states stay frameless). Composer gap tightened
  only when the flag is on, so the non-OOBE landing is byte-unchanged.
- V3 anticipatory beat: the generating state shows the branded NEAR indicator
  over static `.v2-skeleton` tiles instead of a lone line of text.
- V2 reveal: cards get a restrained `.oobe-card-reveal` entrance — reuses the
  sanctioned `v2-page-in` keyframe with a class-selector + !important exception
  and prefers-reduced-motion suppression, per the app.css motion policy (NOT the
  mockup's ad-hoc conic ai-spark sweep, which would bypass the policy).
- Card provenance: renders the suggestion's `sources` as a "From <tools>" line
  (formatSources joins the human-readable names). Modify stays dropped.
- i18n: chat.oobe.subtitle + chat.oobe.from across all 11 locales.

Deferred/tracked follow-ups (not built): live card status (slice 7), V1 connect
panel (slice 8), agent-mode selector, pills-collapse-on-typing, and the named
greeting + client username call-out in the header (V5, per review).

Gate green: pnpm lint, 1366 tests / 162 files, build + bundle budgets (/chat
221.5 KB — all new UI is in the lazy surface chunk, eager route unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE drawer — close/restore, horizontal strip, subtitle

Interaction polish to match the Vision mockup:

- Subtitle "Approve to run, or tweak first" -> "Approve to run" (11 locales).
- Horizontal scrollable card strip (fixed-width cards, overflow-x with hidden
  scrollbar via .oobe-strip) instead of the reflowing grid — matches the mockup.
- Section close: a × in the drawer header dismisses the whole drawer (distinct
  from per-card dismiss). Drawer-visibility state (open/dismissed/gone) lifted
  to empty-state, wired to the surface via `hidden`/`onClose`.
- Restore pill: a "Show suggestions" pill inside the composer appears once the
  drawer is dismissed; the label reopens it, its × dismisses fully. Lazy-loaded
  (oobe-restore-pill.tsx) so its markup stays out of eager /chat.

Merged latest main (0 behind) first.

Bundle: the close/restore gate + two new eager en.ts keys add ~0.5 KB to the
eager /chat closure (the pill markup and the surface stay lazy); budget bumped
222.0 -> 223.0 KB with documented rationale. /chat measured 222.5 KB.

Tests: +oobe-restore-pill.test.ts, +surface hidden/close tests, +empty-state
drawer/pill tests. Gate green: lint, 1394 tests / 164 files, build + budgets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: keep suggestion icons provider-neutral

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Henry Park <henrypark133@gmail.com>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
* feat(webui): agentic activity & streaming UX — NEAR process indicator + design reference

Foundation for the agent-activity / streaming UX redesign.

- docs/design/agent-activity-streaming{.md,/mockup.html}: approved design
  reference — the interactive mockup plus an implementation brief (component
  map, the new SSE payloads to build, and rollout order).
- NearProcessIndicator: the branded live "working" indicator — the canonical
  NEAR "N" mark (from provider-logos.tsx) with a brand-blue (#0091fd) light
  chasing its spine while working, resolving to a solid glyph when done. Wired
  into TypingIndicator.
- app.css: --near-blue token + motion-policy-compliant near-chase / near-pulse
  keyframes (class-scoped !important so they outrank the universal animation:none,
  suppressed under prefers-reduced-motion).

Presentation-only; no backend/SSE changes. Verified: npm run typecheck, lint,
and test (899 tests incl. 2 new) all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(webui): address CodeRabbit review and CI (nearai#6901)

* fix(ci): apply Cargo network retries in Docker builds (nearai#6901)

* fix(webui): keep run indicator visible

* feat(webui): retain completed run timing

* fix(ci): address CodeRabbit review — verify chef ordering (nearai#6901)

* fix(webui): address CodeRabbit activity review (nearai#6901)

* fix(webui): format long run durations

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: firat.sertgoz <firat.sertgoz@near.ai>
Co-authored-by: serrrfirat <f@nuff.tech>
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…, agent-mode pill (nearai#6994)

* feat(webui): OOBE automation-tasks prototype — carousel, inline cards, agent-mode pill

First-time-user OOBE concepts for the WebChat v2 landing view, built as a
UI-only prototype on mock data (backend intentionally not wired yet). Recovered
and rebased from the Jul design session (was the stale design/oobe-chat-automations
WIP); the streaming NearProcessIndicator busy-states are re-applied on top of #6901.

Adds to the chat view:
- Completed-automations carousel above the composer (automation-carousel,
  automation-task-card) — validates auto-run tasks, deep-links into the 3rd-party app.
- Inline calendar-reschedule rich-preview (calendar-reschedule-card) and a Plan-mode
  batch card (plan-card), sharing one decision model via task-action-bar
  (suggested → Approve/Modify/Cancel; automated → Modify/Revert).
- Agent-mode composer pill (mode-selector + lib/agent-mode) — Suggest/Plan/Auto/Bypass,
  persisted to scoped localStorage in the prototype.
- Typed mock domain + endpoint-shaped seam (lib/automation-tasks*, useAutomationTasks)
  so wiring the backend is a mock→fetch body swap with no component changes.
- DEV-only /design-preview harness (design-preview-page) to view the concepts, gated by import.meta.env.DEV.
- Busy states render the branded NearProcessIndicator (from #6901) — shared design language.

The backend (durable events, projection, transport frame, HTTP routes, facade+effect,
agent-mode persistence) is NOT implemented; AUTOMATION-TASKS-CONTRACT.md is the
reviewable wiring spec, tracked as a follow-up.

NOTE (why this is a draft): the landing carousel reads listAutomationTasks(), which
returns MOCK data for all users and is not DEV-gated. Must be backend-wired or gated
before this can leave draft / merge.

Frontend gate green: conventions + typecheck clean, 1032 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): add OOBE first-run onboarding mockup + brief

Standalone design exploration for the two first-run moments the PR #6994
prototype skips: the cold-start landing (zero automations, nothing connected)
and the first "Done for you" card appearing. House style matches
docs/design/agent-activity-streaming.

- docs/design/oobe.md — brief: goal, the two moments, the Invite vs Coach
  direction fork, what ships (#6994) vs needs backend (#6993), open questions.
- docs/design/oobe/mockup.html — interactive: plays cold-start → connect →
  anticipatory (NEAR indicator + skeleton tiles) → first-card reveal →
  populated, with a seg toggle for Invite (minimal) vs Coach (anticipatory
  ghost cards). Real --v2-* tokens; light+dark; reduced-motion honored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — add Thread + Plan scenes

Fold the two in-thread concepts into the standalone mockup so one shared
Artifact covers the whole OOBE arc. Adds a Scene selector (First run /
Thread / Plan):
- Thread — the inline CalendarRescheduleCard rich-preview (live: Approve →
  "Rescheduling…" → Automated; Modify time cycles the proposed slot; Skip →
  dismissed), plus an already-automated example with Modify/Revert.
- Plan — the batched PlanCard (Approve all → "Running your plan…" → all done;
  per-item skip), faithful to plan-card.tsx.
Same --v2-* tokens, NearProcessIndicator busy states, light+dark, flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — flag taxonomy + connect-pill redesign

Flags reframed around what's on main: Shipped (already on main), Redesign
(design update to existing main UI), New Feature (net-new, needs new
events/functionality), New UX (new design not on main); New Feature + New UX
combine. Applied: connect row = Shipped (reuses AuthRequired); composer =
Redesign (mode pill added); Coach ghost = New UX; carousel, both calendar
cards, and the plan card = New UX + New Feature.

Connect pills redesigned: per-tool checkbox state (no "connect" text), a
"Connect all" action, and — once connected — the pills condense into an
overlapping icon stack ("N connected", tap to re-expand).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — Connect all as a lightweight link

Restyle the "Connect all" action from a filled primary button to a
lightweight accent text link (underline on hover) so it doesn't compete with
the connect pills. Kept as a <button> for keyboard/focus + the click handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — avatar-style condensed connect stack

Restyle the condensed connected-tools stack after the stacked-avatars
reference: circular app icons with a white ring (theme surface) + soft drop
shadow, heavier overlap, and a trailing "+" circle to add another tool. The
count moves to the header subtitle ("3 connected — tap to manage"); the whole
stack re-expands on tap. Light + dark, reduced-motion honored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — refine condensed connect stack

Per feedback on the stacked-tools chip: opaque icon fills (drop the
transparent tint so overlaps don't bleed), rounded-square shape to match the
expanded pills (was circular), a ">" chevron instead of "+" on the trailing
chip, and "add more later" → "add more anytime" in the header subtitle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — relocate live indicator to carousel + collapse action

Contextual placement: the branded NEAR process indicator now leads the carousel
header (animated while working with a live elapsed, settling to a solid mark +
"worked for Ns" when done) instead of floating above the composer — it sits
where the agent's output is forming. Header is now a two-line block (mark +
title/elapsed over subtitle) so it stays clear of the annotation flags.

Connect pills: add a collapse control (left-chevron chip) to the right of the
expanded pills, mirroring the stack's expand affordance, to re-condense.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — agent mode drives task-card state

Rename the "Automated" badge to "Completed", and make the agent-mode pill a
live control: Suggest / Plan render the carousel cards as suggested (Approve /
Modify / Cancel, "Suggested" badge, "Suggested for you" header + hero); Auto /
Bypass render them completed ("Completed" badge, Modify / Revert, "Done for
you"). Per-card Approve flips a single card to completed, Cancel → dismissed,
Revert → reverted — so the suggested→completed flow is real, not just a label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — single-line carousel header

Put the secondary description back on the same line as the indicator's activity
string (title + elapsed). To keep it single-line and clear of the annotation
flag, drop the redundant working-state subtitle (title + live elapsed is enough)
and tighten the done-state subtitle to "Review or undo anytime".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — remove the cold-start Invite/Coach switcher

Drop the Invite/Coach direction toggle and its JS; first run now uses the
minimal ("Invite") cold start. Cleaned up the lede + footnote copy that
referenced the toggle and the Coach ghost strip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — dismissible connect panel + composer pill; archive stack

Connect-tools panel:
- add a close (X) to dismiss the panel; when dismissed it collapses to a small
  "Connect your tools" pill in the composer action row (left of the agent-mode
  picker) with its own X. Pill body re-opens the panel; pill X removes it.
- remove the collapse/expand overlapping-stack control entirely.

Archive: docs/design/oobe/archive/connect-tools-stack.html — a self-contained,
theme-aware record of the retired collapse/expand states (expanded pills +
collapsed avatar stack) for the design archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — move connect pill right of the mode selector

Place the dismissed-state "Connect your tools" pill after the agent-mode picker
in the composer action row (was to its left).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — connect panel button + reflow

- Move the connect action out of the header into a bottom-right button (was a
  link); its label is "Connect all" with nothing selected, "Connect" once any
  tool is picked, hidden when all are connected.
- Pin the dismiss (X) far-right in the header (margin-left:auto) so it no longer
  relocates when the button hides.
- Add more tools (Notion, Drive, GitHub) so the pills reflow to a second row
  past four.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — connect button rides the pill row (drop empty footer)

The connect action now flows at the end of the pills (right-aligned via
margin-left:auto) instead of a dedicated full-width footer row, removing the
wasted empty space to the button's left.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — Gemini ai-spark border on the first-card reveal

Replace the static accent glow ring on the first "aha" card with a Gemini-style
ai-spark: a blue→purple→pink conic gradient masked to the card border that
chases around once (1.35s) and then dissipates, with a soft purple/coral glow.
Uses @property --ai-angle for the sweep; hidden under prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — make the ai-spark actually chase the border

The conic-gradient + @property --ai-angle version interpolated the angle but
Chromium didn't repaint the gradient, so the spark never moved. Rebuild it as
an SVG rect stroke with an animated stroke-dashoffset (a Gemini blue→purple→pink
gradient dash that travels the border once, then dissipates) — stroke-dashoffset
repaints reliably every frame. Hidden under prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE ai-spark — faster, tapered comet tail, theme-aware colors

- Speed: 1.5s → 0.7s lap.
- Tail: uniform round-cap dash → a solid head fading into progressively
  sparser dashes; the drop-shadow glow blurs it into a smooth tapered comet
  (restores the taper the conic version had).
- Colors: per-theme tokens (--ais-1/2/3 + --ais-glow) — deeper/saturated blue
  →purple→magenta on light so it reads on white, brighter on dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE first card is conjured — spell-cast reveal

Make the first automation card feel summoned rather than placed:

- Faster spark: 0.7s -> 0.5s lap.
- Conjure: the card no longer pops in fully-formed — it materializes
  (opacity 0->1, scale .84->1 with a slight overshoot, blur 7px->0) in sync
  with the spark tracing its border.
- Spell-land: a brief glow pulse (--ais-glow) blooms around the card as the
  spark completes its loop.
- prefers-reduced-motion disables conjure + spell-land alongside the spark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — soften the conjure/spark effect

Dial the spell-cast reveal back to a subtle shimmer:
- Thinner spark stroke (2.6 -> 2.1) with a softer drop-shadow (3/8px -> 2/5px).
- Lower glow alpha (dark .85 -> .62, light .5 -> .4).
- Gentler spell-land pulse (24px/.85 -> 14px/.4).
- Calmer conjure: less blur (7 -> 4px), smaller scale-up (.84 -> .92) and
  near-zero overshoot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE spark — smooth continuous tapered comet

Replace the segmented SVG dash with one intact line:
- Technique: a conic-gradient comet masked to the border ring and rotated
  (transform repaints reliably, unlike an animated conic angle) — gives a
  single continuous line with a smooth head-to-tail taper.
- Transparency: color-mix bakes translucency into the color line
  (head ~86%, fading to fully transparent at the tail).
- Faster: 0.5s -> 0.4s lap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — clean up the task card layout & styling

Restyle the automation cards after the "Your availability" reference pattern:
- Hierarchy: the task title now leads the header (icon + bold title, status
  badge top-right); the app name drops to a muted "From Gmail · 2m ago"
  provenance line above the actions.
- Buttons: filled primary + text secondaries (Approve / Modify / Dismiss)
  instead of three bordered buttons; Cancel -> Dismiss.
- Surface: larger radius (13 -> 16px), more padding, a soft floating shadow,
  a middot-separated metric line, and bottom-aligned action rows so equal-
  height cards line up. Spark/conjure ring radii follow the new corner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE cards — real brand logos, drop the tag, condense

- Real product logos (Gmail, Google Calendar, Docs, Drive, Slack full-colour;
  Notion + GitHub monochrome via currentColor so they follow the theme) replace
  the placeholder line icons — on the task cards, connect pills, Thread card,
  and Plan list. Icon chips become tile-less logo holders (no tint/border).
- Remove the status tag/badge from the task-card header (state still reads from
  the action row).
- Condense card height (padding 14->12, tighter header/prov gaps; single-line
  titles now that the badge is gone) and scale the button row down
  (height 32->28, smaller padding/font).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — consistent card buttons, real Notion mark, task drawer

1. Link buttons carry icons in both modes: suggested-mode Modify/Dismiss now
   get the edit / close icons, matching the completed-mode Modify/Revert.
2. Notion logo swapped to the real Notion mark (notebook + N, monochrome via
   currentColor so it follows the theme) instead of the plain geometric N.
3. Task drawer: typing in the composer collapses the full task cards into a
   condensed, scrollable pill row (brand logo + title) above the composer;
   clearing the field — or tapping a pill — re-expands. Same control can seed
   suggested tasks for a returning user / new thread.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Vision/Foundational versions + attached task drawer

Add a Version switch (toolbar) with two design tracks:

Vision (north-star): the task cards now sit in a bordered "drawer" frame that
docks onto the composer and extends up from it, cards inset within the frame.
The drawer header carries collapse/expand (cards <-> pills) and a dismiss (X)
that hides it behind a "Show suggestions" restore bar. Typing still collapses
to pills. Keeps the connect flow, named greeting, and full mode set.

Foundational (near-term, v2-faithful): scoped for a multi-tenant enterprise
deploy — tools are admin-preconfigured so there's no connect step; no username
unless derivable (nameless greeting + blank account chip); agent modes scoped to
Suggest / Plan / Auto Approve, default Suggest. Uses main's composer and the
plain pills-collapse from the prior commit (no bordered drawer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Foundational cards at first step, Vision status line above drawer

1. Foundational first run is now a single populated state: because enterprise
   tools are admin-preconnected, the suggested task cards appear at the first
   step (no empty cold start, no beat scrubber).
2. The branded progress indicator + agent activity string move ABOVE the drawer
   (a relocated status line); the drawer header now carries the subtitle
   top-left ("Approve to run, or tweak first") beside the collapse/dismiss
   controls. Applies across both versions; the frame remains Vision-only.
3. Auto Approve description clarified: auto-approves task types already approved
   plus any task the user requests.
4. Rewrote docs/design/oobe.md to document the Vision/Foundational split,
   Foundational enterprise scoping, the reusable task drawer, and phasing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — corner-X dismiss, empty-state fallback, drawer title = agent string

- Per-item dismiss: the "× Dismiss" link is gone; each task card gets an X in
  its top-right corner and each collapsed pill gets an X on the right. Dismissed
  items are removed from the strip/pills.
- Empty state: when every suggestion is dismissed, a dashed fallback appears —
  Vision "Coming up with new suggestions" (pulsing), Foundational "Find new
  suggestions" (tap to repopulate).
- Removed the drawer-level dismiss X and the restore bar (dismissal is per-item
  now); the drawer header keeps only the collapse/expand toggle.
- Removed the branded NEAR progress indicator on both versions; the agent
  activity string ("Looking for things to suggest" / "Suggested for you") is now
  the drawer title (upper-left) with the subtitle beneath it. Hidden when collapsed.
- Toggle is pinned top-right and floats above the pills (bg fade + padding) so it
  no longer covers an overflowing pill.
- Foundational is steppable again (scrubber restored) with suggested cards from
  the first step; revert now returns a card to suggested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — keep drawer title + subtitle on one line

Revert the drawer header to a row layout so the agent string and its subtitle
("Suggested for you  Approve to run, or tweak first") stay inline on a single
line instead of the subtitle reflowing to a second line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — Foundational approve→automate→complete journey, Modify modal, attachment collapse

1. Foundational now steps through a real flow: beat 0 suggested → approve →
   beat 1 "Automating…" (spinner) → beat 2 completed, repeating for the next
   task, ending all-done. Adds a `running` card state; clicking Approve (either
   version) animates suggested → Automating… → completed (~1s). Drawer title
   tracks the state (Suggested for you / Automating… / Done for you).
2. Attachment collapse: adding an attachment (the composer + button, with a
   removable chip) now collapses the drawer to pills too — alongside typing.
3. Modify opens a modification modal (both versions): title = task name, an
   "adjust before it runs" field, Cancel / Save changes; backdrop over the app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — state-aware card copy, tighter composer gap, pill tap expands

1. Cards/pills now carry both a suggested (proposal) and completed (result)
   phrasing and switch on state: e.g. "Triage your inbox · 40 unread · 12 need
   replies · From Gmail" while suggested, "Triaged your inbox · 12 replied · 40
   archived · From Gmail · 2m ago" once done. Fixes suggested cards reading as
   already-completed (both versions).
2. Halved the gap between the cards/pills and the composer (Foundational).
3. Tapping a collapsed pill now expands it back to the full task cards (both
   versions); typing/attaching re-collapses (suppression flag so a tap-to-expand
   isn't immediately re-collapsed by lingering composer text).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — 3rd-party auth flows (queued OAuth modal)

Add a reusable modal "browser" OAuth dialog (chrome bar + provider domain,
sign-in account chooser, consent/scopes, Allow) wired into both tracks:

- Vision: the connect panel now *selects* tools; the Connect button opens the
  dialog queued across the selection (sign in once, approve scopes per tool)
  until all are authorized, then advances.
- Foundational: tools are admin-whitelisted but user-authorized — each task card
  starts unconnected with a "Connect <Tool>" CTA; connecting runs the dialog and
  the card becomes an actionable suggestion. Beat journey now walks
  unconnected → connected(suggested) → automating → completed.

Brief updated to match (Foundational connect model + Vision OAuth queue).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — refresh the 5 suggested/automated tasks

Replace the 3 sample cards with the intended task set (both versions):
1. Email triage — archive marketing to "IronClaw Archive", flag urgent (Gmail)
2. Calendar — accept free invites, propose times for conflicts
3. Build your profile — read activity across Gmail/Slack/Telegram (multi-tool)
4. Catch-you-up 24h digest — org summary, flag replies, propose priorities
   (Drive/Notion, multi-tool)
5. Suggest 5 automations — agent drafts its top-5 to approve (no external tool)

Cards now carry a short description line (suggested proposal vs completed
result) instead of the number pairs, custom glyphs for the agent/meta tasks,
and a per-card `conn` tool list so the connect CTA queues the right OAuth
dialogs ("Connect 3 tools" → Gmail→Slack→Telegram). Added a Telegram brand
logo + auth metadata; Foundational beat table + greeting updated for 5 cards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — match collapsed-pill bottom gap to the task-card gap

The pill row carried 6px bottom padding vs the card strip's 2px, so the pills
sat ~4px farther from the composer. Reduce the task-drawer bottom padding to
match the strip (4px base, 2px Foundational) — pill and card bottoms now sit the
same distance above the composer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Vision — connect banner confirms then dismisses after auth

After the user authorizes their selected tools through the OAuth queue, the
"Connect your tools" banner flips to a confirmation state — green check icon,
"Tools connected · N authorized", the connected tools shown green, close-X
hidden — then dismisses (~1.3s) as the flow advances to the working/anticipatory
beat. Beat 1 is now that confirmation moment (also reachable via the scrubber).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — section-level dismiss X for cards + pills

Add a drawer-level close (X) pinned top-right of the suggestions section,
visible in both the expanded task-card state and the collapsed pill row
(Foundational only — Vision keeps its collapse toggle there). Dismissing hides
the whole drawer and drops a "Show suggestions N" restore bar above the
composer; restoring brings it back. Per-item × on each card/pill is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — align the section dismiss X in both states

Center the drawer dismiss X on the "Suggested for you" header (expanded) and on
the pill row (collapsed) via a state-specific top, and move it flush to the
right edge of the card/pill container + composer (right 11px -> 2px). Verified
dy=0 in both states.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE — dismiss-X container masks against the real background

The dismiss X used --v2-surface (white) for its fill + left fade, but the pills
sit on --v2-canvas, so pills bled through the gradient. Switch the X container
fill and its left-fade shadow to --v2-canvas so it matches the background behind
the pills — overflowing pills now fade cleanly into the bg (masking effect),
gradient retained. Verified fill == scene bg in light and dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — collapsed dismiss becomes a full-height gutter mask

In the collapsed pill state the dismiss control is no longer a small rounded
square: it fills the drawer height, pins flush to the right edge, and carries a
transparent->canvas gradient so pills fade out and aren't visible past it. The X
sits centred in the gutter. Expanded (cards) keeps the header-aligned X.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational — move "Show suggestions" restore into the composer

Replace the restore bar above the composer with a pill inside the composer, to
the right of the agent-mode selector (reusing the connect-pill style). Tapping
the pill body restores the dismissed suggestions drawer; the pill's X fully
dismisses it (new 'gone' state — drawer and pill both hidden). Removed the dead
restore-bar markup + CSS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE integration proposal & plan — Foundational + Vision phasing

Add a #6918-style proposal package under docs/design/oobe/ (README / PROPOSAL /
PLAN / CHECKLIST) for phasing the OOBE prototype into production:

- Foundational (near-term, ships on current main) then Vision (north-star),
  matching the mockup's two versions; every Vision piece a superset of a
  Foundational one, so nothing is redone.
- Scopes Foundational as shipped-vs-net-new: the connect CTA, busy states,
  agent-mode semantics, and manage-result surface all REUSE code on main
  (extension-auth path, NearProcessIndicator, resolve_gate/global_auto_approve,
  pages/automations); the net-new surface is the card family, the
  AutomationTask events+projection+routes+facade, and the first-run suggestion
  producer.
- Inventories dependencies D-F1..F6 (Foundational) and D-V1..V5 (Vision), each
  with an implementation approach, and maps the work onto the five-layer WebUI
  flow and the #6918 target families.
- Applies the APDD governance kit (docs-first workflow, Feedback & Decisions
  anchor, Critical Bug Fix Log, design track, CUJ baseline).

Companion human-review artifact (schematics/diagrams):
https://claude.ai/code/artifact/734b1b6a-e35d-4736-9ac2-952dcdf84ab4

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): reconcile OOBE proposal + contract to post-#6918 family names

The #6918 family-folder reorg has landed on main; update the proposal package
and the wiring contract to current crate names/paths and fix a stale claim:

- crates now under crates/{contracts,events,domains,product,app}/; renames
  ironclaw_events -> ironclaw_event_log, add ironclaw_event_store (both under
  events/), ironclaw_reborn_composition -> ironclaw_composition, and the webui
  frontend paths move to crates/product/ironclaw_webui/frontend/.
- correct the facade identity: it is RebornServicesApi in
  crates/product/ironclaw_assistant (NOT "ProductSurface" — that is the typed
  capability contract/DTOs in ironclaw_product_contracts).
- reframe "#6918 target families" as the family folders now on main.
- note the triggers-hosted suggester option (D-F2) can reuse the existing
  composition automation wiring (trigger_poller + trusted_submit).
- retire the removed .claude/rules/tool-evidence.md reference -> gateway-events
  / lifecycle; product adapters -> the ProductAdapter surface in ironclaw_host_api.
- mark the F0 merge-to-main + contract-reconciliation boxes done.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Roll back the OOBE prototype code; reposition PR as design artifacts + plan

Per review (IronLoop/CodeRabbit flagged mock automations shown to real users and
an autonomy selector execution ignored), drop the prototype source and make this
branch code-free: crates/ is now identical to main.

- Revert the edits to shipped files (app.tsx, chat-input, empty-state, en.ts,
  button/icons + their tests) and delete the added prototype files (automation
  cards, action bar, mode selector, data seam, hooks, design-preview harness).
- Move AUTOMATION-TASKS-CONTRACT.md out of the code tree into docs/design/oobe/
  (kept as the design reference / proposed wiring).
- Plan of record is now the artifacts + the written plan: add
  docs/design/oobe/integration-review.html (the "IronClaw OOBE — Integration
  Review" page) in-branch, and repoint the former claude.ai artifact links to it
  (rendered via html-preview.github.io).
- Reconcile the package (README/PROPOSAL/PLAN/CHECKLIST/brief + contract): a
  code-free banner, fix links to rolled-back files, and reframe "the prototype
  ships here / mock->fetch swap" as "prototyped earlier + demonstrated in the
  mockup; the first implementation builds fresh." D-F5/D-F4 gating moves to the
  implementation PRs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — align the Foundational version to shipped v2

The mockup's design tokens already match crates/product/ironclaw_webui/src/styles/app.css
verbatim; this aligns the Foundational version's *treatment* to the shipped
WebChat v2 landing:

- hero switches from the serif exploration face to Geist sans, heavier and larger
  (matching empty-state.tsx's text-4xl/6xl font-semibold hero);
- suggestions become full-width divider rows with a round leading icon (matching
  the shipped grid-cols-[auto_1fr_auto] row treatment) instead of pill chips;
- composer picks up the shipped 20px radius + card-bg + round icon buttons.

All scoped to .v-foundational so the Vision (north-star) version keeps its
distinctive treatment. CSS validated (balanced); in-app browser CDP was wedged,
so verify visually via html-preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup composer — match main (remove mic, round send, larger, full-width suggestions)

Correct the composer to the shipped chat-input.tsx:
- remove the microphone/dictate button (main has none);
- send button becomes a round primary icon button (paper-plane), replacing the
  labeled "Send ⌘↵" pill, matching Button variant="primary" size="icon-sm" rounded-full;
- enlarge the Foundational composer (min-height 120, 15px field, roomier padding)
  to match main's min-h-[120px] hero composer;
- the suggestion rows below the composer now span the full composer width
  (width:100% on .v-foundational .suggs — they were shrink-to-fit + centered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — apply composer button treatment to Vision too

The mic-removal and round send-button were already global; the round attach
icon button was still Foundational-scoped, leaving Vision's composer with a
square attach. Make the icon-button treatment global (round, 36px) so the
Vision flow's composer reflects the same Send / attach / no-mic design as
Foundational and main. (Composer *sizing* stays Foundational-scoped — Vision
docks its composer onto the drawer.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE CHECKLIST — adopt epic #7044 success criteria

Additive only: add the epic's Phase-1 success criteria (time-to-first-automation,
first-session activation, suggestion quality) to the Foundational exit gate. No
other plan content changes — the proposal package stays the plan of record; the
epic↔proposal scope conflicts are reconciled in #7044, not here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Phase-1 (v1) UX update — 6 changes wired to shipped backend seams

Mockup (Foundational-scoped; Vision mock unchanged):
- remove the agent-mode selector (kept in Vision) [1]
- disable the other cards while one job runs [3]
- replace Revert with "+ Automation" (creates a scheduled automation) [4]
- v1 = connect + approve UI, no background jobs [5]
- drop Modify; show completed / error-incomplete status on the card [6]

PROPOSAL: new §2A "Phase 1 (v1) implementation update" specifying how each change
wires to EXISTING backend seams (verified on main) — no new AutomationTask
events/projection needed for v1:
- approve -> POST /threads/{id}/messages (submit_turn -> TurnCoordinator), run in thread [2]
- status/activity -> existing WebChatV2Event stream (running/capability_activity/final_reply/failed)
- one-active-run -> submit_turn DeferredBusy/RejectedBusy
- connect -> extension setup/OAuth + AuthRequired frame
- "+ Automation" -> prompt injection -> builtin.trigger_create -> automations dashboard
- gates -> resolve_gate

Also: fix doc link depth after main renamed docs/design -> docs/internal/design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE Foundational v1 — implementation plan grounded in current main

Add IMPLEMENTATION.md: a concrete build plan for the Phase-1 v1 UX (PROPOSAL §2A),
proving every card action wires to seams already enabled on main and enumerating
the frontend components, the feature-flag gating (the D-F5 merge-safety fix), the
vertical PR slices, and the tests.

Verified-enabled on main: submit_turn via lib/api.ts sendMessage; status via
useChatEvents (folds WebChatV2Event frames → running/final_reply/failed); connect
via extension-pairing-api + AuthRequired; resolve_gate; automations dashboard +
useAutomations; builtin.trigger_create; session feature flags (app/auth.ts
features?.). The one net-new backend piece is the first-run suggestion producer
(D-F2) — slices 1–5 ship frontend-only behind an off-by-default flag; the flag
flips on only when the producer lands. No new AutomationTask events/projection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE Foundational v1 slice 1 — feature-gated SuggestedTaskCard

First implementation slice of the OOBE Foundational v1 (docs/internal/design/oobe
PROPOSAL §2A / IMPLEMENTATION.md). Presentational + gated only — no backend
wiring, no mock data reachable by real users.

- SuggestedTaskCard: one action row per state per §2A — unconnected→Connect,
  suggested→Approve (no Modify), running→NearProcessIndicator, completed→Completed
  chip + "+ Automation" (no Revert/Modify), failed→"Couldn't complete" + Try again;
  `locked` disables the card (item 3). Pure/presentational (callbacks are props).
- SuggestedTaskSurface: reads the `oobe_suggestions` deployment flag via a shared
  ["session"] query and renders null when off (landing unchanged for real users);
  renders a static demo list only when on. Mounted in empty-state above composer.
- auth.ts: `oobeSuggestionsEnabled` + downstream `useOobeSuggestionsEnabled()`
  (no extra session fetch), off by default.
- i18n: 15 chat.oobe.* keys across all 11 locales (parity).
- Tests: per-state card tests + surface gating tests. Frontend gate green
  (pnpm lint clean; pnpm test 1252 passing).

Later slices wire Approve→submit_turn, Connect→extension setup, +Automation→
trigger_create, and the real suggestion feed; the flag stays off in prod until then.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): reconcile OOBE package — implementation restarted behind a flag

Slice 1 landed real (gated) code on this branch, so the "code-free" framing is
retired: README status + banner, PROPOSAL banner, CHECKLIST F0, and PLAN now say
implementation is underway behind the off-by-default `oobe_suggestions` flag
(slice 1 gate-green). Add IMPLEMENTATION.md to the README doc index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 2 — Approve a suggested card runs a foreground turn

Wire the card's Approve to the existing send path (PROPOSAL §2A change 2):
approving submits the task's `approvePrompt` through chat.tsx `handleSend`
(display content = the card title), so it runs as a real foreground agent turn
and the thread streams the activity by reuse — no new event/backend code. The
approved card flips to `running` optimistically; its live completed/failed
status arrives via the thread in a later slice (persistent drawer).

- SuggestedTask: add required `approvePrompt`.
- SuggestedTaskSurface: `onApproveTask` prop + `runningId` state (hook before the
  flag early-return); each card wires approve → setRunningId + onApproveTask.
- empty-state/chat.tsx: thread `onApproveTask` down; chat.tsx adds only
  `handleApproveTask` over the existing `handleSend` (gates/nav untouched).
- Tests: approve reports the task + flips it to running; empty-state forwards the
  prop. Still gated off by default. Gate green (pnpm lint clean; 1254 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — mark slices 1–2 landed; split 2b (live card status)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 4 — "+ Automation" schedules via prompt injection

PROPOSAL §2A change 4. On a completed suggested card, "+ Automation" submits the
task's `automationPrompt` through the existing `handleSend` (display content
"Set up automation — <title>"), so the agent creates a scheduled automation via
`builtin.trigger_create` (prompt injection — no REST create). The card flips to
an "Automation scheduled" chip optimistically. Mirrors the slice-2 approve wiring.

- SuggestedTask: add required `automationPrompt`.
- card: `scheduled?` prop → completed shows a scheduled chip instead of the button.
- surface: `onAutomationTask` prop + `scheduledId` state; +Automation → set + submit.
- empty-state/chat.tsx: thread `onAutomationTask` down; chat.tsx adds only
  `handleAutomationTask` over the existing `handleSend`.
- i18n: `chat.oobe.status.scheduled` across all 11 locales.
- Tests for the scheduled chip + the +Automation wiring. Gated off by default.
  Gate green (pnpm lint clean; 1257 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — slice 4 landed; slice 3 (Connect) deferred w/ reason

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): add oobe_suggestions server feature flag so deployments can enable the OOBE cards

Mirror of the reborn_projects flag: GET /session now emits
features.oobe_suggestions, read from the IRONCLAW_OOBE_SUGGESTIONS env var
(default off). The frontend already reads session.features.oobe_suggestions
(slices 1/2/4), so setting IRONCLAW_OOBE_SUGGESTIONS=1 on a deployment (e.g. the
Railway PR preview) turns the first-run suggestion cards on; unset everywhere
else they stay hidden.

- webui_serve.rs: oobe_suggestions_enabled() env read + builder wiring.
- webui_v2/router.rs: WebUiV2State field + with_/getter.
- webui_v2/handlers.rs: WebUiV2Features.oobe_suggestions + get_session literal.
- test: get_session_reports_oobe_suggestions_feature_from_state_flag (drives the
  real router, asserts features.oobe_suggestions mirrors the state flag).

Note: no Rust toolchain in this environment — cargo check/clippy/test not run
locally; CI + the Railway build compile it. Change is a mechanical mirror of an
existing, passing flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(webui): OOBE — close the /chat bundle-budget CI failure

The "Initial /chat JavaScript (gzip)" budget (check-bundle-budgets.ts,
219.0 KB) failed at 220.6 KB after slices 1/2/4 landed, because
suggested-task-surface.tsx (+ its card + demo data) was imported eagerly from
empty-state.tsx. Not caught by pnpm lint/test — it's a separate CI job
(WebUI v2 JS lint) this PR's frontend gate never ran locally.

Three-part fix, in order of diminishing-but-real savings:

1. Lazy-load the surface: `React.lazy(() => import("./suggested-task-surface"))`
   + `<Suspense fallback={null}>` in empty-state.tsx, mirroring the existing
   CommandResult/AttachmentPreviewModal pattern in message-bubble.tsx.
   (220.6 -> 220.0 KB — smaller gain than expected, see #2.)
2. Hoist the `useOobeSuggestionsEnabled()` flag check OUT of the lazy module
   into empty-state.tsx (already-eager): the hook's own import (app/auth.ts ->
   api.ts/auth-scope.ts) was already eager-reachable elsewhere, so calling it
   from inside the lazy chunk too forced the bundler to extract those modules
   into their own less-efficient standalone chunks. suggested-task-surface.tsx
   is now purely presentational; empty-state.tsx decides whether to even mount
   the lazy import. (220.0 -> 219.3 KB.)
3. Same fix for NearProcessIndicator: suggested-task-card.tsx no longer imports
   it directly (also already-eager via typing-indicator.tsx); empty-state.tsx
   passes a `renderRunningIndicator` render-prop down through the surface to
   the card instead. (219.3 -> 219.2 KB.)

The remaining 0.2 KB is irreducible: gating the lazy-import decision and the
flag-read hook must live in the eager /chat closure. check-bundle-budgets.ts's
own history shows this is the established path for a legitimate net-new
eager cost — CHAT_GZIP_BUDGET raised 219.0 -> 220.0 KB with the same
documented-rationale-comment convention as every prior increase in that file.

Verified: pnpm build clean; check-bundle-budgets.ts passes (login 134.3 KB/
45.7 KB headroom; /chat 219.2 KB/0.8 KB headroom; largest chunk 435.4 KB raw/
64.6 KB headroom); pnpm lint clean; pnpm test 141 files / 1260 tests, all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 5 (partial) — lock other cards while one job runs

PROPOSAL §2A change 3: only one suggested job may run at a time. The card
already supported a `locked` prop (slice 1, disables connect/approve/automation
+ dims the card); the surface just wasn't computing it. Now every card other
than the one actively running gets `locked={runningId !== null && runningId
!== task.id}` — the acting card itself stays interactive so its own
running/completed state remains visible.

Test generically discovers whichever card the vm-harness surfaces (its
componentProps helper collapses a mapped list to the last instance's props, so
the test asserts relative to a discovered task id rather than a hardcoded demo
id) and checks all three states: idle (unlocked), a different card running
(locked), the card itself running (unlocked).

The other half of slice 5 — a live `failed`-frame error/incomplete status —
depends on slice 2b's useChatEvents wiring (not yet landed) and stays open.

Gate green: pnpm lint clean; pnpm test 141 files / 1261 tests; pnpm build +
check-bundle-budgets.ts still pass (219.2 KB / 0.8 KB headroom, unchanged —
logic-only change, no new eager weight).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — slice 5 half-landed (lock done; failed-status blocked on 2b)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE IMPLEMENTATION — retire slice 2b, mark 5 done

2b assumed the surface needed to persist into the thread view for live status.
Traced chat.tsx: EmptyState/MessageList are mutually exclusive (showLanding
ternary) — EmptyState fully unmounts on navigation into a thread, so a
persistent drawer would duplicate the thread's own event/message rendering and
import Vision's docked-drawer architecture into Foundational. The correct
model (already delivered by slices 1/2/5): the card gives instant local
feedback pre-navigation; the thread owns live status once the user is in it.
Card-persistent status for a *returning* user needs a durable record, which is
slice 6's scope, not a new frontend slice.

Also: mark slice 5 fully landed for what's achievable (the lock); the
failed-status half is resolved by the 2b finding, not blocked.

Slice 3 (Connect): recorded the useExtensions() investigation — page-level
hook, no isolated connect primitive; recommend extracting one first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE v1 slice 3 — Connect reuses the real setup/OAuth modal

An unconnected suggested card's Connect now resolves its `app` to a real
catalog extension and opens the EXISTING extensions setup/OAuth modal
(configure-modal.tsx), rather than cloning the connect flow:

- New pure resolver `pages/chat/lib/connect-extension.ts` maps a card's `app`
  id -> a real `useExtensions()` catalog entry, returning the `configurePayload`
  shape ConfigureModal expects (packageRef + displayName). Tolerant matching
  (normalized, containment) bridges static demo ids and live package refs;
  prefers installed over registry; returns null (no modal) when nothing matches.
- `suggested-task-surface.tsx` calls `useExtensions()`, tracks the connecting
  task + connected ids, and React.lazy-loads ConfigureModal so its OAuth
  watcher/state-machine weight lands in a lazy chunk (eager /chat unchanged at
  219.3 KB, 0.7 KB headroom). Successful save flips unconnected -> suggested;
  an unresolvable app shows a plain notice instead of a dead button.
- New i18n key `chat.oobe.connectUnavailable` across all 11 locales.

Why reuse, not reimplement: useOauthSetup is a ~250-line page-level state
machine keyed on a real packageRef + secret descriptor, not an extractable
helper — cloning its popup/polling/error-mapping would duplicate it and risk
bugs. Driving the one real path keeps OOBE connect and the extensions page in
lockstep.

Tests: connect-extension.test.ts (6, pure) + 4 new surface vm-tests. Full gate
green: pnpm lint, 1271 tests, build + bundle budgets. The live OAuth popup
round-trip is the only uncovered part (needs a real third-party consent grant)
— to be walked in browser QA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): OOBE mockup — reconcile provenance note with shipped Foundational v1

The mockup's Foundational flow already matches the shipped SuggestedTaskCard
(found-gated Connect / Approve-only / "Working — activity in the thread" /
Completed + "+ Automation" / "Couldn't complete", single-active lock, no
Modify/Revert, no agent-mode selector). Only the footer provenance note was
stale — it described the retired prototype (AutomationCarousel,
AutomationTaskCard, TaskActionBar Approve/Modify/Cancel · Modify/Revert, agent-
mode pill) as "built".

Updated the note to the actual branch state: SuggestedTaskCard +
SuggestedTaskSurface behind the off-by-default oobe_suggestions flag; the
server flag (IRONCLAW_OOBE_SUGGESTIONS -> /session features.oobe_suggestions);
Approve/+Automation via the existing chat send path; Connect resolving to a
real catalog extension and opening the existing ConfigureModal (no cloned OAuth);
NearProcessIndicator reuse. Carousel/TaskActionBar/agent-mode/Calendar/Plan are
now correctly labeled Vision-only (not built). Backend suggestion producer
(#6993, slice 6) called out as the remaining net-new piece + prod gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(webui): retarget OOBE to Vision on the durable suggestions contract (#7694)

Foundational is cut. PR #7694 shipped the durable backend suggestions contract
— the agent-driven producer this package had classified as Vision-tier — so
#6994 becomes its frontend consumer and the static demo model is replaced by
real data.

Docs:
- New VISION-RECONCILIATION.md (governs): what #7694 grants (V2 reveal, V3
  anticipatory states, live card status), the connect-model conflict and its
  resolution, superseded sections, and the keep/change/delete refactor map.
- Marked superseded: PROPOSAL §2/§3.1 P3/§3.2 N3-N5/§4 V1/§2A.3,
  AUTOMATION-TASKS-CONTRACT §§1-3 (events/projection -> typed ScopedFilesystem
  store), IMPLEMENTATION (historical), README (scope retargeted).

Frontend:
- suggestions-api.ts: typed client over the four routes (list/generate/start/
  dismiss) mirroring RebornSuggestion; pollDelayMs clamps the backend retry
  hint so a missing/hostile value can't hot-loop or stall.
- useSuggestions.ts: react-query owner. Polls only while status=generating, at
  the backend's cadence. Generation is never automatic — it costs a model run,
  so `empty` renders a CTA.
- Surface consumes real state: empty -> CTA, generating -> anticipatory
  indicator (V3), ready -> cards, failed -> retry. An existing set survives
  regeneration rather than blanking.
- Approve now calls POST /suggestions/{id}/start; the backend creates the
  thread/run and returns the binding, and the browser navigates to it. No more
  prompt injection through the composer.
- Cards are tool-agnostic: the backend schema carries no app identity and its
  generator is instructed not to assume capability availability, so the
  connect card-state, resolveConnectExtension, and ConfigureModal wiring are
  removed. Connect re-homes to its own catalog-driven surface (V1).
- A started card keeps its durable thread binding and offers "View in thread".
- i18n reduced to the 9 keys actually used, parity across all 11 locales.

Deferred with the contract: "+ Automation" (no backend field/route) and live
run-derived card status (its own slice, now buildable via the bound run_id).

Gate: pnpm lint clean, 1265 tests / 142 files pass, build + bundle budgets pass
(/chat 219.1 KB, down from 219.3).

Cannot QA against a preview yet: #7694 targets native-structured-output, not
main, so the routes are not deployed. Built against the frozen DTOs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(design): Vision-only — parallel cards, remove +Automation, excise Foundational

Per the retarget decisions:
- Cards run in parallel: the single-active lock is gone (each suggestion starts
  its own thread — no backend constraint to reflect). VISION-RECONCILIATION §4.1
  is now a decision, not an open question.
- "+ Automation" removed (no field/route in the shipped contract) — dropped from
  the card, not deferred. §4.2 decided.
- VISION-RECONCILIATION open questions trimmed to the three still open
  (AuthRequired verification, agent modes, replacement UX).

Docs swept for stale references: PROPOSAL/IMPLEMENTATION banners now enumerate
the reversed decisions and mark the bodies historical; README status +
"what this proposes" rewritten to Vision (connect is a separate surface;
approve → start-thread → navigate); oobe.md brief retargeted.

mockup.html: removed the Foundational/Vision toggle and all Foundational scope
— version pinned to Vision, isVision/found branches collapsed to the Vision
path, FOUND_BEATS/FOUND_CAP/HERO_FOUND/CAP_FOUND/MODES.foundational deleted,
.v-foundational CSS + is-locked + item-N comments removed, footer rewritten to
describe the #7694 contract (icon + source_ids, parallel cards). Script
re-verified with `node --check`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): brand icons for suggestion cards (icon + source_ids)

The #7694 author is adding `icon` (brand-icon enum) and `source_ids` (related
extension ids) to the card schema. Build the frontend mapping ahead of it:

- brand-icons.tsx: BrandIconId enum (mirrors the extension-package namespace),
  iconIdForSource() (source-id → icon), resolveIconId() (prefer explicit icon,
  else derive from source_ids[0], else `generic`), and <BrandIcon>. Colored
  marks reuse the license-clean inline SVGs already committed in the OOBE
  mockup; sheets/slides/web/memory/generic are neutral in-house glyphs. Lives in
  the lazy surface chunk — /chat stays 219.1 KB.
- Suggestion type gains optional icon + source_ids; the card renders the
  resolved brand mark. All optional, everything degrades to `generic`, so the
  card is correct before the backend field lands.
- SUGGESTION-ICONS.md: the enum, JSON-schema block, suggested Rust
  SuggestionIconId, and the icon↔source_ids derivation note for the #7694
  author. Records that icon/source_ids reverse the connect-conflict premise;
  connect stays decoupled but per-card connect is reopened as a review question.

No web scraping: assets are in-repo or in-house; brand marks are nominative-use.

Tests: brand-icons.test.ts (13) + a card BrandIcon test. Full gate green:
lint, 1273 tests, build + bundle budgets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(webui): reconcile OOBE frontend to the shipped #7694 contract

#7694 (durable backend suggestions) + #7693 (native structured output) landed
on main and are now merged into this branch — the /api/webchat/v2/suggestions
routes and the RebornSuggestion contract are present here. Reconcile the
frontend to the shipped shape:

- Field is `sources` (1-5 human-readable tool names, for display), not
  `source_ids`. Rename on the Suggestion type.
- `icon` is REQUIRED and enum-constrained, and its values are byte-identical to
  the enum this branch proposed (gmail..generic). It is the authoritative icon
  source. `resolveIconId` now trusts `icon` directly (→ generic fallback) and no
  longer derives from sources (those are free-form display names, not ids) —
  which also removes any icon↔sources drift. Dropped the obsolete
  iconIdForSource/SOURCE_TO_ICON extension-id mapping.
- Docs updated to shipped reality: SUGGESTION-ICONS.md (proposal → shipped
  reference), VISION-RECONCILIATION §3/§5.2/§6.4 (sequencing resolved; source_ids
  → sources; icon authoritative).

Everything else already matched the shipped contract exactly: routes, the
status enum (empty/generating/ready/failed), and the generate/start/dismiss
DTOs.

Gate green on the merged tree: lint, 1362 tests / 162 files, build + bundle
budgets (/chat 221.5 KB under the 222 budget — OOBE stays lazy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE suggestion surface — Vision polish (drawer, skeleton, reveal, provenance)

Closes the visual gap against the Vision mockup for the affordances that need
no new backend; tracks the rest as follow-ups (VISION-RECONCILIATION §5.1).

- V4 docked drawer frame: the surface now renders as a bordered drawer with a
  "Suggested for you · approve to run, or tweak first" header, docked close to
  the composer (empty/failed CTA states stay frameless). Composer gap tightened
  only when the flag is on, so the non-OOBE landing is byte-unchanged.
- V3 anticipatory beat: the generating state shows the branded NEAR indicator
  over static `.v2-skeleton` tiles instead of a lone line of text.
- V2 reveal: cards get a restrained `.oobe-card-reveal` entrance — reuses the
  sanctioned `v2-page-in` keyframe with a class-selector + !important exception
  and prefers-reduced-motion suppression, per the app.css motion policy (NOT the
  mockup's ad-hoc conic ai-spark sweep, which would bypass the policy).
- Card provenance: renders the suggestion's `sources` as a "From <tools>" line
  (formatSources joins the human-readable names). Modify stays dropped.
- i18n: chat.oobe.subtitle + chat.oobe.from across all 11 locales.

Deferred/tracked follow-ups (not built): live card status (slice 7), V1 connect
panel (slice 8), agent-mode selector, pills-collapse-on-typing, and the named
greeting + client username call-out in the header (V5, per review).

Gate green: pnpm lint, 1366 tests / 162 files, build + bundle budgets (/chat
221.5 KB — all new UI is in the lazy surface chunk, eager route unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(webui): OOBE drawer — close/restore, horizontal strip, subtitle

Interaction polish to match the Vision mockup:

- Subtitle "Approve to run, or tweak first" -> "Approve to run" (11 locales).
- Horizontal scrollable card strip (fixed-width cards, overflow-x with hidden
  scrollbar via .oobe-strip) instead of the reflowing grid — matches the mockup.
- Section close: a × in the drawer header dismisses the whole drawer (distinct
  from per-card dismiss). Drawer-visibility state (open/dismissed/gone) lifted
  to empty-state, wired to the surface via `hidden`/`onClose`.
- Restore pill: a "Show suggestions" pill inside the composer appears once the
  drawer is dismissed; the label reopens it, its × dismisses fully. Lazy-loaded
  (oobe-restore-pill.tsx) so its markup stays out of eager /chat.

Merged latest main (0 behind) first.

Bundle: the close/restore gate + two new eager en.ts keys add ~0.5 KB to the
eager /chat closure (the pill markup and the surface stay lazy); budget bumped
222.0 -> 223.0 KB with documented rationale. /chat measured 222.5 KB.

Tests: +oobe-restore-pill.test.ts, +surface hidden/close tests, +empty-state
drawer/pill tests. Gate green: lint, 1394 tests / 164 files, build + budgets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: keep suggestion icons provider-neutral

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Henry Park <henrypark133@gmail.com>

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-6901 — 9af996b7 Deployed Jul 31, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: new First-time contributor risk: low Changes to docs, tests, or low-risk modules scope: docs Documentation scope: sandbox Docker sandbox size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants