Agent list connectivity status and empty metadata view
Agent list connectivity status and empty metadata view
Title
fix(server): report agent connectivity and empty metadata
Target branch
main
Summary
Two defects made a never-connected agent look healthy and then fail:
- The agent list rendered the administrative
enabledflag as the status column, and the Chinese label for that flag is “在线” (online).publicAgent(internal/server/api.go:1750) returned no connectivity field at all, so an agent created in the console showed as online before it ever dialed in. - The detail page requested
GET /api/v1/agents/{id}/metadata?includeStale=true. Whenagent_instance_metadataholds no row for the agent,scanAgentMetadatareturnssql.ErrNoRows(internal/storage/repository.go:1607), whichwriteStorageErrormaps to 404 (internal/server/api.go:1875). The console’s catch turned that into the “元数据加载失败” error banner with a retry button, and the designed empty state (web/src/views/AgentDetail.vue:56) was unreachable. The 404 conflated “agent does not exist” with “agent exists but never reported”, and the latter is a legitimate empty collection.
User impact
- The list now shows connectivity and the administrative switch as two columns: 在线/离线 (Online/Offline) from unexpired connection leases, and 启用/已禁用 (Enabled/Disabled) from the flag. A never-connected agent reads as “offline + enabled” instead of “online”.
- The detail page of a never-connected agent shows the “暂无元数据” empty state instead of an error banner.
- The create dialog switch now reads 启用/已禁用, matching what it actually controls.
API, schema, and configuration impact
GET /api/v1/agentsitems gainstatus: "online" | "offline". No other field changes;enabledkeeps its meaning.GET /api/v1/agents/{agentId}/metadatanow answers 200 with emptyitemsandinstancesarrays when the agent exists but never reported. 404 remains for “agent not found” and for “snapshot stale without includeStale=true”. The envelope always carries arrays rather than JSON null, because the console readsitems.lengthwithout a null guard.- No database schema or configuration change.
docs/api/openapi.yamlis updated for both.
Security impact
No change to authentication or authorization. The metadata handler still resolves and authorizes the parent agent before touching metadata, so the empty-view branch cannot be used to probe another user’s resources: an unauthorized caller gets the same 403 as before. The new batch lease lookup receives only the IDs already returned by the owner-filtered page query, so it cannot widen what a caller sees, in line with the rule that permission filtering happens inside pagination semantics.
Changes
internal/storage/repository.go:LeaseRepository.ListActiveAgentIDsplus itsleaseRepoimplementation, the batch form ofListActiveByAgent, mirroring the existingStatsByNodeIDsplaceholder pattern. Empty input issues no query.internal/server/api.go:listAgentsresolves connectivity for exactly one page viaonlineAgentIDsand renders rows throughpublicAgentWithStatus;handleAgentMetadatareturns the empty view onsql.ErrNoRowsand normalizes nil slices to empty arrays.internal/storage/storage_contract_test.go: contract assertions for the batch lookup (expiry filtering, IN filtering, empty input, post-release state).internal/server/agent_list_status_test.go: new red-light test walking offline -> online -> offline across lease register and release.internal/server/metadata_api_test.go: the subtest that pinned the old 404 contract now asserts the 200 empty view.web/src/views/Agents.vue,web/src/api/client.ts,web/src/i18n/messages/{zh-CN,en-US}.ts: two status columns,Agent.statustype, new i18n keys.web/src/tests/agents-view.spec.ts(new) andweb/src/tests/agent-detail.spec.ts: view-level guards.- Docs:
docs/user-guide/server-admin.md,docs/operations/troubleshooting.md,docs/api/openapi.yaml, retroactive plan, this record, regenerated indexes, rebuilt and re-embedded web assets.
Tests run
go build ./...go test ./internal/server ./internal/storage -count=1- passgo test ./... -count=1- no failing packagego test -race ./internal/server ./internal/storage -count=1go vet ./...- cleancd web && npm test -- --run- 34 files / 290 tests passcd web && npm run buildplusrsync -a --delete web/dist/ internal/server/web_dist/and./scripts/verify-web-embed.sh- mirrored and matchinggit diff --check- clean
Red-light evidence before the fix:
TestListAgentsReportsConnectivityStatus:never-connected agent status = "", want offline(field absent).TestMetadataAPI/missing_metadata_returns_an_empty_view:status = 404, body = {"code":404,"msg":"Not Found","data":{"error":"not found"}}, the exact envelope reported from the console.
Release steps
- Merge after review and CI checks.
- Deploy the Server, including the rebuilt embedded console. Agent and Client need no upgrade; there is no schema migration and no lockstep requirement.
- Canary on one node: confirm the list online count matches the dashboard
agentsOnline, and that a never-connected agent shows offline plus the empty metadata state.
Rollback steps
Revert the merge commit and redeploy the previous Server binary and embedded assets. No persisted state to unwind. Rolling back restores both defects: the list renders the enabled flag as connectivity, and a never-connected agent’s detail page shows the 404 error banner.
Reviewer focus
- Whether
statusbelongs on the list response or should be a separate endpoint; the batch lookup keeps it to one extra query per page. - Whether the empty metadata view should carry
stale: falseand zero-valued timestamps, which is what the console’s empty state expects. - The i18n wording change:
agents.activenow means “enabled”, so any other surface reusing that key would change meaning. Onlyweb/src/views/Agents.vueuses it. - Whether
ListActiveAgentIDsshould live onLeaseRepositoryor beside the dashboard aggregate; it is placed with the other lease queries because it filters the same table on the same expiry predicate.
Integration status
Implementation and local validation are complete on codex/agent-online-status-empty-metadata. The plan was confirmed by the user, including the decision to render connectivity and the enabled flag as two columns, and is recorded in docs/superpowers/plans/2026-09-23-agent-online-status-and-empty-metadata.md. Pull-request review and merge are pending.