291 lines
14 KiB
Markdown
291 lines
14 KiB
Markdown
---
|
|
spec: docs/specs/agents/requirements/runtime-updates.md
|
|
decision: docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md
|
|
created: 2026-08-21
|
|
status: complete
|
|
---
|
|
|
|
# Implementation Plan: Managed Runtime Version Awareness
|
|
|
|
## Overview
|
|
|
|
Replace every unversioned managed npm ACP package invocation with an exact
|
|
effective version. The effective version is an install-wide validated operator
|
|
selection when present and a reviewed Kandev default otherwise. Build on the
|
|
existing version dialog and selection store, add a cached read-only update
|
|
status API and accessible blue-dot indicator, and add weekly automation that
|
|
opens reviewable pin-update PRs without changing runtimes automatically.
|
|
|
|
---
|
|
|
|
## Backend
|
|
|
|
### Central managed-runtime pins
|
|
|
|
- Add one machine-readable version catalogue at
|
|
`apps/backend/internal/agent/agents/managed_npm_runtime_versions.json` for
|
|
Claude, Codex, OpenCode, Copilot, and Gemini. Package identity remains in the
|
|
trusted agent metadata; the catalogue maps only those package names to exact
|
|
stable SemVer defaults.
|
|
- Embed and validate the catalogue from a focused Go helper beside
|
|
`managed_npm_runtime.go`. Startup must fail clearly if a trusted managed
|
|
package has no valid default; no command may fall back to an unversioned
|
|
package.
|
|
- Add `DefaultVersion` to `ManagedNPMRuntimeSpec`. `PackageSpec("")`,
|
|
`CachedACPCommand()`, runtime configs, inference configs, and cache keys use
|
|
the default. A non-empty validated override continues to produce
|
|
`package@override`.
|
|
- Keep launch commands on `npx --yes --prefer-offline`. Candidate preparation
|
|
and the existing bounded stale-metadata retry remain online-preferred.
|
|
|
|
### Effective version and operator selection
|
|
|
|
- Define one effective-version resolver: read
|
|
`managed_runtime.active.<agent-id>` from the install-wide settings store,
|
|
require the saved package to match trusted metadata, and otherwise return the
|
|
built-in default.
|
|
- Route the effective version through every centrally built managed ACP command,
|
|
including host utility probes, command previews, standalone sessions,
|
|
containers, and SSH executors. Native-binary preference, passthrough commands,
|
|
and separately distributed login helpers remain unchanged.
|
|
- Extend `managedruntime.SelectionStore` with deletion. The version job accepts
|
|
a trusted `use_default` request, stages and ACP-probes the built-in default,
|
|
then deletes the selection only after success. Candidate failure retains the
|
|
prior selection and capabilities.
|
|
- Extend runtime catalogue, preview, and job DTOs with `default_version` and
|
|
`effective_version`. Preserve `active_version` as the optional persisted
|
|
operator selection. Add structural `use_default` operation copy; the browser
|
|
does not infer operations from translated labels or version strings.
|
|
|
|
### Cached update status API
|
|
|
|
- Add `GET /api/v1/agent-update/status`. It returns one entry per available
|
|
managed runtime with package, default, optional active selection, effective
|
|
version, optional latest version, `checked_at`, and `check_state`.
|
|
- Implement a process-local, mutex-protected per-package cache in the agent
|
|
settings controller. Successful npm `latest` results live for six hours;
|
|
failed lookups produce `unknown` for fifteen minutes. Use injected clock and
|
|
resolver seams in tests and bound concurrent stale lookups to the five trusted
|
|
packages.
|
|
- Compare strict stable SemVer in the backend. Return `update_available` only
|
|
when latest is newer than effective, `up_to_date` otherwise, and `unknown`
|
|
when metadata is unavailable or invalid. One failed package does not fail the
|
|
batch.
|
|
- Invalidate the affected cache entry after a successful version activation or
|
|
return-to-default job. The dialog preview remains an uncached authoritative
|
|
catalogue lookup.
|
|
|
|
---
|
|
|
|
## Frontend
|
|
|
|
### API and page-local status hook
|
|
|
|
- Add typed batch-status contracts to
|
|
`apps/web/lib/api/domains/agent-update-api.ts` and the existing API barrels.
|
|
- Add a settings-domain hook that requests status when Settings > Agents mounts,
|
|
after Rescan, and after a successful version job. Keep status page-local; the
|
|
existing WebSocket-backed update-job store remains authoritative for active
|
|
mutations.
|
|
- Treat `unknown` as no indicator, not as up to date. Do not disable the update
|
|
trigger when the checker fails.
|
|
|
|
### Agent-card update indicator
|
|
|
|
- Pass the backend status for each agent through
|
|
`apps/web/app/settings/agents/page.tsx` and
|
|
`installed-agent-card.tsx` into the existing runtime update control.
|
|
- When `check_state` is `update_available`, overlay a blue dot on the existing
|
|
refresh trigger. Add effective and latest versions to its accessible name and
|
|
desktop tooltip; do not rely on color alone.
|
|
- Opening the trigger continues to fetch a live preview. The dot is a hint and
|
|
never a separate action.
|
|
- Extend the existing dialog/drawer version selector with a clearly labelled
|
|
Kandev-default option when an operator selection exists. The same state and
|
|
mutation handler serve desktop and mobile.
|
|
|
|
### Mobile design contract
|
|
|
|
- **Desktop outcome:** the agent-group card shows a compact update hint and
|
|
opens the existing centered version dialog.
|
|
- **Mobile entry point:** the same 44 px update trigger on the agent-group card.
|
|
- **Nearest exemplar:** the existing `AgentRuntimeUpdateControl`; retain its
|
|
inset bottom `Drawer`, fixed header/footer, safe-area padding, and single
|
|
scrolling body.
|
|
- **Hierarchy and action:** the dot signals availability; tapping the trigger
|
|
opens the authoritative version summary and update/default action. No hover
|
|
is required.
|
|
- **Geometry:** the dot does not change the trigger hitbox, add a scroll owner,
|
|
or introduce horizontal overflow. Shared status and selection logic remain
|
|
viewport-neutral.
|
|
|
|
### Localization
|
|
|
|
- Add copy for update availability, version interpolation, Kandev default, and
|
|
unknown checker behavior to all five required locale catalogues.
|
|
- Generate Traditional Chinese catalogues through `pnpm run i18n:zh-hant` and
|
|
run the focused i18n checks; do not hardcode labels in components.
|
|
|
|
---
|
|
|
|
## Weekly Pin Update Workflow
|
|
|
|
- Add `scripts/update-agent-runtime-pins.mjs` to read the central catalogue,
|
|
query each trusted package's npm `dist-tags.latest`, reject prerelease or
|
|
invalid SemVer values, and rewrite only changed pins. Keep parsing and rewrite
|
|
logic independently testable with fixture metadata and no network.
|
|
- Add `.github/workflows/update-agent-runtime-pins.yml` with weekly and manual
|
|
triggers, least-privilege permissions, pinned action SHAs, a stable bot
|
|
branch, and one grouped Conventional Commit PR. Use the repository-scoped
|
|
`GITHUB_TOKEN` with contents, pull-request, and workflow-dispatch write
|
|
permissions. Explicitly dispatch the six required validation workflows
|
|
against the bot branch because built-in-token branch and PR events do not
|
|
recursively start them. Do not auto-merge.
|
|
- Run the updater tests, targeted managed-runtime Go tests, action-pinning lint,
|
|
and workflow contract test before opening a PR. When no pin changes, exit
|
|
without a branch or PR mutation.
|
|
- Add a workflow contract test under `.github/scripts/` and wire it into
|
|
`lint-action-pinning.yml` so schedule, permissions, token boundary, validation,
|
|
and no-auto-merge behavior cannot silently drift.
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
- **Exact defaults and overrides:** table-driven argv tests cover all five
|
|
packages, empty-selection fallback, explicit selection, native preference,
|
|
host utility, standalone, container, and SSH command construction.
|
|
- **Selection lifecycle:** real settings-store tests cover save, matching package,
|
|
default fallback, delete-on-success, and preservation on failed candidate
|
|
validation.
|
|
- **Update status:** controller and handler tests cover newer/equal/older latest,
|
|
partial registry failure, strict SemVer, cache TTLs, invalidation, and one
|
|
read-only batch response without starting a job.
|
|
- **Frontend status:** API and hook tests cover successful, partial-unknown, and
|
|
post-update refresh behavior. Component tests cover dot visibility,
|
|
accessible version copy, usable unknown state, and return-to-default action.
|
|
- **Workflow:** offline updater fixtures and workflow-contract tests cover
|
|
changed/no-change/prerelease/error cases and secure PR creation.
|
|
|
|
## E2E Tests
|
|
|
|
- **Desktop update hint:** given a newer registry version, opening Settings >
|
|
Agents shows the blue dot; opening it shows the authoritative current-to-latest
|
|
preview and starts no mutation until approval.
|
|
- **Unknown status:** given one failed package lookup, that agent has no dot but
|
|
its update dialog remains usable; another package can still show an update.
|
|
- **Post-update state:** after a successful update, the status refresh removes
|
|
the dot and the selected version survives page reload.
|
|
- **Mobile parity:** on Pixel 5, the dotted 44 px trigger opens the existing
|
|
drawer, exposes the version information without hover, keeps the action
|
|
reachable, and produces no document horizontal overflow.
|
|
|
|
Files:
|
|
|
|
- `apps/web/e2e/tests/settings/agent-runtime-update.spec.ts`
|
|
- `apps/web/e2e/tests/settings/mobile-agent-runtime-update.spec.ts`
|
|
- `apps/web/e2e/tests/settings/agent-runtime-update-helpers.ts`
|
|
|
|
---
|
|
|
|
## Public Documentation
|
|
|
|
- Update `docs/public/agents-and-profiles.md` with shipped defaults, explicit
|
|
selections, the blue update hint, return-to-default behavior, and the accepted
|
|
npm/network/cache limitations.
|
|
- Update `apps/backend/internal/agent/agents/ACP_BRIDGE_VERSIONS.md` so every
|
|
documented command uses `package@effective-version` and distinguishes normal
|
|
offline-preferred launches from online-preferred updates.
|
|
- Search public and internal docs for claims that managed packages are
|
|
unversioned or host-only and reconcile only affected sources.
|
|
|
|
---
|
|
|
|
## Verification Results
|
|
|
|
Pre-remediation baseline. Backend focused tests passed 2,614 tests across seven
|
|
packages and `make -C apps/backend lint` reported zero issues. Frontend focused
|
|
tests passed 22/22, full web lint and typecheck passed, and the i18n gate
|
|
passed. Desktop and mobile E2E passed 14/14 and 4/4. Updater,
|
|
workflow-contract, action-pinning, and `zizmor` checks passed. Public-doc tests
|
|
passed 61/61 and the validator accepted 41 pages. `git diff --check` passed.
|
|
|
|
### Review remediation
|
|
|
|
The review pass corrected existing-PR selection and App-token propagation in
|
|
the weekly workflow, parsed GitHub output flags before catalogue selection,
|
|
preserved structural `use_default` intent, omitted failed lookup timestamps,
|
|
and retried failed page-local status refreshes. The second remediation pass
|
|
also applied least-privilege workflow permissions, package-safe selection
|
|
projection, bounded status lookups, shared default derivation, nil-cache
|
|
initialization, and default restoration when the active version is unknown.
|
|
The follow-up review pass updates terminal `current_version` from the
|
|
successful capability probe, makes the UI prefer terminal `effective_version`
|
|
and retain the complete backend version projection, derives managed-runtime
|
|
test expectations from the catalogue, and validates those commands before the
|
|
weekly workflow commits or pushes. It also keeps the repository token on the push
|
|
step and refreshes existing updater PRs by number. The authentication follow-up
|
|
replaces the unconfigured App token with the repository-scoped `GITHUB_TOKEN`,
|
|
adds explicit dispatch of all required validation workflows, and records the
|
|
repository Actions setting prerequisite.
|
|
|
|
Follow-up verification passes 2,594 focused backend tests, 32 focused web
|
|
tests, full web lint/typecheck/i18n, 7 pin updater tests, 8 workflow-contract
|
|
tests, 9 action-pinning tests, the 19-workflow action linter, `zizmor`, and
|
|
desktop/mobile E2E at 15/15 and 4/4. The aggregate backend phase of `make
|
|
test` passed; its full web Vitest phase did not reach a terminal summary, so it
|
|
was interrupted after the isolated `http-git-server` suite passed 3/3.
|
|
|
|
---
|
|
|
|
## Implementation Waves And Parallel Candidates
|
|
|
|
Execution is sequential in the primary conversation unless the user explicitly
|
|
authorizes subagents after choosing the implementation model.
|
|
|
|
Wave 1:
|
|
|
|
- [x] [Task 01: Add reviewed default pins](task-01-default-pins.md)
|
|
|
|
Wave 2:
|
|
|
|
- [x] [Task 02: Resolve and persist effective versions](task-02-effective-version-selection.md)
|
|
- [x] [Task 04: Automate weekly pin PRs](task-04-weekly-pin-workflow.md)
|
|
|
|
Task 04 is parallel-safe after Task 01 because it owns workflow and script files;
|
|
Task 02 owns runtime selection and API mutation files.
|
|
|
|
Wave 3:
|
|
|
|
- [x] [Task 03: Add cached update status](task-03-update-status-api.md)
|
|
|
|
Wave 4:
|
|
|
|
- [x] [Task 05: Show update awareness in Settings](task-05-settings-update-indicator.md)
|
|
|
|
Wave 5:
|
|
|
|
- [x] [Task 06: Prove desktop and mobile flows](task-06-update-awareness-e2e.md)
|
|
- [x] [Task 07: Update runtime documentation](task-07-runtime-version-docs.md)
|
|
|
|
Task 07 is parallel-safe after Tasks 01-05 because it owns documentation only;
|
|
Task 06 owns E2E fixtures and specs.
|
|
|
|
---
|
|
|
|
## Risks
|
|
|
|
- An exact top-level npm package does not lock transitive ranges. The design
|
|
intentionally accepts npm/cache/network behavior rather than claiming full
|
|
artifact reproducibility.
|
|
- A user selection is install-wide and affects remote commands built by the
|
|
same backend, but candidate ACP validation runs on the Kandev host. A remote
|
|
platform can still reject a host-validated package and must surface its normal
|
|
launch error.
|
|
- Registry checks must not delay application boot or agent launches. They are
|
|
page-triggered, read-only, cached, and separate from the agent catalogue boot
|
|
payload.
|
|
- Built-in-token branch and PR events do not recursively start validation
|
|
workflows. The updater dispatches each required check explicitly, and the
|
|
repository or organization must allow Actions to create and approve PRs.
|