Add A17, the knowledge store contract (Outline)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m3s
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m3s
Part D said what Outline is for but never how to build against it — the only external integration with no A-series component, while Transport, LLM, Browser, Secrets, SSO and Logging all have one. A17 supplies the contract. Settled here: - Sharing is read access, never write access. Every document has exactly one writer, so concurrent writers never exist and Part D's last-write-wins objection disposes of itself. Default shared membership is operator + nisse-ops; nisse-dev gets private space only, since B3 seals it from the LAN and a shared network map is a map of what it is sealed from - Agent-owned knowledge is always a cache, never authority: rely on it to know what exists and where to look, verify a specific fact against the live system before acting on it - Enforcement is structural, because a prompt is not enforcement. A model cannot be stopped from believing what it reads, so the enforceable goal is narrower — an unverified belief must not become an irreversible action — and four mechanisms carry it, leaning hardest on surfacing provenance at the human gate that C2 already requires - Never an instruction channel and never an approval channel, stated both here and in A4 where approvals are specified - Corrections flow through transport rather than direct edits, which is better provenance: a hand edit is invisible to the agent's record, an instruction is authenticated, logged, and reconcilable. Break-glass reuses Part F's idle flag - Retrieved content is snapshotted into the canonical log, since A10 forbids a mutable provider handle being the only reference — with the A3 consequence that boot must re-fetch snapshots for resumed tasks - Outage posture is asymmetric: write failures never block, but a failed read of a procedure bound to a confirm-first action gates, because proceeding is acting unconstrained on exactly the operation someone wrote a procedure for Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8d37b5441
commit
b8ba683595
1 file changed
+119
-1
@@ -175,7 +175,9 @@ Requirements:
|
||||
task. The agent has no path to approve itself.
|
||||
- **Approval sources.** A gate waits on an approval event from any registered
|
||||
**`ApprovalSource`** — the Matrix transport (A1), the Gitea comment poller (B2), or
|
||||
the Vikunja comment stream (C4). First valid approval wins and is consumed; the
|
||||
the Vikunja comment stream (C4). **The knowledge store (A17) is never an approval
|
||||
source**, and never an instruction channel: a wiki page has neither the identity
|
||||
guarantees nor the revision binding an approval requires. First valid approval wins and is consumed; the
|
||||
gate records which channel satisfied it, and the orchestrator echoes the outcome to
|
||||
the other channels so the records converge. Each source authenticates the approver
|
||||
against a per-channel operator allowlist (Matrix MXID, Gitea username, Vikunja
|
||||
@@ -507,6 +509,117 @@ additional cost is marking outcomes as good or bad.
|
||||
Without this, "best model this week" is decided by benchmark chatter, and swapping the
|
||||
frontier tier becomes something to avoid because the effect is unmeasurable. With it, a
|
||||
new model is a config change and a re-run.
|
||||
### A17. Knowledge Store (Outline)
|
||||
|
||||
**Purpose:** durable knowledge that outlives a single task — an agent-maintained
|
||||
**world model** (what infrastructure exists, how it is arranged) and, later,
|
||||
operator-authored **procedures**. Distinct from A2, which holds *task* state: A2
|
||||
is the agent's memory of what it is doing, A17 is its memory of what the world
|
||||
looks like. Neither is a fallback for the other.
|
||||
|
||||
Part D describes the service; this defines the contract.
|
||||
|
||||
**Layout — sharing is read access, never write access.**
|
||||
- Every document has **exactly one writer**: one agent, or the operator. Sharing
|
||||
governs who may *read*. Concurrent writers therefore never exist, which
|
||||
disposes of the last-write-wins objection Part D raises, without needing
|
||||
distributed-write machinery.
|
||||
- **Agent-private** space per agent: its own world model.
|
||||
- **Shared** space: readable by the operator and designated agents. Default
|
||||
membership is **operator + `nisse-ops`**. `nisse-dev` gets private space only —
|
||||
B3 seals it from RFC1918 precisely so it cannot reach the NAS, Proxmox, or the
|
||||
UDM, and a shared network-architecture document is a map of exactly those
|
||||
things. Cross-agent read access is justified per collection, never assumed.
|
||||
- The dev agent's project conventions live in the **repo it is working on**, not
|
||||
here: versioned with the code, reviewed in the PR, and impossible to
|
||||
desynchronise from the project.
|
||||
|
||||
**Agent-owned knowledge is always a cache, never authority.** An agent may rely on
|
||||
it to know *what exists and where to look*; it must confirm a specific fact
|
||||
against the live system *before acting on it*. Hard requirement for anything in
|
||||
C2's confirm-first tier; advisory for read-only and reversible actions.
|
||||
|
||||
**Enforcement is structural, because a prompt is not enforcement.** "Treat this as
|
||||
a cache" is exactly the advisory guarantee A4 and A12 reject, and the model cannot
|
||||
be prevented from *believing* a fact it has read. The enforceable goal is
|
||||
narrower: **an unverified belief must not become an irreversible action.**
|
||||
1. **The store decides what it serves.** Facts carry provenance and an observed-at
|
||||
timestamp; retrieval marks or withholds anything past a staleness bound for
|
||||
high-consequence classes.
|
||||
2. **Verification as a precondition, checked against the log.** For confirm-first
|
||||
actions, refuse unless a verifying observation exists *in this task's canonical
|
||||
log*, recorded after the task began, naming the action's target. Inspectable
|
||||
without asking the model anything — the same move as A12's observable triggers.
|
||||
Approximate by nature, so build it for the confirm-first tier only.
|
||||
3. **Provenance at the human gate** — the primary reliance, because C2 already
|
||||
stops these actions for confirmation. The prompt states evidence quality:
|
||||
"target recalled from the world model, observed 2026-08-02, not verified this
|
||||
session" versus "confirmed by `zfs list` two minutes ago". The operator is
|
||||
already the authority there; this makes the risk legible rather than invisible.
|
||||
4. **Audit, not prevention, for the cheap tiers.** Log which facts were retrieved
|
||||
and which actions followed, so a wrong fact is traceable afterwards.
|
||||
|
||||
Residual risk, stated plainly: a model can retype a fact from context with no
|
||||
retrieval event, and no mechanism here catches that. That is why the world model
|
||||
stays out of the authority path entirely.
|
||||
|
||||
**Never an instruction channel, and never an approval channel.**
|
||||
- **Task instructions never come from the knowledge store.** Work is driven by
|
||||
Vikunja tickets (C4) and operator commands (B2). An agent must never read the
|
||||
store looking for work to do.
|
||||
- **Approvals never come from the knowledge store.** The registered
|
||||
`ApprovalSource`s are Matrix (A1), Gitea (B2), and Vikunja (C4). A wiki page is
|
||||
not one and must not become one.
|
||||
- An agent must never write here intending to cause another agent to act. This
|
||||
extends Part D's rejection of the store as a message bus: the erosion path is
|
||||
one well-meaning "note for the other agent" at a time.
|
||||
|
||||
**Corrections flow through transport, not through direct edits.** The operator
|
||||
corrects a fact by telling the agent, which is strictly better than editing the
|
||||
page by hand: a hand edit is invisible to the agent's record, whereas an
|
||||
instruction arrives on an authenticated channel from an allowlisted identity, is
|
||||
recorded in the canonical log, and can be reconciled against what the agent
|
||||
believed. Corrections are stored **verbatim with attribution**, never paraphrased —
|
||||
a model rewriting a precise fact loses precision invisibly.
|
||||
- **Break-glass:** the operator keeps write permission. The procedure reuses Part
|
||||
F's idle flag — wait for idle, stop the agent, edit freely, restart. Single
|
||||
writer holds trivially while the agent is down, and A2/A3 make stopping safe.
|
||||
- Because that permission exists, an agent re-reads and compares the revision
|
||||
immediately before writing, and re-merges rather than clobbering if it moved.
|
||||
A safety net for the rare edit, not a designed workflow.
|
||||
|
||||
**Canonical log representation (A10).** Retrieved content is **snapshotted into the
|
||||
log** — the text, plus document id and revision for provenance — never referenced
|
||||
by handle alone: A10 forbids a provider handle being the only reference to content,
|
||||
and these documents are mutable. Snapshot once per task at first use, not per turn.
|
||||
**A3 consequence:** a task resumed across a restart carries the old snapshot, so
|
||||
boot reconciliation re-fetches snapshotted knowledge and flags divergence rather
|
||||
than silently continuing on a stale copy.
|
||||
|
||||
**Retrieval is structural first, discretionary second** — the A12/A14 pattern.
|
||||
- Procedures are **bound to C2 action classes**: the policy taxonomy and the
|
||||
procedure index are the same list. Binding is deterministic and auditable
|
||||
("which procedure governed this action?" has an exact answer), and costs nothing
|
||||
extra because the taxonomy must exist anyway.
|
||||
- A model-invoked **search** exists *in addition* for discovery, never as a
|
||||
replacement for the bound retrieval — a procedure the model may skip does not
|
||||
constrain it.
|
||||
|
||||
**Outage posture is asymmetric.** Unlike B2's poller, "never fails a task" does not
|
||||
generalise here.
|
||||
- **Write failure never blocks:** retry, or record locally and reconcile.
|
||||
- **Read failure for a procedure bound to a confirm-first action gates.**
|
||||
Proceeding is not a delayed reaction; it is acting unconstrained on precisely the
|
||||
operation someone wrote a procedure for.
|
||||
- Read failure for read-only or reversible actions proceeds, logging the gap.
|
||||
|
||||
**Accepted single point of failure.** Once the agent is the sole author and Outline
|
||||
the only home, losing Outline loses the world model. No local mirror: infrastructure
|
||||
facts are re-derivable by observation, which is the same premise the cache rule
|
||||
rests on.
|
||||
|
||||
**Deferred:** the operator-writable procedures collection. The interface is defined
|
||||
here; the content and the C2 binding list arrive with the policy taxonomy (Part H).
|
||||
|
||||
---
|
||||
|
||||
@@ -689,6 +802,10 @@ Requirements:
|
||||
back to Outline: searchable, off-box, and an extra externalized record that
|
||||
survives volume loss.
|
||||
- Access via Outline **REST API**, dedicated service account, token from Bitwarden.
|
||||
- **The library contract is A17**, which settles what this section left implicit:
|
||||
per-agent private space plus a shared space, one writer per document, agent-owned
|
||||
knowledge treated as a cache rather than authority, and the structural means of
|
||||
keeping it that way.
|
||||
|
||||
**Deliberately rejected:** using a shared notes store (Outline *or* an Obsidian vault)
|
||||
as an **agent-to-agent message bus**. It's unstructured, race-prone (last-write-wins,
|
||||
@@ -835,6 +952,7 @@ nisse-core/ # shared library (extracted after nisse-dev works)
|
||||
secrets/ # Bitwarden machine-account client (A7)
|
||||
sso/ # LLDAP identity + auth path (A8)
|
||||
logging/ # structured operational logs (A9)
|
||||
knowledge/ # Outline client, world model, procedures (A17)
|
||||
|
||||
nisse-dev/ # app: sealed VM (Part B)
|
||||
workflow (arch/milestone gating), Gitea PR layer, allow-all sandbox
|
||||
|
||||
Reference in new issue
Block a user