fix(mcp): create-target DX — explicit folder_id, ambiguity guard, strict args#65
fix(mcp): create-target DX — explicit folder_id, ambiguity guard, strict args#65sVIKs wants to merge 5 commits into
Conversation
AI ReviewFixes a long-running production incident where Checklist
U5 detail: Issues foundwarning (U6) — Unknown-arg rejection is a silent breaking change with no CHANGELOG entry. warning (U6) — Ambiguity guard changes behavior for existing multi-marker directories. warning (design) — if req, ok := schema["required"].([]string); ok {This assertion succeeds only for Go-literal This review was generated automatically. A human maintainer should still make the merge decision. |
AI ReviewRe-review after synchronize: the third commit surfaces the server's actual error reason from Checklist
U5 detail: Issues foundwarning (U1, new — introduced by commit 3) — The third commit's subject line is 72 characters: The convention cap is 70. Two characters over — not blocking, easy to address if you rebase before merge. warning (U6, unresolved from initial review) — Unknown-arg rejection is a silent breaking change with no CHANGELOG entry. Any external integration, script, or saved prompt that passes extra keys to a tool will start failing after this PR merges. The new commit does not add a warning (U6, unresolved from initial review) — Ambiguity guard changes behavior for existing multi-marker directories. Users whose warning (design, unresolved from initial review) — if req, ok := schema["required"].([]string); ok {This succeeds only for Go-literal This review was generated automatically. A human maintainer should still make the merge decision. |
AI ReviewRe-review after synchronize: commit 4 ( Checklist
U5 detail: Fixed since last review ✅TestUnknownArgsError_EveryToolHasSchema is now a real safety net. The previous version checked only that Issues foundwarning (U1, unresolved from previous review) — Commit 3's subject line is still 72 characters: The convention cap is 70. Two characters over — not blocking. warning (U6, unresolved from previous review) — No warning (U6, unresolved from previous review) — No release-note callout for the ambiguity guard. Users whose warning (design, partially mitigated) — The if req, ok := schema["required"].([]string); ok {The improved test now covers This review was generated automatically. A human maintainer should still make the merge decision. |
41e9631 to
4961a81
Compare
|
Review findings addressed:
Heads-up: the [Unreleased] section may need a trivial merge with #62's once either lands — happy to rebase whichever goes second. |
AI ReviewRe-review after synchronize: commit 5 ( Checklist
U1 detail: All five commit subjects are within 70 characters (64 / 68 / 52 / 64 / 66). Valid types ( U5 detail: Fixed since last review ✅U1 — Commit 3 subject trimmed. Previously flagged as 72 characters ( U6/CHANGELOG — Breaking changes documented. Design — Issues foundNo issues found. This review was generated automatically. A human maintainer should still make the merge decision. |
Root cause of a long production incident: create-process ignored any folder_id argument (silently dropped as unknown), resolved its target from the local directory's <id>_<name>.folder.json marker, and when the working directory contained TWO markers (a pulled production stage next to develop) it silently picked the first one — production — where the only symptom was a misleading server error, "Stage is immutable". - create-process / create-state-diagram / create-folder now accept an explicit folder_id that overrides directory resolution; - a directory with more than one folder/stage marker is now an error naming the candidates instead of a silent first pick; - the result message states the target folder id and HOW it was chosen (explicit folder_id vs. which marker file), so a wrong destination is visible immediately. Verified live: explicit folder_id creates in the requested folder from a directory with ambiguous markers; the no-argument call in that same directory now fails fast with both marker names in the error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Unknown argument keys were silently dropped, so a mistyped or
undeclared argument let the call fall through to defaults and act on
the wrong object with no warning — create-process{folder_id: N} spent
an hour creating into a directory-resolved production folder before
anyone could tell the argument had never been read.
The dispatcher now validates args against the tool's InputSchema (the
registry is the single source of truth) and fails with the unknown
keys and the accepted list. The CLI's env-based folder_id default is
scoped to tools whose schema REQUIRES folder_id (pull-folder and
friends) — previously it was injected into every call, which this
validation immediately exposed on tools like login.
Verified live: create-process with a bogus argument fails with
'unknown argument(s) bogus_arg ... (accepted: folder_id, folder_path,
process_name)'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two DX gaps left after the create-target work:
1. CreateEmptyConv logged the server's rejection ("Stage is immutable",
access denied, ...) to mcp.log and returned a bare 0, so the tool
reported only "failed to create process 'x'" — undiagnosable through
MCP; this cost real field-debugging time when creates landed on an
immutable stage. It now returns (id, error) and the handler passes
the reason through, together with the resolved target:
Error: failed to create process 'x' in folder #685227
(explicit folder_id): API op error: Stage is immutable
2. CLI args always arrive as strings, but handlers type-assert
booleans — so `deploy-stage apply=true` silently ran as a dry-run
and `set-stage-immutable immutable=true` could never work from the
CLI. runCLI now coerces "true"/"false" for arguments whose
InputSchema type is boolean (schema-driven, so string args like
confirm="true" stay strings).
Live-verified on sandbox project 685225: create into the immutable
stage reports the real reason; create with explicit folder_id lands in
dev and reports its resolved target.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Four review findings on the create-target DX branch: - coerceCLIArgs accepts case-variants (True/1/yes) and REFUSES unparseable boolean strings with an error — a boolean the handler cannot read is exactly how `apply=True` degraded to a silent dry-run, the failure mode this branch exists to close. - unknown-argument rejections now flow through the normal result/analytics path instead of returning before the telemetry block, so field misuse is visible in analytics like any other error. - TestUnknownArgsError_EveryToolHasSchema asserts the built allowed set against each schema's declared properties (the old map-presence check was tautological: buildToolAllowedArgs always inserts a key, so a malformed InputSchema — empty allowed set, every call rejected — passed it). - drop resolveCreateTargetForFolder, a zero-value alias. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Review follow-ups: an [Unreleased] entry documents the two behaviour
changes operators must know about (undeclared tool arguments are now
rejected; ambiguous multi-marker directories refuse creates instead of
silently picking the first — previously production). The required-list
parser accepts both a Go-literal []string and a decoded-JSON
[]interface{}, with a test — a bare type assertion would silently
disable the CLI env-default for a registry entry loaded from JSON.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
4961a81 to
1bc87ba
Compare
AI ReviewRe-review after synchronize: the branch was rebased onto the updated Checklist
U1 detail: All five commit subjects are within the 70-character cap (64 / 63 / 52 / 69 / 64 chars). Valid types ( U5 detail: U6 / schema safety-net: Fixed since last review ✅All four findings from the previous review were resolved in commit 5 (reviewed 2026-07-11). This re-review found no regressions and no new issues introduced by the rebase. Issues foundNo issues found. This review was generated automatically. A human maintainer should still make the merge decision. |
The full lesson of the "process landed in production with a misleading Stage-is-immutable error" incident: every way a create can go to the wrong place — or fail without saying why — is closed.
1. Explicit
folder_idon create-process / create-state-diagram / create-folderAn integer
folder_idalways wins; otherwise the target resolves from the local<id>_<name>.folder.json/.stage.jsonmarker as before.2. Ambiguity guard
A directory containing MORE than one marker is an error naming all candidates — not a silent first pick (markers sorted
productionbeforedevelop, which is exactly how creates went to the immutable stage):3. The result reports the resolved target
Process 'x' created in Corezoid folder #685226 (explicit folder_id / resolved from local marker …)— a wrong destination is visible immediately.4. Strict arguments on every tool
The dispatcher rejects undeclared arguments (
unknown argument(s) bogus_arg … (accepted: folder_id, folder_path, process_name)). Unknown keys used to be dropped silently, lettingcreate-process{folder_id: N}fall back to directory resolution and create somewhere else entirely. A registry-wide test asserts every tool declares a schema.5. Create failures carry the server's reason
CreateEmptyConvreturned a bare0and the real reason ("Stage is immutable", access denied, …) went only tomcp.log. Now:6. CLI boolean coercion (schema-driven)
CLI args arrive as strings while handlers type-assert booleans —
deploy-stage apply=truesilently ran as a dry-run,set-stage-immutable immutable=truecould never work.runCLInow coerces"true"/"false"for schema-boolean args only (string args likeconfirmstay strings). The env-basedfolder_iddefault is also narrowed to tools whose schema REQUIRES it (it used to be injected even intologin).Live verification (sandbox project 685225)
folder_idinto dev → lands there, reports "explicit folder_id";Tests
go build,go test,go test -racegreen: ambiguity guard, explicit-id-wins, marker reporting, folder_id-to-the-wire (mock server), unknown-arg rejection, every-tool-has-schema, server-reason surfacing, CLI boolean coercion.🤖 Generated with Claude Code