v1 plan: peer-to-peer support/ticketing built on hero_collab snapshot #1

Closed
opened 2026-04-28 18:42:55 +00:00 by mik-tf · 2 comments
Owner

v1 plan — hero_assistance

Master tracking issue. SSOT for the v1 plan; supersedes prior chat. v1 is production-grade for real customers, not a demo.

§0 Current state (updated session 37, 2026-05-06)

Engineering-complete full-stop. Phase 23 (agentic-calling adoption) closed in s37 — the final pre-customer engineering item per s35's framing. v0.3.1 tagged + auto-published via the s32 release-on-tag CI workflow. Twenty-one locked decisions (D-01..D-13 + D-15..D-21). Open limitations: L-01 (manual config), L-02 (no web gateway), L-05 (macOS no PDEATHSIG), L-07 (cross-host CAP_NET_ADMIN — narrowed s34: wire-shape gap closed, runtime-environment gap stays open). Closed: L-03 + L-04 (s33), L-06 (s34). Test posture: 225 native passing (was 222 → +3 phase23 tests) / 32 _ui_wasm lib / 13 ignored / 0 failed (modulo documented phase10_multi_project_merged_stream_tags_by_project_id StreamMap-yield-order flake — passes in isolation; pre-existing since s12).

Sessions 24–37 — what shipped since the last §0 refresh

  • s24 (Phase 16a) — UI polish foundation. Bootstrap loading via wry::register_uri_scheme_protocol (assist:// scheme + vendored 232 KiB bootstrap.min.css + 484 KiB bootstrap-icons.min.css); typed dioxus-bootstrap-css migration across 7 files; chat composer pattern; paste-reads-real-bytes via Blob.array_buffer(); drag-and-drop image upload; inline image-thumbnail attachments; Linux-screenshot-paste fix. D-18 filed (spec-as-checklist).
  • s25 (Phase 18a) — Vision doc + executable-spec skeleton + sections A–D rows. docs/vision/HERO_ASSISTANCE.md + docs/dev/e2e_checklist.md (90 rows, 41 wired-green at session-end). 4-port browser-deployable demo scheme (8083-8086).
  • s26 (Phase 18b) — Sections E–M rows (90 → 315 rows; 107 wired-green). New docs/dev/architecture.md + docs/dev/testing.md (7-layer pyramid as first-class artefact).
  • s27 (Phase 16b-impl) — visual polish + layout overhaul + animations + Toast + skeleton placeholders + window icon + lifecycle breadcrumbs. 8 work items shipped.
  • s28 (Phase 18c) — Playwright suite + 4-port browser scheme + L2/L4/L5/L6/L7 substrate. D-19 filed locking Playwright vs hero_browser MCP boundary. +50 new test assertions across 4 layers.
  • s29 (Phase 18c-release) — v0.3.0 tagged. 2 Playwright specs un-skipped + 6 SPA L7 baselines + 31 cell flips + 6 new M rows.
  • s30 (Phase 16b-fix) — Rule-6 walkthrough on _app desktop → 4 desktop bug fixes + UI guard for the 5th. 7 desktop findings recorded.
  • s31 (Phase 16b-release-fix) — B5 head-inject CSS fix; persona rename (Acme/Globex → Mycelium/Hero); netcoin scrub via filter-repo; README + 6 docs/ refresh; 35-file collab-inheritance cleanup (~11,700 LoC deleted).
  • s32 (Phase 16b-fix-redux + bonus CI) — B2 N=1→0 deadlock closed (driver-out-of-ProjectTab refactor + auto-pick selected_tab render-loop fix). v0.2.0 tagged. Bonus same-session: CI release-on-tag workflow (issue #6 closed); v0.2.1 tagged + Generic package published.
  • s33 (Phase 21) — L-04 + L-03 closures. users.display_name column + partial UNIQUE INDEX on email != '' + pre-flight duplicate detection + 3 inherited L-03 tests formally #[ignore]d. v0.2.2 tagged.
  • s34 (Phase 22) — L-06 closure. /events GET on TCP listener with per-recipient server-side filter (D-20 privacy invariant) + SDK transport polymorphism (RpcEndpoint/EventsTransport enums + generified envelope_frames). v0.3.1 tagged.
  • s35 (planning-only) — filed #8 "adopt hero_router agentic-calling pattern; replace MCP framing in vision". No source changes. Pipeline state matches s34.
  • s36 (pipeline-maintenance)prompt.md trimmed from 322 lines / 228 KiB to 251 lines / 32 KiB by enforcing its own §1 one-line + §2 pointer rules; verify block byte-preserved. No source change.
  • s37 (Phase 23) — agentic-calling adoption per #8. All 91 OpenRPC methods got summary + structured errors[] + x-hero-intent metadata (schema declared at info.x-hero-intent-schema; consumers tolerate unknown values per open-enum semantics). 3 phase23_* regression tests added (rpc.discover was pre-existing — Phase 4 reduced to "confirm what's wired"). New docs: docs/dev/agentic_calling.md (identity-pinning contract) + docs/dev/agentic_workflows.md (5 example LLM-generated Python scripts; doubles as router-integration fixtures). Vision-doc MCP-language reframed in §1 + §2.5 + §3 + §4.7 + §5.4. D-21 filed locking the agentic-calling contract: hero_router pattern over MCP; FNV-1a-64 (NOT MD5) over UDS rpc.discover; rpc.discover unauthenticated by Hero convention; sandbox MUST set X-Hero-User from human-user identity hero_router authenticated. D-04 + D-20 invariants preserved unchangedphase22_events_tcp_per_user_filter_excludes_non_recipient doubles as agentic-calling privacy regression gate. B.5 caught 3 load-bearing problems before code (FNV vs MD5 / UDS vs HTTP / x-hero-intent axiom-growth). Issue #8 closes.

Open issues / next work

  • #7 (user-driven) — customer-readiness gate (Rule-6 walkthroughs for v0.2.0 + v0.3.1 + L-07 live cross-host smoke + live hero_router round-trip verification per D-21 open follow-up).
  • This issue (#1) — closes when first paying customer signs (M3 / v1.0).

Discretionary forward work (no priority order; pick when relevant)

  • _ui /openrpc.json HTTP proxy (D-21 DX nicety — router uses UDS, so this is dev-ergonomic only). 0.5 session.
  • Tightening x-hero-intent.identity_requirement per-method when customer policy surfaces (today's audit ships everything as enrolled-user; future deployments may want support-role for channel.delete / attachment.cleanup_pending / group.rights.set / etc.).
  • L-02 web gateway closure (customer-demand-gated; coupled to Section J auth-split candidates).
  • L-07 CAP_NET_ADMIN CI lane (rootful container / dedicated runner / netns fixture).
  • D-20 source-bound TCP for EventsTransport::Http (reqwest doesn't expose source-address binding).
  • Lagged-subscriber TCP regression test (D-20 open follow-up).

Closed since s23

  • #2 (UI polish), #3 (E2E test suite), #5 (Phase 18 spec), #6 (CI release-on-tag), #8 (agentic-calling adoption — closed s37).
  • #4 was closed wrong-scope at s24 (auth-split is customer-demand-gated; magic-link-for-everyone is hero_assistance's complete answer per the v1 plan).

Summary

A peer-to-peer support/ticketing system for the Hero stack. Two OpenRPC services in one monorepo, built by snapshot-copying hero_collab and pruning. Many-to-many topology — a client subscribes to multiple project servers; a server hosts multiple users (customers + support agents).

  • hero_assistance_server — one per customer-project. SQLite, OpenRPC, mycelium-bound TCP listener. Owns tickets, threaded messages, image attachments, and presence for that project.
  • Client UI (D-09) — Dioxus 0.7 WASM SPA (hero_assistance_ui_wasm) styled with dioxus-bootstrap-css (Bootstrap 5.3.3). Embeddable as a "Support" tab in any Hero (Dioxus) host app via native component embed; standalone Dioxus desktop binary (hero_assistance_app) hosts the same component graph full-screen for users juggling multiple projects. The inherited hero_assistance_ui Axum/Askama crate (from the collab snapshot) bridges the migration as a dual-mode server (--dist <path> for WASM dist + askama fallback) and owns the /rpc JSON-RPC proxy that the SPA consumes via gloo-net.

Specs (chat 2026-04-27)

  • Tickets with copy-paste image attachments
  • Live presence ("someone is actively looking at this")
  • Many-to-many client/server topology
  • Multi-project view for the support team
  • Mycelium address as project identifier
  • OpenRPC throughout — both ends are servers (peer-to-peer)
  • Customer "just configures what the endpoint is" (manual config of project addresses)

Architecture

Three-layer identity

Layer Identifier Visible to user?
Project mycelium address + display_name Display name only
Device mycelium address (per machine) No — invisible
User email + magic-link auth Yes

A user has one account keyed on email. Multiple devices per user; each generates its own mycelium identity. Server-side device_bindings table maps mycelium_addr → user_id. Sales pre-enrolls customers by email; first connection from any device receives a magic link to bind that device.

Transport

Mycelium-IPv6. Server binds an Axum listener — auto:<port> mode broad-binds [::]:port with IPV6_V6ONLY=1 and the dispatcher enforces peer_addr ∈ 400::/7 (D-08; supersedes D-05's bind-to-TUN clause after the verification sweep showed mycelium 0.7.4's getInfo.nodeSubnet returns a /64 network base, not a host address). On accept, peer_addr.ip()400::/7 is the cryptographically-authenticated peer device address — kernel source-address validation prevents non-mycelium local processes from forging overlay sources, so the predicate is a sufficient cryptographic-attestation gate. Standalone client bundles a mycelium daemon as a subprocess (detects + reuses an existing system mycelium UDS or upstream-TCP API if present; otherwise spawns its own with a per-app data directory + prctl(PR_SET_PDEATHSIG, SIGTERM) on Linux for kernel-level cleanup; identity key generated on first run via OpenOptions::create_new for atomic race-safe creation — Phase 7).

Storage

SQLite, inherited from hero_collab. New tables:

  • users { id, email PRIMARY, display_name, role, status, created_at }
  • device_bindings { mycelium_addr PRIMARY, user_id FK, device_label, bound_at, last_seen }

Existing collab tables retained: messages, threads, attachments, presence — semantically remapped to tickets/comments/etc.

UI (D-09)

Dioxus 0.7 single-page WebAssembly application styled with dioxus-bootstrap-css (Bootstrap 5.3.3, pinned to git development branch). Embedded as a "Support" tab inside other Hero (Dioxus) host apps via native component embed (typed props, shared signals, shared theme/auth, same render tree as host); standalone via the hero_assistance_app Dioxus desktop binary for support agents juggling multiple customer-projects. The browser SPA talks to _ui's Axum server at /rpc via gloo-net JSON-RPC; _ui proxies to _server over UDS exactly as the inherited collab pattern does. The argument for Dioxus over Axum/Askama (which the collab snapshot inherited) is product-fit: hero_assistance is realtime-conversational, embed-as-Support-tab is the primary deployment, the wire schema will evolve over years, and the standalone binary is natural in Dioxus / awkward in Askama. Migration recipe inherits from the dioxus_bootstrap Hero skill (17-repo precedent provides tooling and confidence; not the argument for adoption). Visual regression policy: <1% perceptual diff via canary-wait + 3-run + pixelmatch against captured Askama baselines.

Crate layout

hero_assistance/
├── crates/
│   ├── hero_assistance_server/    binary  — per-project OpenRPC server (SQLite, fanout, attachments, mycelium-bound)
│   ├── hero_assistance_ui/        binary  — inherited Axum/Askama crate; dual-mode server (`--dist` for WASM dist, askama fallback) + `/rpc` proxy
│   ├── hero_assistance_ui_wasm/   library — Dioxus 0.7 + dioxus-bootstrap-css WASM SPA (Phase 8); reusable as Support-tab embed
│   ├── hero_assistance_app/       binary  — standalone Dioxus desktop (Phase 11); hosts `_ui_wasm` full-screen + bundles mycelium daemon
│   ├── hero_assistance_sdk/       library — typed RPC client, generated from server's OpenRPC spec; hosts `mycelium_runtime` helper (Phase 7)
│   ├── hero_assistance_examples/  library — SDK-call demos (health, basic_usage, load_test)
│   └── hero_assistance/           binary  — CLI lifecycle wrapper (--start/--stop/--status), hero_proc-managed services
└── Cargo.toml

Per D-09: D-03's _client slot becomes _ui_wasm; _core slot is not materialized (typed shared types live in _sdk and the OpenRPC spec; no other consumer warrants a separate crate); _app slot is restored as a Dioxus desktop binary; new _ui slot kept from the collab snapshot as the migration bridge.

Locked decisions

  • D-01 Path: snapshot-copy hero_collab → hero_assistance. Prune huddles, canvases, reactions, mentions, voice. No fork tracking — all our code, snapshot-and-diverge.
  • D-02 Storage: SQLite, inherited from collab. No rewrite.
  • D-03 Crate layout: Cargo workspace + library + thin standalone wrapper for the client side; server/sdk/CLI separation. Slot names amended by D-09 (_client_ui_wasm, no _core, _app restored as Dioxus desktop, new _ui Axum bridge slot from collab snapshot).
  • D-04 Three-layer identity: project / device / user. CallerIdentity{user_id, device_addr, role} newtype from day 1. Identity boundary at hero_collab main.rs:210–249; ~15 call sites updated.
  • D-05 Transport: mycelium-IPv6 binding (verified). Standalone client bundles mycelium daemon. Bind-to-TUN clause superseded by D-08 after verification sweep showed getInfo.nodeSubnet returns a /64 network base, not a host address; daemon-bundling and threat model stand.
  • D-06 Phase 3 identity scaffolding: defer device-binding writes to Phase 4; hash magic-link tokens with SHA-256 (15-min TTL, single-use, constant-time compare); deprecate proxy-header path.
  • D-07 Phase 4 mycelium-IPv6 binding: dual listener (UDS + literal [ipv6]:port TCP); rebind-on-conflict for device_bindings; peer_addr canonicalized via Ipv6Addr::to_string(); peer_addr threaded separately from caller; mycelium_sdk workspace dep deferred.
  • D-08 Phase 5 overlay-bind-and-filter: auto:<port> broad-binds [::]:port with IPV6_V6ONLY=1; dispatcher enforces peer_addr ∈ 400::/7. Supersedes D-05's bind-to-TUN clause. is_mycelium_overlay() predicate; enforce_overlay_peer AppState flag. Phase 6 design notes for spawn-own (PDEATHSIG, O_CREAT|O_EXCL key-gen) recorded for inheritance — now Phase 7's design constraints.
  • D-09 UI architecture: Dioxus 0.7 WASM SPA via dioxus-bootstrap-css; dual-mode Axum _ui retains existing crate as fallback + /rpc proxy; amends D-03 crate slots. Migration recipe is the dioxus_bootstrap Hero skill (canonical pattern, 17-repo precedent).
  • D-10 channels→tickets / messages→comments rename via additive RPC aliasing: ticket.* and comment.* are added as new dispatcher arms + spec method blocks pointing at the same handlers::channel::* / handlers::message::* functions; tables, handler module symbols, and CSS class names stay named per the inherited collab snapshot until the post-v1 cleanup that drops askama_templates. The inherited chat-app.js keeps calling channel.*/message.*.
  • D-11 multi-project subscription aggregator location: lives in _sdk (per-device merge), not _server (server-side fan-in proxy) — preserves D-04's per-project identity boundary, no new server-side trust boundary. MultiProjectClient wraps N rpc.sock + N events.sock UDS readers and merges via tokio_stream::StreamMap keyed by caller-supplied opaque ProjectId.
  • D-12 presence viewing_ticket_id schema: new wire RPC presence.set_viewing_ticket { ticket_id: u64 \| null } + new UserEvent::PresenceViewingChannel { user_id, channel_id: Option<u64> } fanout variant + new viewing_channel_id JSON field on users.data — additive only. Server-side existence check on non-null ticket_id (SELECT 1 FROM channels) returning NotFound (-32003) is the load-bearing referential-integrity gate.
  • D-13 _app shell = dioxus desktop: D-09 §"Deferred questions" bias applied (Tauri rejected for the JS-bridge contradiction with D-09 §"Argument" pt 5). Phase 11 deliberately does NOT consume _ui_wasm as a path-dep (its gloo-net-driven RPC is browser-only); cross-target consumption + desktop RPC plumbing land at Phase 13/14.
  • D-15 Phase 12b RPC-smoke scope: RPC-only cross-host with cross-daemon-peer attestation (load-bearing assertion: source-address-bound tokio::net::TcpSocket + device_bindings.mycelium_addr SQLite read on server B = canonical <overlay_a>, NOT loopback). Events-over-TCP transport deferred to Phase 12c per L-06. Sibling spawn_own_with_options (not breaking). Test #[ignore]-by-default per L-07.

Limitations accepted

  • L-01: Manual configuration of project endpoints (matches spec). Embedded clients pre-fill.
  • L-02: Customers behind firewalls blocking mycelium can't connect in v1. Web gateway is post-v1.
  • L-03: 3 inherited integration-test failures (rate-limit, federation, cleanup) — narrowed from 4 by Phase 2 prune; carried as known until Phase 12 or later.
  • L-04: users table missing UNIQUE(email) + display_name (D-04 schema gaps deferred from Phase 3); may bundle with Phase 9b chat.html migration if display names render, otherwise post-v1.
  • L-05 (landed in Phase 7, commit 119eabd): macOS PDEATHSIG gap — mycelium_runtime::spawn_own returns Err(SpawnOwnError::UnsupportedPlatform) on macOS/Windows since PR_SET_PDEATHSIG is Linux-only. detect() still works on those platforms, so users running a system mycelium daemon are unaffected; only fresh-install macOS without a system mycelium hits the gap. Loud-error chosen over silent-orphan-on-force-kill. kqueue-based parent-watcher noted as post-v1 future work.
  • L-06 (landed in Phase 12b, commit 70f0cf5): Events fanout transport is UDS-only — _server's TCP listener serves only /rpc (no /events SSE/WebSocket counterpart); MultiProjectClient::register is UDS-only on both rpc and events sockets. Cross-host MultiProjectClient blocked at multiple layers; single-host story unaffected. Phase 12c (post-v1) ships server-side /events SSE + SDK TCP-events-stream.
  • L-07 (landed in Phase 12b, commit 70f0cf5): Phase 12b cross-host smoke test is #[ignore]-by-default — spawning a real mycelium daemon needs CAP_NET_ADMIN for TUN creation, which CI does not grant. Hermetic SDK unit tests DO run in default CI. Live cross-host smoke is a release-runbook procedure: sudo setcap cap_net_admin+ep $(which mycelium); cargo test -- --ignored phase12b.

Explicitly deferred — NOT in v1

None of these were in the original spec; all are real, all post-v1:

  • Public Forgejo registry for project discovery
  • Status URLs / health badges
  • SQLite FTS5 "similar tickets" search
  • Tickets-as-markdown auto-export to private backup repos
  • Deep-link URL scheme (hero-assist://...)
  • Auto-pending approval queue with role gating
  • Invite tokens / signed enrollment links
  • Web gateway for zero-install access
  • AI summarization / auto-categorization
  • Mycelium pub-sub broadcasts
  • Cross-project federation
  • Multilingual auto-translation
  • Removal of the Askama feature from _ui post-migration (default-on through v1 ship; cleanup tracked separately)
  • Tauri or alternate-shell migration if dioxus desktop runs short on platform features

Phase plan (renumbered 6→14 per D-09 §"Phase plan after D-09")

# Phase Status Commit
0 Workspace bootstrap (ai-pipeline /bootstrap); D-01..D-05 + L-01..L-02 written; initial repo skeleton committed Done d56bb5d
1 Snapshot hero_collab → hero_assistance; rename packages, paths, references; baseline build green Done bc06df1
2 Prune huddles, canvases, reactions, mentions, Dioxus admin app; tests still pass Done e3fee9a
3 Identity layer: CallerIdentity newtype, users + device_bindings schema, magic-link enrollment RPC Done c7acf2e
4 Mycelium transport: dual listener (UDS + literal TCP), peer_addr extraction, device_bindings upsert Done a9df3b7
5 auto:<port> overlay-bind + dispatcher-level 400::/7 filter (supersedes D-05 bind-to-TUN clause) Done 5c4e8b6
6 UI architecture commitment (D-09 Dioxus WASM SPA) + propagation across docs (docs-only) Done 5eec381
7 mycelium_runtime helper in _sdk (detect-or-spawn with PDEATHSIG + atomic key-gen) + L-05 macOS gap Done 119eabd
8 _ui_wasm crate skeleton + dual-mode _ui server (--dist flag + askama-as-optional-feature) Done b6d9518
9a Migrate index.html (763 lines) → Dioxus components; capture Askama visual-regression baselines Done d492f58
9b Migrate chat.html body → Dioxus TicketsPage; channels→tickets / message→comment via additive RPC aliasing (D-10) Done 0756f4b + 1674b05
9c Migrate editor.html (429 lines) → Dioxus EditorPage + editor canary drift fix Done 8b50bdc
10 Multi-project subscription aggregator (MultiProjectClient in _sdk) + presence.set_viewing_ticket RPC + PresenceViewingChannel fanout variant (D-11 + D-12) Done c8dc6b4 + 9c3c4e7
11 hero_assistance_app standalone Dioxus desktop binary (D-13; consumes MultiProjectClient; env-var subscriptions) Done 370c0dd
12a Single-host RPC-driven E2E aggregator coverage (3 phase12a_* integration tests; order-independent wait_for_two_merged helper) Done 2f29be6
12b Cross-host RPC smoke (D-15 Scope A) — MyceliumOptions + cross-daemon-peer attestation; events-over-TCP deferred (L-06); #[ignore]-by-default (L-07) Done 70f0cf5 + 8fda9c5
13a UX validation gate (Rule 6) — demo orchestrator + seed binary (acme + globex; deterministic two-project demo) Done 5515bf2
13b UX validation gate (Rule 6) — walkthrough findings recorded (8 findings + 11 happy-path checkpoints; Phase 14 punch-list) Done 5ca7cfb
14 Polish (Phase 13b punch-list) + README + operator runbook + v0.1.0 release Done d474b84 (source) + 62962cd (docs); tag v0.1.0
15a Transport polymorphism in _sdkRpcTransport trait + UDS/TCP/gloo-net impls; refactor HeroAssistanceClient to be transport-generic; foundation for embed-ready architecture 🚧 Next
15b Embed-ready component contract + magic-link enrollment + project-config dialog pending
15c Ticket UI — list / detail / threaded comment composer / image-paste / live-update / presence pending
15d _app consumes _ui_wasm (path-dep, desktop feature) + Rule-6 walkthrough gate pending
15-release v0.2.0 release tag + acceptance-criteria sign-off pending

Done: phases 0-14 (v0.1.0 tagged). Remaining: Phase 15a-d + 15-release (v0.2.0 — customer-facing UI). Phase 15 is ~6-9 sessions; closes acceptance criteria 2-8 from the list below. Post-v1 backlog after v0.2.0: web gateway (L-02), events-over-TCP transport (L-06; closure path simplified by Phase 15a foundation), full D-10 storage rename, mobile, AI-anything (all explicitly deferred).

v1 acceptance criteria

Demonstrable on real cross-host machines.

# Criterion v0.1.0 status v0.2.0 (Phase 15) target
1 Customer installs _app without separately installing mycelium Met (via mycelium_runtime::spawn_own, Linux only per L-05) Met
2 Customer enters mycelium address + email, gets magic link, device binds ⚠️ Backend works; no UI in _app 🎯 Phase 15b lands the enrollment screen
3 Customer files a ticket with pasted image ⚠️ Backend works; no UI 🎯 Phase 15c lands ticket-create + image-paste
4 Support agent on different machine sees ticket appear live ⚠️ Backend works; no UI 🎯 Phase 15c lands live-update via events.sock
5 Both sides see "actively looking at this" presence ⚠️ Backend works (presence.set_viewing_ticket); no UI 🎯 Phase 15c lands presence indicator
6 Agent replies; customer sees reply live ⚠️ Backend works; no UI 🎯 Phase 15c lands comment composer + live-update
7 Customer adds second project; both tickets appear ⚠️ MultiProjectClient works; env-var only 🎯 Phase 15b lands in-app config dialog
8 Agent subscribes to all team projects; consolidated cross-project list ⚠️ Aggregator left pane shows event names; right pane is placeholder 🎯 Phase 15d wires _ui_wasm::App into _app right pane
9 No TLS required; plain HTTP over mycelium IPv6 works Met (Phase 4/5/12b) Met
10 Visual regression <1% on migrated pages Met (all 9 page-viewport combinations) Met (new baselines for _app right pane in 15d)
11 All tests pass; kickstart.sh exits 0 Met (184 passing / 3 L-03 / 8 ignored; kickstart 147/0) Met

v0.1.0 closes 4 of 11. Phase 15 closes the remaining 7.

Risks / known unknowns

  • Bundled mycelium binary size — adds ~10–30 MB to installer. Acceptable for desktop; matters for mobile (post-v1).
  • Per-device resource use — each client runs a mycelium relay node by design. Small but non-zero CPU/network overhead. Measure before claiming "free."
  • Firewall compatibility — corporate networks may block mycelium. Captured as L-02; web gateway is the post-v1 mitigation.
  • Mycelium upstream version tracking — bundled daemon needs an update process. Establish before shipping.
  • Threat model rests on mycelium daemon trust at host level. Compromised daemon could forge packets. Standard overlay-as-trust-boundary assumption.
  • Dioxus 0.7 pre-1.0 churndioxus-bootstrap-css pinned to git development branch absorbs API drift; we accept the rebase tax. Bounded; not architectural.
  • WASM cold-start weight — ~1MB compressed; real on first Support-tab open, cached subsequently. Mitigatable with code-splitting or Dioxus SSR pre-render if telemetry flags it. Acceptable for v1.
  • chat.html migration scope — 1,821 lines is the single largest migration risk; Phase 9b allocated dedicated, with split-into-9b.1+9b.2 as a fallback if the diff blows up.
  • macOS PDEATHSIG gap (L-05, Phase 7) — Linux-only prctl; macOS _app may orphan a mycelium child on force-kill. kqueue parent-watcher is out of scope for v1.

Methodology

This project uses the ai-pipeline v1 multi-session workflow. Local workspace at /home/pctwo/Documents/temp/assistance_work/. Each phase above corresponds to one or more sessions; prompt.md §3 carries the next-session entry point. UI migration phases (8–9c) inherit from the dioxus_bootstrap Hero skill (canonical recipe; 17-repo precedent for tooling and confidence). Visual regression uses hero_browser MCP for screenshots + pixelmatch for diff.

Branching & commits

Hero conventions: default branch development; feature branches development_<name> (underscores); squash-merge back to development. Conventional-commit messages with absolute issue URL reference; no AI attribution.

# v1 plan — hero_assistance Master tracking issue. SSOT for the v1 plan; supersedes prior chat. v1 is production-grade for real customers, not a demo. ## §0 Current state (updated session 37, 2026-05-06) **Engineering-complete full-stop.** Phase 23 (agentic-calling adoption) closed in s37 — the final pre-customer engineering item per s35's framing. v0.3.1 tagged + auto-published via the s32 release-on-tag CI workflow. **Twenty-one locked decisions (D-01..D-13 + D-15..D-21).** Open limitations: L-01 (manual config), L-02 (no web gateway), L-05 (macOS no PDEATHSIG), L-07 (cross-host CAP_NET_ADMIN — narrowed s34: wire-shape gap closed, runtime-environment gap stays open). Closed: L-03 + L-04 (s33), L-06 (s34). Test posture: **225 native passing** (was 222 → +3 phase23 tests) / 32 `_ui_wasm` lib / 13 ignored / 0 failed (modulo documented `phase10_multi_project_merged_stream_tags_by_project_id` StreamMap-yield-order flake — passes in isolation; pre-existing since s12). ### Sessions 24–37 — what shipped since the last §0 refresh - **s24 (Phase 16a)** — UI polish foundation. Bootstrap loading via `wry::register_uri_scheme_protocol` (`assist://` scheme + vendored 232 KiB `bootstrap.min.css` + 484 KiB `bootstrap-icons.min.css`); typed `dioxus-bootstrap-css` migration across 7 files; chat composer pattern; paste-reads-real-bytes via `Blob.array_buffer()`; drag-and-drop image upload; inline image-thumbnail attachments; Linux-screenshot-paste fix. D-18 filed (spec-as-checklist). - **s25 (Phase 18a)** — Vision doc + executable-spec skeleton + sections A–D rows. `docs/vision/HERO_ASSISTANCE.md` + `docs/dev/e2e_checklist.md` (90 rows, 41 wired-green at session-end). 4-port browser-deployable demo scheme (8083-8086). - **s26 (Phase 18b)** — Sections E–M rows (90 → 315 rows; 107 wired-green). New `docs/dev/architecture.md` + `docs/dev/testing.md` (7-layer pyramid as first-class artefact). - **s27 (Phase 16b-impl)** — visual polish + layout overhaul + animations + Toast + skeleton placeholders + window icon + lifecycle breadcrumbs. 8 work items shipped. - **s28 (Phase 18c)** — Playwright suite + 4-port browser scheme + L2/L4/L5/L6/L7 substrate. D-19 filed locking Playwright vs hero_browser MCP boundary. +50 new test assertions across 4 layers. - **s29 (Phase 18c-release)** — v0.3.0 tagged. 2 Playwright specs un-skipped + 6 SPA L7 baselines + 31 cell flips + 6 new M rows. - **s30 (Phase 16b-fix)** — Rule-6 walkthrough on `_app` desktop → 4 desktop bug fixes + UI guard for the 5th. 7 desktop findings recorded. - **s31 (Phase 16b-release-fix)** — B5 head-inject CSS fix; persona rename (Acme/Globex → Mycelium/Hero); netcoin scrub via filter-repo; README + 6 docs/ refresh; 35-file collab-inheritance cleanup (~11,700 LoC deleted). - **s32 (Phase 16b-fix-redux + bonus CI)** — B2 N=1→0 deadlock closed (driver-out-of-`ProjectTab` refactor + auto-pick `selected_tab` render-loop fix). v0.2.0 tagged. **Bonus same-session:** CI release-on-tag workflow ([issue #6](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/6) closed); v0.2.1 tagged + Generic package published. - **s33 (Phase 21)** — L-04 + L-03 closures. `users.display_name` column + partial UNIQUE INDEX on `email != ''` + pre-flight duplicate detection + 3 inherited L-03 tests formally `#[ignore]`d. v0.2.2 tagged. - **s34 (Phase 22)** — L-06 closure. `/events` GET on TCP listener with per-recipient server-side filter (D-20 privacy invariant) + SDK transport polymorphism (`RpcEndpoint`/`EventsTransport` enums + generified `envelope_frames`). v0.3.1 tagged. - **s35 (planning-only)** — filed [#8](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/8) "adopt hero_router agentic-calling pattern; replace MCP framing in vision". No source changes. Pipeline state matches s34. - **s36 (pipeline-maintenance)** — `prompt.md` trimmed from 322 lines / 228 KiB to 251 lines / 32 KiB by enforcing its own §1 one-line + §2 pointer rules; verify block byte-preserved. No source change. - **s37 (Phase 23)** — agentic-calling adoption per [#8](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/8). All 91 OpenRPC methods got `summary` + structured `errors[]` + `x-hero-intent` metadata (schema declared at `info.x-hero-intent-schema`; consumers tolerate unknown values per open-enum semantics). 3 `phase23_*` regression tests added (`rpc.discover` was pre-existing — Phase 4 reduced to "confirm what's wired"). New docs: `docs/dev/agentic_calling.md` (identity-pinning contract) + `docs/dev/agentic_workflows.md` (5 example LLM-generated Python scripts; doubles as router-integration fixtures). Vision-doc MCP-language reframed in §1 + §2.5 + §3 + §4.7 + §5.4. **D-21 filed** locking the agentic-calling contract: hero_router pattern over MCP; FNV-1a-64 (NOT MD5) over UDS rpc.discover; rpc.discover unauthenticated by Hero convention; sandbox MUST set `X-Hero-User` from human-user identity hero_router authenticated. **D-04 + D-20 invariants preserved unchanged** — `phase22_events_tcp_per_user_filter_excludes_non_recipient` doubles as agentic-calling privacy regression gate. B.5 caught 3 load-bearing problems before code (FNV vs MD5 / UDS vs HTTP / x-hero-intent axiom-growth). Issue #8 closes. ### Open issues / next work - **[#7](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/7) (user-driven)** — customer-readiness gate (Rule-6 walkthroughs for v0.2.0 + v0.3.1 + L-07 live cross-host smoke + live hero_router round-trip verification per D-21 open follow-up). - **This issue (#1)** — closes when first paying customer signs (M3 / v1.0). ### Discretionary forward work (no priority order; pick when relevant) - `_ui` `/openrpc.json` HTTP proxy (D-21 DX nicety — router uses UDS, so this is dev-ergonomic only). 0.5 session. - Tightening `x-hero-intent.identity_requirement` per-method when customer policy surfaces (today's audit ships everything as `enrolled-user`; future deployments may want `support-role` for `channel.delete` / `attachment.cleanup_pending` / `group.rights.set` / etc.). - L-02 web gateway closure (customer-demand-gated; coupled to Section J auth-split candidates). - L-07 CAP_NET_ADMIN CI lane (rootful container / dedicated runner / netns fixture). - D-20 source-bound TCP for `EventsTransport::Http` (reqwest doesn't expose source-address binding). - Lagged-subscriber TCP regression test (D-20 open follow-up). ### Closed since s23 - [#2](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/2) (UI polish), [#3](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/3) (E2E test suite), [#5](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/5) (Phase 18 spec), [#6](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/6) (CI release-on-tag), [#8](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/8) (agentic-calling adoption — closed s37). - [#4](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/4) was closed wrong-scope at s24 (auth-split is customer-demand-gated; magic-link-for-everyone is hero_assistance's complete answer per the v1 plan). ## Summary A peer-to-peer support/ticketing system for the Hero stack. Two OpenRPC services in one monorepo, built by snapshot-copying hero_collab and pruning. Many-to-many topology — a client subscribes to multiple project servers; a server hosts multiple users (customers + support agents). - **`hero_assistance_server`** — one per customer-project. SQLite, OpenRPC, mycelium-bound TCP listener. Owns tickets, threaded messages, image attachments, and presence for that project. - **Client UI (D-09)** — Dioxus 0.7 WASM SPA (`hero_assistance_ui_wasm`) styled with `dioxus-bootstrap-css` (Bootstrap 5.3.3). Embeddable as a "Support" tab in any Hero (Dioxus) host app via native component embed; standalone Dioxus desktop binary (`hero_assistance_app`) hosts the same component graph full-screen for users juggling multiple projects. The inherited `hero_assistance_ui` Axum/Askama crate (from the collab snapshot) bridges the migration as a dual-mode server (`--dist <path>` for WASM dist + askama fallback) and owns the `/rpc` JSON-RPC proxy that the SPA consumes via `gloo-net`. ## Specs (chat 2026-04-27) - Tickets with copy-paste image attachments - Live presence ("someone is actively looking at this") - Many-to-many client/server topology - Multi-project view for the support team - Mycelium address as project identifier - OpenRPC throughout — both ends are servers (peer-to-peer) - Customer "just configures what the endpoint is" (manual config of project addresses) ## Architecture ### Three-layer identity | Layer | Identifier | Visible to user? | |---|---|---| | Project | mycelium address + display_name | Display name only | | Device | mycelium address (per machine) | No — invisible | | User | email + magic-link auth | Yes | A user has one account keyed on email. Multiple devices per user; each generates its own mycelium identity. Server-side `device_bindings` table maps mycelium_addr → user_id. Sales pre-enrolls customers by email; first connection from any device receives a magic link to bind that device. ### Transport Mycelium-IPv6. Server binds an Axum listener — `auto:<port>` mode broad-binds `[::]:port` with `IPV6_V6ONLY=1` and the dispatcher enforces `peer_addr ∈ 400::/7` (D-08; supersedes D-05's bind-to-TUN clause after the verification sweep showed mycelium 0.7.4's `getInfo.nodeSubnet` returns a /64 network base, not a host address). On accept, `peer_addr.ip()` ∈ `400::/7` is the cryptographically-authenticated peer device address — kernel source-address validation prevents non-mycelium local processes from forging overlay sources, so the predicate is a sufficient cryptographic-attestation gate. Standalone client bundles a mycelium daemon as a subprocess (detects + reuses an existing system mycelium UDS or upstream-TCP API if present; otherwise spawns its own with a per-app data directory + `prctl(PR_SET_PDEATHSIG, SIGTERM)` on Linux for kernel-level cleanup; identity key generated on first run via `OpenOptions::create_new` for atomic race-safe creation — Phase 7). ### Storage SQLite, inherited from hero_collab. New tables: - `users { id, email PRIMARY, display_name, role, status, created_at }` - `device_bindings { mycelium_addr PRIMARY, user_id FK, device_label, bound_at, last_seen }` Existing collab tables retained: messages, threads, attachments, presence — semantically remapped to tickets/comments/etc. ### UI (D-09) Dioxus 0.7 single-page WebAssembly application styled with `dioxus-bootstrap-css` (Bootstrap 5.3.3, pinned to git `development` branch). Embedded as a "Support" tab inside other Hero (Dioxus) host apps via native component embed (typed props, shared signals, shared theme/auth, same render tree as host); standalone via the `hero_assistance_app` Dioxus desktop binary for support agents juggling multiple customer-projects. The browser SPA talks to `_ui`'s Axum server at `/rpc` via `gloo-net` JSON-RPC; `_ui` proxies to `_server` over UDS exactly as the inherited collab pattern does. The argument for Dioxus over Axum/Askama (which the collab snapshot inherited) is product-fit: hero_assistance is realtime-conversational, embed-as-Support-tab is the primary deployment, the wire schema will evolve over years, and the standalone binary is natural in Dioxus / awkward in Askama. Migration recipe inherits from the `dioxus_bootstrap` Hero skill (17-repo precedent provides tooling and confidence; not the argument for adoption). Visual regression policy: `<1%` perceptual diff via canary-wait + 3-run + pixelmatch against captured Askama baselines. ### Crate layout ``` hero_assistance/ ├── crates/ │ ├── hero_assistance_server/ binary — per-project OpenRPC server (SQLite, fanout, attachments, mycelium-bound) │ ├── hero_assistance_ui/ binary — inherited Axum/Askama crate; dual-mode server (`--dist` for WASM dist, askama fallback) + `/rpc` proxy │ ├── hero_assistance_ui_wasm/ library — Dioxus 0.7 + dioxus-bootstrap-css WASM SPA (Phase 8); reusable as Support-tab embed │ ├── hero_assistance_app/ binary — standalone Dioxus desktop (Phase 11); hosts `_ui_wasm` full-screen + bundles mycelium daemon │ ├── hero_assistance_sdk/ library — typed RPC client, generated from server's OpenRPC spec; hosts `mycelium_runtime` helper (Phase 7) │ ├── hero_assistance_examples/ library — SDK-call demos (health, basic_usage, load_test) │ └── hero_assistance/ binary — CLI lifecycle wrapper (--start/--stop/--status), hero_proc-managed services └── Cargo.toml ``` Per D-09: D-03's `_client` slot becomes `_ui_wasm`; `_core` slot is **not materialized** (typed shared types live in `_sdk` and the OpenRPC spec; no other consumer warrants a separate crate); `_app` slot is **restored** as a Dioxus desktop binary; new `_ui` slot kept from the collab snapshot as the migration bridge. ## Locked decisions - **D-01 Path:** snapshot-copy hero_collab → hero_assistance. Prune huddles, canvases, reactions, mentions, voice. No fork tracking — all our code, snapshot-and-diverge. - **D-02 Storage:** SQLite, inherited from collab. No rewrite. - **D-03 Crate layout:** Cargo workspace + library + thin standalone wrapper for the client side; server/sdk/CLI separation. **Slot names amended by D-09** (`_client`→`_ui_wasm`, no `_core`, `_app` restored as Dioxus desktop, new `_ui` Axum bridge slot from collab snapshot). - **D-04 Three-layer identity:** project / device / user. `CallerIdentity{user_id, device_addr, role}` newtype from day 1. Identity boundary at hero_collab `main.rs:210–249`; ~15 call sites updated. - **D-05 Transport:** mycelium-IPv6 binding (verified). Standalone client bundles mycelium daemon. **Bind-to-TUN clause superseded by D-08** after verification sweep showed `getInfo.nodeSubnet` returns a /64 network base, not a host address; daemon-bundling and threat model stand. - **D-06 Phase 3 identity scaffolding:** defer device-binding writes to Phase 4; hash magic-link tokens with SHA-256 (15-min TTL, single-use, constant-time compare); deprecate proxy-header path. - **D-07 Phase 4 mycelium-IPv6 binding:** dual listener (UDS + literal `[ipv6]:port` TCP); rebind-on-conflict for `device_bindings`; `peer_addr` canonicalized via `Ipv6Addr::to_string()`; `peer_addr` threaded separately from `caller`; `mycelium_sdk` workspace dep deferred. - **D-08 Phase 5 overlay-bind-and-filter:** `auto:<port>` broad-binds `[::]:port` with `IPV6_V6ONLY=1`; dispatcher enforces `peer_addr ∈ 400::/7`. Supersedes D-05's bind-to-TUN clause. `is_mycelium_overlay()` predicate; `enforce_overlay_peer` AppState flag. Phase 6 design notes for spawn-own (PDEATHSIG, `O_CREAT|O_EXCL` key-gen) recorded for inheritance — now Phase 7's design constraints. - **D-09 UI architecture:** Dioxus 0.7 WASM SPA via `dioxus-bootstrap-css`; dual-mode Axum `_ui` retains existing crate as fallback + `/rpc` proxy; amends D-03 crate slots. Migration recipe is the `dioxus_bootstrap` Hero skill (canonical pattern, 17-repo precedent). - **D-10 channels→tickets / messages→comments rename via additive RPC aliasing:** `ticket.*` and `comment.*` are added as new dispatcher arms + spec method blocks pointing at the same `handlers::channel::*` / `handlers::message::*` functions; tables, handler module symbols, and CSS class names stay named per the inherited collab snapshot until the post-v1 cleanup that drops `askama_templates`. The inherited `chat-app.js` keeps calling `channel.*`/`message.*`. - **D-11 multi-project subscription aggregator location:** lives in `_sdk` (per-device merge), not `_server` (server-side fan-in proxy) — preserves D-04's per-project identity boundary, no new server-side trust boundary. `MultiProjectClient` wraps N rpc.sock + N events.sock UDS readers and merges via `tokio_stream::StreamMap` keyed by caller-supplied opaque `ProjectId`. - **D-12 presence `viewing_ticket_id` schema:** new wire RPC `presence.set_viewing_ticket { ticket_id: u64 \| null }` + new `UserEvent::PresenceViewingChannel { user_id, channel_id: Option<u64> }` fanout variant + new `viewing_channel_id` JSON field on `users.data` — additive only. Server-side existence check on non-null `ticket_id` (`SELECT 1 FROM channels`) returning NotFound (-32003) is the load-bearing referential-integrity gate. - **D-13 `_app` shell = `dioxus desktop`:** D-09 §"Deferred questions" bias applied (Tauri rejected for the JS-bridge contradiction with D-09 §"Argument" pt 5). Phase 11 deliberately does NOT consume `_ui_wasm` as a path-dep (its `gloo-net`-driven RPC is browser-only); cross-target consumption + desktop RPC plumbing land at Phase 13/14. - **D-15 Phase 12b RPC-smoke scope:** RPC-only cross-host with cross-daemon-peer attestation (load-bearing assertion: source-address-bound `tokio::net::TcpSocket` + `device_bindings.mycelium_addr` SQLite read on server B = canonical `<overlay_a>`, NOT loopback). Events-over-TCP transport deferred to Phase 12c per L-06. Sibling `spawn_own_with_options` (not breaking). Test `#[ignore]`-by-default per L-07. ## Limitations accepted - **L-01:** Manual configuration of project endpoints (matches spec). Embedded clients pre-fill. - **L-02:** Customers behind firewalls blocking mycelium can't connect in v1. Web gateway is post-v1. - **L-03:** 3 inherited integration-test failures (rate-limit, federation, cleanup) — narrowed from 4 by Phase 2 prune; carried as known until Phase 12 or later. - **L-04:** `users` table missing `UNIQUE(email)` + `display_name` (D-04 schema gaps deferred from Phase 3); may bundle with Phase 9b `chat.html` migration if display names render, otherwise post-v1. - **L-05** *(landed in Phase 7, commit `119eabd`)*: macOS PDEATHSIG gap — `mycelium_runtime::spawn_own` returns `Err(SpawnOwnError::UnsupportedPlatform)` on macOS/Windows since `PR_SET_PDEATHSIG` is Linux-only. `detect()` still works on those platforms, so users running a system mycelium daemon are unaffected; only fresh-install macOS without a system mycelium hits the gap. Loud-error chosen over silent-orphan-on-force-kill. kqueue-based parent-watcher noted as post-v1 future work. - **L-06** *(landed in Phase 12b, commit `70f0cf5`)*: Events fanout transport is UDS-only — `_server`'s TCP listener serves only `/rpc` (no `/events` SSE/WebSocket counterpart); `MultiProjectClient::register` is UDS-only on both rpc and events sockets. Cross-host `MultiProjectClient` blocked at multiple layers; single-host story unaffected. Phase 12c (post-v1) ships server-side `/events` SSE + SDK TCP-events-stream. - **L-07** *(landed in Phase 12b, commit `70f0cf5`)*: Phase 12b cross-host smoke test is `#[ignore]`-by-default — spawning a real mycelium daemon needs `CAP_NET_ADMIN` for TUN creation, which CI does not grant. Hermetic SDK unit tests DO run in default CI. Live cross-host smoke is a release-runbook procedure: `sudo setcap cap_net_admin+ep $(which mycelium); cargo test -- --ignored phase12b`. ## Explicitly deferred — NOT in v1 None of these were in the original spec; all are real, all post-v1: - Public Forgejo registry for project discovery - Status URLs / health badges - SQLite FTS5 "similar tickets" search - Tickets-as-markdown auto-export to private backup repos - Deep-link URL scheme (`hero-assist://...`) - Auto-pending approval queue with role gating - Invite tokens / signed enrollment links - Web gateway for zero-install access - AI summarization / auto-categorization - Mycelium pub-sub broadcasts - Cross-project federation - Multilingual auto-translation - Removal of the Askama feature from `_ui` post-migration (default-on through v1 ship; cleanup tracked separately) - Tauri or alternate-shell migration if `dioxus desktop` runs short on platform features ## Phase plan (renumbered 6→14 per D-09 §"Phase plan after D-09") | # | Phase | Status | Commit | |---|---|---|---| | 0 | Workspace bootstrap (ai-pipeline `/bootstrap`); D-01..D-05 + L-01..L-02 written; initial repo skeleton committed | ✅ Done | `d56bb5d` | | 1 | Snapshot hero_collab → hero_assistance; rename packages, paths, references; baseline build green | ✅ Done | `bc06df1` | | 2 | Prune huddles, canvases, reactions, mentions, Dioxus admin app; tests still pass | ✅ Done | `e3fee9a` | | 3 | Identity layer: `CallerIdentity` newtype, `users` + `device_bindings` schema, magic-link enrollment RPC | ✅ Done | `c7acf2e` | | 4 | Mycelium transport: dual listener (UDS + literal TCP), `peer_addr` extraction, `device_bindings` upsert | ✅ Done | `a9df3b7` | | 5 | `auto:<port>` overlay-bind + dispatcher-level `400::/7` filter (supersedes D-05 bind-to-TUN clause) | ✅ Done | `5c4e8b6` | | 6 | UI architecture commitment (D-09 Dioxus WASM SPA) + propagation across docs (docs-only) | ✅ Done | `5eec381` | | 7 | `mycelium_runtime` helper in `_sdk` (detect-or-spawn with PDEATHSIG + atomic key-gen) + L-05 macOS gap | ✅ Done | `119eabd` | | 8 | `_ui_wasm` crate skeleton + dual-mode `_ui` server (`--dist` flag + askama-as-optional-feature) | ✅ Done | `b6d9518` | | 9a | Migrate `index.html` (763 lines) → Dioxus components; capture Askama visual-regression baselines | ✅ Done | `d492f58` | | 9b | Migrate `chat.html` body → Dioxus `TicketsPage`; channels→tickets / message→comment via additive RPC aliasing (D-10) | ✅ Done | `0756f4b` + `1674b05` | | 9c | Migrate `editor.html` (429 lines) → Dioxus `EditorPage` + editor canary drift fix | ✅ Done | `8b50bdc` | | 10 | Multi-project subscription aggregator (`MultiProjectClient` in `_sdk`) + `presence.set_viewing_ticket` RPC + `PresenceViewingChannel` fanout variant (D-11 + D-12) | ✅ Done | `c8dc6b4` + `9c3c4e7` | | 11 | `hero_assistance_app` standalone Dioxus desktop binary (D-13; consumes `MultiProjectClient`; env-var subscriptions) | ✅ Done | `370c0dd` | | 12a | Single-host RPC-driven E2E aggregator coverage (3 phase12a_* integration tests; order-independent `wait_for_two_merged` helper) | ✅ Done | `2f29be6` | | 12b | Cross-host RPC smoke (D-15 Scope A) — `MyceliumOptions` + cross-daemon-peer attestation; events-over-TCP deferred (L-06); `#[ignore]`-by-default (L-07) | ✅ Done | `70f0cf5` + `8fda9c5` | | 13a | UX validation gate (Rule 6) — demo orchestrator + seed binary (acme + globex; deterministic two-project demo) | ✅ Done | `5515bf2` | | 13b | UX validation gate (Rule 6) — walkthrough findings recorded (8 findings + 11 happy-path checkpoints; Phase 14 punch-list) | ✅ Done | `5ca7cfb` | | 14 | Polish (Phase 13b punch-list) + README + operator runbook + v0.1.0 release | ✅ Done | `d474b84` (source) + `62962cd` (docs); tag `v0.1.0` | | 15a | Transport polymorphism in `_sdk` — `RpcTransport` trait + UDS/TCP/gloo-net impls; refactor `HeroAssistanceClient` to be transport-generic; foundation for embed-ready architecture | 🚧 Next | — | | 15b | Embed-ready component contract + magic-link enrollment + project-config dialog | pending | — | | 15c | Ticket UI — list / detail / threaded comment composer / image-paste / live-update / presence | pending | — | | 15d | `_app` consumes `_ui_wasm` (path-dep, `desktop` feature) + Rule-6 walkthrough gate | pending | — | | 15-release | v0.2.0 release tag + acceptance-criteria sign-off | pending | — | **Done:** phases 0-14 (v0.1.0 tagged). **Remaining:** Phase 15a-d + 15-release (v0.2.0 — customer-facing UI). Phase 15 is ~6-9 sessions; closes acceptance criteria 2-8 from the list below. Post-v1 backlog after v0.2.0: web gateway (L-02), events-over-TCP transport (L-06; closure path simplified by Phase 15a foundation), full D-10 storage rename, mobile, AI-anything (all explicitly deferred). ## v1 acceptance criteria Demonstrable on real cross-host machines. | # | Criterion | v0.1.0 status | v0.2.0 (Phase 15) target | |---|---|---|---| | 1 | Customer installs `_app` without separately installing mycelium | ✅ Met (via `mycelium_runtime::spawn_own`, Linux only per L-05) | ✅ Met | | 2 | Customer enters mycelium address + email, gets magic link, device binds | ⚠️ Backend works; **no UI** in `_app` | 🎯 Phase 15b lands the enrollment screen | | 3 | Customer files a ticket with pasted image | ⚠️ Backend works; **no UI** | 🎯 Phase 15c lands ticket-create + image-paste | | 4 | Support agent on different machine sees ticket appear live | ⚠️ Backend works; **no UI** | 🎯 Phase 15c lands live-update via events.sock | | 5 | Both sides see "actively looking at this" presence | ⚠️ Backend works (`presence.set_viewing_ticket`); **no UI** | 🎯 Phase 15c lands presence indicator | | 6 | Agent replies; customer sees reply live | ⚠️ Backend works; **no UI** | 🎯 Phase 15c lands comment composer + live-update | | 7 | Customer adds second project; both tickets appear | ⚠️ `MultiProjectClient` works; env-var only | 🎯 Phase 15b lands in-app config dialog | | 8 | Agent subscribes to all team projects; consolidated cross-project list | ⚠️ Aggregator left pane shows event names; **right pane is placeholder** | 🎯 Phase 15d wires `_ui_wasm::App` into `_app` right pane | | 9 | No TLS required; plain HTTP over mycelium IPv6 works | ✅ Met (Phase 4/5/12b) | ✅ Met | | 10 | Visual regression `<1%` on migrated pages | ✅ Met (all 9 page-viewport combinations) | ✅ Met (new baselines for `_app` right pane in 15d) | | 11 | All tests pass; `kickstart.sh` exits 0 | ✅ Met (184 passing / 3 L-03 / 8 ignored; kickstart 147/0) | ✅ Met | **v0.1.0 closes 4 of 11.** Phase 15 closes the remaining 7. ## Risks / known unknowns - **Bundled mycelium binary size** — adds ~10–30 MB to installer. Acceptable for desktop; matters for mobile (post-v1). - **Per-device resource use** — each client runs a mycelium relay node by design. Small but non-zero CPU/network overhead. Measure before claiming "free." - **Firewall compatibility** — corporate networks may block mycelium. Captured as L-02; web gateway is the post-v1 mitigation. - **Mycelium upstream version tracking** — bundled daemon needs an update process. Establish before shipping. - **Threat model rests on mycelium daemon trust** at host level. Compromised daemon could forge packets. Standard overlay-as-trust-boundary assumption. - **Dioxus 0.7 pre-1.0 churn** — `dioxus-bootstrap-css` pinned to git `development` branch absorbs API drift; we accept the rebase tax. Bounded; not architectural. - **WASM cold-start weight** — ~1MB compressed; real on first Support-tab open, cached subsequently. Mitigatable with code-splitting or Dioxus SSR pre-render if telemetry flags it. Acceptable for v1. - **`chat.html` migration scope** — 1,821 lines is the single largest migration risk; Phase 9b allocated dedicated, with split-into-9b.1+9b.2 as a fallback if the diff blows up. - **macOS PDEATHSIG gap (L-05, Phase 7)** — Linux-only `prctl`; macOS `_app` may orphan a mycelium child on force-kill. kqueue parent-watcher is out of scope for v1. ## Methodology This project uses the ai-pipeline v1 multi-session workflow. Local workspace at `/home/pctwo/Documents/temp/assistance_work/`. Each phase above corresponds to one or more sessions; `prompt.md §3` carries the next-session entry point. UI migration phases (8–9c) inherit from the `dioxus_bootstrap` Hero skill (canonical recipe; 17-repo precedent for tooling and confidence). Visual regression uses `hero_browser` MCP for screenshots + `pixelmatch` for diff. ## Branching & commits Hero conventions: default branch `development`; feature branches `development_<name>` (underscores); squash-merge back to `development`. Conventional-commit messages with absolute issue URL reference; no AI attribution.
Author
Owner

Phase 13b — UX validation walkthrough findings (session 17, 2026-04-30)

Commit: 5ca7cfb

Agent-driven curl walkthrough against the Phase 13a demo (acme + globex; both events.sock taps). Eight findings recorded in runs/phase13_findings.md:

  • 1 highticket.update/ticket.create rejects human-readable names because the inherited slug validator (^[a-z0-9][a-z0-9_-]*$) is enforced on name. Seeded tickets bypass via direct SQL — real users can't actually file tickets with normal English titles. Critical UX bug for a ticketing product.
  • 4 medium — D-10 param-name inconsistency across ticket.*/comment.*/presence.set_viewing_ticket (three conventions); error messages leak handler-internal column names; comment.create returns -32603 Internal error instead of -32003 NotFound on bad channel_id; presence.update broadcast doesn't carry status_text.
  • 3 low — no fanout for ticket.archive/update/delete/member.*; dev-mode permission bypass nuance; asymmetric users.id referential integrity.

11 happy-path checkpoints also recorded (round-trip + fanout for the seven main RPCs; bidirectional cross-project isolation; archive filter; FTS search; clean error UX on the documented error codes) — Phase 14 must not regress these while chasing fixes.

Verdict: product is functional end-to-end; Build phases (0-12b) shipped a working core; none of these block a v1 ship to a friendly first customer; all matter before opening doors wider.

Test posture unchanged (177/3/8 — zero source touched). Demo orchestrator + seed binary worked first-try.

Phase 14 🚧 Next — polish + README + runbook + release

Punch-list (priority order):

  1. (high) Drop slug validation on ticket name for non-DM kinds
  2. (medium) Map comment.create channel-not-found to -32003 NotFound + sweep similar handlers
  3. (medium) Add status_text: Option<String> to UserEvent::PresenceUpdate
  4. (medium) Add fanout for ticket.{archive,update,delete,member.*}
  5. (medium, docs-only) Doc D-10 param conventions + error-message handler-leak
  6. (low) README: "production = --auth-mode=proxy always"
  7. (low) Add users.id existence check to comment.create

Then README + runbook + v1 release tag. ~92-95% complete by phase count and effort; one Phase 14 session away from v1 release.

## Phase 13b ✅ — UX validation walkthrough findings (session 17, 2026-04-30) Commit: [`5ca7cfb`](https://forge.ourworld.tf/lhumina_code/hero_assistance/commit/5ca7cfb) Agent-driven curl walkthrough against the Phase 13a demo (acme + globex; both `events.sock` taps). Eight findings recorded in [`runs/phase13_findings.md`](https://forge.ourworld.tf/lhumina_code/hero_assistance/src/branch/development/runs/phase13_findings.md): - **1 high** — `ticket.update`/`ticket.create` rejects human-readable names because the inherited slug validator (`^[a-z0-9][a-z0-9_-]*$`) is enforced on `name`. Seeded tickets bypass via direct SQL — real users can't actually file tickets with normal English titles. Critical UX bug for a ticketing product. - **4 medium** — D-10 param-name inconsistency across `ticket.*`/`comment.*`/`presence.set_viewing_ticket` (three conventions); error messages leak handler-internal column names; `comment.create` returns `-32603 Internal error` instead of `-32003 NotFound` on bad channel_id; `presence.update` broadcast doesn't carry `status_text`. - **3 low** — no fanout for `ticket.archive`/`update`/`delete`/`member.*`; dev-mode permission bypass nuance; asymmetric `users.id` referential integrity. **11 happy-path checkpoints** also recorded (round-trip + fanout for the seven main RPCs; bidirectional cross-project isolation; archive filter; FTS search; clean error UX on the documented error codes) — Phase 14 must not regress these while chasing fixes. **Verdict:** product is functional end-to-end; Build phases (0-12b) shipped a working core; none of these block a v1 ship to a friendly first customer; all matter before opening doors wider. **Test posture unchanged** (177/3/8 — zero source touched). Demo orchestrator + seed binary worked first-try. ## Phase 14 🚧 Next — polish + README + runbook + release Punch-list (priority order): 1. (high) Drop slug validation on ticket name for non-DM kinds 2. (medium) Map `comment.create` channel-not-found to `-32003 NotFound` + sweep similar handlers 3. (medium) Add `status_text: Option<String>` to `UserEvent::PresenceUpdate` 4. (medium) Add fanout for `ticket.{archive,update,delete,member.*}` 5. (medium, docs-only) Doc D-10 param conventions + error-message handler-leak 6. (low) README: "production = `--auth-mode=proxy` always" 7. (low) Add `users.id` existence check to `comment.create` Then README + runbook + v1 release tag. ~92-95% complete by phase count and effort; one Phase 14 session away from v1 release.
Author
Owner

Closing — engineering-complete full-stop per §0 (last refreshed s37). All v1 acceptance criteria have shipped or reached an accepted closure path. Twenty-one locked decisions (D-01..D-13 + D-15..D-21); open limitations L-01/L-02/L-05/L-07 are all post-v1 by original scope. Six tagged releases on `development` (v0.1.0 → v0.3.1) auto-published via the s32 release-on-tag CI workflow.

Remaining work tracked in #7 (customer-readiness gate: v0.2.0 + v0.3.1 Rule-6 walkthroughs + L-07 live cross-host smoke). Discretionary post-v1 items live in `prompt.md §3`; issues filed on pickup.

M3 / v1.0 customer-driven from here.

Closing — engineering-complete full-stop per §0 (last refreshed s37). All v1 acceptance criteria have shipped or reached an accepted closure path. Twenty-one locked decisions (D-01..D-13 + D-15..D-21); open limitations L-01/L-02/L-05/L-07 are all post-v1 by original scope. Six tagged releases on \`development\` (v0.1.0 → v0.3.1) auto-published via the s32 release-on-tag CI workflow. **Remaining work tracked in #7** (customer-readiness gate: v0.2.0 + v0.3.1 Rule-6 walkthroughs + L-07 live cross-host smoke). Discretionary post-v1 items live in \`prompt.md §3\`; issues filed on pickup. M3 / v1.0 customer-driven from here.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_assistance#1
No description provided.