Failure-mode roadmap
Validated against main on 2026-08-22. Open items name the remaining code,
tests, and acceptance conditions. Completed work is recorded at the end.
Do not implement a check until it fails on a reproduction or positive-control fixture. An audit must enumerate its targets and assert that it found them.
1. Add an authenticated production /mcp endpoint
Status: OPEN. Issue #5126.
packages/websocket/src/server.ts mounts /mcp only outside production. The
development mount always uses user "1", so it cannot be enabled unchanged.
Python is not part of this item; its production opt-in already exists.
Implementation:
- Add
NODETOOL_ENABLE_MCP=1. Keep the production route absent by default. - When
enforceAuthis true, use the existing global bearer-token hook and buildagentToolsScopefromreq.userIdwith source"http-session". - When
enforceAuthis false, require a newNODETOOL_MCP_TOKENof at least 32 characters. Validate it inside the globalonRequesthook, before the generic remote-client denial. Compare it in constant time. Do not add a general/mcppublic-route exemption. Add"http-token"to the MCP scope source union and use user"1"only after the token passes. - Return 401 for missing or invalid credentials. Keep the global rate limit, CORS policy, and JSON-RPC input validation on the route.
- Add both variables to the config setting catalog and its Zod validation.
- Add production route tests under
packages/websocket/tests/for both auth modes, a non-loopback client, invalid credentials, the default 404 result, and authenticated GET/DELETE requests after session initialization. - Add an opt-in probe to
scripts/docker-smoke.mjs. Send a complete MCPinitializerequest, includingjsonrpc,id, protocol version, capabilities, and client information. - Document the two MCP flags in
docs/configuration.mdandAGENTS.md.
Acceptance:
- Authenticated production derives the MCP user from the validated session.
- Local production requires the dedicated token.
- Production without the enable flag returns 404.
npm run test --workspace=packages/websocketpasses.
2. Include missing secrets in validate_workflow
Status: OPEN.
validate_workflow calls validateGraph in
packages/agents/src/capabilities/workflows.ts. Changing
modelSelectionError does not change this capability.
Implementation:
- Add optional
availableSecrets(keys)toCreateCapabilityRunOptionsand propagate it throughcreateCapabilityRuntoCapabilityRun. - Pass it to
validateGraphfromvalidate_workflow. - Enumerate every
createCapabilityRuncall site in an audit test. Server, CLI, and MCP hosts with a real secret resolver must inject a callback that uses theirProcessingContext. Hermetic eval hosts must omit it explicitly. - A
missing_secretissue must name the key and direct the agent to Settings > Credentials. Mentionrequest_secretonly when that capability is present in the run’s registered capability set.
Tests must show that a missing key produces an issue, an available key clears
it, and an absent callback preserves current output. Run
npm run test --workspace=packages/agents -- workflows.
3. Make media resolution the browser rendering boundary
Status: OPEN. Follow-up to #4873, #4929, #5028, #5078, #5122, and #5123.
The reported leaks are fixed. Canonical resolution exists in
web/src/utils/resolveMediaUri.ts and web/src/hooks/useResolvedMediaUri.ts.
asset:// remains a valid stored locator, so a zero-hit grep is invalid.
Implementation:
- Add a
ResolvedMediaUrlbranded string toresolveMediaUri.ts. Brand only a non-empty resolved URL; keep missing resultsnullorundefined. - Extend
ResponsiveImageandVideoPlayerinstead of creating parallel image/video primitives. Add one audio primitive. They accept aMediaLocatorand resolve it before settingsrc. - Migrate chat media, storyboard cards, sketch layers, script shot chips, node previews, and App Builder media widgets.
- Check in a consumer inventory for the named surfaces and assert that each
uses a locator-aware primitive. Add a design-lint rule that rejects an
asset://string literal in a JSXsrc. Include a positive-control fixture.
Acceptance: upload -> graph input -> render works for image, video, and audio; the primitives cover stored locators and HTTPS URLs; the lint fixture proves the rule can fail.
4. Preserve provider behavior through decorators
Status: OPEN. Issue #5109.
CassetteProvider inherits generateLoop and can drop an inner override. A
method-existence test cannot detect this.
- For each decorator method, either forward it or reject construction when the behavior cannot be preserved.
- Cover the actual
BaseProvidercontract:generateMessage,generateMessages, traced wrappers,generateLoop, model discovery, media generation, tool support, error classification, lifecycle, and cost. - Replace the diagnostic expectation in
packages/runtime/tests/providers/provider-decorator-inertness.test.tswith behavioral equivalence checks using unique sentinel results. - Start with
CassetteProviderand add each later wrapper to an explicit inventory. In record mode, compare forwarded behavior with the inner provider. In replay mode, compare the recorded normalized contract and cost; do not require calls to the inner provider.
The current implementation must fail the new test before the fix. Then run
npm run test --workspace=packages/runtime -- provider-decorator-inertness.
5. Inventory and consolidate SSRF screening
Status: DONE, shipped 2026-08-22. See SSRF screening inventory and consolidation under Completed work. The numbering stays so older references still resolve.
6. Add property tests to seven pure helpers
Status: OPEN. Use one PR per row. Before adding fast-check, record why the
current dependencies and table-driven tests are insufficient, check its latest
maintenance date, and inspect its transitive tree with npm ls fast-check.
Add it only to the owning workspace.
| Issue | Helper | Workspace | Property |
|---|---|---|---|
| #4909 | normalizeGraph and graph validation |
execution, node-sdk | Malformed arrays never execute; normalization is idempotent. |
| #4910 | deriveImageSizePreset |
web | Rotated dimensions preserve the preset; positive dimensions return a valid preset. |
| #5035 | clampTimeoutSeconds |
agents | Positive finite inputs stay in bounds; sub-second values never become zero. |
| #5091 | matchesFileWatchPattern |
automation-nodes | Line terminators and glob metacharacters do not alter escaped filename semantics. |
| #4939 | hasYieldStatement |
node-sdk | Comment markers in strings do not change executable yield detection. |
| #4987 | findMissingModelNodes |
web | A missing provider is reported; a valid provider/model pair is not. |
| #5116 | mergeIntoSequence |
web | Reassembly preserves track indexes and order, including duplicate indexes. |
Each PR pins the original counterexample, defines generator bounds and a replay
seed, runs in the normal workspace suite, and proves failure by restoring the
original defect once. Before generating values, the test must define its
independent oracle: the complete valid preset set for #4910, parser-derived
executable yield locations for #4939, documented glob semantics for #5091,
and stable source order as the tie-breaker for duplicate indexes in #5116.
Split #4909 into one execution normalization property and one node-sdk
validation property.
7. Complete editor input-path coverage
Status: OPEN. Use three separate PRs.
7A. Shortcut action mapping
web/src/config/shortcuts.ts already owns shortcuts and rejects duplicate
slugs. Add a typed action ID used by menus and handlers. Test missing actions,
duplicate normalized combinations within the same active editor context and
OS, Electron-only actions in web menus, and the command-menu shortcut on
Windows and macOS. Duplicate combinations in disjoint contexts remain valid.
7B. Canvas drop journeys
Add web Playwright journeys for file drop, node creation from a dropped
connection at the cursor, and run-selected with multiple nodes. Use
npm --prefix web run test:e2e. For the Windows Explorer defect, first add an
Electron Playwright dependency, config, script, packaged-app fixture, and
Windows CI job. Update AGENTS.md and Electron testing docs with the command.
Run the case on Windows; a Windows-style string on macOS is not sufficient.
7C. Dialog containment audit
PositionedDialog already clamps to the viewport. Enumerate other dialog
primitives and direct users in a checked-in audit with a non-zero count. A
dialog fails the audit when its rendered bounds extend outside a 600 x 600 px
viewport or its content cannot scroll into view. Migrate failing callers and
add one 600 x 600 px test per migrated primitive.
Completed work
-
An eval case or a suite for each agent capability: shipped 2026-08-22.
packages/cli/src/harness/capability-table.tsnames all 201 exported capabilities — 193 covered, 8 carrying a written gap note — with the implementation file, the suites thecapability-suitesselfcheck runs, and the eval cases whoseexpect.requiredToolsdemand them.scripts/sync-capability-coverage.mjs(npm run capabilities:sync/:check) derives everything but the gap notes from the live registry, the agent suites, and the eval case files, so a new capability with no check fails the check rather than review. Each entry carries a fingerprint of what the capability declares, andnodetool harness gate --base <ref>refuses a contract change whose coverage mapping stood still while saying nothing about a refactor. Fixtures inpackages/cli/tests/harness-registry.test.ts, the table’s own audit incapability-coverage.test.ts, the rule inpackages/agents/AGENTS.md, and.github/pull_request_template.md. - Live provider contract probes: shipped 2026-08-22. The manifest
packages/runtime/src/providers/contract/probe-manifest.tsnames, per entry, the provider, the endpoint, the production decoder, a checked-in raw HTTP response fixture, the required fields whose removal must break the check, and — for one entry per provider — the single live request, capped at one request and USD 0.05 per provider. The decoders were extracted out of the provider methods (decodeChatCompletion,decodeOpenAIModelList,decodeGeminiGenerateContent,decodeGeminiModelsPage,decodeFalLanguageCatalog, the falextract*Urlfamily,kieEnvelopeError,decodeKieTaskSubmission,decodeKieRecordInfo,decodeKieResultUrls), so fixtures and live responses parse through the code a run uses.runProbesreports network failures apart from schema failures, and retains only the response shape (summarizeShape) plus redacted messages, so no credential, prompt, request id, or signed URL reaches an artifact. Nightly:.github/workflows/provider-contract-probe.yml; offline half:packages/runtime/tests/providers/provider-contract-probes.test.ts, wired intoharness gatethrough the newprovider-clientssurface. Docs:docs/provider-contract-probes.md. - Generated provider metadata drift: shipped 2026-08-22. Both generators
have a fixture mode —
packages/{fal,kie}-codegen/src/fixture-generate.ts— that reads only the schema fixtures underfixtures/, named byfixtures/generator-manifest.json, with no network, no pricing, and no timestamps.scripts/provider-codegen-check.mjsgenerates into a temporary directory and diffs every declared node-source and static-metadata output againstfixtures/expected/, failing on a difference, an absent fixture, or a run that compared nothing. Root commandsgenerate:fal:checkandgenerate:kie:checkand the workflow.github/workflows/provider-codegen.ymlrun it;fixture-generate.test.tsin each package covers the same comparison, byte stability, and the absent-fixture failures. - Missing secrets in
validate_workflow: shipped 2026-08-22.CapabilityRun.availableSecretscarries the host’s answer,contextSecretAvailabilitybuilds it from a context that can reach a store, andgetAllMcpToolstakes the factory assecretAvailability. Themissing_secretissue names the key and Settings → Credentials, and addsrequest_secretonly where the run can raise the dialog. Covered bypackages/agents/tests/mcp-tools.test.tsand the call-site auditcapability-run-secrets-audit.test.ts. - Workflow credential preflight: shipped 2026-08-22 in node-sdk, execution, and CLI. The two execution credential suites cover it.
ExecutionSessionpreflight: shipped 2026-08-22. The model and credential checks moved topackages/execution/src/preflight.ts(no models import),ExecutionSession.createselects its context and refuses throughExecutionPreflightErrorbefore the Python bridge andpersistence.onAccepted, andrunWorkflowrefuses through the same contract.session-preflight.test.tscovers both directions;execution-session-hydration-audit.test.tsaudits every host.- Production Python opt-in:
NODETOOL_ALLOW_PYTHON_BRIDGE_IN_PRODUCTION=1is implemented inpython-stdio-bridge.tsand documented indocs/configuration.md. - tRPC POST batches: issue #3979 is closed. Both tRPC clients set
methodOverride: "POST". - Vite preload recovery: issue #4203 is closed. Dedicated tests cover stale deploys, non-stale failures, and the guard window.
- Stale Prompt value: issue #3786 is closed.
PromptComposerBodycommits edits synchronously; its commit and run-from-here suites cover the defect. - Media resolution as the browser rendering boundary: shipped 2026-08-22.
ResolvedMediaUrlinweb/src/utils/resolveMediaUri.tsis the brand;ResponsiveImage,VideoPlayer, and the newAudioPlaybacktake alocatorand resolve it. The rendering surfaces are inventoried inweb/src/__tests__/mediaResolutionBoundary.test.ts, anddesign-tokens/no-unresolved-media-src(fixtureweb/scripts/test-media-src-rule.mjs) rejects a locator literal in a JSX url attribute. - SSRF screening inventory and consolidation: shipped 2026-08-22.
isBlockedIpLiteralinpackages/runtime/src/providers/safe-url.tsis the one address table; the sandbox’snetwork-guard.tsimports it and the websocket runner’s private copy (isSafeExternalUrl) is gone.fetchExternalMedia(packages/runtime/src/external-media-fetch.ts) is the media-ref egress policy —safeFetch, withNODETOOL_ALLOW_PRIVATE_MEDIA_FETCH=1as the self-hosted LAN opt-out — and the fifteen media and result-download sites that used a barefetchnow call it orsafeFetch. The inventory ispackages/runtime/tests/url-egress-inventory.ts(86 entries: guarded, fixed-host, private-integration, browser, sandbox-bridge, screening); the reader-facing doc, with the DNS-rebinding decision and the contribution checklist, isdocs/url-egress-inventory.md; andurl-egress-audit.test.tsdiscovers URL surfaces from source in both directions, so a new unclassifiedfetch(url)fails. - Cross-origin media CSP: issue #5125 is fixed and covered by
web/src/__tests__/contentSecurityPolicy.test.ts.