Skip to content

feat(doctor): read-only agent-bundle doctor for host installs and runtime endpoints (#101 stage 1) - #236

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
wave6/101-doctor
Sep 1, 2026
Merged

feat(doctor): read-only agent-bundle doctor for host installs and runtime endpoints (#101 stage 1)#236
ScriptedAlchemy merged 2 commits into
mainfrom
wave6/101-doctor

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Stage 1 of #101 per the G4 gate decision in #107 rev 2: a strictly read-only agent-bundle doctor CLI command that diagnoses install and runtime health per host. The deferred mutation lifecycle (repair/uninstall/reinstall, any --fix surface) stays deferred; repair guidance is expressed only as recovery text. Per G6, nothing here touches the Workbench.

Diagnostic surface

  • Host probesclaude/codex via bounded --version probes (5 s timeout, 1 MiB output cap, reusing the feat(claude): validate artifacts with host devtools #193 pattern): available (with version), unavailable (ENOENT — honest info skip), failed (started-but-failed probes are errors). Cursor via pinned directory evidence (~/.cursor), since no Cursor version-probe contract is pinned.
  • Installed-bundle inventory — Cursor: enumerates ~/.cursor/plugins/local using the pinned loader-manifest candidates (.cursor-plugin/plugin.json, .claude-plugin/plugin.json, plugin.json), flagging symlinked entries and manifest-less directories as corrupt and .name.stage-* leftovers as interrupted-install. Claude/Codex: honestly unknown — the hosts own their registries and no read-only inventory verb is pinned.
  • Bundle checks (--from <bundle-dir>) — identity resolution mirrors feat(install): make host bundles directly installable #195's install contract. Cursor: installed / missing / drifted (content digest differs, via the exported feat(install): make host bundles directly installable #195 treeHash) / conflicted (version collision) / corrupt (unsafe tree or invalid destination). Claude: registration proof via the pinned claude --plugin-dir <dir> plugin list --json verb (registered / unregistered / failed, skipped honestly when the binary is unavailable). Codex: unknown (recorded deferral).
  • Runtime endpoint scan — scans the event-runtime endpoint directory (paired with events/ipc.ts by a regression test; read-only): live sockets, stale-socket warnings, and claim locks interpreted per the fix(events): reclaim provably-dead orphaned endpoint claims instead of failing permanently #229 owner-record semantics — invalid owner record or provably-dead owner → stale-lock warning; an owner that cannot be proven dead → live with an info note that the runtime fails closed rather than stealing claims. win32 scan is an honest skip (no pinned named-pipe enumeration contract).

Conventions

  • Diagnostics AB7300AB7315, registered in docs/diagnostics.md (also records feat(install): make host bundles directly installable #195's previously undocumented AB700x family).
  • Exit code nonzero only on error-severity diagnostics; warnings/infos never gate. --json emits one stable JSON document.
  • The only touched landed file behavior: install.ts exports treeHash (no behavior change). events/ipc.ts untouched.

Test plan

  • pnpm lint — 0 errors, 0 warnings
  • pnpm typecheck
  • 26 doctor tests (probe states, inventory states, drift/conflict/corruption, registration proof, claim-owner liveness, endpoint-dir pairing regression, CLI human/JSON/exit codes) + install/event-ipc suites
  • Full unit pool: 2,330 passed, 0 failed, 5 skipped

@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b555172

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T23:33:14.462553Z b555172 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 1, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@236
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@236
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@236

commit: b555172

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5551722e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

finding: Object.freeze({ ...base, state: 'failed' }),
};
}
if (!containsPluginName(inventory, identity.name)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match Claude registration against the emitted plugin id

For a successful claude --plugin-dir ... plugin list --json, the pinned native harness validates registration through an id such as packed-native-smoke@inline (tests/support/packed-native-smoke.ts, lines 271–280), not through a property whose value exactly equals the bare plugin name. Consequently, a healthy direct-loaded bundle can fail this predicate, receive AB7311, and make doctor exit 1. Parse the documented list shape and match the expected <name>@inline id.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #243: the registration proof now follows the pinned contract from tests/support/packed-native-smoke.ts — top-level array, registered exactly when some entry has id === '<name>@inline'. A non-array shape is an honest AB7312 failure, and the name-only false positive is pinned by a regression test.

Comment on lines +376 to +377
typeof (value as { readonly name?: unknown }).name === 'string' &&
typeof (value as { readonly version?: unknown }).version === 'string'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept valid versionless Cursor manifests

When a locally installed Cursor plugin omits version, this rejects the manifest and reports the whole entry as corrupt with an error exit. The repository's pinned Cursor schema requires only name and explicitly leaves version optional (src/adapters/schemas/cursor/plugin.schema.json, lines 7–26), so Doctor misdiagnoses a host-valid installation; return the finding with an optional version instead of requiring this field.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #243: readInstalledManifest now matches the pinned Cursor schema — name required, version optional (non-string version still invalidates the candidate) — and the bundle version-collision branch only fires when the installed copy records a version, so a versionless destination with differing content reports drifted. Regression tests cover versionless-installed, non-string-version, and drifted-not-conflicted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant