Context and memory: live context, memory, sinks
Live Context, context sources, memorialization before execution, git-backed organization memory, and the output sinks work is delivered through.
Last updated
Orgabot separates three things that are easy to conflate: what a role is told (context), what the org knows (memory), and where finished work is delivered (sinks).
Live Context
Live Context is a per-scope, provider-agnostic store of memorialized requirements and knowledge, organized as organization → business unit → repository. The default is the local filesystem (.orgabot/live-context in-repo; .orka/live-context is still read for a repository that carries the older name); Google Drive and GitHub issues are opt-in.
orgabot context sources list
orgabot context bind <source-id> org <org> | unit <org> <client> | project <project>
orgabot context sync <source-id> | --all
orgabot context distill [--org <org>] [--project <project>] "<requirement>"distill auto-scopes a sentence and captures it as a durable requirement at the right level: the Chief of Staff deciding whether "we always squash-merge" belongs to one repo, one unit, or the whole org.
Memorialization: write it down before you run it
A typed mission's intent and requirements are written to an external durable source before execution. If there is no writable source, the mission holds fail-closed with memorialization_unwritable.
orgabot context holds
orgabot context resume
orgabot context canonical set|show|clear # the memorialization target for a scope
orgabot context issue-source set <project> <owner>/<repo> # use GitHub issues for the registered repository rootThe repository-root registration owns repo-level issue relationships. A monorepo child is an execution target beneath that root and cannot become an implicit issue or connector owner. If the root is missing, register or restore it before configuring the issue source; if several root registrations exist, use orgabot project set-issue-owner to choose between those roots. The command cannot select a child project.
An unset scope falls back to the local filesystem default. The most common cause of a surprise hold on a fresh repository is simply that no issue source is configured yet.
Context sources
| Source | Notes |
|---|---|
| Local text | The default; lives in the repo under .orgabot/ (or .orka/, first match). |
| GitHub issues | Per-project; the same path issue curation uses. |
| Google Drive | Bring your own Google Cloud credentials, client secret and Picker key in the keychain, client id in config. There is deliberately no shipped vendor OAuth client. A dashboard folder browser handles selection. |
orgabot context drive-source set|connect|credentials|folders|show|clear|listOrganization memory
Org and unit-scope durable memory is stored in a designated git repository; project-scope memory writes into that project's own .orgabot/memory/ (or .orka/memory/, whichever that repository carries). Distilled items are opened as a PR, merged, and reindexed. Memory changes go through the same review path as code. After a merge (or a manual approve/reject), orgabot memory sync-approvals reconciles the approval gate and commits the rewritten records onto the default branch so they do not linger as uncommitted working-tree dirt.
A record reaches a repository only when both its scope and its access level allow it. An item whose access level is restricted or privileged stays in the local memory store only, whatever its scope, because a repository cannot enforce who reads it. Every writer follows this rule: adding an item, approving or rejecting it, retiring a project, opening distillation pull requests, and orgabot memory rewrite-missing. The skip is recorded as a memory_git_write_skipped event with a warning. Role-, mission-, restricted- and privileged-scope items were already local-only and still are. orgabot memory reindex also refuses a restricted or privileged record it finds in a repository and lists it as rejected, so a hand-written file cannot bring one back.
How pending memory records reach a pull request
Distilled items start pending, and their records are written straight into the target repository's working tree, so for a while they show up in git status as untracked .orgabot/memory/mem_*.md files. That is expected. Leave them alone: the distillation duty (fired by orgabot duty run-due) delivers them on one accumulating draft pull request per repository. Merging that pull request approves every item it lists, and closing it rejects them.
- Mission commits never pick them up. A mission's normal commit, a salvage commit, and the
--include-working-treesnapshot all leave newly addedmem_*.mdrecords in<state-dir>/memory/(at the repository root or a registered project's root) out, and log each one they skip, so a pending proposal cannot ride an unrelated pull request onto the default branch. A mission's edit to an already tracked record is still committed, a record that arrives from the base branch during a merge is kept, and a look-alike file elsewhere (a test fixture, say) commits normally. - Untracked records go first. Each sweep surfaces a repository's untracked records before the ones already committed (those are safe on the default branch and only need their review surface), up to 100 items per repository per run.
- The PR has a capacity. The description lists every item a merge approves, so it stops accepting items near GitHub's body limit. The sweep then reports that the PR is at capacity, and the remaining items wait for the next PR after you merge or close the open one.
- Records already committed still get reviewed. If a record is already on the default branch (for example from a manual bulk commit), the pull request carries it with an empty commit, because the pull request is the approval surface. A pull request made only of such records shows no changed files, and merging it still approves them:
orgabot memory sync-approvalsreads whether the pull request was merged or closed, never its diff. If GitHub will not squash-merge an empty diff, use a merge commit instead; closing the pull request rejects the items. - Items whose scope has no git-backed location, or whose record was never written, stay in the approval queue. A sweep skips them without using up its per-run cap, names the first few missing records in its output, and counts the rest.
Writing records that were never written
A memory item's record is written when the item is created. If its scope had no usable git location at that moment, the item was kept in Orgabot's store only and no file was written. That happens when orgabot memory repo pointed at a directory that no longer exists, or when the project was not registered. The distillation sweep reports these items as missing records and skips them, so they never reach a pull request. Fixing the setting afterwards does not write the old records by itself.
Run orgabot memory rewrite-missing once the location is right again, for example right after you correct a wrong orgabot memory repo set, or after you register the project that was missing:
orgabot memory rewrite-missing --dry-run # count what would be written, write nothing
orgabot memory rewrite-missing [--org <org>] # write the missing recordsFor every organization (or just the one named with --org), it writes a record for each memory item whose scope now routes to a repository and whose file is absent. It uses the same writer as the normal path. It prints counts per organization, per route (memory repository or project repository) and per destination folder, and lists every write that failed along with the reason. It exits with a nonzero status if any write failed.
It is safe to run more than once:
- It never overwrites a record file that already exists.
- It never changes an item's approval state. A pending item gets a pending record, and an approved item gets an approved one.
- It never runs git. Every record it writes is an untracked file in the working tree.
- Items that are store-only by design (role, mission, restricted and privileged scopes) are skipped, and so is any item whose access policy is restricted or privileged, whatever its scope. Items whose scope still has no location are skipped too. The output says why each group was skipped.
Not every record it writes reaches a pull request by itself. The output splits the written records into two groups:
- Delivered by the distillation sweep: pending, agent-distilled items that no distillation pull request has claimed yet. The sweep carries these onto its draft pull request, up to 100 per repository per run, and merging that pull request approves them.
- Records no sweep commits: everything else. That means approved or rejected items, items a pull request already claimed, and pending items that came from a manual note, an import or a mission artifact. For an item an open pull request claims, merge or close that pull request, then run
orgabot memory sync-approvals, which commits its record. For the rest, commit the record yourself.orgabot memory approveororgabot memory rejectcommits the record of the item it acts on, or you can commit the files by hand. The command still writes them, becauseorgabot memory reindexrebuilds the store from the record files.
orgabot memory reindex does the opposite job: it rebuilds Orgabot's store from the record files.
orgabot memory repo set <repo> | show | clear
orgabot memory reindex
orgabot memory rewrite-missing [--dry-run] [--org <org>]
orgabot memory sync-approvals
orgabot memory list <org>
orgabot memory findings <org> [--status open|resolved]
orgabot memory resolve <org> <findingId>|--all [--note "text"]
orgabot memory approve|reject <org> <itemId>From the dashboard
The same maintenance is on Security → Memory Access, under Memory maintenance. It needs no organization selected, because the memory repository setting and both runs cover the whole machine. Every action calls the same code as the CLI command it mirrors, so the dashboard never writes memory a different way.
- Memory repository shows the current setting (the equivalent of
orgabot memory repo show) and where organization- and unit-scope records land. When the directory no longer exists, the setting is flagged in red: every organization- and unit-scope record then falls back to the JSON store only. Enter an absolute path (or one starting with~/) to set it, or press Clear. The dashboard refuses a relative path, because it has no working directory of yours to resolve one against.orgabot memory repo showflags a missing directory the same way. - Write missing records runs
orgabot memory rewrite-missing. Pick all organizations or one, then press Dry run: it writes nothing and shows the counts per organization, per route and per destination folder, plus why each group of items was skipped. Only after a dry run is Write N missing record(s) offered, and it writes exactly the scope that was counted. Changing the organization discards the dry run. The result lists every write that failed along with the reason. - Reindex memory runs
orgabot memory reindex, including the reconciliation of open distillation pull requests that runs first, and shows the record counts, the folders it scanned, and any file it could not parse or rejected.
Memory items carry a classification and a confidence, and they are injected into mission briefs as informational input only: approved memory is never new authority.
Browsing and reviewing memory
The dashboard's Security → Approvals & Access tab lists every memory record the organization holds, next to the findings. Each record shows its statement, scope, classification, confidence, freshness, approval state, and where it came from, plus how many times it was actually supplied to an agent and how many times it was withheld. Filter by approval state, by scope, or by searching the statements.
Provenance is stated rather than implied: an agent distilled record reads as agent-generated, never as something a person curated.
Approving from the panel does exactly what orgabot memory approve does, because it is the same endpoint and the same governed command. Approving marks one statement usable; it merges nothing, picks no winner, and deletes nothing.
The panel respects the access policy. A record you may not read is not rendered, and the same check the assembly path uses decides that, so the panel cannot show you a statement an agent was correctly denied. Records held back are counted with their access level, so a filtered list is never mistaken for the whole store: knowing a statement exists matters even when you cannot read it.
A project-scoped record is readable when you hold that project, asked one project at a time through the same check. The panel identifies you as the organization's accountable human, and only when there is exactly one: with several humans it falls back to showing what everybody may see rather than handing one person's actor-scoped statements to another. An expired assignment unlocks nothing.
Where memory content may go
Access decides who may read a record. It does not decide where the content goes, and those are different questions: a record supplied to a role is composed into a mission brief and handed to whatever provider runs that mission.
Each provider carries a declared handling ceiling with a stated basis:
| Provider | May receive | Basis |
|---|---|---|
| Claude Code, Codex, Grok (local CLIs) | up to restricted | run locally, send prompts under the operator's own account |
| Anthropic, OpenAI, OpenRouter APIs | up to internal | third-party API key, no recorded handling agreement |
| anything unrecognized | up to internal | already receives the instruction and the repository |
The ceiling governs the two tiers above internal. A mission's instruction and its whole repository already travel to whichever provider runs it, so withholding an internal sentence from a provider that is simultaneously receiving the codebase would cost every mission its organization memory and protect nothing.
A mission that can fail over to another provider is composed under the lowest ceiling in its whole chain, because a fallback reuses the brief verbatim rather than recomposing it.
organization, role, and project records are all internal: they name an audience, not a sensitivity. restricted and privileged are their own tiers.
No provider receives `privileged` content. That tier is for statements an organization decided only a human may read, and a default that sent them to a model would make the tier meaningless.
A record above a target's ceiling is withheld from the brief and recorded in that assembly's denied list, so the audit trail shows what actually reached a worker. The denial carries ids and tiers, never the statement.
Releasing above a ceiling takes an explicit, approved, time-bounded exception naming one record and one target. It is not a mode or a flag, and it does not carry: a decision to send one statement to one provider says nothing about the next statement or the next provider.
Granting an exception
Two steps, deliberately separate. Requesting an approval releases nothing.
# 1. Mint the approval, bound to these exact terms (item, target, tier, expiry).
orgabot memory declassify request <org> --item mem_… --target anthropic \
--tier restricted --expires 2026-09-08T17:00:00.000Z
# 2. Resolve it, as an operator.
orgabot approve apr_…
# 3. Redeem the resolved approval.
orgabot memory declassify grant <org> --item mem_… --target anthropic \
--tier restricted --expires 2026-09-08T17:00:00.000Z --approval apr_… \
--reason "one incident review"
orgabot memory declassify list <org> # live, expired, and revoked grants
orgabot memory declassify revoke <org> dcl_… # end one earlyThe dashboard equivalent is Security → Memory Access, which reads the same records and writes through the same governed mint.
Four rules the mint enforces:
- The expiry is required, must be a full ISO instant with a zone, and must be in the future. A shorter form is refused rather than stored, because the release gate would ignore it and the record would stay withheld with nothing saying why.
- The approval must have been minted for that exact release. The item, target, tier, and expiry are digested into its action, so an approved record for anything else authorizes nothing.
- The approver is read off the approval, never supplied. An approval that names no decider grants nothing.
- A grant is only ever
restrictedorprivileged, and it cannot raise a ceiling. Nothing on either surface changes what a provider may receive in general; the exception covers one record and one target.
Every grant and revocation lands an elevated organization audit entry carrying the record id, the target, and the tier, never the statement.
Memory findings (conflict alerts)
A memory finding is not a vulnerability and not a code-review finding. It is a conflict alert: two or more current memory records share a topic key (conflictKey) but disagree on the statement. Example: several items keyed highwire-verify-command that each claim a different verify command.
They show up on Security → Approvals & Access as "Memory findings", and on the CLI:
orgabot memory findings <org> --status open
orgabot memory resolve <org> <findingId> --note "kept the later statement; older item is stale"
orgabot memory resolve <org> --all --note "operator bulk acknowledgement"One topic keeps one open finding. A new write that disagrees on a conflictKey which already has an open finding updates that finding (its item list and last-conflict time) instead of stacking another alert, so an evolving topic is one open conflict, not dozens of overlapping ones.
Resolve (acknowledge) marks the alert seen. It does not fix the conflict. The finding moves open → resolved with a timestamp and optional note. The conflicting memory items stay in the store. Resolve does not merge them, pick a winner, delete either record, or change what missions will be told. The next write that disagrees on the same key after a resolve opens a fresh finding. orgabot memory resolve <org> --all acknowledges every open finding in one write, so clearing acknowledgement-only noise never costs one command per finding.
To actually reconcile a topic, pick a winner: approve the statement that should stand and reject the ones that should not (orgabot memory approve|reject <org> <itemId>). The dashboard's Memory findings list now shows the disagreeing statements on each finding with keep / reject controls that do the same thing. When rejection leaves at most one current statement on the topic, the conflict no longer exists and its finding auto-resolves.
Do not confuse this with orgabot finding remediate, which records a different kind of organization finding and can launch a governed remediation mission:
orgabot finding remediate <org> <role> "<finding>" [--project <project>]Output sinks
A sink is a governed destination for mission reports and output: a place work leaves the system.
orgabot sink list
orgabot sink deliver buffer --org <org> --role <role> --item <context-item-id> --channels <id,…>Delivery is gated by a write grant plus an approval release: an agent can compose and queue a post, but publishing waits on the approval queue. The Buffer social-syndication path runs on that contract end to end, driven by a weekly duty and the Channel Playbook capability plugin.
The standing documentation rule
A change to the system gets a corresponding documentation update when one applies. If a mission changes behavior an operator relies on, the same mission updates the page that describes it. See Writing documentation.