Repository navigation
Restructure the system prompt into a documented shape #2364
Description
The system prompt groups rules by tool and mechanism, so rules about one behavior are spread across sections, and some core behavior is missing or lives in the consumer persona. We need one documented prompt shape, a known order of blocks with a priority, that later prompt changes keep. That shape follows published OpenAI and Anthropic prompts and research.
Background
A Notion notion-fetch call returned 404 object_not_found. Junior then made claims that the evidence did not support:
- It stated a cause ("not shared with my connection") and gave setup steps from general product knowledge. The error does not tell a wrong ID apart from missing access.
- When the user pushed back, Junior agreed and gave a new cause ("write access"). It made no tool call. The cause came from a recalled memory. The failing call was a read.
Prompt findings (packages/junior/src/chat/prompt.ts):
- Core has no rule that a claim must rest on evidence from the current run. Git history shows only one "never guess" line, about skill names, and fix(prompt): keep skill routing in skill policy #1179 removed it.
- The strongest rules about evidence are in the consumer's
SOUL.md. The prompt header says to follow that section "for voice and tone", and lets other rules override it. Other installs do not get these rules. task-executionsays "infer conservatively and continue". It does not say whether that means user intent or facts.tool-policyholds rules for single tools (watches, cards, automation credentials).policies/agent-steering.mdsays these belong in the tool descriptions.- The Slack output target ("1–5 sentences") does not say that an "unknown: X or Y" answer is complete.
<runtime-turn-context>says its blocks are "trusted runtime facts". Recalled memories are inside it, and the memory block says "possibly stale".- One prompt serves every model profile. The turn that guessed ran on the
standardprofile.
Related runtime gap: MCP tool results do not say whether the actor's connection or the plugin service account made the call (chat/services/mcp-auth-orchestration.ts). So Junior has no evidence for claims about which account made the call.
Reference shapes
- Codex (
openai/codex,codex-rs/models-manager/models.json, per-modelinstructions_template): permission → autonomy and persistence → personality (short, core-owned) and writing → working with the user → general work rules → skills, apps, plugins. Runtime context goes in separate messages by role: developer messages for policy, and a user-role message for AGENTS.md and the environment. Each model has its own template. - Claude Code (v2.1.312 fragments, extracted in
Piebald-AI/claude-code-system-prompts; the extract does not record the order the fragments are put together in): intro and security → system → doing tasks → actions with care → tools → tone and writing → environment. It has separate blocks for Reporting outcomes ("claim must rest on a result you observed in this session"), Corrections, system messages, and memory hygiene. Memory arrives as<system-reminder>background context.
Proposed shape (maintainer proposal)
- Identity and job
- Working principles: scope and autonomy (infer intent, not facts), evidence and claims, corrections
- Context and trust: thread context, runtime context, memory, plugin context, AGENTS.md
- Actions and safety
- Cross-tool rules only. Single-tool rules move into tool descriptions.
- Communication and output
- Persona (
SOUL.md): voice only - World (
WORLD.md)
Automation-run and child-agent-run modes replace blocks 2–4, as they do now.
Requirements:
- Document the shape and its priority order as a policy. Each later prompt change must fit an existing block, or update the policy.
- Base the shape, and later changes to it, on published OpenAI and Anthropic prompts and research. Record the sources used.
Open questions: per-model templates vs. routing diagnosis turns to a stronger profile; moving memory out of the trusted runtime-context block. Related: #658, #773, #1844 (a change in prompt structure changes prompt-cache behavior).
via David Cramer.
--
Activity
sentry-junior commentedon Oct 29, 2026
Research results (all sources fetched 2026-10-29). This answers the open research items in the issue. Unverified items are marked.
Sources agree on these points
#avoid_errors,#express_uncertainty, and#avoid_sycophancy("should not change its stance solely to agree with the user").#chain_of_command,#ignore_untrusted_data(spec).Claude Code 2.1.312 assembly order (taken from the linux-x64 binary, function
rsr)__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__goes between the shared and the session sections.<system-reminder>attachments in user messages. Unverified: that these go on the first user turn.Junior transport (at
a8ddb35)systemblock plususermessages. No message uses adeveloperrole. OpenAI models go through the gateway'santhropic-messagesAPI (pi/client.ts). The gateway docs do not say howsystemmaps to OpenAI roles. Unverified.<runtime-turn-context>header calls the block "trusted runtime facts", and that block also holds memories and plugin context.<plugin-system-context>is added to the system prompt every turn (agent/prompt.ts). So plugin-written text gets operator authority. Unverified: whether it stays the same from turn to turn.cache_controlhas any effect for OpenAI models.promptSnippetandpromptGuidelinesare deprecated. Junior renders them as<tool-guidance>in the user context message, and three are still in use.Conflict with a big-bang rewrite
OpenAI 5.6 says: "Do not rewrite a working prompt stack all at once." It asks for "the smallest targeted instruction that fixes a measured regression." A restructure therefore needs baseline evals first. The first step moves the existing text without rewording it, and the evals must show no change. After that, each change gets measured on its own.
Open decisions
<plugin-system-context>stays in the system prompt or moves to the context message.sentry-junior commentedon Oct 29, 2026
Follow-up research: persona placement, eval coverage, caching
Sources fetched on 2026-10-29. Inferences are marked.
1. Persona placement
No vendor source tests where a persona block sits relative to the behavior rules. So any choice is an inference from the sources below.
For the end of the static prompt
Tone and styleis the last shared (static) section, from the binary.<tone_and_formatting>block is in the middle. The last block is a one-line<tone_preference>. This matches Anthropic's advice to repeat the key style rule near the end of a long prompt.For an early position
Risk of the end position. By the GPT-4.1 finding, the last text has an advantage in a conflict. Today SOUL.md holds behavior rules too: formatting defaults, reaction rules, "don't claim abilities", and epistemics. At the end, text that the operator wrote could win over core rules.
Proposal (fits the current lean toward the end):
2. Eval coverage for prompt changes
The current setup can catch a prompt change that breaks things. It cannot reliably show if a restructured prompt is better or worse.
What is real. Behavioral and integration evals share
src/fixture/agent.ts. The agent, model, router, Guardian and compaction are real. The app is built withcreateApp(), so the realbuildSystemPromptruns. The two suites differ only in policy: integration fails on any miss, and behavioral uses rubric scores with an 80% pass floor.Gaps:
resolveHomeDir()inchat/discovery.ts: there is noapp/dir nearpackages/junior-evals. SoloadSoul()falls back toDEFAULT_SOUL(prompt.ts:43), and there is no World section. Blocks 7 and 8 are never tested. Not confirmed with a run.prompt.tstriggers nothing. Behavioral and integration path filters only watchpackages/junior-evals/**. A prompt PR needs atrigger-evals*label. No workflow runs onpush,scheduleorworkflow_dispatch, so there is no baseline for main.standard=openai/gpt-6-luna,handoff=anthropic/claude-opus-5.5. Coverage of each model depends on the router.src/recording-setup.tskeeps the recordings of a passed test and drops a failed one. So a rerun is a retry until green, and pass rates lean optimistic.scripts/report.mjsuploads runs toevals.sentry.devwhenSENTRY_EVALS_API_KEYis set (not checked).policies/evals.md). "Claims a cause without a tool call" needs an assertion onconversation.toolCalls, not a rubric.Coverage by block:
conversation/actors,participationparticipation(8 cases),coding/githubskills/providers"double-check a source-backed fact",integration/conversation/attachmentsgoogle/scheduling(correction with new info),skills/action-reviewactors,event-automations/delivery(provider text as data), Guardian suiteaction-review,guardian/, scheduler credentialscoding/subscriptions,skills/routingconversation/output,steeringscheduler/*,agent/delegation,integration/conversation/subagentsNeeded for a prompt eval setup:
insertMemory()). Assert a new tool call or an explicit "unverified". Add variants for a provider 5xx, stale memory against fresh tool output, and injection in memory, plugin context and tool output.standardorhandoffper run, and run both for prompt PRs.packages/junior/src/chat/prompt.tsand SOUL/WORLD loading to the behavioral and integration path filters. Add a run on main as a baseline.3. Caching
The policy will state two rules: the system prompt is static, and earlier transcript turns never change.
<plugin-system-context>is the one item that the runtime builds on each turn and adds to the system prompt (agent/prompt.ts:571). Verified: thesystemPrompthook gets only the plugin's base context andplatform, with no turn data (systemPromptPluginContext,plugins/agent-hooks.ts:172). No plugin ingetsentry/juniororjunior-prodimplements it today. So it is static in practice. But the contract does not enforce it, because the hook is async and runs on each turn. The policy should say that the hook must return the same text for each deployment and platform.docs(prompt): State the intent of each system prompt section
test(evals): Add evals for provider failure, pushback, stale memory, …
sentry-junior commentedon Oct 30, 2026
#2371 merged. It adds the written prompt shape (
policies/system-prompt.md) and reorganizes the prompt to match it. #2370, #2377 and #2410 added and fixed the core-behavior evals.Follow-up work is tracked here:
Closing this issue as done.
sentry-junior commentedon Oct 30, 2026
Closing as done. #2371 merged the documented prompt shape (
policies/system-prompt.md) and the reorganized prompt, and #2370, #2377 and #2410 added and fixed the core-behavior evals. Remaining work is tracked in #2411, #2412, #2413, #2414, #2415, getsentry/junior-prod#576, and #2416 (merge conflicts on idle PRs).