Knowledge¶
bosun kg keeps what bosun should know about your organization: people (work facts
only), processes and the product, as atomic facts with sources, so a fact stated once is not lost
when the conversation ends (design: docs/design/knowledge.md). It
lives in $BOSUN_CONFIG_DIR/knowledge/, never in this repo, with its own git history (one
commit per change; BOSUN_KNOWLEDGE_GIT=0 turns it off, and it is skipped if git is missing). A lock
that a git command cut short left there (the index's, HEAD's or a branch's, older than 30 seconds)
is removed by the next write, with a note on stderr for each, so the history goes on recording.
That git runs none of your own git hooks, templates or file watcher, and nothing of it is still
running when a command returns (its housekeeping runs inside the commit; a git command past 30
seconds is stopped with all it started, and the write warns). A .git there that is no repository
at all (no HEAD, objects or refs in it) is deleted by the next write, which starts the history
again and says so in one line; one that git cannot read right now (a typo in your git config, a
newer git's repository format) is left as it is, and each write warns until git reads it again.
A problem with the history never fails a write, which is saved by then: it is a warning.
Every change goes first into a journal there, one transaction per command: journal/<replica>.jsonl
for this Mac's changes, and journal/local.jsonl for those that never leave this Mac: whatever
names a person, and every fact you stated (one with your quote, --from-user, or a transcript
source); view.json records how far the files reflect it (Sync and sharing).
The files are the journal's rebuild. A write cut short anywhere between the journal and view.json
is finished by the next write (a kg forget too), and a command that only reads uses the rebuild
without saving it. A knowledge file left with a line that does not parse (a power cut or a full disk
during a write) is read the same way, with a line on stderr naming it, and the next write writes it
again; only with no journal to rebuild it from does it stop every command, naming the line to fix or
remove.
A change made to the files outside the journal (by hand, a git checkout or git revert of files
in the knowledge folder, or a bosun from before the journal; view.json records a digest of each
file, so this is told apart from a write cut short) is adopted by the next write: it goes into the
journal as one transaction named adopt, with a line on stderr, and the write goes on from the
journal's rebuild. What the journal cannot take comes back, since the fact log only grows: a record
removed by hand comes back, and one edited in place comes back beside its edited copy, which is
adopted too; an edited add record takes the fact over (its status is the edited record's again,
and the updates before it no longer show). Close a fact with kg supersede instead. That write says
so in one line, and the knowledge folder's git history keeps the files as they were edited (unless
BOSUN_KNOWLEDGE_GIT=0, or the write starts the history again because it deletes: then nothing
keeps them, as that history holds what it deletes, and the line says so).
A person deleted on request is never taken back. One gone from the files whom a forgotten record
matches (an older bosun's kg forget, or removed by hand) is forgotten in the journal too: their
changes become placeholders. One found in files restored from before a kg forget is left out and
goes from the files; the write says so without naming them, and kg add-entity <id> --allow-forgotten
adds them again if they asked.
Every deletion of a person (kg forget, kg expire when it deletes a person fact, one made outside
the journal) also deletes every backup bosun update took (backups/ in the state folder). A backup
holds the whole knowledge base, forgotten.jsonl and the journal included, so restoring one from
before the deletion would bring the person back with no trace of it. The line says how many went
("kg: deleted 2 update backups taken before this; going back past it is no longer possible"), never
whom. A deletion cut short deletes them when the next write finishes it.
If a write that deletes the git history (it holds what a person deleted on request named) cannot
delete all of .git, it stops there and names it: delete it, and the next write finishes. While
secret.key is missing over keyed records, no person is taken in from changed files at all, as
adding one is refused then; the write says why, and they go from the files, so restore those again
once the key is back. A write that has to finish a kg forget cut short while that key is missing
is refused whole, naming the key, and the first write once it is back finishes the forget.
A command that only reads shows restored files as they are until the next write, since reads never
write; and person facts an older bosun's retention purge took out come back from the journal until
the next kg expire purges them again. Checking out an older commit of the whole folder takes the
journal back with it, and what came after is gone
(Sync and sharing). Going back to the release before the journal
is not supported: once a fact has a minted id (f-3k9x2a), that release's kg batch and kg
lookup stop on it (Sync and sharing). How cut writes,
outside edits, who is forgotten and the history restarts work, and the rare double faults accepted,
is in Sync and sharing.
kg validate compares the files with the journal (--json for scripts; exit 1 on any finding), and
bosun doctor runs it: damage, a position that does not match or a rebuild that differs fails,
since writes are refused or data is at risk; a conflict, an update waiting for its fact, an outside
edit the next write adopts or a file that does not parse, which the next write writes again, warns.
If a command says the knowledge base's journal is damaged, or behind view.json (a journal file
deleted, a partial restore), nothing was written, and the files are still whole. To go on from
them, move knowledge/journal/ aside (keep it): the next write starts a new journal from the
files, as a new replica, so that no fact id is minted twice. The state folder's replica.json may
stay: a journal that starts again (moved aside, emptied or removed) always gets a new id, made by
that write in place of the old one. replica.json is this Mac's id in the ledger's journal too, so
this gives the Mac a new id in both journals, which both carry on under; the ledger's old replica
file stays in its journal (Setup). A write cut short as it starts the new
journal is finished by the next write, as any other.
bosun kg add-entity process/release --name "Release process" --alias releases
bosun kg add-entity gate/canary-checks --name "Canary checks"
bosun kg propose releases has_step canary-checks --order 3 --source ledger:mypr:acme/api#7 --source report:task:2026-10-01/1
bosun kg propose releases rule --value "a deploy rolls out to production gradually once the canary checks pass" \
--from-user --quote "it rolls out gradually once the canary checks pass" --transcript <session.jsonl>
bosun kg batch # the day's proposals, grouped and numbered
bosun kg batch apply 'confirm all except 2b'
bosun kg context --for releases # a small cited pack of confirmed facts
- Entities are
<type>/<slug>on three axes: people (person,team), process (process,step,gate,channel,system) and product (repo,service,environment,feature,decision,principle,lesson). A name or strong alias works anywhere an id does; weak aliases are related terms for search. Near-identical names are reported as merge suggestions. - Facts use a closed set of predicates (
bosun kg schema); a site adds its own inknowledge/schema.json. About a person only work facts are kept (role, team, title, timezone, stated working hours, preferred channels, what they review, own or approve, stated work preferences); anything else is refused, as is anything inferred about a person. A new fact's id isf-and six characters (f-3k9x2a), minted from the journal so that two Macs never make the same one; facts from before keep theirs (f12), and both work wherever a fact id is asked for. - Confirmation without the spam: a fact is confirmed at once when the quote of your words is
in a prompt you typed in that session's transcript (tool results and pasted text never count),
when two trusted sources of different kinds back a low-risk fact that names no person, or when a
profile source gives a title or timezone. Slack, PR, ticket and document text never confirms
anything. The rest waits for
kg batch: one list, grouped by entity and axis, rework-preventing groups first, at most 10 groups, 8 facts a group and 40 facts a day (BOSUN_KNOWLEDGE_BATCH_CAP,_GROUP_FACTS,_BATCH_FACTS), answered in one line; "confirm all except 2b" leaves 2b proposed, only "reject 2b" rejects it. The same batch can be answered in the console's review page (#/knowledge/review). Unreviewed proposals expire after 30 days unless something observes them again. - Two timelines: facts are never edited or deleted when they change:
kg supersedecloses one (valid_to,superseded_by), andkg facts <entity> --as-of <date>shows what held then. - Maintenance:
kg lintreports contradictions, stale facts (BOSUN_KNOWLEDGE_STALE_DAYS), orphans, alias collisions and expired proposals;kg validatechecks the files against the journal they are the rebuild of (above); person facts not re-observed for 12 months are deleted bykg expire, andkg forget person/<slug>deletes a person on request (with the git history, which restarts, and their changes injournal/local.jsonl, each left as a placeholder that keeps its place in the sequence and nothing of what it held; what writes cut short left of that file,local.jsonl.torn-*, goes too, and so does what they left in the knowledge folder,*.tmp, such as akg batch'sbatch.json.tmp; the history never holds a torn file). What it keeps is a keyed hash of the id, undersecret.keyin the knowledge folder (made on the first write, readable only by you, not in the history, never leaving this Mac). Once such records exist,kg forgetand adding a person need that key; if it is missing the command refuses and names its path, so restore it from a backup. If it is gone for good, moveforgotten.jsonlandknowledge/journal/aside, andsecret.key, if it is there, all together (the journal's deletion markers hold the same records, and the next write starts a new journal from the files, as a new replica, as for a damaged one; the state folder'sreplica.jsonmay stay): the people in them can then be added again (your choice, knowingly), and the next write makes a new key. Asecret.keythat is not a key (empty, the wrong length, unreadable) while no such record needs it is moved aside by the next write, kept assecret.key.aside-<time>(not in the history), and made again, with a line on stderr. - Seeding:
kg import <file>reads entities and facts as JSON lines, records every fact as a proposal (--auto-confirmapplies the rules above), is a dry run unless--apply, and says how many daily batches the review takes;tools/survey_to_kg.pyconverts a survey's markdown tables, with a mapping file kept next to the survey, into such a file.bosun doctorshows the counts and runskg validate.