benchbook
↑↓ navigate↵ openDocs · Wiki · Contract
GitHub
Contractagents-domain-knowledge.md

agents-domain-knowledge.md

benchbook — Domain Rules: Knowledge

Loaded during INGEST for the knowledge domain. Read alongside agents-core.md.


What belongs here

General concepts, tools, products, frameworks, techniques — things that exist in the world independently of you. The knowledge domain is the default home for anything you’ve learned about rather than own.

The distinction that matters, per the Entity Placement Rule in agents-core.md:

So a container runtime is a knowledge entity; the particular server you run it on is not.

Default subdomain: null.

Page types this domain produces: source, entity, reference.

Knowledge sources go to the global wiki/sources/ folder, like every other source — only the domain: knowledge frontmatter marks them as belonging here.


Dispatch flow

K1. Create the source page at wiki/sources/<slug>.md## Summary## Key Takeaways## Content (when fetchable) → ## Sources.

K2. Identify the entities the source is about. Propose them; create only approved ones. Apply the Entity Placement Rule for each — don’t default everything to knowledge.

K3. Two-way link. The entity’s sources: frontmatter gets the source slug; the source body links to the entity with a relative link.

K4. Update existing entities silently where the source adds facts, and note it in the report.

K5. Channel trigger — if this source’s channel: value now has three sources, propose a channel/author entity (template below).

K6. References — only if the source ranks or compares 4+ entities, and only with human approval. For 1–3, use entity pages with ## Compared To cross-links.


Predicate Vocabulary

Predicates in a ## Relations triplet table must come from this list, on every page that carries one — not only knowledge entities. LINT flags anything else. If a fact doesn’t fit any predicate, it’s usually a discrete attribute — put it in ## Key Facts rather than forcing a relation. That rule is load-bearing: an object that is prose rather than a page (monitors → "5 backup units, 2 workflows, 3 timers") is not a triple at all, however well the predicate fits.

Core — 18 predicates

The set that applies to any wiki, whatever you keep in it.

PredicateMeaningInverse
is_atype / category membership
made_bymanufactured / created by a commercial makermakes
makesmanufacturer → its productmade_by
includesbundles / containspart_of
part_ofcomponent of a larger wholeincludes
usesdepends on / employsused_by
used_byemployed byuses
runshardware/host → software it runsruns_on
runs_onsoftware → its host platformruns
integrates_withworks together with (symmetric)
competes_withalternative to (symmetric)
replacessupersedes / successor of
requireshard dependency
supportsoptional capability / compatibility
enablesmakes possible
evaluatesproject/person assessing, adopting, changing or deciding about the entity. This is the project→entity predicate — see belowevaluated_for
evaluated_forentity → the project/person assessing, adopting or changing itevaluates
compared_toweighed side-by-side for reference (symmetric) — not necessarily rivals; use competes_with for actual alternatives

Provenance and ownership — 8 predicates

PredicateMeaningInverse
built_byentity → your own project that built, wrote or revised it. Not made_by, which means a commercial manufacturer: a bike is made_by its brand, whereas a podcast you generate is built_by the project that generates itbuilds
buildsproject → the artifact it producedbuilt_by
ownsperson → the thing they own. The only person-facing predicate; use it where the owner actually mattersowned_by
owned_byentity → its owner (a page in wiki/people/, or the household)owns
feedsdata source → the consumer it supplies. Kept separate from uses because it records provenance and direction of data flow, not dependencyfed_by
fed_byconsumer → its data sourcefeeds
triggered_byentity → what causes it to run (scheduler, bot command, event). Not fed_by, which is about data arriving rather than invocationtriggers
triggersscheduler/trigger → what it invokestriggered_by

Operations — 10 predicates (only if you run services)

Skip this block if you don’t operate infrastructure. These five pairs exist because the wiki this came from documents a home server fleet, and they’re load-bearing there — but on a wiki about books, recipes or research they will never fire. Dropping them costs nothing; LINT only flags predicates you use that aren’t listed, never predicates you don’t use.

PredicateMeaningInverse
backed_up_byentity → what protects its data. Deliberately not uses: a service does not employ a backup tool, it is protected by it — and that distinction is what makes a backup gap visiblebacks_up
backs_upbackup system → what it protectsbacked_up_by
securessecret/credential store → what it holds secrets forsecured_by
secured_byentity → where its secrets livesecures
monitorsobservability tool → what it watchesmonitored_by
monitored_byentity → what watches itmonitors
frontsreverse proxy → the service it exposesfronted_by
fronted_byservice → the proxy in front of itfronts
stores_data_onentity → where its data physically lives. Distinct from uses: it answers “what breaks, and what is lost, if that storage dies”stores_data_for
stores_data_forstorage → whose data it holdsstores_data_on

The subject of a row does not have to be the page’s own entity — pick the direction that reads naturally and use the inverse predicate when needed. Each fact is stored exactly once, on whichever page reads naturally, which is why a page’s own table shows only half its relations and why scripts/relations.py exists (see below).

Which predicate for a project → entity relation?

evaluates, unless something more specific clearly fits. It is the general project→entity predicate, not a purchase-only one: it covers assessing, adopting, rejecting and changing, and it covers resolved outcomes as well as open ones — a project that dropped a tool still evaluates it, and a migration project evaluates the deployment it is migrating.

Reach for uses when the project genuinely depends on the entity to run, builds when it produces it, replaces when it supersedes it. Otherwise evaluates.

This wording is the result of getting it wrong. The definition originally read “project/person considering the entity (e.g. purchase decision)”, which was narrower than ten months of actual use — narrow enough that a proposal for a new targets/targeted_by pair got written before anyone noticed evaluates already did the job. The fix was to widen the definition, not to add the pair. Worth copying as a habit: when a vocabulary looks like it has a gap, check whether an existing term is just described too tightly.

Why a controlled vocabulary

In the original wiki this list was derived after the fact, from 66 existing entity pages, and the derivation immediately surfaced synonym drift — is_made_by / produced_by / made_by all in use for the same relation. Left alone, a free predicate field becomes unqueryable within months.

It drifted again three months later, and the second drift is the more useful lesson. The LINT check that enforces this vocabulary was scoped to knowledge entity pages, because that’s where relations started. By the time anyone measured, 709 triples existed and 185 of them sat outside that scope, carrying 124 off-vocabulary predicates no check could seebacked up by on eight pages against backed_up_by on one, runs on beside runs_on. Inside the old scope, 0 of 524 rows were off-vocabulary: the check worked perfectly everywhere it looked, which is exactly what made it hard to notice it wasn’t looking everywhere.

So: the vocabulary applies wherever the table appears, and the check’s scope must match the rule’s scope. A check that is right about the region it inspects is not the same thing as a check that is right.

The repair took the vocabulary from 18 predicates to 36. Every pair added had to carry at least three rows of independent evidence already present in the wiki — the pairs were derived from the drift, not invented for completeness. Two candidates were declined on that basis (deployed_for, because part_of already read correctly; documented_by, because ## Sources is where that belongs), and one pair was adopted at only two rows as a deliberate, flagged exception. That is the bar worth copying: extend the vocabulary from evidence you already have, not from cases you can imagine.

Querying the graph

./scripts/relations.py <page-slug> reports a page’s inbound and outbound edges. You need it because each fact is stored exactly once — reading a page’s own ## Relations table shows you roughly half of what the wiki knows about it.

./scripts/relations.py esphome              # inbound + outbound, depth 1
./scripts/relations.py hallway-display --depth 2   # walk two hops
./scripts/relations.py --orphans            # pages with no edges either way

Depth 1 is the usable unit. Depth 2+ grows fast and is for the rare “what is the blast radius of this change” question, not for orientation. Traversal deliberately never follows the three symmetric predicates (integrates_with, competes_with, compared_to) — each is a 2-cycle by definition, and walking them turns a neighbourhood into the whole graph. They’re still reported at depth 1, where they’re useful.


Channel / Author Entities

Proposed at the 3rd source from one origin (a YouTube channel, a blog author, a podcast), created at wiki/knowledge/entities/<origin-slug>.md. Tracked via the channel: field on source pages.

SectionPurpose
## SummaryWho they are
## What They ProduceSubject matter, language, format
## QualityYour own evaluation — reliability, blind spots, where they’re worth trusting
## SourcesAuto-updated list of all ingested sources from this origin

Channel entities do not get ## Relations, ## When to Use, or ## Compared To — those don’t fit a producer.

The ## Quality section is the point of these pages. Three sources in, you have an opinion about whether this origin is reliable; writing it down is what stops you re-forming it every time.


Checklist