Skip to content

fix: address code-review follow-up nits (confirm-stamp ordering, gate error classification, guest remap scope, RG existence check) - #41

Merged
Gregory Joseph (gnjoseph) merged 1 commit into
feat/spe-mcp-serverfrom
agents/pr3-followups
Jul 9, 2026
Merged

fix: address code-review follow-up nits (confirm-stamp ordering, gate error classification, guest remap scope, RG existence check)#41
Gregory Joseph (gnjoseph) merged 1 commit into
feat/spe-mcp-serverfrom
agents/pr3-followups

Conversation

@gnjoseph

Copy link
Copy Markdown
Collaborator

Four LOW-severity follow-up nits from the PR #3 review — each small, surgical, and low-risk. Full pipeline green (lint / typecheck / build / test), 731 passed | 3 skipped.

The 4 nits

1. Stamp confirmed BEFORE billing elicitation (src/tools/provision.ts) — robustness

Previously the session was stamped confirmed only after the owning app was resolved, which ran after the billing-model / sub-RG elicitations. If the agent dropped appSelection while answering a later billing prompt, the always-ask app gate could re-fire.
Fix: stamp the confirmation flag as soon as the owning app is settled (right after the app gate, before any billing/sub-RG elicitation can return-and-reenter). The ownerScope gate is relaxed to key on resolved intent only (arg ?? state) — not session confirmation — so the earlier stamp doesn't suppress its ask. The confirmBilling financial-safety gate ordering (guided sub/RG → region check → confirmBilling → createSyntexAccount) is unchanged, and the app-gate always-ask semantics are preserved.
Test: orchestration.test.ts — app settled + a subsequent billing round-trip that omits appSelection does NOT re-return the app-selection ask.

2. Move context gate inside the handler try (control-plane mutation tools) — error classification

resolveContextGate(contextChoice) on confirm calls stampContextConfirmedwriteStatewriteSecureFile, which can throw (EACCES/EIO). The gate was called above each handler's try, so a stamp-write failure surfaced via the generic dispatch catch instead of the tool's own classifier.
Fix: moved the gate inside the existing try (still before the tool's main work) in create-container-type.ts, register-container-type.ts, container-type-app-grants.ts (add + remove), and container-type-permissions.ts (owner grant + revoke). Early-return-on-gate behavior is identical.
Test: gate-error-classification.test.ts — with writeState mocked to throw, every gated handler called with contextChoice=confirm returns its own classified isError result (not an unhandled throw).

3. Scope the guest-owner error remap to the self-target path (src/tools/container-type-permissions.ts)

isGuestOwnerRejection(e) remapped any Graph error mentioning "guest"/"#ext#" to guest guidance — so an explicit member userId grant failing for an unrelated "guest"-containing reason got misdirected guidance.
Fix: apply the guest-owner-rejection remap only when the target is the signed-in user (default self-target). Explicit-userId failures surface the raw reason(e). The proactive userType === "Guest" self-target check is unchanged.
Test: container-type-tools.test.ts — explicit member userId failing with a "guest"-containing message → raw reason surfaced (NOT guest guidance); self-target guest rejection → guest guidance (existing behavior preserved).

4. Verify a user-entered new resource-group name (src/tools/standard-billing-target.ts) — fail cost-free

On the 0-RG path the helper prompted for a NEW RG name and proceeded unverified; a typo/non-existent RG only failed later at createSyntexAccount — after the container type existed. The server cannot create the RG.
Fix: added a non-throwing resourceGroupExists(name, subscriptionId) helper (src/azure-cli.ts, az group show) and probe the entered name. If it definitively does not exist → return actionable guidance (az group create ... then re-run) and fail cost-free before any CT/billing resource is created. An indeterminate probe (az missing / auth / transient) degrades gracefully to the prior behavior (proceed). Auto-select-singleton and multi-RG elicit paths are unchanged.
Test: standard-billing-target.test.ts (unit: missing → guidance/does-not-proceed; exists → proceeds/verified; indeterminate → proceeds/unverified; singleton + multi paths never probe) and provision-guided-billing.test.ts (integration: 0-RG + missing entered name → cost-free guidance, createApplication/createContainerType/createSyntexAccount NOT reached).

Guardrails honored

Local pipeline

npm run lint ✓ · npx tsc --noEmit ✓ · npm run build ✓ · npm testTests 731 passed | 3 skipped.

… error classification, guest remap scope, RG existence check)

Four LOW-severity follow-ups from the PR #3 review, each small and surgical:

1. provision.ts - stamp the session confirmed as soon as the owning app is
   settled, BEFORE the billing-model / sub-RG elicitations can return an
   agent-guided ask and be re-invoked. Prevents the always-ask app gate from
   re-firing if the agent drops appSelection while answering a later billing
   prompt. The ownerScope gate is relaxed to key on resolved intent only (not
   session confirmation) so the earlier stamp does not suppress its ask. The
   confirmBilling financial-safety gate ordering is unchanged.

2. Control-plane mutation tools (create/register container type, app-grant
   add/remove, owner grant/revoke) - move resolveContextGate() INSIDE each
   handler's try so a stamp-write failure on contextChoice=confirm
   (writeState/writeSecureFile EACCES/EIO) is classified by the tool's own
   error handling instead of the generic dispatch catch. Early-return-on-gate
   behavior is unchanged.

3. container-type-permissions.ts - scope the guest-owner-rejection remap to the
   self-target (signed-in user) path only. An explicit member userId grant that
   fails for an unrelated reason mentioning "guest" now surfaces its raw reason
   instead of misdirected guest guidance. The proactive userType==="Guest"
   self-target check is preserved.

4. standard-billing-target.ts / azure-cli.ts - verify a user-entered NEW
   resource-group name (0-RG path) via a non-throwing resourceGroupExists probe
   (az group show). A non-existent RG now fails COST-FREE with actionable
   guidance before any container type / billing account is created, instead of
   only failing later at createSyntexAccount. An indeterminate probe (az
   missing / auth / transient) degrades to the prior behavior. The
   auto-select-singleton and multi-RG elicit paths are unchanged.

Tests: gate-error-classification.test.ts (Nit 2) and standard-billing-target.test.ts
(Nit 4) added; container-type-tools.test.ts (Nit 3), orchestration.test.ts (Nit 1),
and provision-guided-billing.test.ts (Nit 4 integration) extended.
Full suite: 731 passed | 3 skipped. lint / typecheck / build all clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gnjoseph
Gregory Joseph (gnjoseph) merged commit 8e83795 into feat/spe-mcp-server Jul 9, 2026
5 checks passed
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