feat(MAPCO-11434): implement a claimed ticket with the Agent SDK and verify it locally - #7
Open
razbroc wants to merge 1 commit into
Open
feat(MAPCO-11434): implement a claimed ticket with the Agent SDK and verify it locally#7razbroc wants to merge 1 commit into
razbroc wants to merge 1 commit into
Conversation
…ally MAPCO-11434. Hands a claimed ticket to the Claude Agent SDK inside the clone, runs the clone's own inferred test command, and retries up to a bound before giving up. The diff is left in the working tree; nothing is committed or pushed. The model's tool surface is the security control, not the prompt: `tools` names file and search tools only, so no Bash is built for the session, and Bash, PowerShell, the worktree tools, subagents, the network tools and every MCP tool are denied by name as well. Settings sources are off, so the clone cannot widen its own permissions. A write counts as a change only when its `tool_result` comes back without an error. Reading the attempted `tool_use` alone meant a failed Edit or a denied Write certified a pristine tree as a verified diff, which is the one thing this slice exists to guarantee. Not wired into runCycle yet, and `DescriptionPort` has no implementation: the poll does not fetch a description and `JiraTicket` carries none, so every ticket is handed back before the first model turn until that lands. The chart has no ANTHROPIC_API_KEY secretKeyRef yet either.
|
🎫 Related Jira Issue: MAPCO-11434 |
6 tasks
This was referenced Aug 27, 2026
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.
Stacked on #6 — review that first, and this PR retargets as the stack merges.
Hands a claimed ticket to the Claude Agent SDK inside the clone, runs the clone's own inferred test command, and retries up to a bound before giving up. The diff is left in the working tree — nothing is committed, nothing is pushed, no branch is created.
The tool surface is the control, not the prompt
toolsis the SDK's base-set option, so the tools not named there are never built for the session: there is noBashfor the model to reach for, no subagent to delegate agit pushto, and no MCP server it can be handed. On top of that,Bash,PowerShell, the worktree tools,Agent/Task, the network tools,Skillandmcp__*are denied by name — a deny rule outranks every other step of the permission evaluation, so they stay off even if someone later widens the base set.settingSources: []withstrictMcpConfigmatters more than it looks: the clone is a repository off the internet, and with the project source enabled its own.claude/settings.jsonwould be read as permission rules — letting the thing being worked on widen what may be done to it. The cost is that the clone'sCLAUDE.mdis not loaded either, which is a real loss and the right side of the trade.src/agent/prompt.tsalso asks the model not to use git. That sentence is documented as not being the control. It is there so a model that goes looking is told why, anddeniedToolsreports it if it does.This was a genuine bug on the way in, and it is the one worth a reviewer's attention.
wroteFilesoriginally inferred "the tree changed" from an attemptedtool_useblock. So anEditwhoseold_stringdid not match, or aWritethe permission layer refused, reportedchanged— and the worker went on to run the clone's suite against an unmodified tree, watch it pass, and certify a verified diff that did not exist. Certifying the diff is the entire point of this slice.It now pairs each write
tool_usewith thetool_resultthat answers it, by id, and treats a missingis_erroras success rather than reading a falsy value as a pass. A call that was denied, or that the run never got back to, has notool_resultat all and so counts as no change. Four tests cover it: a refused write, a write that never came back, a mix where one of two landed, and a successful read result that must not satisfy an unrelated write attempt.The test plan is taken before the model runs
The inferred command is read off the pristine clone before the first hand-off and every attempt is graded against that same snapshot, so a model cannot rewrite the command that grades it. Inference is
test:ci→test→test:unitfrom the clone's ownpackage.json, with the npm placeholder script rejected.Install lifecycle scripts are compared before
npm ciruns them, and a manifest the model moved installs with--ignore-scripts.spawnRunnerusesshell: falseand stripsGITHUB_TOKEN/GH_TOKEN/GH_PATfrom the child environment — proven by a real spawn, not a mock.SDK bindings were read off the installed package
Worth stating because it nearly went wrong: fetching the TypeScript reference returned invented type names, so every option, message field and the
mcp__*wildcard was verified againstnode_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts(v0.3.237) instead. Nothing here rests on a guessed binding.One caveat a reviewer confirming the no-shell guarantee should know: the
toolsdocstring notes that native builds may provide search via Bashfind/greprather than the dedicated Grep/Glob tools. That is about search availability, not about Bash becoming reachable.Acceptance criteria
ANTHROPIC_API_KEYonly, no fallback to the login token, and the login token is stripped from the model's child env. The chart half is missing —helm/templates/deployment.yamlwires every sibling env var and no model credential, so the pod crash-loops with no value to set.cwdand the prompt are right, butDescriptionPorthas no implementation: the poll does not fetch a description andJiraTicketcarries none, so every ticket is handed back before the first model turn. Four lines acrossPOLL_FIELDS,McpTicket,toTicketandJiraTicket.handBackTicketarrives in thecount-attemptsPR.)Not wired in
implementTickethas no caller.src/cycle.tsis untouched, and there is no clone-provisioning step yet (MAPCO-11433), so the ticket's Expected Result cannot be demonstrated end to end from this PR alone.Refs: MAPCO-11434