fix(onboarding): the install intro read config as an object; it is a Map - #947
Merged
Conversation
Caught by live verification, not CI, and it made #943 a no-op on the exact path it was written for. `AgentInstallation` declares `config: { type: Map, of: Mixed }` (AgentRegistry.ts:235). On a LIVE Mongoose document `config.runtime` is therefore undefined — a Map needs `.get()`. `deriveAgentState` reads `config?.runtime` directly, so the install route handed it a document, it saw an empty runtime, answered `unknown`, and chose the cheerful invitation copy. The proof was two readers of one field disagreeing. After the host:'byo' stamp shipped, a seat minted through the REAL connect flow derived: /agent-states (uses .lean(), Maps become plain objects) → never-connected install route (holds the live document) → unknown posted intro: "Mention me with @stamp-verify-agent when you need me." Exactly the promise #943 exists to stop making, to a seat with nobody home. Every unit test passed throughout — they all pass plain objects, which is the shape `.lean()` gives and the shape this call site did not have. So the new suite pins the failure mode itself: the same input as a Map reads `unknown` with no fixCommand, and normalizing it first restores `never-connected`. Built field-by-field rather than spread, because spreading a Mongoose document copies internals instead of fields (they live under `_doc`) and would fail the same silent way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8
lilyshen0722
added a commit
that referenced
this pull request
Aug 14, 2026
…ap (#948) The dry run caught this, which is the only reason it is a commit and not an incident. `config` is `{ type: Map, of: Mixed }` (AgentRegistry.ts:235), so on a live Mongoose document `config.runtime` is undefined. The scan omitted `.lean()`, so every row filtered out on a property that cannot exist. Against production: installs scanned: 545 already stamped: 0 <- two seats were demonstrably stamped candidates to stamp: 0 <- ~200 are candidates With `--apply` that would have written nothing and printed success — the migration-reports-success-and-writes-nothing failure this file already warned about, arriving through a different door than the one guarded. This is the same Map-vs-object defect just fixed in the install intro (#947). I wrote this script before diagnosing that one and never came back to it, which is the actual lesson: the fix was applied where the bug was found rather than everywhere the shape is read. Scan now uses `.lean()`, plus a `runtimeOf` reader that handles both shapes so a caller passing a live document degrades to a correct read instead of a silently empty one. Write switched to `updateOne` with a dotted `$set`: the rows are lean so there is no document to save, a dotted path writes one key without rewriting the whole config (a concurrent install touching another key is not clobbered), and it sidesteps Mixed-path dirty tracking entirely. The counter now reports what the DB says it modified rather than what the loop intended. Claude-Session: https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Caught by live verification, not CI — and it made #943 a no-op on the exact path it was written for.
The bug
AgentInstallationdeclaresconfig: { type: Map, of: Mixed }(AgentRegistry.ts:235). On a live Mongoose document,config.runtimeis thereforeundefined— a Map needs.get().deriveAgentStatereadsconfig?.runtimedirectly, so the install route handed it a document, it saw an empty runtime, answeredunknown, and picked the cheerful invitation copy.How it surfaced
Two readers of one field disagreeing. After the
host:'byo'stamp (#945) shipped, I minted a seat through the real connect flow and asked both:That is precisely the promise #943 exists to stop making, to a seat with nobody home.
Why no test caught it
Every unit test passed throughout, because they all pass plain objects — the shape
.lean()gives, and the shape this call site did not have. Green CI, inert in production.The new suite pins the failure mode itself rather than the fix:
unknownwith nofixCommand— the live bug, assertednever-connected+commonly agent run <name>Note on the shape
Built field-by-field rather than spread: spreading a Mongoose document copies internals instead of fields (they live under
_doc) and would fail the same silent way.Still outstanding after this
The pre-existing seats —
verify-seat,npm-verify-agent, and the ~200 others — still readunknownand needscripts/backfill-byo-host-stamp.ts(merged in #945, dry-run by default).🤖 Generated with Claude Code
https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8