Repository navigation
P8: lazurio doctor — the Environment's state in one read-only answer, every check mapped from an existing reader (base-instructions-12) - #77
Conversation
…y check mapped from an existing reader (launchpad-parity B9, P8)
- src/doctor/doctor.ts: collectDoctor composes collectRecovery (update
state, self-check, Folder state, unit, health socket), readStatus (running
vs active, a verified newer release, Folder refresh needed), observeFolder
(preset, template revision), the recorded Machine binding against the live
handover (identity, then document digest; reason only, never a digest),
toolsOverview under the Folder's read lock or toolsStatus without one, and
readFolderCatalog (every Organization and module with its typed reason).
No check of its own, no write, no network; sign-in probes only with
--sign-in.
- One shape per check, {id, outcome ok|warn|fail|skipped, reason?,
context?}: enumerated ids and reasons, and a context allowlist that
extends recover's tier 1 (catalog names only in identifier form, else
`invalid`). Verdict ok|attention|broken; exit 0, 10, 3, usage 2.
- src/doctor/cli.ts: lazurio doctor [--folder] [--sign-in] [--json]; the
human form groups the checks in the catalog's column style (control
characters escaped), Czech or English by the Folder's locale. The
catalog's columns() is exported for it.
- tests/doctor.test.ts: healthy fixture ok with nothing written, pending
transaction broken, missing required and recommended tools, a module not
executable, the hosted binding against a rewritten or foreign handover,
Recovery mode on the health socket, update available in Czech, usage, and
the real command line; JSON checked against the tier-1 rules and the human
rows against the JSON.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… product update section (base-instructions-12) One bullet, the same on a hosted Machine and a workstation, after `lazurio update status`: doctor shows the Environment's state read-only (product, Folder, tools, Organizations and modules, Launchpad) and changes nothing. The template revision becomes base-instructions-12 and the snapshots are regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…th its source, the tier-1 answer and the C.2 preflight Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Taking the operation lock of a state without `.operation-lock` creates the lock directory, a write. Doctor now reads the tool selection under the lock only when recover's Folder check is ok; otherwise it reads the catalog's tiers with the selection the Folder recorded, without a lock, as observeFolder does. A test removes the lock directory and finds the tree unchanged after doctor. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — exact-head review of 8b3273a (P8). REQUEST_CHANGES; do not merge.
-
[P1]
--jsonleaks free text through a tool version (src/doctor/doctor.ts:204-206,src/doctor/doctor.ts:417-436). The Tier-1 validator allows an arbitrary 40-character alphanumeric prerelease/build suffix, andtoolsStatusextracts it from tool stdout. In a disposable fixture with a Folder namedfolderincidentorchidand fakegh --versionoutputgh version 2.63.0-versionincidentorchid, the CLI returnstool.context.toolVersion: "2.63.0-versionincidentorchid"; the Folder marker itself does not appear. This directly breaks the stated no-free-text JSON contract. Keep only a bounded product-defined version form (e.g. numeric segments or explicitly enumerated prerelease types), or omit an unrecognized tool version, and add a regression that probes both Folder name and tool output. -
[P2] The read-only assertion does not prove byte identity (
tests/doctor.test.ts:285-309).tree()records only path, size and mtime; overwriting a file with different same-length bytes while retaining mtime still passes. Hash/compare file contents (and symlink targets as appropriate) before/after healthy and unrecognized-state runs. The unrecognized lock-directory regression currently passes; this is an evidence gap, not an observed write.
Evidence: isolated detached clone at the named SHA; bun --smol test tests/doctor.test.ts 10 pass; manual/render/tools snapshot shard 57 pass, 37 snapshots; lint/typecheck pass; git diff --check clean. Both required CI jobs (check (ubuntu-24.04), check (macos-14)) succeeded on this SHA, and no unresolved review threads were present. The existing reader mappings and base-instructions-12 snapshots were traced; the current human table is projected from the same result, though its parity test checks only selected fields rather than every context value. No merge performed.
…ad-only proof compares file bytes and link targets (review 5341832065)
- [P1] toolVersion admitted a 40-character suffix, so a version line such
as `gh version 2.63.0-versionincidentorchid` carried free text into
--json. The rule is now `^[0-9]{1,6}(\.[0-9]{1,6}){0,3}$`; anything else
is dropped from the context and the check keeps its outcome. A regression
test puts the marker in the Folder's name, an Organization directory and
both tools' version lines and finds it in neither output.
- [P2] tree() records mode, size and modification time of every entry, the
SHA-256 of every file's bytes and every link's target (lstat), so the
before/after comparison of the healthy, pending and unrecognized-state
runs proves byte identity.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # docs/launchpad-development.md
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — APPROVED on exact head e4b700e (base main after #79); do not merge in this review lane.
Both blockers from review 5341832065 are fixed. The tool-version context now admits bounded numeric segments only and omits suffixes. I repeated the version-line probe: gh 2.63.0-versionincidentorchid and composio 0.7.1+incidentorchid are extracted but omitted from public context, while plain gh 2.63.0 is retained. The end-to-end regression plants incidentorchid in the Folder name, Organization directory and both version outputs; neither --json nor human text leaks it. The read-only tree comparison now includes SHA-256 of file bytes and symlink targets, and passes for healthy, pending and unrecognized state.
Isolated detached clone at this SHA: doctor tests 11 pass (including the version-line probe); folder manual/render/tools 57 pass and 37 snapshots; lint and typecheck pass; git diff --check clean. Required macOS 14 and Ubuntu 24.04 check runs are completed/success on this exact SHA. Final live preflight: mergeable, zero unresolved threads, no pending reviewer requests. No merge performed.
Why
Slice P8 of the Launchpad parity program (#65 B9; C.2 step 4): the read-only preflight of the Machines switch and the operator's or agent's one-command view of an Environment's state. Until now that view was spread over
update status,tools status,organization list,recoverand the handover commands.What changes
lazurio doctor [--folder <abs>] [--sign-in] [--json]:{kind: "doctor", verdict ok|attention|broken, locale, checks[{id, outcome ok|warn|fail|skipped, reason?, context?}]}; exit 0 / 10 / 3, usage 2. No check of its own — every answer is mapped from an existing reader: update state, self-check and Launchpad unit/health fromcollectRecovery; running vs active version, update available and Folder refresh fromreadStatus; Folder state, preset, Machine kind and template revision fromobserveFolder; the recorded binding vs the live handover (machine-identity-changed,handover-changed,binding-absent,handover-unreadable, never a digest); tools from the catalog tiers (required missing = fail, recommended/enabled missing = warn); every Organization and module fromreadFolderCatalogwith its typed reason (unreadable Organization or non-executable module = warn);machine-entryrecorded or not on a hosted preset.recover: every context passes the recover allowlist plus doctor's own enumerated keys; no absolute path, no free text; names that are not identifier-shaped print asinvalid.lazurio doctorin the product-update section (hosted and workstation).docs/launchpad-development.md"Doctor" (every check id with its source, the C.2 preflight).What deliberately does NOT change
No update/recover/module core, no Launchpad server, no
/settings/diagnosticspage. Sign-in probes are opt-in (--sign-in) because they may contact providers.Verified
Author of the slice:
bun run check907 pass. Thread owner'sbun run checkon 8b3273a: 907 pass, 0 fail, exit 0. Linux supervised paths covered only through recover's existing tests (no real systemd here).Review round (Pablo 5341832065, head 8b3273a → e4b700e)
toolVersionadmits numeric segments only (^[0-9]{1,6}(\.[0-9]{1,6}){0,3}$), anything else omitted; regression probes a Folder name, an Organization directory and both tools' version lines with the incidentorchid marker in --json and human output.tree()now hashes file bytes and records symlink targets; used on the healthy, pending-transaction and unrecognized-state runs.mainafter P6: the gateway's GET /api/internal/hosted/modules/<id>/ensure, answered by the module core with the resident's wire contract; internal namespace same-origin even on GET #79. Thread owner'sbun run checkon 3265e17: 908 pass, 0 fail, exit 0; lint/typecheck/doctor/snapshot/ensure tests on e4b700e.Risks / follow-ups
collectRecoveryas-is, which also prepares recover's evidence and issue (read-only, thrown away) — a cost, not a defect.docs/launchpad-parity.mdrow 30 still says "not implemented" (the table has no status column to edit consistently).🤖 Generated with Claude Code