* Add the LogicSRC OpenContext specification OpenContext is an open specification for durable, portable, permissioned, provenance-aware context shared between humans and AI agents. It defines how organizational knowledge is described, authorized, versioned, resolved, audited, and handed between replaceable workers without losing institutional state. Follows the OpenPRD/OpenOntology pattern already in the repo: self-contained JSON Schemas in @logicsrc/schemas, a reference implementation package, CLI subcommands, docs, examples, and an OpenPRD record. Schemas (8, all self-contained so a third party can fetch one file and validate against it with no further resolution): manifest, object, bundle, role, provenance, decision, diagnostic, audit-event — registered in @logicsrc/validators and schemas:validate. Reference implementation (@logicsrc/opencontext): loader with upward manifest discovery, the full resolution pipeline, authority/supersession, permissions, redaction, lifecycle, provenance, deterministic digests, doctor, search, graph, history/diff, guarded writes, audit events, and file/http/git/sqlite adapters. CLI: all 15 specified commands, as a standalone `opencontext` binary and as `logicsrc context`, sharing one implementation so the two cannot drift. Design decisions worth noting: - Supersession is declared, never inferred from version numbers. Inferring it would hide the governance failure it represents and make multiple-active-versions and duplicate-canonical impossible to detect. - The bundle digest identifies the resolved context, not the moment it was computed, so generated_at/bundle_id/digest/as_of are excluded while objects, lifecycle states, exclusions and warnings are covered. That is what lets a decision record cite exactly the context that produced it. - A role's own max_classification beats an inherited one, so a ceiling on a shared base role cannot silently cap a role deliberately granted more; requesting several roles at once still takes the lowest, so combining roles never escalates. - Scope wildcards match whole dotted segments only. A trailing .* covers a subtree; an interior * matches exactly one segment. Substring matching here would be an access-control bug. - --include narrows an existing scope and is applied after it, never merged into it, so a request can never widen what a role holds. Verified: 226 tests across core primitives, permissions/redaction, the resolution pipeline, security, the published conformance fixtures (13 valid, 35 invalid, 8 resolution scenarios), project behaviour, and the five shipped examples — which are held to --strict and a 100% health score. Benchmarks meet every published budget (resolve 1,000 objects in ~33ms against a 2s target). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Point install docs at @logicsrc/opencontext; record the npm name collision The unscoped `opencontext` name is already published on npm by an unrelated third party (federicodeponte/opencontext, 2.0.0), so `npx opencontext` would install a stranger's package. Docs now use `npx @logicsrc/opencontext`; the bin stays named `opencontext` so the command reads as the PRD specifies once installed. Recorded in PRD 0003 as a blocker to resolve before any publication, along with the fact that no @logicsrc spec package has ever been published, so there is no existing release path to slot into. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Advance the logicsrc-mcp next-PRD-id assertion to 0004 standards.test.ts asserts prd_next_id against the live prd/ directory, so adding PRD 0003 makes the next free id 0004. The test's own comment anticipates this: "advances with every PRD added". Caught by CI, not locally — the earlier verification ran per-package tests for the packages this branch touches, and logicsrc-mcp is coupled to the PRD directory without importing from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.6 KiB
Integration patterns
OpenContext never requires a specific LLM provider, framework, or database. These are the shapes people actually deploy.
System prompts
The most direct use: resolve, render, prepend.
import { OpenContext, renderBundle } from "@logicsrc/opencontext";
const oc = await OpenContext.load("./opencontext.yaml");
const { bundle } = oc.resolve({ agent: "support-agent", task: userMessage });
const messages = [
{ role: "system", content: renderBundle(bundle, "markdown") },
{ role: "user", content: userMessage }
];
The Markdown renderer groups by layer, opens with a statement that everything below is context rather than instruction, and fences untrusted spans. Do not flatten it into raw text — the envelope is load-bearing. See security.
Record the digest alongside whatever the agent produces, and the decision stays reconstructable after the model is replaced.
RAG pipelines
OpenContext is the control plane above retrieval, not a replacement for it.
// 1. Your retriever proposes candidates.
const candidates = await vectorStore.search(query, { k: 50 });
// 2. OpenContext decides what this consumer may actually see.
const scope = oc.scope({ agent: "support-agent" });
const allowed = candidates.filter((hit) => {
const object = oc.get(hit.id);
return object ? authorize(object, scope).allowed : false;
});
Two rules worth stating plainly:
- Never rank before authorizing. Embedding similarity has no idea what a role may read.
- Retrieval rank is not authority. The top hit is not thereby the truth; a
referencenote that scores well does not outrank acanonicalpolicy.
A reasonable division of labour: vectors find candidates, OpenContext decides eligibility and authority, and the bundle is what reaches the model.
MCP servers
MCP is complementary. A server exposes operations over the same resolution rules:
context.get one object, subject to authorization
context.search lexical search, authorized before results are returned
context.resolve a bundle for a consumer and task
context.list what exists in scope
context.explain why an object was included or excluded
MCP access must use the same permission and resolution rules as the CLI and SDK. An MCP server that resolves with a wider scope than the agent holds is a privilege escalation wearing a protocol.
Carry the caller's identity into resolve({ agent }) rather than resolving unrestricted and filtering afterwards.
Agent frameworks
Bundles are framework-neutral: resolve, render, hand over.
const bundle = oc.bundle({ agent: agentId, task });
// Any framework — the bundle is just text plus metadata.
agent.setSystemPrompt(renderBundle(bundle, "markdown"));
agent.setCapabilities(bundle.permissions ?? []);
bundle.permissions carries the capability strings the role holds, for your runtime to enforce. OpenContext transports and scopes them; it does not perform your application's actions.
For multi-agent systems, give each agent a role in the manifest rather than a bespoke prompt. A hand-written prompt is context that exists only inside that agent — exactly the state this specification exists to end.
CLI agents
opencontext resolve --role support --task "$TASK" --format markdown > /tmp/context.md
my-agent --system /tmp/context.md "$TASK"
Discovery searches upward, so this works from any directory in the project.
Human onboarding
The same bundle that briefs an agent briefs a person:
opencontext resolve --role support --format markdown > onboarding.md
If it is not good enough for a new hire, it is not good enough for an agent — and the reverse is the useful test for whether your context is actually written down.
CI/CD
name: OpenContext
on: [pull_request, push]
jobs:
context:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx @logicsrc/opencontext validate --strict
- run: npx @logicsrc/opencontext doctor --strict
- run: npx @logicsrc/opencontext bundle --role support --output bundle.json
- uses: actions/upload-artifact@v4
with:
name: context-bundle
path: bundle.json
Useful gates:
opencontext conflicts --strict # reject duplicate canonical policies
opencontext stale --strict # reject expired required context
opencontext doctor --strict --min-score 95 # enforce a health floor
Publishing the bundle as a build artifact means every release records exactly what its agents knew.
GitOps
Context lives in the repository and changes through pull requests, reviewed like code.
- A policy change is a diff a human approves.
opencontext diffshows the governance-relevant fields, not every byte.git://<rev>/<path>reads context out of any past commit, offline.- Branch protection on
context/gives you approval workflow without a hosted service.
API servers
app.post("/context/resolve", async (req, res) => {
const identity = await authenticate(req); // your IdP, not OpenContext
const { bundle } = oc.resolve({
agent: identity.agentId, // never from the request body
task: req.body.task
});
res.json(bundle);
});
Take the consumer identity from your authenticated session, never from the payload. OpenContext enforces what a named consumer may read; it does not authenticate who is asking.
Recording decisions
const { bundle } = oc.resolve({ agent, task });
const answer = await model.complete(renderBundle(bundle, "markdown"), task);
oc.add({
id: `decisions.${today}-${slug}`,
type: "decision",
title,
decision: answer.decision,
rationale: answer.rationale,
decided_by: { type: "agent", id: agent },
bundle: { bundle_id: bundle.bundle_id, digest: bundle.digest }
}, { allowPromotion: false });
Note allowPromotion: false. An agent records a decision at ordinary authority; a human promotes it to approved. Observed context does not become truth automatically.
Anti-patterns
Copying context into a prompt template. It drifts within a week, and nothing tells you.
Resolving unrestricted and filtering later. Unauthorized context has already been ranked, logged, and possibly cached.
Treating a bundle as a cache. It is a snapshot for one task at one instant. Re-resolve; it costs milliseconds.
Writing back inferred context as canonical. That is how a model's guess becomes company policy without anyone deciding.
Stripping the untrusted envelope to save tokens. The label is what stops a ticket from reading as an instruction.