-
Notifications
You must be signed in to change notification settings - Fork 0
Design Doc
Current — this is the living version; regenerated at 2026-08-06T18:42:39Z. Historical snapshots are linked from Index-Releases.
Generated from the repository at ddcf3c4 (tag v0.4.1). Every code claim below cites a path and symbol. Sections of the standard template that do not apply to this system are listed in §14 with reasons, rather than filled with invented content.
v0.9.0 retrieval: search/pack/validate use a disposable ladder (SQLite FTS5 index → ripgrep → scan). Git + Markdown is still the source of truth. See docs/designs/retrieval-ladder.md — this snapshot predates that work and should not be cited for search performance.
PKC is a plugin, not an application. It ships agent procedures (skills), slash commands, an agent definition, hooks, and ~4,200 lines of dependency-free Python that turn project reasoning — meetings, experiments, discoveries, decisions — into an OKF knowledge graph stored as ordinary Markdown with YAML frontmatter.
There is no server and no build step. Git is the database. The unit of output is a Markdown file that a human can read in a PR diff. A disposable SQLite/FTS5 index under knowledge/.pkc/ (gitignored) and an optional ripgrep prefilter accelerate search, pack, and validate; deleting them is always valid recovery. See retrieval-ladder.md.
Two hosts run the same tree: Claude Code (via .claude-plugin/) and Grok Build (via .grok-plugin/, which reads Claude-compatible plugins natively).
From docs/prd.md and docs/vision.md, the system must:
- Capture informal knowledge (meetings, spikes, research, decisions) without a separate app.
- Materialize WikiTicket work items into the same graph, without owning their status.
- Maintain typed edges so impact analysis and progressive disclosure work.
- Stay Git-native and PR-reviewable.
- Degrade gracefully when okf-plugin or WikiTicket is absent.
Explicit non-goals (docs/vision.md): a general notes app, owning work status, reimplementing OKF graph algorithms, realtime collaboration.
Human / agent activity
(meetings, experiments, discovery, coding, planning)
│
┌────┼────────────────┐
▼ ▼ ▼
WikiTicket PKC capture Direct OKF authoring
(bin/worklog) skills (okf-author)
│ │
│ materialize
└───►──────┤
▼
OKF knowledge graph
(Markdown + YAML, in Git)
▼
Impact / query / progressive disclosure
(okf-plugin)
Three plugins coexist. PKC is the capture and materialization layer; okf-plugin owns graph algorithms; WikiTicket owns work status and wiki publishing. Contracts are documented in docs/integration-okf-wikiticket.md.
Four layers, each replaceable without touching the others:
| Layer | Location | Responsibility |
|---|---|---|
| Agent procedures |
skills/*/SKILL.md (20) |
Judgment: extract structure from free text, choose edge types |
| Command surface |
commands/*.md (20) |
Thin wrappers that invoke a skill with $ARGUMENTS
|
| Deterministic core |
scripts/pkc_*.py (18) |
Paths, frontmatter, idempotency, catalogs, validation |
| Storage | A bundle directory of Markdown | The graph itself |
The split is deliberate and load-bearing: anything deterministic belongs in Python; the model is only trusted with judgment. A skill that writes Markdown directly instead of calling a script is a bug.
scripts/pkc_common.py hand-rolls a YAML subset — _parse_simple_yaml() (lines 170–235) and dump_frontmatter() / _dump_key() (lines 303–339) — rather than importing PyYAML.
Why: the plugin must run on bare python3 in any host, including sandboxes with no package installation. Cost: the parser handles the frontmatter subset only — nested maps, lists, inline [a, b] arrays, and scalar coercion. Consequence: a frontmatter construct the parser cannot express must be avoided, or the parser extended. Adding a dependency is not an option.
Every concept write funnels through write_concept() (scripts/pkc_common.py, lines 356–396). It owns four invariants the rest of the system assumes:
- Merge — existing frontmatter is preserved key-by-key, never clobbered.
-
truth_statebarrier — a file markedsnapshot,superseded, orarchivedis skipped by acurrentwrite unless the caller passesforce. -
Three-way return —
created/updated/skipped. Identical content returnsskippedwithout writing. -
stable_timestamp— preserves the original timestamp when the title is unchanged, so re-captures produce no diff churn.
Idempotency is not a feature bolted on top; it is this function's return value. CI asserts it directly (.github/workflows/ci.yml, "Materialize fixture idempotency" step, grep -q "0 created").
item_fingerprint() and fingerprint_matches() (scripts/pkc_materialize.py) hash only the worklog fields that reach rendered output — FINGERPRINT_FIELDS. A matching fingerprint short-circuits before the frontmatter dict and body string are constructed, so an unchanged item never reaches write_concept().
Tradeoff: the fingerprint is stored in the concept's own frontmatter, so the check still reads the file. It avoids rendering and writing, not reading. A sidecar manifest would avoid the read too, at the cost of a second source of truth that can drift from the files.
Sharp edge: a field added to the rendered concept but not to FINGERPRINT_FIELDS will never trigger a re-render.
Observability: short-circuited items report unchanged; write_concept()'s byte-identical result reports skipped. Keeping them distinct is what makes the optimization verifiable from outside the process — an mtime check cannot tell them apart, because write_concept() declined to write in both cases even before fingerprints existed.
resolve_knowledge_root() (scripts/pkc_common.py, lines 158–167) resolves in strict order: explicit --bundle → .pkc/config.yml knowledge_root → the first of knowledge/, sample-knowledge/, .okf/ that contains an index.md.
Consequence worth knowing: creating a knowledge/ directory in this repo would silently retarget every bare --repo . invocation away from sample-knowledge/. CI is insulated because every step passes --bundle explicitly.
| Script | Role |
|---|---|
pkc_common.py |
Frontmatter, catalogs, bundle init, scrubbing, fingerprints |
pkc_capture.py |
Meeting / experiment / discovery / decision / assumption / question |
pkc_materialize.py |
Worklog fold + docs → concepts |
pkc_link.py / pkc_promote.py
|
Typed edges; informal → formal promotion |
pkc_pack.py |
Progressive-disclosure context packs |
pkc_validate.py |
Structure and link validation |
pkc_doctor.py |
One-screen bundle health check |
pkc_action_items.py |
Meeting actions → TicketLink / worklog |
pkc_scrub.py |
Secret and PII redaction |
pkc_transcript.py / pkc_thread.py / pkc_pr_capture.py / pkc_adr_import.py
|
Ingestion |
pkc_search.py / pkc_digest.py / pkc_release_notes.py / pkc_federate.py
|
Query and reporting |
Every script shares one CLI shape: --repo <path> (default .), optional --bundle, and usually --json for machine-readable output. CI asserts against the JSON, not the prose.
- User pastes notes;
/pkc-capture-meetingloadsskills/pkc-capture-meeting/SKILL.md. - The agent extracts title, date, attendees, decisions, action items.
-
pkc_capture.py meetingwritesmeetings/YYYY-MM-DD-<slug>.mdplus aDecisionRecordper decision, linkeddecides/originates_from. - Catalog indexes refresh; one line appends to
log.md. - Optionally, action items become work items via
bin/worklog addandTicketLinkconcepts.
Re-running on the same title and date updates in place — the path is derived from the slug, and write_concept() returns skipped when nothing changed.
-
bin/worklog foldemits the work log as JSON. -
load_fold()(scripts/pkc_materialize.py, lines 37–50) normalizes both a bare array and{"items": [...]}. - Epics and stories become
Featureconcepts; every item with a ULID also gets aTicketLink. - Unchanged items short-circuit on fingerprint (§6.3).
-
truth_statebarriers are respected — a snapshot is never overwritten by a routine run.
pkc_pack.py walks typed edges from a seed concept — 2 hops and ~20 nodes by default, 1 hop and ≤8 nodes with --tiny. The tiny bound exists for chat and mobile contexts and is asserted in CI.
Concept types map to directories via TYPE_TO_DIR (scripts/pkc_common.py, lines 31–48); catalogs are enumerated in CATALOGS. Adding a type means touching, in order: TYPE_TO_DIR → CATALOGS → a template in templates/ → the catalog case list in scripts/pkc-curate.sh → a sample under sample-knowledge/.
Edges are typed and stored twice on purpose: links: [{target, rel}] in frontmatter for machines, and a Markdown link under ## Related for humans reading the file directly. add_typed_link() maintains both and dedupes. Targets are always absolute in-bundle paths. Permitted relations are listed in DEFAULT_RELATIONS; see docs/typed-edges.md.
scrub_text() (scripts/pkc_common.py, lines 123–142) runs over ingestion paths before anything is written. SECRET_PATTERNS covers OpenAI-style keys, GitHub tokens (ghp_, gho_), Slack tokens, AWS access keys, Google API keys, PEM private key blocks, and bearer tokens. PII_PATTERNS covers emails, phone numbers, and US SSNs.
Scrubbing is on by default for transcript, thread, and PR capture. CI asserts a transcript fixture is redacted. New ingestion paths must scrub; this is the one place where "add it later" is not acceptable, because the unscrubbed content lands in Git history.
.pkc/config.yml (schema: .pkc/config.schema.json, example: .pkc/config.example.yml) controls the knowledge root, pack sizes, scrub toggles, materialize include list, capture defaults, the WikiTicket bridge, and federation remotes. It is read by load_config() and is optional — every value has a working default.
tests/test_pkc.py — 27 tests, stdlib unittest, no pytest, no fixtures framework. Run the whole suite with python3 tests/test_pkc.py, or one class with python3 tests/test_pkc.py TestIncrementalMaterialize.
CI (.github/workflows/ci.yml) is the real specification. Beyond the unit tests it compiles every script, validates and doctors sample-knowledge, asserts the golden pack shape (≥5 nodes; tiny ≤8 nodes at 1 hop; mermaid emits flowchart), exercises search/digest/release-notes/thread/ADR fixtures, and asserts materialize idempotency. A second workflow, worklog-invariants, enforces the work-log rules described in docs/worklog-spec.md.
The okf-interop job is continue-on-error by design: PKC must not break when okf-plugin is unavailable.
| Risk | Impact | Current position |
|---|---|---|
| Hand-rolled YAML parser | A frontmatter construct outside the subset fails silently | Accepted; the subset is sufficient and the zero-dep property is worth more |
| Fingerprint field list is manual | A new rendered field that is not in FINGERPRINT_FIELDS never re-renders |
Documented in CLAUDE.md; a test asserting the two lists agree is the obvious next guard |
resolve_knowledge_root() order is implicit |
Creating knowledge/ retargets bare commands |
Documented; CI always passes --bundle
|
| Version string in six manifest sites | A release can ship inconsistent metadata | Release checklist in docs/worklog-spec.md; a lockstep test is not yet written |
Database design, Cache design, API design, Event-driven processing, Deployment architecture, Observability — this system has no database, cache, network API, message bus, or deployed runtime. It is a set of files and CLI scripts invoked by an agent host.
MCP server integration, AI endpoint design, Managed AI platform integration — not built. MCP server mode is an open epic (GitHub #5); until it ships, documenting it here would be fiction.
Performance and scalability — the only performance-relevant decision is fingerprint-gated materialization, covered in §6.3. Bundle sizes are in the hundreds of files; no scaling work is warranted yet.
Tracked as work items rather than prose — see docs/roadmap.md and the GitHub issues it links. The live v0.5.0 milestone covers agent auto-context injection, Risk and Acceptance concept types, and MCP server mode.
- Index-Releases
- Latest snapshot: Roadmap-2026-08-30_v0.9.0-release
- Index-Status
- Index-Decisions