Repository navigation
Conversation
…ord steps Both AI calls of the step already ask the model to justify its answer, and both justifications were dropped. Keep them, along with the fields the AI compared the candidates on, in pendingData and — when the loaded record is the one the AI suggested — in executionResult, so a run can be explained after the fact. No-AI paths (Manual, degraded-to-Manual, xToOne) persist nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The step already asks the model why it picked that field and value, and threw the answer away. Keep it in pendingData, then in executionResult next to the written values. A pre-recorded value has no AI reasoning, and a user who confirms a value different from the AI's makes the choice theirs, so the justification is dropped — re-submitting the AI value unchanged keeps it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each candidate tool is offered a top-level `reasoning` field before binding, so the model explains its pick within the same forced tool call rather than in an extra round-trip. Zod schemas (Forest connectors) are extended, JSON Schemas (MCP servers) get the property injected — duck-typed, since ai-proxy and the executor resolve different zod instances. The field is stripped from the args before the real tool runs, and persisted as toolSelectionReasoning, which stays client-side and survives the confirmation flow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5 new issues
|
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (4)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
PMerlet
commented
Sep 22, 2026
PMerlet
left a comment
Member
Author
There was a problem hiding this comment.
Spec: no Linear ticket is linked to this PR, so the functional check was limited to the PR description, which the diff matches.
…asoning Compare a confirmed record id by value, so the justification survives the confirmation the front actually sends, and only when the suggested relation is the one followed. Move the update-record justification to executionParams, written before the mutation and kept out of later steps' context by a formatter of its own. Never let re-reading the suggested value abort the update the user asked for. Ask an MCP tool that owns `reasoning` for the justification under a reserved key instead, and strip only the key that was injected. Persist the MCP justification in the write-ahead marker too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Probing a tool's schema for `.extend` threw when a parameterless tool declared none, failing the step before the model was ever called. Such a tool is now offered untouched, so it runs as it did and only its selection goes unexplained. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…elected The model answers with a tool name alone, and the tool behind it is the first of that name, so a second source exposing the same name must not overwrite the first one's key. It kept the last, which stripped the wrong argument and lost the justification. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The executors already compute that answer to decide whether the justification survives, then drop it, leaving a consumer to re-derive the disagreement by diffing the proposal against what was written, per step type. It is persisted as `aiSuggestionOverridden`, written only where an AI proposal was actually put to a human, so its absence never reads as agreement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An empty field list was persisted as the comparison the AI made, though it means no field-selection pass ran at all, and a blank or non-string answer was persisted as a justification. Both are now left out, the record justification replaces the field list as what says an AI ranked anything, and a tool selected without a justification is logged, which separates a silent model from an injection that never reached the provider. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 tasks
…oposal A boolean said a human disagreed without saying about what, and on a related record two disagreements were folded into one: following another relation and picking another record. The second has no meaning once the first happened, so `aiSuggestionRuling` names which one it was, `kept` / `record-changed` / `relation-changed` there and `kept` / `value-changed` on an update. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kept only what naming cannot carry: why two record ids are compared as text, why an empty field list is not an empty comparison, why a tool schema is duck-typed, and why re-reading a suggested value must not abort the update. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n prompts It lived in pendingData and executionParams, which the step summary replays into every later step. A formatter guarded the one path where the step completed, and the step summary has two others: a proposal the human rejected, and one they handled themselves. The justification now sits beside them, in `aiSuggestion`, which no summary path reads, so the formatter is gone with the leak it half-covered. The ruling also reads the relation from that suggestion. A relation preview rewrites pendingData.suggestedField to whatever the human asked to see, so comparing against it recorded every relation override as `kept`, with the AI's justification attached to a relation the human chose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This branch has not been deployed
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.

The three executors already ask the model to justify its answer and drop it. This keeps it, and records how a human ruled on it.
aiSuggestionof its own, written whenever an AI produced the proposal: the value reasoning forupdate-record, the compared fields plus the field and record reasoning forload-related-record, andtoolSelectionReasoningformcp. It sits besidependingData/executionParams/executionResult, none of which it touches, because the step summary replays those into every later step's prompt.aiSuggestionRulingrecords how a human ruled:kept/value-changedon an update,kept/record-changed/relation-changedon a related record. Absent means no AI proposal was put to a human, so absent never reads as agreement.pendingData.suggestedFieldto whatever the human asked to see, so the ruling reads the AI's own relation fromaiSuggestion.fieldinstead.mcp: areasoningproperty is added to each candidate tool's schema before binding, under a reserved key when the tool declares its own, stripped from the args before the tool runs. A tool whose schema cannot be augmented is offered untouched rather than failing the step.Info.Front counterpart: ForestAdmin/forestadmin#9974. Supersedes #1674.
No breaking change: every added field is optional,
StepOutcomeis untouched, and runs persisted by 1.29.0 are read without them.🤖 Generated with Claude Code
Note
Persist AI reasoning for
update-record,load-related-record, and MCP stepssuggestionandrulingfields to execution data types in step-execution-data.ts forupdate-record,load-related-record, and MCP steps.McpStepExecutor.selectTooldynamically injects a collision-safereasoningfield into Zod or JSON schemas, extracts it from the AI response, and excludes it from the actual tool invocation arguments.LoadRelatedRecordStepExecutorandUpdateRecordStepExecutorcapture AI reasoning during selection, persist it before executing or awaiting confirmation, and attach akept,record-changed,relation-changed, orvalue-changedruling when a user confirms or overrides the AI's choice.StepSummaryBuilderexcludes AI justification text from generated context summaries.McpStepExecutor.withReasoningField; tools with unsupported or missing schemas fall back to normal execution without capturing reasoning.Macroscope summarized 939af6d.