IW-652: Per-user dashboard on a shared dev machine — tailnet service, service links, Claude session links #683

Merged
mph merged 65 commits from IW-652 into main 2026-10-02 09:18:52 +00:00
Member

Summary

Each user on a shared dev machine runs their own iw dashboard as a systemd --user service on the tailnet. The dashboard shows, for every worktree, links to its running services and to its live Claude Code Remote Control sessions.

Changes

  • Phase 1 (#660) — per-user server + owner-aware CLI. iw server run (foreground, no browser, for a service manager). A config with no loopback address is refused. /api/status reports the owner. The CLI refuses to register with the server of another user. The page header names the owner and the port. Unit file and operator rules are in docs/server-config.md.
  • Phase 2 (#664) — service declarations + service links. ./iw service add|list|rm and WorktreeServices write iw-services.json in the per-worktree git dir (schema in schemas/iw-services.schema.json). Cards and the detail page show one link per service, with liveness ("port answers", "no answer", "not checked"). The probe runs only when Host names this machine, inside one bounded deadline. Docs are in docs/worktree-services.md.
  • Phase 3 (#674) — Claude session links. One "Claude" link per live Remote Control session (https://claude.ai/code/<bridgeSessionId>). A session is live only while its process runs (/proc/<pid>/stat start time). An unreadable registry shows "Claude sessions cannot be read".

Read the diff with git diff origin/main...HEAD -- . ':!project-management' (99 files, +9252/-487). The full review packet is in project-management/issues/IW-652/review-packet.md, and the per-phase packets are next to it.

Testing

  • Unit and integration tests for each phase (counts in the per-phase packets). CI ran green on each phase PR (#660, #664, #674).
  • Browser verification: phases 2 and 3 passed 4 of 4 scenarios each (verification-phase-0{2,3}.md). Phase 1 browser verification was skipped. The by-hand check on ff-dev (two users, two ports, the tailnet) is still open.
  • No local full-suite run of the merged branch; CI on this PR is the full-suite verdict.

Follow-ups

Filed from this issue: #675–#682. Comments added to #440 and #586. One lesson went to kanon note dev-docs#411.

Release notes

See project-management/issues/IW-652/release-notes.md (Czech).

Closes #652

🤖 Generated with Claude Code

## Summary Each user on a shared dev machine runs their own iw dashboard as a `systemd --user` service on the tailnet. The dashboard shows, for every worktree, links to its running services and to its live Claude Code Remote Control sessions. ## Changes - **Phase 1 (#660) — per-user server + owner-aware CLI.** `iw server run` (foreground, no browser, for a service manager). A config with no loopback address is refused. `/api/status` reports the owner. The CLI refuses to register with the server of another user. The page header names the owner and the port. Unit file and operator rules are in `docs/server-config.md`. - **Phase 2 (#664) — service declarations + service links.** `./iw service add|list|rm` and `WorktreeServices` write `iw-services.json` in the per-worktree git dir (schema in `schemas/iw-services.schema.json`). Cards and the detail page show one link per service, with liveness ("port answers", "no answer", "not checked"). The probe runs only when `Host` names this machine, inside one bounded deadline. Docs are in `docs/worktree-services.md`. - **Phase 3 (#674) — Claude session links.** One "Claude" link per live Remote Control session (`https://claude.ai/code/<bridgeSessionId>`). A session is live only while its process runs (`/proc/<pid>/stat` start time). An unreadable registry shows "Claude sessions cannot be read". Read the diff with `git diff origin/main...HEAD -- . ':!project-management'` (99 files, +9252/-487). The full review packet is in `project-management/issues/IW-652/review-packet.md`, and the per-phase packets are next to it. ## Testing - Unit and integration tests for each phase (counts in the per-phase packets). CI ran green on each phase PR (#660, #664, #674). - Browser verification: phases 2 and 3 passed 4 of 4 scenarios each (`verification-phase-0{2,3}.md`). Phase 1 browser verification was skipped. The by-hand check on `ff-dev` (two users, two ports, the tailnet) is still open. - No local full-suite run of the merged branch; CI on this PR is the full-suite verdict. ## Follow-ups Filed from this issue: #675–#682. Comments added to #440 and #586. One lesson went to kanon note dev-docs#411. ## Release notes See `project-management/issues/IW-652/release-notes.md` (Czech). Closes #652 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Dashboard on a shared dev machine: reachable over the tailnet, links to
each worktree's running services, all users' worktrees

Artifacts:
- analysis.md
- issue.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The issue now asks for a per-user dashboard with links to each
worktree's services and Claude Code session. Other users' worktrees
and authentication are out of scope.

Artifacts:
- analysis.md
- issue.md
- review-state.json

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolved:
- analysis.md: service declaration -> each project declares its services
  from its own start hook; iw-cli ships a schema, a Scala API and
  ./iw service add|list|rm; the list lives in the per-worktree git dir.
  Port allocation is a follow-up (FOLLOW-UPS.md).
- analysis.md: session URL -> read Claude Code's session registry
  ($CLAUDE_CONFIG_DIR/sessions), link every Remote Control session of
  the worktree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
worktree-service-links supersedes dev-version-link (#352).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-user dashboard on a shared dev machine: reachable over the tailnet,
links to each worktree's services and Claude Code session

Artifacts:
- tasks.md
- review-state.json (tasks_ready)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ServerStatus becomes the /api/status wire model with an owner field.
iw server status probes the port when no PID is live; iw server start
refuses when the port already answers. Every ServerClient request
checks the owner. Two out-of-scope defects go to FOLLOW-UPS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue: IW-652
Phase: 1 - Per-user dashboard as a tailnet service + owner-aware CLI

This commit marks the checkpoint for phase 1 context generation.
Use ag-revert IW-652 1 to return to this point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-user dashboard as a tailnet service + owner-aware CLI

Artifacts:
- phase-01-tasks.md
- phase-01-context.md: updateLastSeen goes through registerWorktree

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-user dashboard as a tailnet service + owner-aware CLI
Some checks failed
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 14s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
CI / compile (pull_request) Has been cancelled
a2835844ee
- iw server run: foreground server for a systemd user unit, no PID file, no browser
- Loopback check on iw server run and iw server start; start refuses a port that already answers
- Owner on /api/status (ServerStatus wire model) and owner and port in every page header
- ServerClient checks the owner before every request and never registers with another user's server
- iw server status reports a server with no live PID through /api/status
- docs/server-config.md: run the dashboard as a service, loopback rule, exposure, owner-check limits
chore(IW-652): update review-state for phase 01
Some checks failed
CI / contract (pull_request) Has been skipped
CI / changes (pull_request) Successful in 5s
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 14s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
CI / compile (pull_request) Has been cancelled
a7548800b5
phase(IW-652): phase 01 verification report
All checks were successful
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m43s
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 14s
CI / dashboard-build (pull_request) Successful in 1m15s
CI / test (pull_request) Successful in 15m58s
c54f74369f
Reviewed-on: #660
Issue: IW-652
Phase: 2 - Service declaration tools + service links on the card

This commit marks the checkpoint for phase 2 context generation.
Use ag-revert IW-652 2 to return to this point.

Applies the service-link decisions to analysis.md (pages, 30 s refresh,
probe to the arrival address, no-Host rule, file shape, labels, browser
check in verification) and the 20-28h phase estimate to tasks.md.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Service declaration tools + service links on the card

Artifacts:
- phase-02-tasks.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- iw service add|list|rm declares a worktree's services in the per-worktree git directory (schemas/iw-services.schema.json)
- Every card kind and the detail page show the declared services as links built from the request Host, with a liveness label probed on the address the request arrived on
- Bounded probe: parallel connects under one deadline, result and DNS caches with limits, each failure logged at most once per minute
- Responses with service links carry Cache-Control: no-store and Vary: Host
- Verification fixture (scripts/verify-fixture.sh) and webapp-knowledge for Playwright checks
- Three review iterations with 0 critical findings; refactors deferred to FOLLOW-UPS.md
fix(IW-652): give iw service its exit-code row in CommandDeclarationTest
Some checks failed
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 9s
CI / lint (pull_request) Successful in 12s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
CI / compile (pull_request) Has been cancelled
8a665eb49d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore(IW-652): update review-state for phase 02
Some checks failed
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 11s
CI / compile (pull_request) Successful in 1m41s
CI / dashboard-build (pull_request) Successful in 1m25s
CI / test (pull_request) Has been cancelled
db3d63d3f5
verify(IW-652): phase 02 browser verification
Some checks failed
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 12s
CI / compile (pull_request) Successful in 1m38s
CI / test (pull_request) Failing after 17m12s
CI / dashboard-build (pull_request) Successful in 1m27s
a7d26f886e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: count a zombie listener as stopped in verify-fixture (phase 2 CI)
All checks were successful
CI / format (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / compile (pull_request) Successful in 1m37s
CI / dashboard-build (pull_request) Successful in 1m26s
CI / test (pull_request) Successful in 16m49s
46358116e2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore(IW-652): phase 02 awaiting review, card message reset
All checks were successful
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 12s
CI / test (pull_request) Successful in 16m58s
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / compile (pull_request) Successful in 1m40s
CI / dashboard-build (pull_request) Successful in 1m28s
39c45b7fc2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-on: #664
Issue: IW-652
Phase: 3 - Claude Code session links on the card

This commit marks the checkpoint for phase 3 context generation.
Use ag-revert IW-652 3 to return to this point.

Decisions with Michal: "Claude" + session name link text; a WorktreeLinks
value replaces the serviceLinks view parameter; estimate 7-10h.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude Code session links on the card

Artifacts:
- phase-03-tasks.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Claude row on every card kind and the worktree detail page, one link per Remote Control session
- WorktreeLinks replaces the serviceLinks view parameter; a lookup miss shows cannot be read
- ClaudeSessionsReader reads CLAUDE_CONFIG_DIR/sessions, logs a broken registry once per change
- Liveness check: a session of a killed process or a reused PID gives no link; a check that cannot run shows cannot be read
- Unit, integration and BATS tests on the real jar; verification fixture with session files
phase(IW-652): phase 03 review packet with the liveness check
Some checks failed
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 12s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
CI / compile (pull_request) Has been cancelled
6074621abf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore(IW-652): update review-state for phase 03
Some checks failed
CI / lint (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / compile (pull_request) Successful in 1m38s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
c2a468d456
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
phase(IW-652): phase 03 verified scenarios in the review packet
All checks were successful
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 15s
CI / compile (pull_request) Successful in 1m40s
CI / dashboard-build (pull_request) Successful in 1m27s
CI / test (pull_request) Successful in 17m4s
a63c1a737a
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore(IW-652): update review-state to awaiting_review for phase 03
All checks were successful
CI / changes (pull_request) Successful in 4s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 12s
CI / compile (pull_request) Successful in 1m38s
CI / dashboard-build (pull_request) Successful in 1m25s
CI / test (pull_request) Successful in 17m7s
d6dd7491e5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-on: #674
Follow-ups filed: 8 (#675, #676, #677, #678, #679, #680, #681, #682), commented: 2 (#440, #586), struck: 0
Lessons: 1 approved, 0 rejected, 0 still pending

Promoted:
- A catch of java.net.ConnectException around an sttp 4 request never matches → kanon note #411 (review-skill:error-handling-reviewer)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
workflow(IW-652): final review packet and release notes
Some checks failed
CI / changes (pull_request) Successful in 5s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 14s
CI / test (pull_request) Has been cancelled
CI / dashboard-build (pull_request) Has been cancelled
CI / compile (pull_request) Has been cancelled
136066837c
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
workflow(efforts): close IW-652 loop (dashboard-control-plane)
All checks were successful
CI / changes (pull_request) Successful in 5s
CI / contract (pull_request) Has been skipped
CI / format (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 13s
CI / compile (pull_request) Successful in 1m37s
CI / dashboard-build (pull_request) Successful in 1m27s
CI / test (pull_request) Successful in 17m11s
bb9ac71a34
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mph merged commit 56db4029a6 into main 2026-10-02 09:18:52 +00:00
Sign in to join this conversation.
No description provided.