Skip to content

Kiosk K3a: agent sidecar lifecycle, external-agent mode, standalone Linux distribution - #101

Merged
thevladbog merged 18 commits into
mainfrom
claude/idento-kiosk-desktop-app-k3
Jul 21, 2026
Merged

Kiosk K3a: agent sidecar lifecycle, external-agent mode, standalone Linux distribution#101
thevladbog merged 18 commits into
mainfrom
claude/idento-kiosk-desktop-app-k3

Conversation

@thevladbog

@thevladbog thevladbog commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the K1-era sidecar-spawn stub actually work, adds an "external agent" connection mode so the kiosk can talk to a standalone agent on another machine, and gives that standalone agent an official Linux/systemd distribution.

  • Sidecar lifecycle (Rust): spawn_agent/stop_agent/restart_agent Tauri commands backed by AgentProcess state; clean shutdown via RunEvent::Exit (tauri-plugin-shell's own cleanup only covers processes spawned through its JS-invoked IPC command, not Command::spawn() called directly from Rust, so this is additionally required).
  • Restart supervisor (TS): useAgentSupervisor rides the existing useAgentHealth poller — 3 consecutive failures trigger a restart, exponential backoff (1s→30s cap) between further attempts, full reset on the next healthy check. Evaluates off the query cache's synchronous subscription rather than a useEffect dependency array (TanStack Query defers the React-facing re-render by one macrotask; the cache's own listeners are synchronous — verified against the installed @tanstack/query-core source).
  • External agent mode: Equipment gains an Embedded/External toggle. agent_request's SSRF-hardened URL builder is generalized to accept a caller-supplied {base_url, token} target instead of only the hardcoded embedded one, preserving every existing anti-injection invariant for both paths. A wrong token is now surfaced explicitly (previously /health's auth-exempt status let a mistyped token show a false "connected").
  • Standalone Linux distribution: agent/dist/idento-agent.service + install.sh (systemd unit, dialout group, prints the Base URL + auth token to paste into Equipment), plus a new agent-standalone-bundle release CI job mirroring the existing per-arch binaries job.

Out of scope (deferred to K3b): automated sidecar-binary embedding into official Tauri release bundles, tauri-plugin-updater, code signing, a desktop-v* release workflow.

Process

Spec → plan → 9 tasks executed via subagent-driven-development (fresh implementer + reviewer per task), then a final whole-branch review. Two tasks needed a fix-and-re-review round (a verified TanStack Query timing deviation in the supervisor hook; a set -e/pipefail bug in install.sh). The final review found one Important, since-fixed issue (external-mode token-mismatch false-green) plus two cosmetic Minors (double-v version prefix, unused Rust import).

⚠️ Not yet verified on real hardware

Two things this branch could not exercise in its sandboxed development environment and that should be checked before relying on this in production:

  • Live sidecar lifecycle: actually spawning/stopping/restarting the bundled agent via a real Tauri GUI session, and confirming the OS process dies when the window closes.
  • install.sh on a real Linux/systemd box (e.g. a Raspberry Pi).

Test plan

  • npm test -w idento-desktop — 70/70 passing
  • npm run typecheck -w idento-desktop — clean
  • npm run build -w idento-desktop — clean
  • cargo build / cargo test --lib (desktop/src-tauri) — clean, 15/15, zero warnings
  • Manual: live Tauri GUI sidecar spawn/stop/restart + exit-kills-process
  • Manual: install.sh on a real Linux/systemd box

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for connecting to either an embedded agent or a standalone external agent.
    • Added external agent URL and token configuration in the Equipment setup flow.
    • Added automatic recovery for embedded agent connection failures.
    • Added agent version and connection details to the Run screen.
    • Release builds now include Linux ARM64 and AMD64 standalone agent bundles.
  • Bug Fixes

    • Improved unauthorized-agent error messaging.
    • Corrected duplicate version prefixes in agent details.
  • Documentation

    • Added guidance for configuring external agent connections.

CI Bot and others added 14 commits July 21, 2026 19:22
…l agent mode, standalone Linux distribution

9 tasks covering: generalized build_agent_url/agent_request for external
targets (Rust, TDD), sidecar spawn/stop/restart commands + clean shutdown,
agentConfig.ts mode persistence, agent.ts routed through the configured
target, restart supervisor hook + app-boot wiring, Equipment's
embedded/external toggle, agent version/port surfaced in status displays,
agent/dist/ systemd unit + install script, and a new release.yml job to
publish per-arch standalone agent bundles.

