Skip to content

Agent playbook

Read me at the start of every session. This is the load-bearing discipline that turns passive capture into reliable cross-session memory.

Hooks already record what you did (files touched, commands run, prompts seen). They cannot infer why, whether it worked, or how it was fixed. That’s your job, via four explicit tools.

1. decision_log — every non-obvious architectural / technical choice

Section titled “1. decision_log — every non-obvious architectural / technical choice”

Trigger: you just picked between two real alternatives, or chose a non-default approach, or made a call that the next agent would have to re-derive from code archaeology.

decision_log(
project_id=<this project>,
title="Use pgvector instead of a separate vector DB",
rationale="Scaleway Managed RDB ships pgvector; avoids a second store and lets us join fragments to structured entities transactionally.",
alternatives_considered=["Pinecone", "Self-host Qdrant"],
tags=["architecture", "phase-6"],
)

Don’t log: typos, obvious refactors, single-line bug fixes, code style tweaks. Decisions are forever — keep the bar high.

2. bug_resolve — when a bug is actually fixed

Section titled “2. bug_resolve — when a bug is actually fixed”

Trigger: you fixed something the test suite (or the user) flagged as broken. Use this even if you didn’t open a bug_report first — call bug_report then bug_resolve in the same turn.

The contract enforces both fields, server-side returns 409 otherwise:

  • root_cause — non-empty, what was actually wrong.
  • fix_narrative≥ 20 characters, how a future agent will understand the fix without re-investigating.
bug_resolve(
project_id=<>, bug_id=<>,
root_cause="asyncpg pool acquire raced with starlette BaseHTTPMiddleware's response wrapping",
fix_narrative="Dropped BaseHTTPMiddleware; routes now read app.state.pool directly and acquire per-query.",
)

3. deploy_log — before AND after every deploy

Section titled “3. deploy_log — before AND after every deploy”

Trigger: you ran a deploy script (CI deploy.yml, scw container update, kubectl apply, anything that mutates a staging/prod environment).

# Mark pending the moment you trigger:
deploy_log(project_id=<>, environment="prod", status="started", version="v0.1.0", commit_sha="513b069")
# When it returns 0 / Scaleway reports ready:
deploy_log(project_id=<>, environment="prod", status="succeeded", version="v0.1.0", closes_bug_ids=[])

A future agent looking at a regression must be able to correlate it with a specific deploy. No deploy log → no correlation → wasted hours.

4. credential_ref_upsert — when a new secret enters the project

Section titled “4. credential_ref_upsert — when a new secret enters the project”

Trigger: you provisioned an API key, generated a JWT signing key, created an SSH deploy key, or otherwise added a secret to Keychain / Secret Manager / Gitea Secrets that future agents will need to know about.

Never log the value. Only the reference:

credential_ref_upsert(
project_id=<>,
name="erold.prod.api-key",
credential_type="api_key",
store="keychain",
lookup_key="erold.prod.api-key",
provision_instructions="Run `secret set erold.prod.api-key`, paste from app.erold.dev → Settings → API Keys.",
)

The server rejects payloads containing value, secret, token, password, key, hash fields at any depth. If your provision_instructions contains a literal secret pattern (AKIA…, sk-ant-…, ghp_…, etc.) the server also rejects.

Don’t re-log what passive capture already records:

You didHook capturesDon’t also call
Edit / Write a filefile_change event with pathlog()
Bash commandpre_tool_use_bash + post_tool_use_bash (hash, exit, duration)log()
User typed a promptuser_prompt_submit (hash, word count, intent class)
Subagent finishedsubagent_stop event
Session endssession_end event

get_context() returns a gaps[] array. If you see any of these on session start, surface to the user and offer to fill them:

  • no_decisions_logged — fragments exist but no Decision entities. Bad cross-session continuity.
  • no_deploys_logged — there’s history of code changes but no deploys. Can’t correlate regressions.
  • no_credential_refs_registered — credentials are in use (you’ll see secret get calls in Bash history) but never registered as references.
  • unresolved_bugs_lack_root_cause — open bugs have no root_cause — investigation is incomplete.
  • fragments_search_lagging — Smart-Strip pipeline is behind (~1-3 s). Retry search shortly; structured entities are still authoritative.
  • You think you logged a decision, but get_context doesn’t show it. The local outbox queues events client-side. If the daemon is down or the backend is unreachable, your log call goes into the outbox JSONL but hasn’t shipped yet. Check ~/.erold/outbox/events.jsonl and ~/.erold/error.log. Re-running the daemon (any new SessionStart spawns it idempotently) drains the queue.

  • bug_resolve returns 409. You’re transitioning from a status other than investigating. Call bug_investigate first, then bug_resolve.

  • Pydantic 422 with CREDENTIAL_VALUE_FORBIDDEN. Your payload had a field named value, secret, token, password, key, hash, or encrypted_value. Strip it. The contract is: names + how to fetch, never the value itself.

Decisions / bug fix narratives / deploy notes are read by future agents in a different session, with no other context. Write them as if you’re briefing a smart colleague who just walked in:

  • Lead with the conclusion.
  • One sentence of why.
  • One sentence of what was rejected.
  • Concrete: file paths, function names, error codes — not “the thing was broken so I fixed the thing.”

Bad: "Fixed the bug." Good: "Race in TenantRLSMiddleware: held one conn for whole request while routes acquired others from the same pool. Switched to app.state.pool only; auth acquires per-call."