Skip to content

T-0W31-per-host-engine-mapping-turning-a-workflow-step-into-the

State: closed/done · Impact: medium · Complexity: medium

A per-host/engine mapping turns a workflow step into the first message sent to that host.

The mapping lives in solutions/ontological/lib/services/session/engine-message.ts (not under lib/services/host/ — that package owns the host CONTRACT, not first-message construction), keyed by AgentEngine ('claude' | 'codex'), not by HostName:

  1. claudeFirstMessage(step) (pre-existing, unchanged): a skill: step becomes Claude’s /sdlc:<skill> <args> slash command; a prompt: step passes through as literal text; run:/script: have no message form and throw.
  2. skillBodyFirstMessage(caller, step) (new): the non-Claude mapping for a skill: step. Codex/cursor have no /sdlc:<skill> slash-command surface, so instead of naming the skill, this reads the skill’s own SOURCE prose note — plugin/plugins/sdlc/skills/<slug>/<slug>.md under this tool’s OWN packaged surface root (@lib/util/plugin-root’s pluginSkillsDir(), which already handles all three install modes: dev tree, npm install, compiled binary) — strips its frontmatter (@lib/util/frontmatter’s splitFrontmatter), and hands the body over verbatim as the first message. No projectRoot/plugin_source parameter is involved: the skill corpus ships WITH this tool, not with the consumer project session launch runs against, unlike gate/ops/skill-prose.ts’s pluginSourceDir-based walk (a dev-repo-only lint over this repo’s own source tree). No {{args}}/templating convention exists in skill prose today (skills like /sdlc:backlog-capture just describe “everything after the skill name” in prose and rely on Claude Code’s own slash-command mechanics), so step.args, when present, are appended as one plain trailing line rather than substituted into a template. A missing skill note throws OpError('INVALID_INPUT', …) rather than a bare ENOENT.
  3. firstMessageFor(caller, engine, step) (pre-existing, extended): engine === 'claude' → branch 1; otherwise a skill: step now uses branch 2 instead of throwing (its prior behavior — see the module’s git history/T-AD81); a prompt: step passes through either way; run:/script: still throw OpError. No projectRoot parameter was added — see branch 2 above for why the mapping never needs one.
  4. Wired into both existing callers, unchanged in shape beyond the new parameter: session/ops/launch.ts’s resolveLaunch (interactive AND headless — same function serves both) and orchestrator/step-runner.ts’s per-step loop (headless only).

Depends on T-QO8E: this mapping’s non-Claude branch only makes sense because that task establishes claude/codex/cursor as ENGINE-NATIVE hosts with no slash-command surface of their own — the mapping is a direct consequence of that host-contract shape, even though the code change itself lives entirely in engine-message.ts, not in lib/services/host/.

The mapping stays keyed purely by engine ('claude'/'codex'), never by the three engine-native HostNames T-QO8E added — those hosts ignore req.engine and hardcode their own CLI, so which branch a caller gets can’t be decided in this module without a HostName it deliberately never sees. That host-awareness was initially left as an open gap (a --host cursor/--host codex launch with no explicit --engine codex silently got Claude’s slash-command text), then closed one layer up in ops/launch.ts: runLaunch now resolves the interactive launch’s HOST before computing the message, and a small engineForHost helper there picks the message-producing engine from that host ('claude' host → engine: 'claude'; 'codex'/'cursor' host → engine: 'codex') before calling firstMessageFor. See ops/launch.ts’s module doc and engine-message.ts’s module doc for the resolved shape.

AreaNote
solutions/ontological/lib/services/session/engine-message.tsskillBodyFirstMessage, firstMessageFor’s extended branch (no projectRoot param — skill notes resolve via pluginSkillsDir())
solutions/ontological/lib/services/session/ops/launch.tsfirstMessageFor call sites, unchanged signature
solutions/ontological/lib/services/orchestrator/step-runner.tsfirstMessageFor call site, unchanged signature
solutions/ontological/lib/services/session/engine-message.test.tsNew unit tests: skill/prompt steps, claude + codex engines
  • AC-1: A skill: step and a prompt: step each produce a distinct, correctly-shaped first message for at least two engines/hosts: engine: 'claude' (Claude’s /sdlc:<skill> slash command) and engine: 'codex' (the skill’s own prose-note body) — proven in engine-message.test.ts against a real fixture skill note written to a throwaway project root (no mocked filesystem, no real claude/codex process).
  • The engine/host coupling described above was closed in ops/launch.ts (engineForHost), not in this module — this task’s mapping itself stays engine-keyed only.
  • session attach/session list and any .sdlc/sessions/ record wiring — T-QMGC’s job.
  • A sessions: config section — not part of this mapping.
  • T-QO8E — the engine-native claude/codex/cursor hosts this mapping’s non-Claude branch exists to serve.

T-QO8E


← Back to Tasks