Verified the Rust API surface (tauri 2.11.5, tauri-plugin-shell 2.3.5)
against the actual vendored crate source rather than from memory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dency bug, doc comment

useAgentSupervisor's effect depended only on [health.data, health.isLoading];
since checkAgentHealth() resolves the same boolean on every consecutive
failed poll, health.data never changes value between polls and the effect
would never re-run past the first failure. Added health.dataUpdatedAt (a
fresh timestamp per settled poll regardless of value) to the dependency
array. Also documented why Equipment.tsx's new reconnectAgent() is
deliberately not shared with the pre-existing mount effect's
cancellation-guarded logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds useAgentSupervisor (K3a Task 5): restarts the embedded agent sidecar
after 3 consecutive health-check failures via the Rust restart_agent
command, with exponential backoff (1s->30s), full reset on the next
healthy poll, and a no-op in external mode. Evaluates the ["agent","health"]
query cache directly (queryClient.getQueryState + getQueryCache().subscribe)
rather than a useEffect keyed on useAgentHealth()'s returned data --
TanStack Query defers the observer's React-facing re-render by one
macrotask (notifyManager batches it through a 0ms setTimeout), which is
invisible in the running app but caused the poll-counting tests to
observe stale data one tick late under fake timers; subscribing to the
cache's own (synchronous) notifications avoids that lag while still
reading the same single source of truth the status chip uses.

AgentLifecycle mounts once at the app root, spawning the embedded sidecar
on boot (best-effort, swallowed outside Tauri) and keeping the supervisor
alive for the session.
…il abort

Under set -euo pipefail, a failing left-hand command in a pipeline (e.g.
`hostname -I` on a freshly-imaged Pi with no network yet, or `grep -o`
finding no match yet in a config-write race) propagates its non-zero
status into the plain variable assignment, which trips set -e and kills
the whole script -- for HOST_IP, right after the systemd service was
already installed and started but before the Base URL/Token block ever
prints, defeating a core purpose of the script. Append `|| true` inside
each command substitution's pipeline so a failing source command yields
an empty variable instead of aborting; existing fallback logic (default
IP text, "not generated yet" token message) already handles the empty
case correctly. No other behavior changed.
…ble-v version, unused import

Addresses 3 findings from the K3a whole-branch review:

1. (Important) Equipment.tsx: /health and /info are auth-exempt, so a
   mistyped external-agent token still shows "connected" -- the first
   real endpoint call (e.g. /printers) then 401s. Add a distinct
   agentUnauthorized signal (detected from the 401 in
   fetchEquipmentData()'s error, in both reconnectAgent and the mount
   effect) with a translated warning line in the agent-connection
   section.

2. (Minor) agentDetail.ts's formatAgentDetail: strip any existing
   leading v/V from `version` before re-adding one, so a released
   standalone agent's CI-baked "v1.2.3"-shaped version string doesn't
   render as "vv1.2.3". Added a regression test.

3. (Minor) commands.rs: drop the unused `Manager` import (only needed
   in lib.rs, which is untouched) to clear a harmless compiler warning.
Copilot AI review requested due to automatic review settings July 21, 2026 20:49

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added documentation Improvements or additions to documentation agent ci desktop labels Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@thevladbog, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 69e32811-19bc-4ce0-bda9-f6261111efe5

📥 Commits

Reviewing files that changed from the base of the PR and between b4c7c48 and b0812e8.

📒 Files selected for processing (8)
  • .github/workflows/release.yml
  • .superpowers/sdd/pr101-review-fix-report.md
  • .superpowers/sdd/progress.md
  • desktop/src/features/checkin/useAgentSupervisor.test.tsx
  • desktop/src/features/checkin/useAgentSupervisor.ts
  • desktop/src/lib/agent.test.ts
  • desktop/src/lib/agent.ts
  • docs/superpowers/specs/2026-07-21-kiosk-k3a-agent-distribution-design.md
📝 Walkthrough

Walkthrough

The desktop now supports embedded or external agents with persisted configuration, Rust-proxied requests, managed sidecar lifecycle, health-based restarts, connection controls, and status metadata. Release automation also builds amd64 and arm64 standalone agent bundles.

Changes

Kiosk K3a agent support

