From 3738732156ee3639e8a98b803226cd0aa5b41290 Mon Sep 17 00:00:00 2001 From: Brydon DeWitt Date: Sat, 23 May 2026 00:00:40 -0400 Subject: [PATCH] docs(agents): document Jinja system-message constraint and session-start delivery - Add forbidden pattern: using experimental.chat.system.transform for session-start injection causes Jinja 'System message must be at the beginning' on Qwen-family GGUF models when orchestrator spawns subagents - Add note to session-start.sh section explaining OpenCode delivery via output.parts.unshift() on first chat.message turn (not system.transform) --- .agents/AGENTS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index 44d8139..2a026ef 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -151,6 +151,12 @@ calling the script. See `agent-infrastructure.md` item 22 for the refactor plan. - Fires once per session. Good for: current branch, active investigations, dead ends. - Not good for: precise rule reminders (use PostToolUse or nested AGENTS.md). +- **OpenCode delivery:** injected as a synthetic `text` part via + `output.parts.unshift()` on the first `chat.message` turn. **Not** via + `experimental.chat.system.transform` — that hook fires for task-spawned + subagent sessions after a user message is already in context, which causes + Qwen-family GGUF models to abort with a Jinja "System message must be at the + beginning" error. See Forbidden Patterns below. ### `stop.sh` — End-of-session reflection @@ -237,6 +243,15 @@ Some things cannot be unified and live in tool-specific locations: model may be correct. Applies to: OpenCode tool names (`read`/`edit`/`task`), parameter names (`offset`/`limit` not `startLine`/`endLine`), and plugin guard logic. +- ❌ Using `experimental.chat.system.transform` to inject session-start content + in OpenCode. That hook fires for every model call — including task-spawned + subagent sessions — **after** the task prompt (a user message) is already in + the conversation. Pushing to `output.system` at that point places a system + message at a non-zero position, which Qwen-family GGUF models reject with + _"System message must be at the beginning"_ (Jinja chat template guard). + Fix: inject session-start as a synthetic `text` part via `output.parts.unshift()` + on the first `chat.message` turn (guarded by an `initializedSessions` set). + Text parts have no position constraint. Committed `f0d21e9` in dotfiles. - ❌ Asserting that a third-party tool does **not** support a feature (config mechanism, directory, option) without fetching the tool's current docs first. Training data is frequently stale. Negative claims ("X doesn't have Y") must