Skip to content

ScheduledTaskExecutionTemplate.backend is durable state nothing reads #3306

Description

@Astro-Han

Problem

ScheduledTaskExecutionTemplate.backend is durable state that nothing reads.

The template freezes how an Automation should run — cwd, connection slug, model, thinking level, permission mode, collaboration and orchestration mode — and backend alongside them. But the fire path never consults it: HostScheduledTaskCoordinator builds the execution session from execution.llmConnectionSlug, execution.model, execution.permissionMode and the two modes, and passes no backend at all (packages/runtime-host/src/server/scheduled-task-coordinator.ts, #createSession).

After #3249 it is not merely unread but unreadable: session creation no longer accepts a backend from any caller, so there is no longer a parameter the template's copy could be passed to. The field is now pure weight — persisted, encoded on the wire, validated by two decoders, consumed by nobody.

One write path also still copies a legacy value into new records. executionTemplateFromHeader takes header.backend verbatim and does not pass through normalizeExecution, so an Automation created from a session whose durable header still says 'fake' (#3211) freezes that retired value into a brand-new template. This is unreachable in practice — the creating tool runs inside a turn, and a 'fake' session cannot start one — but it is the same defect #3249 closed for session creation, left open in the Automation writer.

Found while reviewing #3249; raised by @M4n5ter there and split out as a separate change because it touches a durable record and the wire contract.

Desired outcome

New canonical Automation records do not carry backend, and stored records that do are still readable.

Concretely: drop the field from ScheduledTaskExecutionTemplate and stop writing it in executionTemplateFromHeader, while the protocol decoder keeps tolerating the key on records written by older builds. No migration — the same position #3226 and #3249 took for session headers. Nothing reads the value, so no behavior changes either way.

Alternatives or workarounds

  • Do nothing. No user-visible symptom today. The cost is that the field rots: the next person to read a stored Automation reasonably assumes backend selects something, and the 'fake' copy path stays open.
  • Enforce it at fire time instead — have the fire path read the template's backend and refuse retired values. This keeps a field whose only remaining purpose would be to police itself, and re-introduces a backend choice on a surface that no longer has one. Not recommended, but it is the other coherent answer.

Change surface

Line numbers are on 8de1f29a9.

  • packages/core/src/scheduled-task.ts:55 — drop backend from ScheduledTaskExecutionTemplate. normalizeExecution (:491, :496-497) stops requiring and validating it; :530 stops copying it.
  • packages/runtime-host/src/server/scheduled-task-coordinator.tsexecutionTemplateFromHeader stops copying header.backend. This is the write path that can freeze a retired value into a new record.
  • packages/runtime-host/src/protocol/scheduled-task.ts:554 — the encoder stops emitting backend.
  • packages/runtime-host/src/protocol/scheduled-task.ts:513-529the one trap. decodeExecution uses requireShapedRecord, which is a closed shape: any key not listed as required or optional is rejected. Deleting 'backend' from both lists would make every Automation frozen by an older build fail to decode. Move it from the required list to the optional one and drop the isBackendFor check; BackendOrigin and isBackendFor (:646-650) then have no remaining caller and go too.

That asymmetry — gone from the type, still tolerated on the wire — is the whole point of the change, and it is the part worth reviewing carefully.

Verification

npm --workspace @maka/core run build
npm --workspace @maka/runtime-host run build
node --test --test-concurrency=4 packages/core/dist/__tests__/scheduled-task.test.js
node --test --test-concurrency=4 packages/runtime-host/dist/__tests__/scheduled-task-*.test.js
npm run format:check && npm run lint

A regression test for the tolerated key is worth adding: decode a stored template that still carries backend: 'fake' and assert it round-trips without the field. Note that some files in this repo contain NUL bytes, so absence checks must use git grep -aI — plain grep/rg silently skip them.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requesthelp wantedExtra attention is needed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions