diff --git a/README.md b/README.md index 2fffd40..c0e293f 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ at a throwaway. You stay the only `nightshift → main` gate. ## More +- [`docs/context-guidance.md`](docs/context-guidance.md) — task-relevant briefings, scoped preferences, and how to evaluate context changes - [`DESIGN.md`](DESIGN.md) — architecture, the TODO queue, and the golden rule (never lose the log) - [`SECURITY.md`](SECURITY.md) — what's captured, where it's stored, who can see it, how to report a vuln - [`docs/privacy.md`](docs/privacy.md) — what an entry looks like, redaction gaps, how to delete/disable/audit your data diff --git a/assets/skills/continue/SKILL.md b/assets/skills/continue/SKILL.md index 6209989..4b250fa 100644 --- a/assets/skills/continue/SKILL.md +++ b/assets/skills/continue/SKILL.md @@ -162,8 +162,9 @@ done ``` Read hits with the **same slug rules as Step 3** (full `__/` slug, `--fuzzy`, never `2>/dev/null`), plus two rules specific to building real context: -- **Read the 3-5 most relevant hits IN FULL** — and follow any `[[links]]` on those - pages to others that clearly bear on the task. A single page is rarely enough. +- Read the relevant source pages, including surrounding decisions and gotchas. + Follow `[[links]]` when they resolve a gap that matters to this task; reuse pages + already read in this session unless their contents or the task have changed. - **Don't pre-filter the page** with `devbrain brain get … | grep ` — grep throws away the surrounding decisions/gotchas that are exactly what a fresh worker is missing. Synthesize from the full text in Step 8 instead. @@ -187,13 +188,15 @@ devbrain todo context "$id" <<'CTX' CTX ``` `todo context` appends (or replaces, on a re-run) a `## Context (synthesized …)` -section in the task body — multi-line, idempotent. **Aim for ~500-1000 words** — that's -roughly what 3-5 fully-read pages distill down to, and it's the floor for actually -carrying the decisions, constraints, file pointers, and prior work a fresh worker needs, -not a one-line gesture. If your draft is well under ~500 words, you probably under-read -in Step 7 — go back and read more pages rather than padding. The one exception: if the -brain genuinely surfaced little, write the little there is and say so explicitly ("brain -had little on this") rather than inventing filler. +section in the task body — multi-line, idempotent. Include the objective and acceptance +criteria, accepted decisions, relevant constraints, current artifact/code state, failed +approaches that must not be repeated, remaining work, and source page/file pointers +with relevant dates. For time-sensitive facts, retain when they were observed or last +verified; flag stale or conflicting notes instead of silently dropping their qualifications. +State unresolved gaps. Stop reading when these needs are covered; there is no minimum +page count or word count. A small task may need a short brief; a complex task may need +substantial context. If the brain has little relevant information, say so rather than +padding or retrieving unrelated history. Then **show it to the user** — print the brief back (`devbrain todo show "$id"`, whose body now includes the `## Context` section, or just paste it) so they see what's framing the build diff --git a/assets/skills/distill/SKILL.md b/assets/skills/distill/SKILL.md index e94936e..4c9466a 100644 --- a/assets/skills/distill/SKILL.md +++ b/assets/skills/distill/SKILL.md @@ -422,6 +422,15 @@ Then: (plan-before-code, staging-not-prod, verify-before-done, commit+push), cost/infra. A one-off ask is queue/brain material, not a standing default. You only need the user-prompt blocks (the `## HH:MM:SS` headers and the text beneath), not the response samples — keeps it cheap. + **Preserve scope when promoting a steer.** Repetition within one project or incident + does not make a rule universal. Keep project-specific rules in that project's section + or brain page, and retain conditions such as "for schema changes" or "before a paid batch". + Do not turn a requested audit, report format, review process, or one model's workaround + into an always-on requirement. Prefer durable goals and constraints; keep model names, + effort levels, provider routes, and compaction thresholds in explicit configuration or + task-specific guidance unless the owner deliberately chooses a standing default. + Record supporting examples, source dates, and detailed rationale in relevant brain pages + instead of copying their full history into the global preferences page. 4. **Converge, don't grow — consolidate-or-add, never append a duplicate.** Append-only is wrong: it makes the page grow without bound. The page has a **hard cap of 8192 bytes** (8 KB — the dashboard's Global Preferences meter shows size/cap and turns red over it; the value is diff --git a/assets/skills/work/SKILL.md b/assets/skills/work/SKILL.md index d2fb3d1..b23217c 100644 --- a/assets/skills/work/SKILL.md +++ b/assets/skills/work/SKILL.md @@ -17,8 +17,8 @@ The split is simple: **`/work` reads the brain, but doesn't write it or brief a human.** `/continue` exists to *resume a human* — it folds last session's log back into the brain (`/distill`), refreshes the live git/PR world, and hands back a briefing. In an unattended loop none of the *writing* or *reporting* pays off: N -parallel workers re-folding the same log is wasted work (~15% of turn time for zero -gain on a build turn), and there's no human to brief. But the *reads* — pulling +parallel workers re-folding the same log duplicate memory maintenance, +and there's no human to brief. But the *reads* — pulling project conventions and the task's prior decisions out of gbrain — are exactly what keep the MVP correct, so `/work` keeps both. @@ -101,10 +101,10 @@ if you change the identity resolver or the stash-safety rule there, mirror it he devbrain brain "$qmode" "$q" 2>/dev/null | head -8 done ``` - Read the **3-5 most relevant hits IN FULL** (same slug rules as Step 2), follow their - `[[links]]`, and **don't pre-`grep`** the page — that throws away the surrounding - decisions/gotchas a fresh worker is missing. Together with Step 2 this is the context - that makes the build correct. + Read the relevant source pages with their surrounding decisions and gotchas (same + slug rules as Step 2). Follow `[[links]]` when they resolve a task-relevant gap; + reuse pages already read unless the task or source has changed. Don't pre-`grep` + away the context needed to interpret a decision. 5. **Synthesize + attach to the TODO.** Distill what you read into a context brief for *this* task — not a page dump. Write it to the task so it persists and the next @@ -118,10 +118,13 @@ if you change the identity resolver or the stash-safety rule there, mirror it he **Approach implied:** CTX ``` - Aim for **~500-1000 words** — roughly what 3-5 fully-read pages distill to, and the - floor for actually carrying the constraints/file-pointers/prior-work a fresh worker - needs. Well under that means you under-read in Step 4 — go back, don't pad. **Attach - and move straight on** — do not print the brief back; the task file is the only reader. + Cover the objective and acceptance criteria, accepted decisions, relevant constraints, + current artifact/code state, failed approaches, remaining work, and source pointers with relevant dates. + For time-sensitive facts, retain when they were observed or last verified; flag stale + or conflicting notes instead of silently dropping their qualifications. + State unresolved gaps. Stop when these needs are covered, without a minimum page or + word count; preserve substantial context when the task needs it. **Attach and move + straight on** — do not print the brief back; the task file is the only reader. 6. **Branch off the base, then build a MINIMAL MVP.** Start clean from the target branch: ```bash diff --git a/docs/context-guidance.md b/docs/context-guidance.md new file mode 100644 index 0000000..99a16f8 --- /dev/null +++ b/docs/context-guidance.md @@ -0,0 +1,53 @@ +# Context that follows the task + +devbrain should preserve the decisions an agent needs without making every task +replay the same setup. This is a shared workflow contract for Claude and Codex; +model settings belong to the host or project configuration. + +At session start, retrieve relevant project context and inspect the queue. The +current user request remains the assignment. Starting a session does not itself +authorize `/continue`, distillation, or work on another queued item. An explicit +`/continue` still runs its existing resume, distill, and task workflow. + +## Choose depth by missing information + +The continue/work briefing needs the objective, acceptance criteria, accepted +decisions, constraints, current state, failed approaches, remaining work, and source +pointers. Preserve observation or verification dates when freshness matters, and +identify stale evidence, conflicts, and missing information. + +Read source pages with enough surrounding context to interpret their decisions. +Reuse relevant pages already read; follow links or search again to resolve a real +gap. There is no minimum page count or word count, and no maximum brief length. +One relevant page can cover a small task. A cross-service change may need many. +Attach the brief to the task so later workers can recover the context and sources. + +## Keep preferences scoped + +Global preferences contain durable cross-project defaults. A repeated correction +within one incident does not establish a universal rule. Preserve the condition +that makes a preference useful: schema review for schema changes, for example, +rather than a full database review before every edit. Keep project rules scoped +and detailed evidence in the relevant brain page. Existing hand-edit precedence, +deletion history, maintenance gating, and privacy rules still apply. + +## Check changes before calling them an optimization + +Compare representative tasks against the existing setup using the same model, +effort, tools, and acceptance criteria. Include a small edit, a task with substantial +prior context, and a task involving stale or conflicting notes. Check correctness, +retained decisions and sources, tool calls, latency, and measured token usage. When +available, report cached input separately and count reasoning within output. + +Do not equate shorter instruction files with measured usage savings or equal +quality. Do not compare an API-equivalent estimate with a subscription invoice. +Adjust model effort or compaction policy separately, based on the host's actual +behavior and the task's quality requirements; avoid universal token thresholds. + +As reviewed on 2026-09-19, both providers recommend revisiting older scaffolding: +[OpenAI's Astra guidance](https://developers.openai.com/blog/rethinking-skills-and-prompts-for-gpt-6-astra) +emphasizes precise skill triggers and reading relevant documentation; +[Anthropic's Fable 5 guidance](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5) +recommends removing overly prescriptive instructions while preserving useful memory +and verification for long runs. Their effort and verification recommendations +differ. Those model-specific choices should not become universal devbrain defaults. diff --git a/internal/hooks/hooks.go b/internal/hooks/hooks.go index f5f5137..ee948fb 100644 --- a/internal/hooks/hooks.go +++ b/internal/hooks/hooks.go @@ -253,8 +253,10 @@ func SessionStart(e *Event) error { "The brain is usually faster and more current than re-deriving from the code. " + "To READ a page a search surfaces, pass its FULL `/` slug from the output to " + "`devbrain brain get \"/\" --fuzzy` — not the bare page name " + - "(the brain is one namespace, so a bare slug is page_not_found). To resume this project in full " + - "— brief + work the top task — run /continue." + "(the brain is one namespace, so a bare slug is page_not_found). Reuse context already gathered " + + "for the active task; search again for changed scope, missing facts, or stale evidence. " + + "Run /continue only when the user requests the devbrain resume workflow; it also distills " + + "memory and works a queued task. Session startup alone does not authorize that workflow." if warn != "" { msg += " ⚠ " + warn } diff --git a/internal/hooks/hooks_test.go b/internal/hooks/hooks_test.go index 09d6863..30bee11 100644 --- a/internal/hooks/hooks_test.go +++ b/internal/hooks/hooks_test.go @@ -165,6 +165,15 @@ func TestSessionStartNudge(t *testing.T) { if wrap["hookSpecificOutput"]["hookEventName"] != "SessionStart" { t.Error("hookEventName missing") } + for _, want := range []string{ + "devbrain brain search", + "Run /continue only when the user requests the devbrain resume workflow", + "Session startup alone does not authorize that workflow", + } { + if !strings.Contains(msg, want) { + t.Errorf("startup nudge lost its retrieval/authorization boundary %q", want) + } + } } func TestSessionStartStampsRemote(t *testing.T) { diff --git a/internal/install/markers.go b/internal/install/markers.go index 73db87c..c95eae6 100644 --- a/internal/install/markers.go +++ b/internal/install/markers.go @@ -97,9 +97,12 @@ func claudeMdBody(dataDisplay string) string { body := fmt.Sprintf(`## devbrain (cross-project brain) Every prompt is captured to the private data repo at `+"`%s`"+` -(routing by git remote -> `+"`projects//`"+`). At the start of a session, -or when the user explicitly asks "where was I" / "continue", run `+"`/continue`"+` -to pull this project's brain and refresh the live world; it includes `+"`/distill`"+`. +(routing by git remote -> `+"`projects//`"+`). At session start, retrieve +relevant brain context for the current request and inspect `+"`devbrain todo list`"+`. +Run `+"`/continue`"+` when the user requests the devbrain resume workflow; it includes +`+"`/distill`"+` and working a queued task. Continuing an active task does not select +a different queue item. Session startup alone does not authorize memory maintenance +or queue execution. Never initiate `+"`/distill`"+` proactively. An explicit `+"`/continue`"+` or `+"`/distill`"+` invocation is already consent: run it immediately without asking again. Do not infer consent from progress, a final response, a session boundary, a commit, @@ -109,8 +112,8 @@ or a PR being created or merged. last resort.** Before answering a non-trivial question about a project, before asking the user something the brain may already record, and whenever you pick up or resume work, run `+"`devbrain brain search \"\"`"+` (or `+"`devbrain brain query \"\"`"+` -with an OpenAI key) FIRST. The brain is usually faster and more current than -re-deriving from the code or asking — even mid-task, not just on `+"`/continue`"+`. +with an OpenAI key) FIRST. Reuse context already gathered for the active task; +search again when the task changes, a relevant gap remains, or evidence may be stale. To READ a page a search surfaces, pass its FULL `+"`/`"+` slug from the output to `+"`devbrain brain get \"/\" --fuzzy`"+` — not the bare page name (the brain is one namespace, so a bare slug is `+"`page_not_found`"+`), and do not pipe the @@ -141,9 +144,12 @@ func agentsMdBody(dataDisplay, prefs string) string { body := fmt.Sprintf(`## devbrain (cross-project brain) Every prompt is captured to the private data repo at `+"`%s`"+` -(routing by git remote -> `+"`projects//`"+`). At the start of a session, -or when the user explicitly asks "where was I" / "continue", run `+"`$continue`"+` -to pull this project's brain and refresh the live world; it includes `+"`$distill`"+`. +(routing by git remote -> `+"`projects//`"+`). At session start, retrieve +relevant brain context for the current request and inspect `+"`devbrain todo list`"+`. +Run `+"`$continue`"+` when the user requests the devbrain resume workflow; it includes +`+"`$distill`"+` and working a queued task. Continuing an active task does not select +a different queue item. Session startup alone does not authorize memory maintenance +or queue execution. Never initiate `+"`$distill`"+` proactively. An explicit `+"`$continue`"+` or `+"`$distill`"+` invocation is already consent: run it immediately without asking again. Do not infer consent from progress, a final response, a session boundary, a commit, @@ -154,13 +160,11 @@ or a PR being created or merged. The devbrain skills are installed at last resort.** Before answering a non-trivial question about a project, before asking the user something the brain may already record, and whenever you pick up or resume work, run `+"`devbrain brain search \"\"`"+` (or `+"`devbrain brain query \"\"`"+` -with an OpenAI key) FIRST. To read a surfaced page, pass its full +with an OpenAI key) FIRST. Reuse context already gathered for the active task; +search again when the task changes, a relevant gap remains, or evidence may be stale. +To read a surfaced page, pass its full `+"`/`"+` slug to `+"`devbrain brain get \"/\" --fuzzy`"+`. -**At the start of a session in a repo, brief yourself** — devbrain injects no -context into Codex, so fetch your own: run `+"`devbrain brain search \"\"`"+` and -`+"`devbrain todo list`"+` to see what the brain records and what's queued. - **End your final message of each turn with a one-sentence recap** of what you actually did or concluded this turn — outcome, not preamble. devbrain sweeps the last sentence of your final message from the transcript as the diff --git a/internal/install/markers_test.go b/internal/install/markers_test.go index 7112bcc..f189b3e 100644 --- a/internal/install/markers_test.go +++ b/internal/install/markers_test.go @@ -23,14 +23,17 @@ func TestMarkerBodiesLimitMidSessionDistill(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { body := strings.Join(strings.Fields(tt.body), " ") - for _, forbidden := range []string{"After meaningful progress", "after ordinary turns", "session is clearly ending", "ask for permission"} { + for _, forbidden := range []string{"After meaningful progress", "after ordinary turns", "session is clearly ending", "ask for permission", "At the start of a session, or when the user explicitly asks"} { if strings.Contains(body, forbidden) { t.Errorf("marker contains proactive or ambiguous distill trigger %q", forbidden) } } for _, want := range []string{ - "At the start of a session, or when the user explicitly asks", - tt.continueCommand + "` to pull this project's brain and refresh the live world; it includes `" + tt.distill, + "At session start, retrieve relevant brain context for the current request", + "inspect `devbrain todo list`", + "Run `" + tt.continueCommand + "` when the user requests the devbrain resume workflow; it includes `" + tt.distill, + "Session startup alone does not authorize memory maintenance or queue execution", + "Continuing an active task does not select a different queue item", "Never initiate `" + tt.distill + "` proactively", "An explicit `" + tt.continueCommand + "` or `" + tt.distill + "` invocation is already consent: run it immediately without asking again", "Do not infer consent from progress, a final response, a session boundary, a commit, or a PR being created or merged",