Layer / File(s) Summary
Rust targeting and sidecar lifecycle
desktop/src-tauri/src/commands.rs, desktop/src-tauri/src/lib.rs
External targets, token selection, URL validation, managed sidecar commands, and exit cleanup are implemented and tested.
Agent target configuration and request routing
desktop/src/lib/agentConfig.ts, desktop/src/lib/agent.ts, desktop/src/lib/*test.ts
Embedded/external settings are persisted and agent requests route through the selected target with authorization.
Boot and health-based sidecar supervision
desktop/src/components/AgentLifecycle.tsx, desktop/src/features/checkin/useAgentSupervisor.ts, desktop/src/App.tsx, desktop/src/features/checkin/*test.tsx
Embedded startup and health-triggered restart behavior use thresholding and exponential backoff, while external mode skips restarts.
Connection-mode equipment workflow
desktop/src/pages/Equipment.tsx, desktop/src/i18n.ts, desktop/README.md
Equipment pre-flight supports embedded/external selection, external credentials, reconnecting, and unauthorized-agent messaging.
Status display and standalone release artifacts
desktop/src/features/checkin/*, desktop/src/pages/Run.tsx, .github/workflows/release.yml, docs/superpowers/*, .superpowers/sdd/*
Agent version and address details appear in Run status, and release workflows package architecture-specific standalone bundles with supporting design and verification records.

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

Sequence Diagram(s)

sequenceDiagram
  participant EquipmentPage
  participant agentConfig
  participant agent_request
  participant ExternalAgent
  EquipmentPage->>agentConfig: save external URL and token
  EquipmentPage->>agent_request: request agent health or equipment
  agent_request->>ExternalAgent: send validated authenticated request
  ExternalAgent-->>agent_request: return response
  agent_request-->>EquipmentPage: return agent data or authorization error
Loading

Possibly related PRs

  • thevladbog/idento#83: Introduces the agent /info response consumed by the new desktop status hooks.
  • thevladbog/idento#99: Related desktop agent plumbing used by the embedded/external routing changes.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: agent lifecycle, external-agent mode, and standalone Linux distribution.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/idento-kiosk-desktop-app-k3

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

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

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

Inline comments:
In @.github/workflows/release.yml:
- Around line 127-165: Add a validation step or dependency to
agent-standalone-bundle that runs the agent’s tests, linting, and security
checks before the Package bundle and upload-artifact steps. Reuse the
repository’s established validation commands or reusable job, and ensure
artifact publication cannot proceed when any check fails.
- Around line 141-150: Update the Build step in the release workflow to assign
github.ref_name to RELEASE_TAG, validate RELEASE_TAG against the expected
release-tag format, and use the validated shell variable in the
main.agentVersion ldflags value instead of directly interpolating
github.ref_name.

In @.superpowers/sdd/final-review-fix-report.md:
- Line 53: Update all fenced code blocks in final-review-fix-report.md,
including the listed occurrences, to specify sh for command examples and text
for build output, resolving markdownlint MD040 without changing the block
contents.

In `@desktop/src/features/checkin/useAgentSupervisor.ts`:
- Around line 92-100: The cooldown timer in the restart flow should trigger a
health refetch when it expires, rather than only clearing cooldownActiveRef and
updating backoffMsRef. Update the timer callback near restartAgentProcess so the
refetch result is settled and drives the existing health/restart decision logic,
preserving the intended 1s→2s→4s progression and cooldown gate behavior.

In `@desktop/src/lib/agent.ts`:
- Around line 25-27: Update the browser request construction around the target
base URL and fetch call to parse and validate the base as a URL, rejecting
non-HTTP(S) schemes and any username or password userinfo before sending
credentials. Resolve path with new URL(path, base) rather than string
concatenation, and preserve the existing fallback and Authorization behavior for
valid targets.

In `@docs/superpowers/specs/2026-07-21-kiosk-k3a-agent-distribution-design.md`:
- Line 28: Update the K3a documentation around externalBin and tauri.conf.json
to state that bundle.externalBin remains [] in committed configuration; allow
externalBin: ["sidecars/idento-agent"] only as an uncommitted local override for
manual sidecar testing, while preserving manual spawning from
src-tauri/sidecars/.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9595ac28-a060-41dc-9abe-f668832b51d6

📥 Commits

Reviewing files that changed from the base of the PR and between 221e06b and b4c7c48.

⛔ Files ignored due to path filters (2)
  • agent/dist/idento-agent.service is excluded by !**/dist/**
  • agent/dist/install.sh is excluded by !**/dist/**
📒 Files selected for processing (23)
  • .github/workflows/release.yml
  • .superpowers/sdd/final-review-fix-report.md
  • .superpowers/sdd/progress.md
  • desktop/README.md
  • desktop/src-tauri/src/commands.rs
  • desktop/src-tauri/src/lib.rs
  • desktop/src/App.tsx
  • desktop/src/components/AgentLifecycle.tsx
  • desktop/src/features/checkin/agentDetail.test.ts
  • desktop/src/features/checkin/agentDetail.ts
  • desktop/src/features/checkin/hooks.test.tsx
  • desktop/src/features/checkin/hooks.ts
  • desktop/src/features/checkin/useAgentSupervisor.test.tsx
  • desktop/src/features/checkin/useAgentSupervisor.ts
  • desktop/src/i18n.ts
  • desktop/src/lib/agent.test.ts
  • desktop/src/lib/agent.ts
  • desktop/src/lib/agentConfig.test.ts
  • desktop/src/lib/agentConfig.ts
  • desktop/src/pages/Equipment.tsx
  • desktop/src/pages/Run.tsx
  • docs/superpowers/plans/2026-07-21-kiosk-k3a-agent-distribution.md
  • docs/superpowers/specs/2026-07-21-kiosk-k3a-agent-distribution-design.md

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml

### Verification (all run from repo root)

```

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add fence language identifiers.

These fences trigger markdownlint MD040. Use sh for commands and text for build output.

Proposed fix
-```
+```sh
 npm test -w idento-desktop
</details>

   


Also applies to: 58-58, 63-63, 68-68, 74-74, 82-82, 90-90

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.23.0)</summary>

[warning] 53-53: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @.superpowers/sdd/final-review-fix-report.md at line 53, Update all fenced
code blocks in final-review-fix-report.md, including the listed occurrences, to
specify sh for command examples and text for build output, resolving
markdownlint MD040 without changing the block contents.


</details>

<!-- fingerprinting:phantom:poseidon:terra -->

<!-- cr-indicator-types:potential_issue -->

<!-- cr-comment:v1:d220c85c1c9ed9da65262274 -->

_Source: Linters/SAST tools_

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread desktop/src/features/checkin/useAgentSupervisor.ts
Comment thread desktop/src/lib/agent.ts Outdated
Comment thread docs/superpowers/specs/2026-07-21-kiosk-k3a-agent-distribution-design.md Outdated
CI Bot and others added 4 commits July 22, 2026 00:03
…esktop-app-k3

# Conflicts:
#	.superpowers/sdd/progress.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sanitize github.ref_name before shell interpolation in the new
agent-standalone-bundle release job (mirrors onprem-bundle's existing
RELEASE_TAG/case-validation pattern), add a go test step before
packaging, validate external-agent base URLs in agent.ts's browser-dev
fallback path (rejects non-http(s) schemes and userinfo before
building the fetch URL), and make useAgentSupervisor's exponential
backoff actually drive retry cadence via a forced health-query refetch
instead of passively gating behind the 20s poll interval. Also
clarifies the K3a spec doc's externalBin wording to match the actual
committed-vs-local-only behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@thevladbog

Copy link
Copy Markdown
Owner Author

Thanks for the review — addressed in 845d8db:

  • Template-injection in agent-standalone-bundle (release.yml): fixed by mirroring the pre-existing onprem-bundle job's RELEASE_TAG env-var + case validation pattern exactly.
  • No validation before publishing: added a go test ./... step before packaging. Didn't add full lint/SAST tooling — no such tooling exists anywhere else in this repo's Go CI jobs (including the binaries job this one mirrors), so that would be scope creep beyond this PR.
  • Browser-fallback URL validation in agent.ts: added a resolveAgentBaseUrl helper rejecting non-http(s) schemes and userinfo, with new tests. Note this path is dev-only (never used in the shipped Tauri app, which always validates on the Rust side) and every path argument passed to it is a hardcoded literal, not runtime input — so it doesn't need the same path-charset restriction the Rust IPC boundary does.
  • useAgentSupervisor's backoff was a passive gate, not a real scheduler: good catch — fixed so the cooldown timer forces a health-query refetch instead of waiting for the next ~20s scheduled poll, which is what actually makes the exponential backoff drive retry cadence. Test extended to prove sub-20s cadence.
  • Spec doc externalBin wording: fixed — was inconsistent with the plan's own binding constraint (externalBin stays [] in the committed repo).

Declined: the markdownlint fence-language nitpick on .superpowers/sdd/final-review-fix-report.md — that's a throwaway internal report file, not documentation.

@thevladbog
thevladbog merged commit 9acca77 into main Jul 21, 2026
34 checks passed
@thevladbog
thevladbog deleted the claude/idento-kiosk-desktop-app-k3 branch July 21, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent ci desktop documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants