Skip to content

feat(site): refresh aegntic.ai company website#29

Open
aegntic wants to merge 8 commits into
mainfrom
feat/aegntic-website-refresh
Open

feat(site): refresh aegntic.ai company website#29
aegntic wants to merge 8 commits into
mainfrom
feat/aegntic-website-refresh

Conversation

@aegntic

@aegntic aegntic commented Mar 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Modern hero section with "AI Agents That Evolve" headline and animated badge
  • Services grid with 4 offerings: Custom Agent Development, AI Infrastructure, Consulting & Integration, Self-Modifying Systems
  • Product showcase for ClawReform with feature list and terminal-style code block
  • Case studies section: DevOps (80% reduction), Security (24/7), Data (Auto-adaptive)
  • Tech stack badges: Rust Core, Tailscale Mesh, MCP Protocol, A2A Multi-Agent, Vector Memory
  • Dark/light theme toggle with glassmorphism design
  • Mobile responsive with scroll reveal animations

Test plan

  • Open site/index.html in browser
  • Verify all sections render correctly
  • Test dark/light theme toggle
  • Test mobile responsiveness
  • Verify all links work (GitHub, email, social)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added import and scanning for discovering MCP servers, skills, and plugins locally.
    • Integrated OpenClaw orchestration service for multi-agent task management.
    • Added local skill discovery and import capabilities from user directories.
    • Enhanced model selection interface with filtering, presets, and availability indicators.
    • Improved Obsidian integration with automatic vault linking and seeding.
    • Launched aegntic.ai public landing page.
  • Documentation

    • Added comprehensive launch task guides and coordination playbooks for public alpha.
    • Added metallic design baseline for visual consistency across UI surfaces.

@aegntic and others added 8 commits March 13, 2026 04:26
…ation

- Added trace_id field to AgentEntry and Session structs
- Added trace_context.rs module for task-local TraceId storage
- Added openclaw.rs with canonical types for OpenClaw HTTP proxy routes
- Added docker-compose.openclaw.yml for service orchestration
- Added PostgreSQL schema with seed data

- All 279 tests pass
- Build compiles
- Clippy passes with zero warnings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oof, gitignore updates

- Fix clippy lints: use #[default] derive for GoalStatus, IssueStatus, AgentKind
- Remove redundant Default impls in company.rs and openclaw.rs
- Fix unnecessary clone in trace_context.rs
- Allow clippy::items_after_test_module in tui/mod.rs
- Reformat re-exports in types/lib.rs and route formatting in routes.rs
- Add bun/bunx fallback to dashboard-proof.sh (no longer requires npx)
- Relax dashboard-proof version check to title+agents-panel assertion
- Add .worktrees/, .swarm/, output/demo/ to .gitignore
- Extract is_loopback_request(), is_public_dashboard_path() helpers
- Add loopback-only bypass for Obsidian bootstrap routes (POST /api/agents,
  GET/PUT /api/agents/{id}/files/*) so the local dashboard can seed and
  sync memory files without forcing an auth prompt
- Add tests for bootstrap bypass (loopback allowed, remote rejected,
  unrelated routes rejected)
Environments without local TCP listener support (sandboxed CI, containers)
now skip gracefully instead of failing. Adds the guard macro to all tests
in api_integration_test, daemon_lifecycle_test, load_test, and wire/peer.
OpenClaw integration:
- Add openclaw.rs module proxying to local OpenClaw Docker services
  (registry, dispatcher, scheduler, evaluator, repair)
- Register 13 new /api/openclaw/* routes in server.rs
- Add reqwest + lazy_static dependencies for HTTP proxying

Auto-import system:
- Add auto_import.rs module for scanning local directories to discover
  MCP servers, skills, and plugins
- Register /api/import/scan, /api/import, /api/import/suggestions routes
- Add import.js page (scan UI, item selection, import flow)
- Add model-selector.js page (rich model browsing with tier/provider filters)
- Wire both JS pages into webchat.rs SPA bundle
…PI error fix button

Skills page:
- Rework installed skills to compact list view with inline remove
- Add Local tab for scanning and importing skills from local directories
- Improve empty state with structured onboarding options (ClawHub, Local, Build)

Settings/Models:
- Add model tier filter presets (Frontier, Smart, Fast, Local)
- Add available-only toggle for model catalog
- Add Import tab for settings page

Chat:
- Add 'Fix API Configuration' button on system error messages (401/403/API errors)

Styles:
- Add skill-list, skill-row, model-preset-btn component styles to components.css
- Extend app.js with local skill scanning and import helpers
…sion restore archives

- Add 16 launch task specs (docs/launch-tasks/0-15) defining the parallel
  public alpha launch plan with dependencies, proof rules, and worktree conventions
- Add metallic design baseline (docs/design/launch-metallic-baseline.md)
- Add cli-swarm orchestration scripts (launch.py, launch_live_windows.py,
  verify.sh, proof-demo.sh, status.py) for multi-agent kitty/tmux coordination
- Add session restore snapshot (2026-03-13-launch-alpha) preserving the
  launch alpha planning session state, prompts, and conversation context
- Add cli-swarm overview doc (docs/cli-swarm.md)
- Modern hero section with "AI Agents That Evolve" headline
- Services grid: Custom Agent Development, AI Infrastructure,
  Consulting & Integration, Self-Modifying Systems
- Product showcase for ClawReform with feature list and code block
- Case studies: DevOps (80% reduction), Security (24/7), Data (Auto)
- Tech stack badges: Rust Core, Tailscale Mesh, MCP Protocol, A2A
- Dark/light theme toggle with glassmorphism design
- Mobile responsive with scroll reveal animations

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @aegntic, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces significant infrastructure for OpenClaw multi-service orchestration, auto-import capabilities for MCP servers and skills, trace context management, comprehensive UI enhancements, and extensive launch automation tooling. It spans backend APIs, frontend pages, type definitions, testing utilities, Docker deployment, and launch task documentation.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
.gitignore, crates/clawreform-api/Cargo.toml
Added ignore patterns for .worktrees/, output/demo/, .swarm/ directories; added reqwest and lazy_static dependencies.
Auto-Import API Module
crates/clawreform-api/src/auto_import.rs
New 528-line module implementing HTTP endpoints for scanning local directories and importing MCP servers, skills, and plugins. Defines request/response types, scan logic with filesystem traversal, import dispatch, and suggestion retrieval for common directories.
OpenClaw Service Integration
crates/clawreform-api/src/openclaw.rs
New 350-line module proxying HTTP requests to OpenClaw Docker services (registry, dispatcher, scheduler, evaluator, repair). Includes status aggregation and typed response structures for multi-service health and operation results.
Route Registration & Module Exports
crates/clawreform-api/src/lib.rs, crates/clawreform-api/src/server.rs
Added public module declarations for auto_import and openclaw; registered new HTTP routes for both subsystems (scan, import, suggestions, status, agent ops, dispatch, scheduling, evaluation, repair).
Authentication & Middleware
crates/clawreform-api/src/middleware.rs
Refactored loopback detection into reusable helpers; added "local Obsidian bootstrap" exception allowing specific routes from loopback IPs only; includes unit tests for bootstrap authorization.
Existing Routes Updates
crates/clawreform-api/src/routes.rs
Minor fixes: added trace_id: None to fallback Session, updated UUID for COMPANY_AGENT_ID, simplified aggregation logic, reformatted builder calls.
Webchat HTML Script Loading
crates/clawreform-api/src/webchat.rs
Added two new embedded page scripts (model-selector.js and import.js) to the HTML assembly.
UI Component Styling
crates/clawreform-api/static/css/components.css
Added 264 lines of CSS: tier labeling, model cards grid system with selection/availability states, model preset controls, provider grouping, import card list, and skill list with badges.
Index Page HTML Expansion
crates/clawreform-api/static/index_body.html
Added "Fix API Configuration" button for system errors; refactored Skills page with Local tab for scanning/importing; added Import page with directory input and grouped results; updated model catalog UI with cards grid and filter controls; enhanced Obsidian setup flows.
Main App JavaScript
crates/clawreform-api/static/js/app.js
Added showApiKeyFix() flow, expanded Obsidian auto-setup with ensureObsidianReady(), improved seed eligibility detection, added ensureDefaultObsidianLink() and maybeAutoLaunchObsidian() helpers.
New Page Modules
crates/clawreform-api/static/js/pages/model-selector.js, crates/clawreform-api/static/js/pages/import.js
Two new 249 and 173-line JavaScript modules implementing rich client-side state for model selection (filtering, presets, derived datasets) and import workflows (scanning, selection, importing with progress).
Existing Page Updates
crates/clawreform-api/static/js/pages/settings.js, crates/clawreform-api/static/js/pages/skills.js
Added showAvailableOnly filtering and tier/provider filter helpers to settings; added local skills scanning, import-single, and import-all workflows to skills page.
Trace Context Infrastructure
crates/clawreform-runtime/src/trace_context.rs, crates/clawreform-runtime/src/lib.rs
New 94-line module implementing task-local trace ID storage via Tokio, with getters/setters and unit tests; exported new public module.
Type System Extensions
crates/clawreform-types/src/openclaw.rs, crates/clawreform-types/src/lib.rs, crates/clawreform-types/src/agent.rs, crates/clawreform-types/src/company.rs
Added 864-line OpenClaw schema module with canonical types for agents, tasks, dispatch, evaluation, repair, memory, and lifecycle. Added trace_id: Option<TraceId> to AgentEntry and Session structs. Simplified Default derivations for GoalStatus/IssueStatus enums. Exported OpenClaw types at crate root.
Kernel & Memory Updates
crates/clawreform-kernel/src/..., crates/clawreform-memory/src/..., crates/clawreform-runtime/src/...
Added trace_id: None initialization across agent/session construction paths in kernel, memory, and runtime modules (ballast updates for new struct field).
Test Infrastructure
crates/clawreform-api/tests/..._test.rs, crates/clawreform-wire/src/peer.rs, crates/clawreform-cli/src/tui/mod.rs
Added local_tcp_supported() probe and skip_if_no_local_tcp! macro to multiple test files; applied to integration/load/daemon/wire tests for conditional skip in restricted environments; added Clippy lint allow to TUI tests.
OpenClaw Infrastructure & Deployment
openclaw/IMPLEMENTATION_PACK.md, openclaw/docker-compose.openclaw.yml, openclaw/openclaw_registry.sql
Added 696-line implementation guide, 210-line Docker Compose stack (PostgreSQL, Redis, 5 FastAPI services), and 349-line PostgreSQL schema with enums, tables, indexes, views, and seed data.
CLI Swarm Orchestration
scripts/cli-swarm/launch.py, scripts/cli-swarm/launch_live_windows.py, scripts/cli-swarm/proof-demo.sh, scripts/cli-swarm/status.py, scripts/cli-swarm/verify.sh
Added 454, 695, 58, 42, and 66-line scripts for launching parallel agent sessions (coordinator + workers), managing visible windows via kitty/tmux, running proof/demo workflows, monitoring status, and verification runs with sandbox support.
Dashboard Proof Enhancement
scripts/dashboard-proof.sh
Updated URL parsing and JSON extraction to prefer bun, replaced sidebar version scraping with page-title check, added agents panel visibility verification.
Landing Page
site/index.html
Added 1056-line aegntic.ai landing page with theme toggle, smooth scrolling, hero/services/showcase/case-studies/tech-stack/CTA sections, and responsive layout.
Launch Task Documentation
docs/launch-tasks/ (15 files), docs/design/launch-metallic-baseline.md
Added 14 numbered task specs (pre-site-deployment through design-system-translation) with execution templates, scope, deliverables, and prompt chains; added metallic design baseline specification.
Session Restore Bundle
docs/session-restore/2026-03-13-launch-alpha/
Added comprehensive restore documentation including working state, constraints, conversation handoff, per-task prompts, launch manifest, kitty session config, and launcher snapshots for non-sandbox migration.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Browser
    participant WebUI as ClawREFORM Web UI
    participant API as clawreform-api
    participant AutoImport as Auto-Import Module
    participant FS as Local Filesystem
    participant OpenClaw as OpenClaw Services
    participant Registry as Registry Service
    
    User->>WebUI: Click "Find Local Skills"
    WebUI->>API: POST /api/import/suggestions
    API->>AutoImport: get_suggestions()
    AutoImport->>API: Return suggested directories
    API->>WebUI: Suggestion list
    
    User->>WebUI: Select directory & scan
    WebUI->>API: POST /api/import/scan {directory}
    API->>AutoImport: scan_directory()
    AutoImport->>FS: Traverse directories recursively
    AutoImport->>AutoImport: Classify items by manifest patterns
    FS->>AutoImport: Item metadata (descriptions, versions)
    AutoImport->>API: Discovered items grouped by type
    API->>WebUI: Skills/MCP servers/Plugins list
    
    User->>WebUI: Select items & import
    WebUI->>API: POST /api/import {selected items}
    API->>AutoImport: import_items()
    AutoImport->>AutoImport: Dispatch by item_type
    alt Skill Import
        AutoImport->>FS: Copy skill source to skills home
        AutoImport->>API: Reload skill registry
    else MCP Server/Plugin
        AutoImport->>API: Log & acknowledge (future expansion)
    end
    AutoImport->>API: Import results {imported, failed, status}
    API->>WebUI: Update discovered items & refresh lists
    WebUI->>User: Success toast & updated UI
Loading
sequenceDiagram
    participant CLI as Launch Script
    participant Worktree as Git Worktree
    participant Tmux as Tmux Session
    participant Window as Kitty Window
    participant Agent as Task Agent (CLI)
    participant API as clawreform-api
    
    CLI->>Worktree: Create linked worktree for task
    Worktree->>Worktree: Checkout task-specific branch
    
    CLI->>Worktree: Sync bootstrap docs & prompts
    Worktree->>Worktree: Copy launch-tasks, baseline design
    
    CLI->>Tmux: Create tmux session
    Tmux->>Agent: Start agent with environment
    Agent->>API: Execute task (scan, import, etc.)
    API->>Agent: Return results
    Agent->>Worktree: Write artifacts to output/
    
    CLI->>Window: Generate kitty session file
    Window->>Tmux: Attach window to session
    Window->>Agent: Display live agent output
    Agent->>Window: Print logs & progress
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

This PR introduces extensive heterogeneous changes across the entire codebase: new backend services (auto-import, OpenClaw proxying), comprehensive frontend pages with complex JavaScript state management, new type system foundations (trace context, OpenClaw schemas), Docker deployment infrastructure, sophisticated orchestration tooling with multi-language scripts, and extensive launch automation documentation. The changes span authentication middleware, API routing, UI components, CSS systems, JavaScript state management, Rust type definitions, database schema, containerization, and bash/Python scripting. While many individual cohorts are straightforward (ballast trace_id additions, test macro applications), the overall scope demands careful review of interconnections, particularly between the new API endpoints, frontend integration, database schema design, and the launch automation framework.

Possibly related PRs

Poem

🐰 In gardens of tasks, we've planted and grown,
Fifteen parallel seeds in worktrees well-sown,
With traces and schemas and imports so bright,
The swarm now orchestrates—kitty windows take flight!
From localhost to OpenClaw's distributed sight,
ClawREFORM launches into the glowing night.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/aegntic-website-refresh

@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: 11

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (2)
scripts/dashboard-proof.sh (1)

18-35: ⚠️ Potential issue | 🟠 Major

Subpath deployments break the health check.

base_url() strips everything after the origin, but Line 83 always probes ${BASE_URL}/api/health. A URL like https://host/app/#agents will therefore curl https://host/api/health, so the proof fails before the browser step even starts. Make the health endpoint prefix-aware or at least overridable.

🩹 Minimal fix
 BASE_URL="$(base_url "${URL}")"
+HEALTH_URL="${DASHBOARD_HEALTH_URL:-${BASE_URL}/api/health}"
@@
-HEALTH_JSON="$(curl -fsS "${BASE_URL}/api/health")"
+HEALTH_JSON="$(curl -fsS "${HEALTH_URL}")"

Also applies to: 83-83

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/dashboard-proof.sh` around lines 18 - 35, The health check breaks for
subpath deployments because base_url() currently returns only the origin and the
script always probes "${BASE_URL}/api/health"; update base_url() or the health
probe to preserve the URL path (strip fragment/query but keep the path) or make
the health path overridable. Concretely, change base_url() (and where BASE_URL
is set) so it returns origin+path (no fragment/query) or add a
HEALTH_ENDPOINT/HEALTH_PATH env variable and compute HEALTH_URL from BASE_URL
plus the preserved path, then use that HEALTH_URL instead of
"${BASE_URL}/api/health" in the health probe commands to ensure subpath
deployments like "https://host/app/#agents" probe "https://host/app/api/health"
(reference functions/vars: base_url(), BASE_URL and the health probe usage of
"${BASE_URL}/api/health").
crates/clawreform-api/src/routes.rs (1)

9125-9139: ⚠️ Potential issue | 🟠 Major

Don’t swallow persistence failures in the shared save helper.

structured_set can fail, but this helper drops the error. That means /api/company/... mutations can return 200/201 even when nothing was persisted, which turns storage issues into silent data loss.

🛠️ Suggested direction
-fn save_company_entries<T>(state: &AppState, key: &str, entries: &[T])
+fn save_company_entries<T>(state: &AppState, key: &str, entries: &[T]) -> Result<(), String>
 where
     T: serde::Serialize,
 {
     let payload = match serde_json::to_value(entries) {
         Ok(value) => value,
         Err(error) => {
             tracing::warn!("Failed to serialize {key} entries: {error}");
-            return;
+            return Err(format!("Failed to serialize {key} entries: {error}"));
         }
     };
-    let _ = state
+    state
         .kernel
         .memory
-        .structured_set(*COMPANY_AGENT_ID, key, payload);
+        .structured_set(*COMPANY_AGENT_ID, key, payload)
+        .map_err(|error| format!("Failed to persist {key} entries: {error}"))
 }

Then have the company mutation handlers convert that Err into a 5xx instead of reporting success.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/src/routes.rs` around lines 9125 - 9139,
save_company_entries currently swallows errors from
state.kernel.memory.structured_set; change it to return a Result (e.g.,
Result<(), E> or anyhow::Error) instead of void, propagate the
serde_json::to_value error as Err and return the Err from the structured_set
call (referencing save_company_entries, structured_set, COMPANY_AGENT_ID, and
AppState), and update the company mutation handlers that call
save_company_entries to map any Err into a 5xx response instead of reporting
success so persistence failures surface to clients.
🟠 Major comments (27)
site/index.html-651-653 (1)

651-653: ⚠️ Potential issue | 🟠 Major

Mobile navigation is removed without an alternative path.

Line 652 hides .nav-links on small screens, but no mobile menu/toggle is provided around Line 716-721. This removes direct section navigation on mobile.

Also applies to: 716-721

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@site/index.html` around lines 651 - 653, The CSS rule hiding `.nav-links` on
small screens removes access to navigation; restore mobile navigation by adding
a visible toggle and a show-state for `.nav-links`: introduce a
`.mobile-nav-toggle` control (button/icon) and a CSS state such as `.nav-open
.nav-links` or `.nav-links.open` that displays the menu when active, and wire a
small JS handler (e.g., a click listener on `.mobile-nav-toggle`) to toggle the
`nav-open` class on a container (header or body) or toggle `.open` on
`.nav-links`; update accessibility attributes (aria-expanded) on the toggle and
ensure the existing `.nav-links` selector is used for the show/hide behavior so
navigation works on mobile (refer to `.nav-links`, `.mobile-nav-toggle`, and the
`.nav-open` or `.open` state).
site/index.html-634-643 (1)

634-643: ⚠️ Potential issue | 🟠 Major

Core content is hidden when JS/observer is unavailable.

Line 634 hides all .reveal elements by default, and they only become visible through JS at Line 1028-1042. If JS is blocked/fails, large sections remain invisible.

Suggested fix (progressive enhancement)
-.reveal {
-    opacity: 0;
-    transform: translateY(30px);
-    transition: opacity 0.6s ease, transform 0.6s ease;
-}
-
-.reveal.visible {
+.reveal {
     opacity: 1;
     transform: translateY(0);
 }
+
+.js .reveal {
+    opacity: 0;
+    transform: translateY(30px);
+    transition: opacity 0.6s ease, transform 0.6s ease;
+}
+
+.js .reveal.visible {
+    opacity: 1;
+    transform: translateY(0);
+}
 <script>
+    document.documentElement.classList.add('js');
     // Theme Toggle
     function toggleTheme() {

Also applies to: 1028-1042

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@site/index.html` around lines 634 - 643, The CSS currently hides all .reveal
elements by default which breaks content when JS/observer fails; change to
progressive enhancement by making .reveal visible by default and only hide them
when JS is enabled: add a top-level marker class (e.g., "js-enabled") on
document.documentElement from your initialization JS (the code that currently
sets up the observer), move the initial hidden styles into .js-enabled .reveal {
opacity:0; transform:translateY(30px); transition:... } and keep .js-enabled
.reveal.visible { opacity:1; transform:translateY(0); } so content is accessible
without JS and the existing observer logic can continue to toggle the .visible
class as before.
site/index.html-715-715 (1)

715-715: ⚠️ Potential issue | 🟠 Major

Logo link href="#" throws an invalid selector error and breaks navigation.

Line 715 contains href="#", and the click handler at Line 1048 passes this to document.querySelector(), which throws a DOMException because "#" is not a valid CSS selector. This prevents the logo from functioning as intended.

Suggested fix
 document.querySelectorAll('a[href^="#"]').forEach(anchor => {
     anchor.addEventListener('click', function(e) {
-        e.preventDefault();
-        const target = document.querySelector(this.getAttribute('href'));
+        const href = this.getAttribute('href');
+        if (!href || href === '#') return;
+        const target = document.querySelector(href);
         if (target) {
+            e.preventDefault();
             target.scrollIntoView({ behavior: 'smooth', block: 'start' });
         }
     });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@site/index.html` at line 715, The logo anchor uses href="#" which is passed
to document.querySelector() and causes a DOMException; update the anchor
(class="logo") to use a valid selector/value (for example href="#top" or a
data-target attribute) or change the click handler that calls
document.querySelector() to guard/normalize the href (e.g., ignore or map "#" to
a valid selector before calling document.querySelector()). Ensure you update the
code paths that read the anchor's href (the click handler using
document.querySelector) so they never pass the raw "#" string to
document.querySelector().
docs/design/launch-metallic-baseline.md-5-11 (1)

5-11: ⚠️ Potential issue | 🟠 Major

Commit the reference boards or link them from a shared location.

The /home/ae/Downloads/... paths and “current planning thread” only exist for the author, so other contributors cannot reconstruct this baseline from the repo. Check the boards into version control or replace them with shared relative links.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/launch-metallic-baseline.md` around lines 5 - 11, The markdown
lists local-only image references (e.g., 1_clawREFORM_design_z-axis.png,
2_clawREFORM_design_debossed.png, 4_clawREFORM_design_layeringmetals.png,
7_clawREFORM_design_lightspill.png and “attached concept boards”) that others
cannot access; commit those reference boards into the repository (e.g.,
docs/design/assets/) or upload them to a shared location and replace the local
paths in launch-metallic-baseline.md with stable relative repo paths or
canonical shared URLs, and update the document’s bullet list to point to those
committed asset filenames or links so others can reconstruct the baseline.
scripts/cli-swarm/proof-demo.sh-19-30 (1)

19-30: ⚠️ Potential issue | 🟠 Major

Don't leave the rendered MP4 in an unignored repo folder.

Lines 19-29 create demo/clawreform-remotion.mp4, copy it to output/demo/, and never clean the original. Running the proof script will dirty the worktree with a large generated binary even though output/demo/ is already the intended artifact location.

🧹 Minimal cleanup
   if [[ -f "${ROOT_DIR}/demo/clawreform-remotion.mp4" ]]; then
     cp "${ROOT_DIR}/demo/clawreform-remotion.mp4" "${VIDEO_OUT}"
+    rm -f "${ROOT_DIR}/demo/clawreform-remotion.mp4"
+    rmdir "${ROOT_DIR}/demo" 2>/dev/null || true
   fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/proof-demo.sh` around lines 19 - 30, The script currently
renders into "${ROOT_DIR}/demo/clawreform-remotion.mp4" and copies it to
"${VIDEO_OUT}" but leaves the large binary in the repo demo folder; modify the
flow in the proof-demo.sh script (look for the block using ROOT_DIR,
REMOTION_DIR, and VIDEO_OUT and the demo/clawreform-remotion.mp4 path) so the
artifact is not left behind — either render directly to "${VIDEO_OUT}" (pass
that path into the render command) or, if rendering must target
"${ROOT_DIR}/demo", remove the source file after a successful cp (check
existence, cp to VIDEO_OUT, then rm "${ROOT_DIR}/demo/clawreform-remotion.mp4");
ensure error handling preserves the copy if rm fails.
docs/launch-tasks/14_clawREFORM_design-system-translation.md-37-37 (1)

37-37: ⚠️ Potential issue | 🟠 Major

Use a repo-relative baseline link here.

Line 37 points to /home/ae/clawreform/..., which only resolves on one workstation and breaks rendered docs. The prompt chain should reference the checked-in baseline relatively and the image pack from a shared location.

✏️ Suggested edit
-1. Read [launch-metallic-baseline.md](/home/ae/clawreform/docs/design/launch-metallic-baseline.md) and the referenced local image pack. Distill real design tokens for depth, shadow, bevel, glow, and type.
+1. Read [launch-metallic-baseline.md](../design/launch-metallic-baseline.md) and the shared image pack. Distill real design tokens for depth, shadow, bevel, glow, and type.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/launch-tasks/14_clawREFORM_design-system-translation.md` at line 37, The
markdown contains an absolute workstation link to launch-metallic-baseline.md;
replace that absolute /home/... target (the link target for
launch-metallic-baseline.md) with a repo-relative link to the checked-in
baseline file and update the referenced image pack link to a shared
repo-relative location so the docs render correctly for all contributors.
docs/session-restore/2026-03-13-launch-alpha/launch-tasks/12_clawREFORM_email-list-super-sherlock.md-19-24 (1)

19-24: ⚠️ Potential issue | 🟠 Major

Add explicit email compliance constraints.

The scope and prompt chain currently omit consent/unsubscribe/compliance requirements. That creates legal/privacy risk when this plan is executed.

Suggested additions
 ## Scope
 
 - lead source map
 - segmentation
 - launch email sequence
 - high-priority outreach targets
 - follow-up logic
+- consent/opt-in status and suppression rules
+- unsubscribe language and CAN-SPAM/GDPR compliance checks
 
 ## Prompt Chain
 
 1. Build the email intelligence model for launch. Define likely lead sources, segments, scoring, and priority outreach logic in `lead-source-map.md` and `segmentation-model.md`.
-2. Write the launch email sequence: announcement, reminder, proof follow-up, and design-partner outreach.
+2. Write the launch email sequence: announcement, reminder, proof follow-up, and design-partner outreach, including compliant footer/unsubscribe handling.
 3. Leave a sherlock-style note in `handoff.md` that explains who deserves immediate attention after launch and why.

Also applies to: 40-42

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/launch-tasks/12_clawREFORM_email-list-super-sherlock.md`
around lines 19 - 24, Update the plan to include explicit email compliance
constraints tied to the listed items: for "launch email sequence", "follow-up
logic", "high-priority outreach targets", "lead source map", and "segmentation"
specify required consent (documented opt-in or double opt-in), mandatory
unsubscribe/opt-out footer and functioning one-click unsubscribe link in all
templates, suppression-list enforcement (global unsubscribe, bounces, spam
complaints) before sends, retention of consent and suppression audit logs,
honoring jurisdictional rules (e.g., CAN-SPAM, GDPR) including data subject
rights and opt-out processing times, and a requirement that any sending pipeline
or automation (the sequence/follow-up workflow) checks the suppression/consent
store and rate-limits sends to avoid harassment or throttling issues.
docs/session-restore/2026-03-13-launch-alpha/state/visible-launch-manifest.json-3-87 (1)

3-87: ⚠️ Potential issue | 🟠 Major

Hardcoded absolute paths make the restore manifest non-portable.

Lines 3-87 lock the manifest to /home/ae/..., so restores fail on different machines and leak local directory layout in-repo.

🔧 Proposed direction (representative)
 {
   "run_name": "launch-alpha",
-  "session_file": "/home/ae/clawreform/.swarm/launch-alpha/kitty-live.session",
+  "session_file": ".swarm/launch-alpha/kitty-live.session",
   "tasks": [
     {
       "task_id": "1",
-      "worktree": "/home/ae/clawreform/.worktrees/01-pre-site-deployment",
-      "artifact_dir": "/home/ae/clawreform/.worktrees/01-pre-site-deployment/output/launch/01-pre-site-deployment",
+      "worktree": ".worktrees/01-pre-site-deployment",
+      "artifact_dir": ".worktrees/01-pre-site-deployment/output/launch/01-pre-site-deployment",
       ...
     }
   ]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/visible-launch-manifest.json`
around lines 3 - 87, The manifest currently contains hardcoded /home/ae/...
absolute paths (see keys like "session_file", each task's "worktree",
"artifact_dir", "prompt_file", "log_file" and the embedded paths in
"screen_command" and "agent_command"), making restores non‑portable; update the
manifest to use relative paths or templated placeholders (e.g. ${WORKTREE_ROOT}
or ${REPO_ROOT} and ${AGENT_BIN}) instead of /home/ae, and change each task
entry (tasks[].worktree, tasks[].artifact_dir, tasks[].prompt_file,
tasks[].log_file, tasks[].screen_command, tasks[].agent_command and top‑level
session_file) to reference those templates so a restore script can substitute
local values at runtime (or compute absolute paths from the repo root), then
adjust any restore/launcher code that reads visible-launch-manifest.json to
perform the substitution/resolution before launching.
docs/session-restore/2026-03-13-launch-alpha/launch-tasks/2_clawREFORM_audit_dl-path.md-10-10 (1)

10-10: ⚠️ Potential issue | 🟠 Major

Align this task’s worktree location with .worktrees/.

Line 10 points to ../clawreform-launch-02-audit-dl-path, which conflicts with the documented .worktrees/... launch flow used by launch_live_windows.py. Please standardize to avoid task bootstrap failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/launch-tasks/2_clawREFORM_audit_dl-path.md`
at line 10, The worktree path string "../clawreform-launch-02-audit-dl-path" in
the document should be changed to the standardized .worktrees location to match
launch_live_windows.py; locate the line containing the literal
"../clawreform-launch-02-audit-dl-path" and replace it with
".worktrees/clawreform-launch-02-audit-dl-path" (or the appropriate subdirectory
under .worktrees/) so the task bootstrap uses the documented .worktrees/...
flow.
docs/session-restore/2026-03-13-launch-alpha/launch-tasks/8_clawREFORM_youtube-launch-automation.md-10-10 (1)

10-10: ⚠️ Potential issue | 🟠 Major

Standardize task paths to prevent operator drift.

Line 10 uses ../clawreform-launch-... instead of the documented .worktrees/... pattern, and Line 46 uses a machine-local absolute doc path. Both should be normalized to repo-portable paths.

Also applies to: 46-46

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/launch-tasks/8_clawREFORM_youtube-launch-automation.md`
at line 10, Replace the hardcoded worktree and machine-local absolute doc paths
with repo-portable relative paths: change the Worktree value that currently
reads "../clawreform-launch-08-youtube-launch-automation" (referenced at Line
10) to the standardized
".worktrees/clawreform-launch-08-youtube-launch-automation" pattern, and update
the absolute documentation path referenced at Line 46 to a repo-relative path
(e.g., a path under the repository like "docs/..." or the appropriate relative
path from this file) so both entries are portable across machines and follow the
documented .worktrees convention.
docs/session-restore/2026-03-13-launch-alpha/launch-tasks/9_clawREFORM_reddit-medium-quora-chain.md-10-10 (1)

10-10: ⚠️ Potential issue | 🟠 Major

Worktree path conflicts with the launcher's .worktrees/ contract.

Line 10 documents ../clawreform-launch-..., but the live-window operator flow specifies .worktrees/... for sandbox-safe execution. This mismatch is likely to misroute task setup and operator expectations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/launch-tasks/9_clawREFORM_reddit-medium-quora-chain.md`
at line 10, The documented worktree path
"../clawreform-launch-09-reddit-medium-quora-chain" in
launch-tasks/9_clawREFORM_reddit-medium-quora-chain.md conflicts with the
launcher's required sandbox-safe `.worktrees/` contract; update Line 10 to use
the `.worktrees/` namespace (e.g.,
`.worktrees/clawreform-launch-09-reddit-medium-quora-chain`) so operator flows
expect and create the task under the correct sandbox location, ensuring the
documented path matches the runtime contract referenced by the launcher and
live-window operator.
docs/session-restore/2026-03-13-launch-alpha/launch-tasks/1_clawREFORM_pre-site-deployment.md-10-10 (1)

10-10: ⚠️ Potential issue | 🟠 Major

Fix path portability and launcher contract consistency.

Two doc path issues here:

  • Line 10: ../clawreform-launch-01-pre-site-deployment conflicts with the .worktrees/... convention used by the live-window launcher docs.
  • Line 41: /home/ae/clawreform/... is machine-specific; use a repo-relative link so it renders and works for all contributors.

Also applies to: 41-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/launch-tasks/1_clawREFORM_pre-site-deployment.md`
at line 10, Replace the hardcoded and machine-specific paths to make docs
portable and consistent with the launcher contract: change the Worktree
reference `../clawreform-launch-01-pre-site-deployment` to the `.worktrees/...`
convention used by the live-window launcher docs (e.g.,
`.worktrees/clawreform-launch-01-pre-site-deployment`) and replace any absolute
machine path like `/home/ae/clawreform/...` with a repo-relative path (e.g.,
`clawreform/...` or `./clawreform/...`) so links render correctly for all
contributors.
scripts/cli-swarm/launch_live_windows.py-161-173 (1)

161-173: ⚠️ Potential issue | 🟠 Major

Relaunches won't pick up updated task instructions.

copy_missing_tree() only fills gaps, the design baseline is only copied on first run, and the prompt file is never regenerated once it exists. Because git worktree add already checks out tracked docs, edited launch docs in the main workspace are silently ignored even on a new worktree. Re-running the launcher after updating the task pack or baseline will keep launching stale instructions.

♻️ Suggested refresh behavior
-        if not target.exists():
-            target.parent.mkdir(parents=True, exist_ok=True)
-            shutil.copy2(path, target)
+        target.parent.mkdir(parents=True, exist_ok=True)
+        shutil.copy2(path, target)
-    if not dst_design.exists():
-        dst_design.parent.mkdir(parents=True, exist_ok=True)
-        shutil.copy2(src_design, dst_design)
+    dst_design.parent.mkdir(parents=True, exist_ok=True)
+    shutil.copy2(src_design, dst_design)
-    if prompt_file.exists():
-        return prompt_file

Also applies to: 263-267, 329-353

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch_live_windows.py` around lines 161 - 173,
copy_missing_tree currently only copies missing files, causing updated
baseline/task prompt files to be ignored; update copy_missing_tree to also
overwrite destination files when the source is newer or content differs (e.g.,
compare mtime or checksum) and ensure directories are created as before, so
tracked docs edited in the main workspace propagate into the worktree; apply the
same refresh/overwrite logic to the other copy sites noted (around the blocks
referenced at the other occurrences) so regenerated prompt/baseline files are
replaced rather than left stale.
scripts/cli-swarm/launch_live_windows.py-52-67 (1)

52-67: ⚠️ Potential issue | 🟠 Major

The launcher ignores the per-task CLI/model matrix.

Every TaskSpec here is hardwired to opencode and HUNTER_ALPHA_MODEL, but the launch commander/operator docs assign different CLIs and models per task. Running this launcher won't actually start the intended Claude/Gemini/pi/Codex workers, and tasks that rely on pi/Claude-specific behavior will start under the wrong client.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch_live_windows.py` around lines 52 - 67, TASKS
currently hardcodes the CLI and model to "opencode" and HUNTER_ALPHA_MODEL for
every TaskSpec, causing wrong worker clients to be launched; update the TASKS
construction so each TaskSpec uses the per-task CLI and model from the launch
matrix (e.g., a TASK_CLI_MAP and TASK_MODEL_MAP or a single TASK_MATRIX dict)
instead of the constants opencode and HUNTER_ALPHA_MODEL, or modify the TaskSpec
instantiation to accept cli and model values passed in from the existing
launcher configuration; ensure TaskSpec (class TaskSpec) fields and any code
that consumes TASKS (the launcher/launch function) handle missing values by
falling back to sensible defaults.
docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot-308-309 (1)

308-309: ⚠️ Potential issue | 🟠 Major

Preparation shouldn't fail just because screen is missing.

Line 309 resolves screen during sync_launch_inputs(), so even the prepare-only path exits early on hosts that only want the kitty session file. Check for screen in the launch/open branches instead.

Suggested fix
 def make_screen_command(session_name: str, shell_command: str) -> list[str]:
-    return [require_binary("screen"), "-dmS", session_name, "bash", "-lc", shell_command]
+    return ["screen", "-dmS", session_name, "bash", "-lc", shell_command]
 ...
     if args.launch:
+        require_binary("screen")
         for item in selected:
             subprocess.run(shlex.split(item["screen_command"]), check=True)
         subprocess.run(
             [require_binary("kitty"), "--detach", "--session", str(session_file)],
             check=True,
         )
 ...
     elif args.open_windows_only:
+        require_binary("screen")
         subprocess.run(
             [require_binary("kitty"), "--detach", "--session", str(session_file)],
             check=True,
         )

Also applies to: 319-327, 403-415

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot`
around lines 308 - 309, The prepare-only path fails because make_screen_command
eagerly calls require_binary("screen") during sync_launch_inputs; change
make_screen_command to not resolve the binary (return ["screen", "-dmS",
session_name, "bash", "-lc", shell_command]) and move require_binary("screen")
into the actual launch/open branches (the code around the current launch/open
handling formerly at lines 319-327 and 403-415) so the binary is only checked
when you actually attempt to start a screen session; alternatively, if you
prefer explicitness, add a parameter to make_screen_command to accept the
resolved screen path and call require_binary in the launch/open code before
passing it in.
scripts/cli-swarm/launch.py-312-313 (1)

312-313: ⚠️ Potential issue | 🟠 Major

Prepare-only mode shouldn't require screen.

Line 313 resolves screen while building every SessionSpec, so launch.py --count 1 fails on hosts without screen even when --launch is off.

Suggested fix
-    screen_command = [require_screen(), "-dmS", screen_name, *launch_command]
+    screen_command = ["screen", "-dmS", screen_name, *launch_command]
 ...
     if args.launch:
+        require_screen()
         for spec in specs:
             subprocess.run(spec.screen_command, check=True)

Also applies to: 443-445

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch.py` around lines 312 - 313, The code currently calls
require_screen() while building screen_command (via variables screen_name and
screen_command) for every SessionSpec, which forces resolving `screen` even in
prepare-only runs; change this so require_screen() is only invoked when actually
launching sessions (i.e., when --launch is true or the code path that executes
the screen wrapper). Concretely, modify the logic that constructs
screen_name/screen_command (and the analogous code around the blocks that
reference screen_name at the 443-445 area) to skip wrapping launch_command in a
screen call when prepare-only/--launch is False: keep launch_command as-is for
prepare mode, or set screen_command to None/leave it unset, and call
require_screen() and build the final screen_command only in the runtime/launcher
function that executes the session. Ensure references to screen_name,
screen_command, and require_screen() are adjusted accordingly.
scripts/cli-swarm/launch.py-61-64 (1)

61-64: ⚠️ Potential issue | 🟠 Major

Honor --coordinator consistently or remove the flag.

Line 298 hardcodes coordinator sessions to pi, so --coordinator codex still builds a pi command while the manifest records "coordinator": "codex". That makes the CLI misleading and can fail if only the requested coordinator is installed.

Also applies to: 298-308, 418-423

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch.py` around lines 61 - 64, The CLI flag
"--coordinator" is declared but its value is ignored where coordinator sessions
are constructed (you can see hardcoded "pi" in the coordinator-session
construction around the block that builds the manifest and again in the later
session creation code). Replace the hardcoded "pi" with the parsed coordinator
value (e.g., use the argument/variable holding args.coordinator or the local
"coordinator" variable) wherever coordinator sessions/commands are created so
the runtime command matches the manifest; alternatively, if the design requires
a single fixed coordinator, remove the "--coordinator" flag and all references
to it to avoid divergence. Ensure you update all occurrences in the coordinator
session construction blocks (the sections corresponding to the 298-308 and
418-423 ranges) to use the same coordinator symbol.
docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot-75-75 (1)

75-75: ⚠️ Potential issue | 🟠 Major

Constrain run names so they can't escape .swarm/.

Both Line 382 and Line 318 join the raw run name into a filesystem path. Absolute names or .. segments will write manifests, prompts, and logs outside the intended .swarm/<name>/ directories.

Suggested fix
+def resolve_run_root(base: pathlib.Path, name: str) -> pathlib.Path:
+    swarm_root = (base / ".swarm").resolve()
+    run_root = (swarm_root / name).resolve()
+    if run_root.parent != swarm_root:
+        raise SystemExit("--name must be a single directory name under .swarm/")
+    return run_root
+
 ...
-    run_root = worktree / ".swarm" / run_name
+    run_root = resolve_run_root(worktree, run_name)
 ...
-    run_root = workspace / ".swarm" / args.name
+    run_root = resolve_run_root(workspace, args.name)

Also applies to: 318-320, 382-383

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot`
at line 75, The run name passed via the parser's "--name" argument is used
directly when building paths (see uses at the join sites around lines
referenced) and can escape the .swarm directory; add a validation/sanitization
step: implement a small validator function (e.g., validate_run_name) and use it
as the argparse type for parser.add_argument("--name", ...). The validator
should reject absolute names (os.path.isabs), any path separators or ".."
segments (check for os.sep or "/" or ".."), or alternatively enforce a safe
regex like ^[A-Za-z0-9._-]+$; on failure raise argparse.ArgumentTypeError.
Replace raw use of the parsed name at the path-joining sites with the validated
value (or use pathlib.Path(name).name and ensure it equals the original to
detect stripping) so manifests, prompts, and logs cannot be written outside
.swarm/<name>/.
docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot-142-146 (1)

142-146: ⚠️ Potential issue | 🟠 Major

Put -b before the worktree path.

The current code produces git worktree add <path> -b <branch> HEAD when creating new branches, but git worktree add expects options like -b before <path>. This will cause branch creation to fail on first-time task branches.

Suggested fix
-    cmd = ["git", "worktree", "add", str(worktree_path)]
+    cmd = ["git", "worktree", "add"]
     if branch_exists(workspace, task.branch):
-        cmd.append(task.branch)
+        cmd.extend([str(worktree_path), task.branch])
     else:
-        cmd.extend(["-b", task.branch, "HEAD"])
+        cmd.extend(["-b", task.branch, str(worktree_path), "HEAD"])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot`
around lines 142 - 146, The current construction of cmd puts the worktree path
before the -b option for new branches which makes git fail; update the
branch-not-exists branch of the conditional so options come before the path (use
cmd = ["git","worktree","add","-b", task.branch, str(worktree_path), "HEAD"] or
insert "-b" and task.branch into cmd before str(worktree_path]) while leaving
the existing-branch case (cmd.append(task.branch)) unchanged; reference symbols:
cmd, branch_exists(workspace, task.branch), worktree_path, task.branch.
crates/clawreform-api/src/routes.rs-9090-9096 (1)

9090-9096: ⚠️ Potential issue | 🟠 Major

Changing COMPANY_AGENT_ID will hide previously saved company data.

This ID is the storage namespace for company_goals and company_issues. Switching it means existing deployments start reading an empty namespace, so saved goals/issues effectively disappear until you migrate or dual-read the previous ID.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/src/routes.rs` around lines 9090 - 9096, Do not change
the storage namespace represented by COMPANY_AGENT_ID because it maps persisted
data for company_goals and company_issues; instead restore the original constant
value (leave COMPANY_AGENT_ID untouched) or, if a change is unavoidable,
implement a migration/dual-read strategy: add code to read from the old AgentId
as well as the new one and migrate records for company_goals and company_issues
(or provide a one-time migration utility), update documentation/comments near
COMPANY_AGENT_ID to warn about the persistence impact, and ensure any deploy
includes the migration path before switching live reads to the new ID.
crates/clawreform-api/src/server.rs-305-317 (1)

305-317: 🛠️ Refactor suggestion | 🟠 Major

Route these new handlers through routes.rs.

These additions wire crate::auto_import::* and crate::openclaw::* straight into the router, which bypasses the API surface split this repo expects. If the logic should stay in dedicated modules, add thin forwarding handlers in routes.rs and register those here instead.

As per coding guidelines, "New API routes must be registered in server.rs router AND implemented in routes.rs".

Also applies to: 648-703

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/src/server.rs` around lines 305 - 317, The new routes
currently register crate::auto_import::scan_directory, import_items, and
get_suggestions (and similarly the crate::openclaw handlers referenced later)
directly in server.rs; instead add thin forwarding handlers in routes.rs (e.g.,
auto_import_scan, auto_import_import_items, auto_import_get_suggestions) that
call the existing crate::auto_import functions, export them from routes.rs, and
then register those forwarding handlers in server.rs where you currently call
crate::auto_import::scan_directory, crate::auto_import::import_items, and
crate::auto_import::get_suggestions; do the same for the openclaw handlers
mentioned in the later block so all new API endpoints are implemented in
routes.rs and only routed from server.rs.
crates/clawreform-api/static/js/pages/import.js-96-103 (1)

96-103: ⚠️ Potential issue | 🟠 Major

Make selectAll() idempotent.

Right now it toggles every eligible row. If the user manually selected one item first, clicking "Select all" will deselect that item instead of leaving it selected.

🛠️ Suggested fix
     selectAll() {
-      var self = this;
-      for (var item of this.discoveredItems) {
-        if (item.can_import && !item.already_installed) {
-          self.toggleSelect(item);
-        }
-      }
+      this.selectedItems = this.discoveredItems.filter(function(item) {
+        return item.can_import && !item.already_installed;
+      });
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/static/js/pages/import.js` around lines 96 - 103,
selectAll currently toggles every eligible item which makes it non-idempotent;
update selectAll (in this file) to only call toggleSelect for items that are
eligible (item.can_import && !item.already_installed) AND not already selected
by checking the item's selection state (e.g., item.selected or
item.isSelected()). In short: replace the unconditional toggleSelect call inside
the loop with a conditional that calls toggleSelect only when the item is not
already selected so clicking "Select all" never deselects user-selected rows.
crates/clawreform-api/static/js/pages/import.js-51-77 (1)

51-77: ⚠️ Potential issue | 🟠 Major

Clear stale scan state and always reset scanning.

The early return and the catch both leave this.scanning stuck true, and a failed rescan keeps the previous results/selection around. The second discoveredItems assignment also drops the || [] guards, so a partial response can throw before cleanup.

🛠️ Suggested fix
     async scanDirectory(dir) {
       this.scanPath = dir;
       this.scanning = true;
       this.scanError = '';
+      this.scanResults = null;
+      this.discoveredItems = [];
+      this.selectedItems = [];
 
       try {
         var data = await ClawReformAPI.post('/api/import/scan', { directory: dir });
         if (data.error) {
           this.scanError = data.error;
           return;
         }
         this.scanResults = data;
         this.discoveredItems = []
           .concat(data.mcp_servers || [])
           .concat(data.skills || [])
           .concat(data.plugins || []);
-
-        // Flatten for UI display
-        this.discoveredItems = this.scanResults.mcp_servers
-          .concat(this.scanResults.skills)
-          .concat(this.scanResults.plugins);
-        this.scanning = false;
       } catch(e) {
         this.scanError = 'Scan failed: ' + e.message;
         this.showToast('error', 'Scan failed: ' + e.message);
+      } finally {
+        this.scanning = false;
       }
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/static/js/pages/import.js` around lines 51 - 77, The
scanDirectory function leaves this.scanning true and stale results on
early-return or error; update scanDirectory (the async method that calls
ClawReformAPI.post) to always set this.scanning = false in a finally block,
clear/initialize this.scanResults / this.discoveredItems / this.scanError before
returning when data.error is present, and when building discoveredItems use the
guarded concatenation (e.g., (data.mcp_servers || []) .concat(data.skills || [])
.concat(data.plugins || [])) instead of the duplicate unguarded assignment so
partial responses won't throw.
crates/clawreform-runtime/src/trace_context.rs-14-18 (1)

14-18: ⚠️ Potential issue | 🟠 Major

This trace context can be lost silently.

tokio::task_local! is per-task, so it will not automatically follow spawned work, yet the module docs read like propagation is end-to-end. On top of that, set_trace_id()/clear_trace_id() ignore try_with failures, so missing scope just drops the trace with no signal. Please either pass/re-scope TraceId explicitly for spawned work, or narrow the contract and make missing context fail loudly.

Also applies to: 33-45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-runtime/src/trace_context.rs` around lines 14 - 18, The
task-local TRACE_CONTEXT (tokio::task_local!) is currently silently dropped for
spawned tasks because set_trace_id()/clear_trace_id() ignore try_with failures;
update the implementation to either explicitly propagate the TraceId when
spawning work or make missing context fail loudly: replace the silent try_with
ignores in set_trace_id()/clear_trace_id()/any try_with sites so they return
Result or log/panic on Err, and add a helper (e.g., spawn_with_trace or
scope_trace) that takes a TraceId and re-scopes TRACE_CONTEXT for spawned
tokio::spawn tasks; ensure the unique symbols TRACE_CONTEXT, set_trace_id,
clear_trace_id, try_with (and any spawn helper you add) are updated so trace
propagation is explicit or failures are surfaced.
crates/clawreform-api/static/js/app.js-518-520 (1)

518-520: ⚠️ Potential issue | 🟠 Major

Don't auto-create a seed agent when an existing agent just lacks markdown.

The widened obsidianCanSeedAgent check means simply opening #obsidian can now create a brand new memory-seed agent for an otherwise normal workspace. That is a hidden write side effect on a navigation path, and it also flips future graph loads over to the synthetic agent via the stored preferred id. Limit silent seeding to the zero-agent case, or make the no-markdown path an explicit CTA.

Safer immediate guard
-        if (this.obsidianCanSeedAgent) {
+        if (/No agents found/i.test(this.obsidianGraphError || '')) {
           await this.createObsidianSeedAgent({ silent: true });
         }

Also applies to: 545-555

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/static/js/app.js` around lines 518 - 520, The
obsidianCanSeedAgent getter currently treats "no markdown files" as permission
to auto-create a seed agent, causing unintended silent writes; change its logic
to only allow automatic seeding when there are zero existing agents (e.g., check
this.agents?.length === 0) or otherwise require an explicit user action/CTA for
the no-markdown path. Update the obsidianCanSeedAgent getter and any other
occurrence of the same widened check (the similar block around lines 545-555) to
include the zero-agent guard (or replace silent seeding with an explicit CTA
flow) so opening `#obsidian` doesn't create a memory-seed for a workspace that
already has agents.
openclaw/docker-compose.openclaw.yml-17-25 (1)

17-25: ⚠️ Potential issue | 🟠 Major

Don't expose the datastores with repo-tracked credentials.

Docker publishes these ports on all host interfaces by default. With POSTGRES_PASSWORD: openclaw_secret and an unauthenticated Redis on 6379, anyone who can reach the developer host can attach to the stack. Bind the datastore ports to 127.0.0.1/expose only, and source the passwords from an untracked .env instead of hardcoding them into the compose file and every DATABASE_URL.

Also applies to: 37-41, 62-63, 91-92, 115-116, 145-146, 170-172

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openclaw/docker-compose.openclaw.yml` around lines 17 - 25, The compose file
currently hardcodes credentials and publishes DB ports to all interfaces; change
the postgres service to bind the port to localhost (e.g., "127.0.0.1:5432:5432"
or use expose: - "5432") and replace POSTGRES_PASSWORD: openclaw_secret with a
variable reference (POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}") sourced from an
untracked .env file; apply the same pattern for other datastore services (e.g.,
Redis on 6379 and any other POSTGRES* or DATABASE_URL entries mentioned) so
credentials are removed from the repo and ports are not published publicly, and
update any DATABASE_URL usages to read from the .env values (e.g., DATABASE_URL:
"${DATABASE_URL}").
crates/clawreform-api/src/openclaw.rs-110-113 (1)

110-113: ⚠️ Potential issue | 🟠 Major

Manual JSON string formatting can produce invalid JSON if error contains quotes.

If the error message contains double quotes, backslashes, or other special characters, the resulting JSON will be malformed. Use serde_json::json! for safe JSON construction.

🐛 Proposed fix (apply to all similar occurrences)
-                Err(e) => (StatusCode::BAD_GATEWAY, format!("{{\"error\": \"{}\"}}", e)),
+                Err(e) => (StatusCode::BAD_GATEWAY, serde_json::json!({"error": e.to_string()}).to_string()),

This pattern appears throughout the file (Lines 110, 113, 129, 132, 148, 151, 171, 174, 187, 190, 207, 210, 226, 229, 245, 248, 267, 270, 291, 294, 310, 313, 329, 332, 345, 348). Consider extracting a helper function:

fn error_response(e: impl std::fmt::Display) -> (StatusCode, String) {
    (StatusCode::BAD_GATEWAY, serde_json::json!({"error": e.to_string()}).to_string())
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/src/openclaw.rs` around lines 110 - 113, Replace manual
JSON string construction like format!("{{\"error\": \"{}\"}}", e) with a safe
helper function error_response that returns (StatusCode::BAD_GATEWAY, String)
and uses serde_json::json! to build {"error": e.to_string()} and .to_string();
call error_response(e) at each occurrence (e.g., the two instances shown and the
other listed lines) so error messages with quotes or escapes produce valid JSON.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2088b161-1999-4386-8841-992bb98c5c4a

📥 Commits

Reviewing files that changed from the base of the PR and between 85697b9 and d28bb12.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (94)
  • .gitignore
  • crates/clawreform-api/Cargo.toml
  • crates/clawreform-api/src/auto_import.rs
  • crates/clawreform-api/src/lib.rs
  • crates/clawreform-api/src/middleware.rs
  • crates/clawreform-api/src/openclaw.rs
  • crates/clawreform-api/src/routes.rs
  • crates/clawreform-api/src/server.rs
  • crates/clawreform-api/src/webchat.rs
  • crates/clawreform-api/static/css/components.css
  • crates/clawreform-api/static/index_body.html
  • crates/clawreform-api/static/js/app.js
  • crates/clawreform-api/static/js/pages/import.js
  • crates/clawreform-api/static/js/pages/model-selector.js
  • crates/clawreform-api/static/js/pages/settings.js
  • crates/clawreform-api/static/js/pages/skills.js
  • crates/clawreform-api/tests/api_integration_test.rs
  • crates/clawreform-api/tests/daemon_lifecycle_test.rs
  • crates/clawreform-api/tests/load_test.rs
  • crates/clawreform-cli/src/tui/mod.rs
  • crates/clawreform-kernel/src/kernel.rs
  • crates/clawreform-kernel/src/registry.rs
  • crates/clawreform-memory/src/session.rs
  • crates/clawreform-memory/src/structured.rs
  • crates/clawreform-runtime/src/agent_loop.rs
  • crates/clawreform-runtime/src/compactor.rs
  • crates/clawreform-runtime/src/lib.rs
  • crates/clawreform-runtime/src/trace_context.rs
  • crates/clawreform-types/src/agent.rs
  • crates/clawreform-types/src/company.rs
  • crates/clawreform-types/src/lib.rs
  • crates/clawreform-types/src/openclaw.rs
  • crates/clawreform-wire/src/peer.rs
  • docs/cli-swarm.md
  • docs/design/launch-metallic-baseline.md
  • docs/launch-tasks/0_clawREFORM_launch-commander.md
  • docs/launch-tasks/10_clawREFORM_content-creator.md
  • docs/launch-tasks/11_clawREFORM_content-distributor.md
  • docs/launch-tasks/12_clawREFORM_email-list-super-sherlock.md
  • docs/launch-tasks/13_clawREFORM_launch-control-room.md
  • docs/launch-tasks/14_clawREFORM_design-system-translation.md
  • docs/launch-tasks/15_clawREFORM_live-window-operator-sheet.md
  • docs/launch-tasks/1_clawREFORM_pre-site-deployment.md
  • docs/launch-tasks/2_clawREFORM_audit_dl-path.md
  • docs/launch-tasks/3_clawREFORM_release-packaging-proof.md
  • docs/launch-tasks/4_clawREFORM_onboarding-first-run-smoke.md
  • docs/launch-tasks/5_clawREFORM_parallel-proof-screenshots.md
  • docs/launch-tasks/6_clawREFORM_public-alpha-go-live.md
  • docs/launch-tasks/7_clawREFORM_twitter-x-launch-chain.md
  • docs/launch-tasks/8_clawREFORM_youtube-launch-automation.md
  • docs/launch-tasks/9_clawREFORM_reddit-medium-quora-chain.md
  • docs/session-restore/2026-03-13-launch-alpha/README.md
  • docs/session-restore/2026-03-13-launch-alpha/constraints-summary.md
  • docs/session-restore/2026-03-13-launch-alpha/conversation.md
  • docs/session-restore/2026-03-13-launch-alpha/design/launch-metallic-baseline.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/0_clawREFORM_launch-commander.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/10_clawREFORM_content-creator.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/11_clawREFORM_content-distributor.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/12_clawREFORM_email-list-super-sherlock.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/13_clawREFORM_launch-control-room.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/14_clawREFORM_design-system-translation.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/15_clawREFORM_live-window-operator-sheet.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/1_clawREFORM_pre-site-deployment.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/2_clawREFORM_audit_dl-path.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/3_clawREFORM_release-packaging-proof.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/4_clawREFORM_onboarding-first-run-smoke.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/5_clawREFORM_parallel-proof-screenshots.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/6_clawREFORM_public-alpha-go-live.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/7_clawREFORM_twitter-x-launch-chain.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/8_clawREFORM_youtube-launch-automation.md
  • docs/session-restore/2026-03-13-launch-alpha/launch-tasks/9_clawREFORM_reddit-medium-quora-chain.md
  • docs/session-restore/2026-03-13-launch-alpha/non-sandbox-delta.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/01-pre-site-deployment.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/02-audit-dl-path.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/04-onboarding-first-run-smoke.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/05-parallel-proof-screenshots.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/10-content-creator.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/13-launch-control-room.md
  • docs/session-restore/2026-03-13-launch-alpha/prompts/14-design-system-translation.md
  • docs/session-restore/2026-03-13-launch-alpha/restore-checklist.md
  • docs/session-restore/2026-03-13-launch-alpha/state/kitty-live.session
  • docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot
  • docs/session-restore/2026-03-13-launch-alpha/state/visible-launch-manifest.json
  • docs/session-restore/2026-03-13-launch-alpha/working-state.md
  • openclaw/IMPLEMENTATION_PACK.md
  • openclaw/docker-compose.openclaw.yml
  • openclaw/openclaw_registry.sql
  • scripts/cli-swarm/launch.py
  • scripts/cli-swarm/launch_live_windows.py
  • scripts/cli-swarm/proof-demo.sh
  • scripts/cli-swarm/status.py
  • scripts/cli-swarm/verify.sh
  • scripts/dashboard-proof.sh
  • site/index.html

Comment on lines +197 to +200
include_str!("../static/js/pages/model-selector.js"),
"\n",
include_str!("../static/js/pages/import.js"),
"\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify HTML elements exist for the new page scripts

# Check for model-selector related elements
echo "=== Checking for model-selector HTML elements ==="
rg -n "model-selector|modelSelector" crates/clawreform-api/static/index_body.html

# Check for import page related elements  
echo "=== Checking for import page HTML elements ==="
rg -n "import.*page|importPage" crates/clawreform-api/static/index_body.html

# Verify the JS files exist
echo "=== Verifying JS files exist ==="
ls -la crates/clawreform-api/static/js/pages/model-selector.js crates/clawreform-api/static/js/pages/import.js 2>&1

Repository: aegntic/clawreform

Length of output: 466


Add HTML element for model-selector page or remove the script inclusion.

The import.js script has a corresponding HTML element (<div x-show="tab === 'import'" x-data="importPage()"> at line 5016 of index_body.html). However, model-selector.js is included in webchat.rs but has no corresponding HTML element in index_body.html. Per the pattern established by the import page, dashboard page scripts require both the script inclusion and a matching HTML div element with Alpine.js data binding.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/src/webchat.rs` around lines 197 - 200, The pull
request currently includes model-selector.js in webchat.rs without a matching
Alpine/HTML container in index_body.html; either remove the
include_str!("../static/js/pages/model-selector.js") from the list in webchat.rs
or add a corresponding HTML element in index_body.html similar to the import
page (e.g., a <div x-show="tab === 'model-selector'"
x-data="modelSelectorPage()">) so the script has a matching Alpine binding;
update the symbol names model-selector.js and modelSelectorPage (or whichever
Alpine initializer is defined) to ensure the script and HTML div names match.

Comment on lines +256 to +265
showApiKeyFix() {
// Reset the API key configuration and show the OpenRouter gate
this.openRouterKeyInput = '';
this.openRouterError = '';
this.openRouterProviderStatus = 'unknown';
this.showOpenRouterGate = true;
// Navigate to wizard if not already there
if (Alpine.store('app').page !== 'wizard') {
this.navigate('wizard');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

showApiKeyFix() is navigating through the wrong object.

page and navigate() belong to the app() component, not the global store. This code therefore always enters the branch and then throws TypeError: this.navigate is not a function, so the recovery button cannot reach the wizard.

🐛 Minimal fix
     showApiKeyFix() {
       // Reset the API key configuration and show the OpenRouter gate
       this.openRouterKeyInput = '';
       this.openRouterError = '';
       this.openRouterProviderStatus = 'unknown';
       this.showOpenRouterGate = true;
-      // Navigate to wizard if not already there
-      if (Alpine.store('app').page !== 'wizard') {
-        this.navigate('wizard');
-      }
+      if ((window.location.hash || '').replace('#', '') !== 'wizard') {
+        window.location.hash = 'wizard';
+      }
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/static/js/app.js` around lines 256 - 265, In
showApiKeyFix(), the code checks and calls navigation on Alpine.store('app')
which doesn't expose page/navigate; replace the store access with the component
instance so the correct methods are used: change the condition and call from if
(Alpine.store('app').page !== 'wizard') { this.navigate('wizard'); } to use this
(e.g., if (this.page !== 'wizard') { this.navigate('wizard'); }) within the
showApiKeyFix method so it reads the component's page and invokes its navigate()
method.

Comment on lines +312 to +321
var commonDirs = [
'~/.claude/skills',
'~/.codex/skills',
'~/.gemini/skills',
'~/.opencode/skills',
'~/.cline/skills',
'~/.aider/skills',
'~/.cursor/skills',
'~/.continue/skills',
'~/.local/share/mcp/skills',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd -a '^auto_import\.rs$' crates/clawreform-api/src | head -n1)"
echo "File: $file"
rg -n -C4 'home_dir|dirs::home_dir|shellexpand|tilde|expanduser|canonicalize|scan.*directory|directory:' "$file" || true

Repository: aegntic/clawreform

Length of output: 2034


🏁 Script executed:

file="crates/clawreform-api/static/js/pages/skills.js"
if [ -f "$file" ]; then
  sed -n '359,363p' "$file"
fi

Repository: aegntic/clawreform

Length of output: 323


Fix tilde expansion for fallback scan paths on the backend.

The frontend sends ~/.claude/skills literally to /api/import/scan (lines 359–363), but the backend treats it as a literal path without expanding ~ to the user's home directory. The line let dir = PathBuf::from(&body.directory); in crates/clawreform-api/src/auto_import.rs:99 must expand the tilde before filesystem operations, otherwise fallback directory scans will silently fail.

Use a crate like shellexpand or manually expand ~ using dirs::home_dir() before creating the PathBuf.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/clawreform-api/static/js/pages/skills.js` around lines 312 - 321, The
backend is treating incoming directories like "~/.claude/skills" as literal
paths; locate where PathBuf::from(&body.directory) is used (in the import/scan
handler in auto_import.rs) and expand a leading tilde before creating the
PathBuf (use shellexpand::tilde or expand manually with dirs::home_dir()), then
continue with the existing filesystem operations using the expanded path; ensure
you only expand a leading "~" or "~/" and preserve other inputs.

Comment on lines +104 to +105
def repo_root() -> pathlib.Path:
return pathlib.Path(__file__).resolve().parents[2]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

repo_root() resolves to the wrong directory.

For this file path, parents[2] is <repo>/docs/session-restore, not the repository root. All later path joins then look for docs/launch-tasks under the wrong tree and write .swarm/ in the wrong place.

Suggested fix
 def repo_root() -> pathlib.Path:
-    return pathlib.Path(__file__).resolve().parents[2]
+    current = pathlib.Path(__file__).resolve()
+    for parent in current.parents:
+        if (parent / ".git").exists():
+            return parent
+    raise SystemExit("Could not locate the repository root")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot`
around lines 104 - 105, The repo_root() helper incorrectly uses
pathlib.Path(__file__).resolve().parents[2] (which yields
<repo>/docs/session-restore); update repo_root() to return the actual repository
root by using the correct parent index
pathlib.Path(__file__).resolve().parents[4] (or replace with a small loop that
walks up until it finds a repo marker like .git/pyproject.toml) so all
downstream joins (in code that uses repo_root()) point at the real repository
root.

Comment on lines +162 to +206
def local_env_exports(run_root: pathlib.Path, cli_name: str, session_dir: pathlib.Path) -> dict[str, str]:
home_dir = run_root / "homes" / cli_name
for directory in (
home_dir,
home_dir / ".config",
home_dir / ".local" / "share",
home_dir / ".local" / "state",
home_dir / ".cache",
home_dir / ".pi" / "agent",
home_dir / ".codex",
home_dir / ".config" / "opencode",
home_dir / ".local" / "share" / "opencode",
home_dir / ".gemini",
home_dir / ".claude",
session_dir,
):
directory.mkdir(parents=True, exist_ok=True)

env = {
"HOME": str(home_dir),
"XDG_CONFIG_HOME": str(home_dir / ".config"),
"XDG_DATA_HOME": str(home_dir / ".local" / "share"),
"XDG_STATE_HOME": str(home_dir / ".local" / "state"),
"XDG_CACHE_HOME": str(home_dir / ".cache"),
"PI_CODING_AGENT_DIR": str(home_dir / ".pi" / "agent"),
"PI_OFFLINE": "1",
"CODEX_HOME": str(home_dir / ".codex"),
"OPENCODE_CONFIG_HOME": str(home_dir / ".config" / "opencode"),
"OPENCODE_DATA_HOME": str(home_dir / ".local" / "share" / "opencode"),
"GEMINI_CLI_HOME": str(home_dir / ".gemini"),
"CLAUDE_CONFIG_DIR": str(home_dir / ".claude"),
"SWARM_SESSION_DIR": str(session_dir),
}
forwarded = [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"GEMINI_API_KEY",
"GOOGLE_API_KEY",
"OPENROUTER_API_KEY",
"OPENCODE_API_KEY",
]
for key in forwarded:
if key in os.environ:
env[key] = os.environ[key]
return env

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't write API keys into visible-launch-manifest.json.

The forwarded provider keys are embedded into shell_command, copied into each task's screen_command, and then persisted in the manifest. That writes live secrets under .swarm/ on disk.

Also applies to: 298-305, 329-340, 364-371

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@docs/session-restore/2026-03-13-launch-alpha/state/launch_live_windows.py.snapshot`
around lines 162 - 206, The code in local_env_exports collects sensitive API
keys into the env dict (forwarded list) which then get embedded into
shell_command/screen_command and persisted to visible-launch-manifest.json;
remove or redact these secrets before any manifest serialization and instead
supply them only at runtime. Modify local_env_exports (and any code that builds
shell_command/screen_command) to: 1) stop placing forwarded keys into any data
structure that is later serialized to disk (omit them from the returned env dict
used for manifest), 2) provide a runtime-only mechanism to inject these keys
when launching processes (e.g., pass them via the process environment directly
or a non-persisted runtime config), and 3) ensure manifest-writing code filters
out or masks keys named in forwarded (OPENAI_API_KEY, ANTHROPIC_API_KEY,
GEMINI_API_KEY, GOOGLE_API_KEY, OPENROUTER_API_KEY, OPENCODE_API_KEY) so they
never appear in visible-launch-manifest.json; apply the same change to the other
similar blocks referenced (lines ~298-305, 329-340, 364-371).

CREATE INDEX idx_artifacts_owner ON artifacts(owner_agent);
CREATE INDEX idx_artifacts_project ON artifacts(project);
CREATE INDEX idx_artifacts_type ON artifacts(artifact_type);
CREATE INDEX idx_artifacts_created ON artifacts(created_at DESCENDING);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Same syntax error: DESCENDINGDESC.

🐛 Proposed fix
-CREATE INDEX idx_artifacts_created ON artifacts(created_at DESCENDING);
+CREATE INDEX idx_artifacts_created ON artifacts(created_at DESC);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CREATE INDEX idx_artifacts_created ON artifacts(created_at DESCENDING);
CREATE INDEX idx_artifacts_created ON artifacts(created_at DESC);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openclaw/openclaw_registry.sql` at line 227, The CREATE INDEX statement for
idx_artifacts_created uses invalid SQL syntax "DESCENDING"; update the index
creation for artifacts on created_at to use the correct direction token "DESC"
(i.e., modify the idx_artifacts_created definition to use created_at DESC).

CREATE INDEX idx_dispatch_trace ON dispatch_records(trace_id);
CREATE INDEX idx_dispatch_task ON dispatch_records(task_id);
CREATE INDEX idx_dispatch_agent ON dispatch_records(selected_agent);
CREATE INDEX idx_dispatch_created ON dispatch_records(created_at DESCENDING);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Same syntax error: DESCENDINGDESC.

🐛 Proposed fix
-CREATE INDEX idx_dispatch_created ON dispatch_records(created_at DESCENDING);
+CREATE INDEX idx_dispatch_created ON dispatch_records(created_at DESC);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CREATE INDEX idx_dispatch_created ON dispatch_records(created_at DESCENDING);
CREATE INDEX idx_dispatch_created ON dispatch_records(created_at DESC);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openclaw/openclaw_registry.sql` at line 233, The CREATE INDEX statement for
idx_dispatch_created on dispatch_records uses the invalid keyword DESCENDING;
update the index definition in the CREATE INDEX statement (the
idx_dispatch_created index on dispatch_records) to use the correct SQL sort
keyword DESC instead of DESCENDING so the statement becomes valid.

Comment on lines +315 to +326
forwarded = [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"GEMINI_API_KEY",
"GOOGLE_API_KEY",
"OPENROUTER_API_KEY",
"OPENCODE_API_KEY",
]
for key in forwarded:
if key in os.environ:
env[key] = os.environ[key]
return env

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't persist forwarded API keys into generated artifacts.

The env dict includes live credentials, render_agent_shell_command() serializes them into a shell string, and that string is then written into the starter scripts and visible-launch-manifest.json. That leaves plaintext secrets under .swarm/ and every prepared worktree. Pass secrets through the tmux/subprocess environment instead, and only record non-sensitive metadata in generated files.

Also applies to: 423-426, 451-485, 580-595

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch_live_windows.py` around lines 315 - 326, The current
code appends live API keys from os.environ into the env dict (the forwarded list
and the env return used by render_agent_shell_command()), which then gets
serialized into starter scripts and visible-launch-manifest.json; remove adding
secrets into the returned env and stop serializing them. Instead, keep only
non-sensitive metadata in env/manifest, and when launching agents (where
render_agent_shell_command(), tmux/subprocess launch code, or any start_agent_*
functions are invoked) inject secrets into the child environment at runtime by
reading os.environ inside the launch path or passing a separate secure_env to
subprocess.Popen/tmux environment APIs; specifically, stop populating the
forwarded keys into env (the forwarded list and the loop that sets env[key] =
os.environ[key]) and ensure render_agent_shell_command() and any code that
writes starter scripts only use the sanitized metadata, while the actual
process-launch code reads secrets directly from os.environ and supplies them to
the process environment without persisting them to disk.

Comment on lines +150 to +155
def ensure_clean_dir(path: pathlib.Path, force: bool) -> None:
if path.exists():
if not force:
raise SystemExit(f"{path} already exists; rerun with --force to replace it.")
shutil.rmtree(path)
path.mkdir(parents=True, exist_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Constrain --name to a direct child of .swarm/.

Line 397 trusts args.name as a path fragment. --name .. --force resolves outside .swarm/, so ensure_clean_dir() can recursively delete the repo root or any other reachable directory.

Suggested fix
+def resolve_run_root(workspace: pathlib.Path, name: str) -> pathlib.Path:
+    swarm_root = (workspace / ".swarm").resolve()
+    run_root = (swarm_root / name).resolve()
+    if run_root.parent != swarm_root:
+        raise SystemExit("--name must be a single directory name under .swarm/")
+    return run_root
+
 ...
-    run_root = workspace / ".swarm" / args.name
+    run_root = resolve_run_root(workspace, args.name)

Also applies to: 396-398

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch.py` around lines 150 - 155, The code currently
trusts args.name as a path fragment which allows escapes like ".." and enables
deleting outside .swarm; validate args.name before using it: ensure it's not
absolute, contains no path separators (e.g. Path(args.name).name == args.name),
and is not "." or "..", then construct the target as swarm_dir / args.name and
pass that safe path to ensure_clean_dir; update the code paths where args.name
is used (e.g., where you build the .swarm child and where ensure_clean_dir(path,
force) is called) to perform this validation first and raise a clear error if
the name is invalid.

Comment on lines +165 to +200
def local_env_exports(root: pathlib.Path, tool: str, session_dir: pathlib.Path) -> dict[str, str]:
home_dir = root / "homes" / tool
for directory in (
home_dir,
home_dir / ".config",
home_dir / ".local" / "share",
home_dir / ".local" / "state",
home_dir / ".cache",
home_dir / ".pi" / "agent",
home_dir / ".codex",
home_dir / ".config" / "opencode",
home_dir / ".local" / "share" / "opencode",
home_dir / ".gemini",
home_dir / ".claude",
session_dir,
):
directory.mkdir(parents=True, exist_ok=True)
env = {
"HOME": str(home_dir),
"XDG_CONFIG_HOME": str(home_dir / ".config"),
"XDG_DATA_HOME": str(home_dir / ".local" / "share"),
"XDG_STATE_HOME": str(home_dir / ".local" / "state"),
"XDG_CACHE_HOME": str(home_dir / ".cache"),
"PI_CODING_AGENT_DIR": str(home_dir / ".pi" / "agent"),
"PI_OFFLINE": "1",
"CODEX_HOME": str(home_dir / ".codex"),
"OPENCODE_CONFIG_HOME": str(home_dir / ".config" / "opencode"),
"OPENCODE_DATA_HOME": str(home_dir / ".local" / "share" / "opencode"),
"GEMINI_CLI_HOME": str(home_dir / ".gemini"),
"CLAUDE_CONFIG_DIR": str(home_dir / ".claude"),
"SWARM_SESSION_DIR": str(session_dir),
}
for key in ("OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", "OPENCODE_API_KEY", "OPENROUTER_API_KEY"):
if key in os.environ:
env[key] = os.environ[key]
return env

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't persist token-bearing launch commands.

local_env_exports() forwards live API keys, render_shell_command() bakes them into the shell string, and Lines 415-440 write/print that string via launch_command and screen_command. That leaks secrets to .swarm/<name>/manifest.json and stdout.

Also applies to: 203-206, 314-325, 415-440

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/cli-swarm/launch.py` around lines 165 - 200, local_env_exports
currently includes live API keys and
render_shell_command/launch_command/screen_command bake them into persisted
shell strings (which are written to manifest.json and stdout), leaking secrets;
to fix, stop embedding secret values: remove API token keys (OPENAI_API_KEY,
ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENCODE_API_KEY, OPENROUTER_API_KEY) from
any data that will be persisted or printed, and instead pass them only at
runtime via process environment or a temp env-file with restrictive permissions.
Concretely, change local_env_exports to exclude token values when the output
will be stored/printed, update
render_shell_command/launch_command/screen_command to reference tokens by name
(e.g., rely on the invoking process env or an env file path) rather than
interpolating their values into the command string, and ensure any manifest.json
or stdout writers redact or omit these keys before writing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant