fix(server): orchestration var resolution, extraction gating, and error messages - #526
Merged
Merged
Conversation
…essages
Addresses the SOAT bug/feature report from the Marketing Squad build. Several
reported items (MCP action scoping F-2, knowledge similarity_score F-3, and
input_schema crediting in validation F-4) were already resolved on main; this
change fixes the genuinely-open items and adds regression coverage.
Orchestration variable resolution (F-4/F-5):
- Seed run input under an `input` namespace in addition to flat top-level keys,
matching the pipeline/formation `{"var":"input.<name>"}` convention, and
credit that namespace in the static validator. A graph authored against the
documented contract now sees its run input in every node expression and
input_mapping.
- Make orchestration-run `input` a caseTransform pass-through so snake_case
input keys round-trip verbatim; previously they were camelCased into state
while `var` references kept the original casing, so every underscore-bearing
input key resolved to null inside node logic even though it reappeared in the
snake_case-restored final-state dump.
- writeToState builds a nested object for a dotted output_mapping target
(`state.proposed.action_id`) so a downstream node reads it back with
`{"var":"proposed.action_id"}` (the var reader descends dot-paths).
- buildRunError serializes a non-Error thrown value (e.g. json-logic-engine's
bare `{type:'Unknown Operator'}` for a multi-key map mapper) instead of
collapsing it to the opaque "[object Object]".
Memory extraction gating (F-6) and stale config (F-7):
- Add a per-turn `extract` flag on POST /agents/:id/generate: `false` suppresses
extraction for a single (e.g. operational) turn, `true` forces it on for a
turn even when the agent does not enable it by default; omitted follows the
agent's stored config.
- Normalize knowledge_config casing at extraction read time so a
formation-deployed agent whose stored config is still snake_case extracts
correctly without needing to be re-saved.
Docs updated (agents, memories, orchestrations) and a smoke step added for the
input namespace. parseDuration extracted to orchestrationDuration.ts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NakdELuVYYyjpoyV9VcSKW
arantespp
enabled auto-merge (squash)
July 13, 2026 07:43
…olds The new non-Error handling in buildRunError (describeThrown) added defensive branches — empty-object serialization, primitive throws, and the circular/non-serializable catch — that no REST entry point can trigger, which dropped global coverage below the tuned jest thresholds and failed CI's `pnpm run test`. Add a direct lib test for the exported buildRunError covering Error, DomainError, a bare object, an empty object, a primitive, and a circular value, so every branch is exercised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NakdELuVYYyjpoyV9VcSKW
…riteToState Per maintainer request, relax the per-file coverage threshold from 80 to 75. Also cover the new dotted-path branches of writeToState (multi-level nesting, merge into an existing object, and overwriting a non-object/array intermediate) via applyOutputMapping, so the new orchestration state-write behavior is fully exercised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NakdELuVYYyjpoyV9VcSKW
Deploy Outputs
|
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.
Addresses the SOAT bug/feature report from the Marketing Squad build. While triaging, several reported items turned out to be already resolved on
main; this PR fixes the genuinely-open items and adds regression coverage across the board.Already resolved (verified, regression-guarded where useful)
actionsis an enforced allowlist onmcptools (filtered at enumeration and rejected at call time). The report'sactions: nullpremise is stale. No code change; a denylist remains a possible future add-on.knowledge/searchnull relevance scores. The field was renamedscore→similarity_scoreand is populated from the pgvector distance (1 - distance) and sorted descending; the null-score symptom no longer reproduces. Added a characterization test asserting a query populates a numericsimilarity_scoreand thatmin_scorefilters against the true value.input_schemanot crediting{"var"}. The validator already creditsinput_schema.propertiesfor bare references. The real gap was the namespaced form — fixed below.Fixed
Orchestration variable resolution (F-4 / F-5)
inputnamespace in addition to flat top-level keys, matching the pipeline/formation{"var":"input.<name>"}convention, and the static validator credits that namespace. A graph authored against the documented contract now sees its run input in every node expression andinput_mapping.inputis now acaseTransformpass-through (like a tool'sinput). Previously snake_case input keys were camelCased into state whilevarreferences kept the original casing, so every underscore-bearing key resolved tonullinside node logic even though it reappeared in the snake_case-restored final-state dump — exactly the reported symptom.output_mappingtarget (state.proposed.action_id) now builds a nested object, so a downstream node reads it back with{"var":"proposed.action_id"}(thevarreader descends dot-paths).buildRunErrorserializes a non-Errorthrown value (e.g.json-logic-engine's bare{type:'Unknown Operator'}for a multi-keymapmapper) instead of collapsing it to the opaque[object Object].transform/conditionevaluate against full state; projecting nodes evaluateinput_mappingagainst state and receive only the projection;pollaugments state withresponse/attempt. Unifying them would be incorrect (an agent node must not receive the whole state as its prompt).Memory extraction (F-6 / F-7)
extractboolean onPOST /agents/:id/generate:falsesuppresses extraction for a single (e.g. operational/tool-listing) turn,trueforces it on for a turn even when the agent does not enable it by default (given awrite_memory_id); omitted follows the agent's stored config. No effect on streaming/requires_actionturns.knowledge_configcasing is normalized at extraction read time, so a formation-deployed agent whose stored config is still snake_case extracts correctly without needing to be re-saved.Tests
knowledge.test.ts— numericsimilarity_scorecontract +min_scorefiltering.orchestrationValidation.test.ts— namespacedinput.<name>reference accepted against a closedinput_schema.orchestrations.test.ts— run input visible via theinputnamespace; dottedoutput_mappinground-trips through a nestedvarread; a non-Errorthrow records a readable message.memoryExtraction.test.ts—extract:falsesuppresses,extract:trueforces, and a stale snake_case stored config still extracts (read-time normalization).smoke-tests.sh— deterministic input-namespace run assertion.Verification
pnpm typecheck,pnpm eslint,pnpm lint-openapi, andpnpm docs-lintpass. Affected unit suites (orchestrations, orchestrationValidation, orchestrationNodeExecutors, memoryExtraction, knowledge, tools, agents, jsonLogicMapping, mcp, caseTransform) all pass against the real Postgres/pgvector testcontainer. SDK and CLI were regenerated from the OpenAPI change (extractfield); generated files are gitignored and rebuilt in CI.🤖 Generated with Claude Code
https://claude.ai/code/session_01NakdELuVYYyjpoyV9VcSKW
Generated by Claude Code