DevBrain organizes documents by project, but a key identifies one document across the whole store; project filters reads and isn't part of a document's identity.
Every key starts with its project name, and every call passes the project. For example, use key: "devbrain:state:current" with project: "devbrain". Two projects that both write an unprefixed state:current overwrite each other.
Unprefixed keys belong to the default project, which holds general DevBrain instructions such as this guide (ref:devbrain-usage). Calls that omit project read and write default. If a query returns no results, check the key's project prefix and the project scope.
devbrain— DevBrain's own documentation, architecture, sprint docs, backlog, known issues
For any project, the correct session startup is:
GetDocument(key="{project}:state:current", project="{project}")— load current state- If working a sprint:
GetDocument(key="{project}:sprint:{name}", project="{project}")— load active spec - Updates: write directly with
UpsertDocument— no manual upload needed
This replaces any manual file upload workflow. DevBrain is the canonical source.
Project names and document keys are case sensitive on some platforms. Always use lowercase:
- ✅
project: "devbrain" - ❌
project: "DevBrain"
Note: OpenAI Codex Desktop allows mixed-case names in its UI but lowercases them internally.
ListDocuments(project: "devbrain")
GetDocument(key: "devbrain:state:current", project: "devbrain")
SearchDocuments(query: "durable functions", project: "devbrain")
Before importing or syncing a document, check whether the stored version is already up to date. This avoids unnecessary writes and wasted tokens.
GetDocumentMetadata(key: "devbrain:sprint:license-sync", project: "devbrain")
Returns key, project, tags, updatedAt, updatedBy, contentHash (SHA-256), and contentLength (character count) — without the content body. Use this to:
- Check if a document exists
- See when it was last updated and by whom
- Compare content length against your candidate as a fast "obviously different" check
CompareDocument(key: "devbrain:sprint:license-sync", content: "...candidate text...", project: "devbrain")
Returns { found, match, storedContentHash, candidateHash, ... }. The server hashes the candidate content and compares against the stored hash. You can also pass a precomputed contentHash instead of raw content.
- Call
GetDocumentMetadata— if not found, upsert immediately - If found, compare
contentLengthagainst your candidate's character count — if obviously different, upsert - If lengths match, call
CompareDocumentwith the candidate content — ifmatch: true, skip the write - If
match: false, upsert
This pattern avoids pulling the full stored document into context just to decide whether to write.
For exact text edits, use the guarded two-step flow instead of doing a blind full overwrite.
PreviewEditDocument(
key: "ref:devbrain-usage",
oldText: "old phrase",
newText: "new phrase",
project: "default",
expectedOccurrences: 1
)
Preview returns whether the edit is possible, how many matches were found, whether the request is ambiguous, and the current contentHash to use when applying.
ApplyEditDocument(
key: "ref:devbrain-usage",
oldText: "old phrase",
newText: "new phrase",
project: "default",
expectedOccurrences: 1,
expectedContentHash: "<hash from preview>"
)
If the document changed after preview, apply refuses the write and tells you to preview again.
- Call
PreviewEditDocument - Confirm
WouldReplace: trueand theMatchCountis what you expected - Pass the returned
CurrentContentHashintoApplyEditDocument - If apply reports that the document changed since preview, re-run preview and try again
- Use
expectedOccurrencesto refuse zero-match or multi-match edits unless they are intentional - Use
caseSensitive: truewhen casing matters and you want to avoid accidental matches - Use
UpsertDocumentwhen you are replacing the whole document on purpose; use preview/apply when you want a narrow, literal patch
To adjust tag metadata on an existing document, use EditTags instead of re-sending the full content through UpsertDocument.
EditTags(
key: "ref:devbrain-usage",
project: "default",
add: ["workflow"],
remove: ["draft"]
)
The server applies the diff:
- Tags in
addthat are already present are no-ops - Tags in
removethat are absent are ignored (idempotent) - A tag that appears in both
addandremoveis rejected — the call fails without writing - If
addandremoveare both empty, nothing is written - If the resulting tag set is identical to the current one, nothing is written
EditTags never modifies the document content. It touches tags, updatedAt, and updatedBy only. Use this whenever you just need to label or un-label an existing document — it's dramatically cheaper than an UpsertDocument that has to re-emit the whole body.
Every project key starts with the project name. Unprefixed keys are only for general DevBrain material in the default project.
Keys use colon as the separator. Slash-separated keys (sprint/foo) still work for backward compatibility, but colons are the canonical, recommended convention — they signal "DevBrain key" at a glance and avoid being confused with file paths.
| Prefix | Use |
|---|---|
{project}:sprint:{name} |
Sprint specs and retrospectives |
{project}:state:current |
Current project state |
{project}:arch:{name} |
Architecture docs |
{project}:decision:{name} |
Architecture decision records |
{project}:ref:{name} |
Project reference material |
ref:{name} |
General DevBrain reference material in the default project |
- Check that the key starts with the project name, e.g.
devbrain:state:current - Check casing — project names and keys are case sensitive
- Try specifying the project explicitly
- Use ListDocuments with no prefix to see what's stored in that project
- Try SearchDocuments with a broad keyword
- The document may not exist yet — ask the user if they'd like to create it
Yes — fully. Cosmos DB is the backing store, not in-memory. Documents survive indefinitely across all sessions and all clients until explicitly deleted or overwritten.
Cosmos DB has a 2MB per-document limit. Typical sprint specs (15-30KB) and state documents (up to ~40KB) are well within this limit.
No. UpsertDocument is full overwrite semantics — there is no history. The updatedAt field tracks the last write time but previous versions are not retained, so be deliberate about overwrites. Use the narrowest write for the change: PreviewEditDocument / ApplyEditDocument for exact text edits, AppendDocument to add entries to a growing log (session history, decision logs), EditTags for tag-only changes, and UpsertDocument only when you mean to replace the whole document.