spillwavesolutions/project-knowledge-capture
Project Knowledge Capture: meetings, experiments, decisions, TicketLink/WikiTicket work, and project-memory nouns into a durable OKF knowledge graph. Multi-host bindings and write isolation for shared second brains.
Changelog
Unreleased
0.9.6 — 2026-09-19
Symlinked-root patch. Closes #85.
Fixed
-
Search works on a symlinked bundle root (#85).
rgprints resolved paths, andsearch()built each hit's display path against the bundle the caller passed, so a bundle that still held a symlink did not match andrelative_toraised. Search failed outright. The bundle is the root cause, not the line that raised: every engine derives its paths from the bundle it is handed, sosearch()now resolves it once. That is one syscall per call, not one per file, which is the property_filter_rg_hitsalready protects.candidate_filesstill takes the bundle as given, because callers expect paths under the bundle they passed.Not macOS-only.
/varis a symlink to/private/varthere, so every temporary bundle reproduces it. A symlinked checkout or a container bind mount is the same shape on Linux. CI has neither, which is why it shipped.
Changed
tools/ci-local.shparses every.github/workflows/*.ymlbefore it runs anything else, and fails when the glob finds none. #82 was an unparseableci.ymlthat failed at startup with zero jobs, sotestandokf-interopdid not run for a week while pull requests still looked green. A workflow cannot check itself.CHANGELOG.mdno longer carries a second## Unreleasedheading left over from the 0.7.2 era. Its bullets moved into the 0.7.3 section they describe.
0.9.5 — 2026-09-19
Retrieval-ladder parity patch from the v0.9.4 review. Closes #78, #79, #80, #82 via #81.
Fixed
ci.ymlparses again (#82). The v0.9.4--summaryassertions embedded multi-linepython3 -cblocks at column 1 inside arun: |block; the workflow failed at startup with zero jobs on every run since 2026-09-12, sotestandokf-interophad not executed in CI for a week. Collapsed to single-linepython3 -c; assertions unchanged.- Search parity across engines (#78). The filename stem no longer enters the
search haystack (
pkc_search.py;pkc_index.pyhay column). A titleless concept whose filename matched the query was found byscanand the index but invisible to thergprefilter, breaking the "identical ranking across rungs" contract. Filenames are not content. IndexSCHEMA_VERSION→ 2 so existing indexes rebuild themselves. Display titles still fall back to the stem. find_rg()fails closed on an unusable override (#79).PKC_RG_PATH/OKF_RG_PATH/SECOND_BRAIN_RG_PATHpointing at a missing or non-executable path now disables rg instead of silently falling through toPATH— the semantics research-graph already uses, so one env var means one thing across plugins._inbound_via_rgresolves the bundle once, not per hit, and applies the concept rules on strings (is_concept_rel);rg_list_filesalready returns resolved paths.
Changed
- Auto-context hook injects the summary card (#80).
pkc_auto_context.pyemitspkc_pack --summary(with a 2–3 sentence seed excerpt) instead of the ranked-nodes pack markdown, so the parent never receives pack bodies — consistent with the retrieval-isolation rule (/pkc-retrieve). agents/knowledge-retriever.mdstep 6 no longer describes--summaryas pending.
0.9.4 — 2026-09-12
Retrieval isolation: a Claude-style sub-agent walks and scores the graph so
hit lists and full pack markdown stay out of the parent working context.
pkc_search.py / pkc_pack.py remain the deterministic engine.
Added
- knowledge-retriever agent (
agents/knowledge-retriever.md). Retrieval-only child: search, score fit, pack (--tinythen optional 2-hop deepen), return a Summary Card. Does not capture, write nodes, or open brain sessions. /pkc-retrieveskill. Parent-facing: spawnknowledge-retriever; consume only the card. Re-spawn to deepen or try an alternate seed. Orthogonal architecture questions still fan out to SACarchitecture-retriever.pkc_pack.py --summary. Compact card-friendly markdown (seed, hops, nodes, tokens/budget, reverse-index engine, 5–8 lead nodes, edge count). Mutually exclusive with--mermaidfor stdout shape.--jsonkeeps the structured result and addssummary_markdown,lead_nodes,edge_count.--summary-seed-excerptoptionally adds a 2–3 sentence seed excerpt (default off). Fail-closed token budget still applies.
0.9.3 — 2026-08-31
Correctness patch for catalog rendering and the rg-backed reverse index. Both defects were found by porting the system-architecture-capture v0.5.4 fixes to the shared PKC code.
Fixed
- Catalog rendering no longer crashes on a YAML scalar title.
title: 421parses as an int, and_escape_link_label()assumed a string, so one legacy title aborted the refresh after capture had already written concepts. Bothrefresh_catalog_index()andensure_catalog_index()now share one label helper, which also stops a falsy-but-real title (0,false) from falling back to the file stem. (#72) - The rg-backed inbound pack no longer drops matches when the bundle is
addressed through a symlink.
rg_list_files()resolves its hits, sopath.relative_to(bundle)raised and the handler silently discarded a real inbound edge while the pack still reportedreverse_index: rg. (#73)
0.9.2 — 2026-08-31
Dry-run safety patch for WikiTicket materialization.
Fixed
pkc_materialize.py --dry-runnow makes no changes to the target bundle: concepts, catalog indexes,log.md, write events, and directories remain byte-for-byte unchanged. JSON and text reports distinguish planned creates and updates from applied actions. (#69)
0.9.1 — 2026-08-30
Patch for the v0.9.0 retrieval ladder. Git + Markdown stays source of truth.
Fixed
- Cold index build no longer
DELETE FROM ftsper new file. FTS5 cannot look up by a plain column, so that delete scanned the growing table once per file (O(N²); ~43 s at 10k concepts). New rows skip both the FTS and edges deletes; incremental updates still delete so REPLACE cannot leave a duplicate FTS row. (#66) - Index-backed search no longer materializes
iter_conceptsorPath.resolve()s the universe against every hit. Prefix filter is a stringstartswith. rg hits convert to bundle-relative strings and apply the concept skip rules on path parts. Broad terms match scan cost; selective terms drop toward refresh cost. test_setup_install_without_yes_exits_2no longer assumes the machine has norgon PATH. EmptiesPATHand restoresPKC_RG_PATHviaaddCleanup._upsert_nodestores the pre-scan mtime+size instead of re-statting after the read (TOCTOU: a write between read and stat stored a fresh signature over stale content)._inbound_via_rgno longer builds a second needle and then slices it away.
0.9.0 — 2026-08-30
Retrieval ladder: Git + Markdown stays source of truth. Accelerators are disposable.
Added
- ripgrep accelerator for search and pack (#58).
pkc_search.pyusesrg -las a candidate prefilter whenrgis on PATH (orPKC_RG_PATH/OKF_RG_PATH). Ranking stays in Python, so scores match a full scan.--no-rgforces the linear walk. The--rgflag the docstring already promised now exists. - rg-backed reverse index in
pkc_pack.py. Inbound/backlink discovery is O(subgraph) when rg is present; otherwise the previous full scan. Parsed edges are cached for the duration of onepack()call. /pkc-setupskill +scripts/pkc_setup.py(#59). Detects python / rg / SQLite FTS5. May install ripgrep only with--yes. Never from a hook.pkc_doctor.pyreports a toolchain section (rg found/missing, FTS5, index).- SQLite/FTS5 incremental index (#62).
scripts/pkc_index.pywritesknowledge/.pkc/index.sqlite(gitignored). Search, pack inbound, and validate self-heal via mtime+size on every call. LIKE on a stored haystack is the default candidate prefilter so Python scores stay identical to a scan.--engine ftsis the FTS5 MATCH opt-in.--no-index/PKC_NO_INDEX=1fall through to rg then scan./pkc-indexskill for status/refresh/drop.
Notes
- Ruby rewrite: closed-won't-do (#60). Dual implementations drift; the bottleneck is the full-bundle rescan, not the language. Escalation is rg → SQLite FTS5 index → okfcli, not a second runtime.
- Retrieval ladder locked in
docs/designs/retrieval-ladder.md. Git + Markdown stays source of truth;**/.pkc/index.sqlite*is gitignored.
0.8.1 — 2026-08-24
- Noun-ownership migration guide:
docs/user_guide/noun-ownership-migration.md(TicketLink CLI move, received work types, catalogs this pack owns).
0.8.0 — 2026-08-24
Added
- Noun ownership. PKC now owns project-memory nouns including work types
previously parked in okf-plugin:
TicketLink,DecisionRecord,Feature,Epic,Story,Task,Subtask,Bug,Branch,Project, plusPlaybook,Runbook,Reference. scripts/pkc_ticket_link.py— WikiTicket / GitHub Issues → TicketLink (moved from okf-plugin).- JSON schemas for the new nouns under
schemas/okf-concepts/. - README noun table lists every type this plugin supports.
Changed
- Catalog and ContextPack stay in okf-plugin. PKC wraps packs with a token budget; it does not own those types.
0.7.4
- Three-host hooks: Codex + Cursor-native when Claude hooks exist.
0.7.3 — 2026-08-17
- Cursor host.
.cursor-plugin/plugin.json(Cursor Plugins) plus.cursor/rules/second-brain.mdc. Docs:docs/CURSOR.md.docs/GROK_BOT.mdnow covers Grok Bot spawning Cursor cloud agents. - Host manifests (
.claude-plugin,.codex-plugin, Grok marketplace) now match rootplugin.json0.7.2. Claude Code was still labeled 0.7.1.
0.7.2 — 2026-08-16
- ContextPack token budget matches second-brain-core 0.3.3: default 1/4 of
SECOND_BRAIN_WINDOW_TOKENS(128000 → 32000). Override with--max-tokensorSECOND_BRAIN_PACK_MAX_TOKENS. - Pack is fail-closed when the rendered subgraph exceeds the budget.
--writeis skipped. Node clip (--max-nodes/--tiny) is not a token budget. - Bodies off unless that node is the pack root. Neighbors keep title, type,
path, and frontmatter
descriptiononly. - Auto-inject (
pkc_auto_context) uses the same gate; over-budget stays silent.
0.7.1 — unreleased
Added
- Required identity on every knowledge write.
--authororSECOND_BRAIN_IDENTITYis fail-closed. Successful created/updated writes stampauthoron the concept and emit aWriteEventunderwrite-events/. Wired through capture, materialize, pr_capture, adr_import, promote, action_items, thread, and transcript. Matches the second-brain-coreresolve_author/emit_write_eventpattern.
0.7.0 — unreleased
Added
-
First-class work items.
Epic,Story,Task,Subtask,Bug, andBranchmap to their own catalogs. Materialize still writesTicketLinkfor compatibility and now also writes the specialized type. A work item withbranch:also upserts aBranchnode. -
Per-type recommended fields come from the shared schema pack (
x-recommended). Existing bundles still validate with zero errors. -
Shared OKF concept schemas.
schemas/okf-concepts/pluspkc_validate.pynow loads the okf-plugin BaseConcept pack (required:type+titleonly). Soft by default.truth_stateaccepts the DEKC valueshistoricalandproposedin addition tocurrent|snapshot|superseded|archived. -
TicketLink
kind=bugrefinement. Warns (does not error) when a bug ticket has no structural link to a Module/Package/Release/CodeChange and nobranch. -
Project type maps to
projects/(not a catalog — no index rewrite). -
Auto-context injection. A
UserPromptSubmithook (scripts/pkc_auto_context.py) injects a Feature's tiny pack when the prompt names one. Detection is afeatures/path that exists and istype: Feature, or a 26-char ULID matching a Feature'sworklog_id; a path wins over a ULID, since it is what the human actually typed. Gated bypkc.pack.auto_inject_on_feature(default true) andpkc.enabled. -
Multi-host bindings + write isolation. Root Agent Plugins 1.0
plugin.json,.codex-plugin,docs/GROK_BOT.md,docs/LANG_CHAIN_DEEP_AGENTS.md,docs/ISOLATION.md,docs/ONBOARDING.md, host wrappers, andscripts/brain_session.py/pkc-session. Concurrent writers readmainand writebrain/<actor>/<session-id>. Public tests use fictional lumenfield-detector / northstar-console only.UserPromptSubmitis the only hook whose output reaches the model before the turn runs, which is why detection lives there — aPostToolUsehook fires after Claude has already decided what to read.
Fixed
-
pkc_packcould not see a concept that pointed at the seed.extract_edgesread only the current node's outbound links, so a Risk, an Acceptance criterion, or anything authored by hand or by a sibling plugin against the same bundle stayed invisible from the Feature it named. The capture helpers papered over this by writing an inverse edge back onto the target, which covers only the concepts those helpers create.pack()now builds a reverse index once per call and walks both directions. Each edge keeps the direction it was authored in, so both renderers draw the true arrow unchanged.The index is built from
iter_concepts(), which already skipsindex.md,log.mdandpacks/. That exclusion is load-bearing, not tidiness: every concept is listed by its catalog index, so following those inbound edges would pull whole directories into every pack and crowd out the knowledge the seed actually relates to. Onsample-knowledgethe 2-hop pack goes from 5 nodes to 14 — with the catalogs left in it was 18 nodes, 4 of them generated listings. -
A materialize run that rendered nothing still appended to
log.md.append_logran unconditionally, so the incremental path short-circuited every concept on its fingerprint and then wrote a log line saying so. A git diff for a run that did no work, which is the exact churn the fingerprint exists to prevent. The log line is now skipped when every row isunchanged. Arefusedwrite still logs — a blocked write is worth a record.catalogs_touched()had the same flaw and now filters tocreatedandupdated, but it was never a diff:refresh_catalog_indexpreserves an existingtimestamp, so it rewrote every catalog index with identical bytes. That cost is wasted I/O proportional to the number of catalogs on a run that touched nothing, not a dirty tree.CI asserted
0 createdand then that nothing was rendered. Neither could see this, because both read the JSON report rather than the bundle. The new step commits the bundle, re-runs, and fails on a non-emptygit status --porcelain— which names the offending file. -
The CI compile list had drifted five scripts behind.
.github/workflows/ci.ymlhand-listed the scripts topy_compileand never gaineddigest,release_notes,thread,federate, oradr_import— all shipped in 0.4.0, none compiled by CI since. Both CI andnpm run typechecknow globscripts/pkc_*.py, astools/ci-local.shalways did.
Notes
- Silence is the hook's contract. A
UserPromptSubmithook's stdout becomes model context on every turn, so every failure path — no match, no bundle, no config, malformed stdin — exits 0 printing nothing. Both halves have tests and a CI step; the silent half is the one a regression breaks invisibly. - The prompt field is named
promptin the hooks reference anduser_promptin the plugin-dev skill. The script reads both rather than betting on one. - No mermaid in the injected pack: a diagram costs tokens the model cannot act on any better than the edge list printed beside it.
0.6.0 — 2026-08-10
Seven fixes, found by running this plugin alongside system-architecture-capture
and data-engineering-knowledge-capture against a single shared bundle.
Fixed
-
Frontmatter round-trip doubled backslash escaping.
_fmt_scalarescaped backslashes and quotes;_scalarstripped only the surrounding quotes. Every write-modify-write cycle re-escaped already-escaped text, so a script editing one field corrupted every quoted string in the file. Self-concealing: reading back with the same parser returned a value that looked correct, so the damage lived only in the bytes on disk. (#32) -
A bracketed concept title dropped the catalog edge, at both renderers.
[AREA] Thingrendered as[[AREA] Thing](/cat/x.md), which the graph reader's link regex cannot match — a missing edge rather than a broken one, whichvalidatedoes not report. Bothensure_catalog_indexandrefresh_catalog_indexinterpolated the title raw; they now share one escape helper. This half needs the matching reader change to take effect: escaping does not rescue a reader whose label class is[^\]]+. (#31) -
refresh_catalog_indexaccepted any catalog name, so a caller could drive this renderer over a sibling plugin's catalog. It now refuses catalogs this plugin does not declare. This alone does not stabilise a shared bundle — for a catalog two plugins both declare it passes in both. (#34) -
resolve_knowledge_rootfell through tosample-knowledge/in silence. The order is documented; the gap was that onlypkc_materializeannounced which bundle it used. It now names the intended and actual root on stderr. This repo ships asample-knowledge/, so a capture run inside a clone wrote there. The configured root still wins whenever it is usable. (#33) -
append_loglost concurrent updates. Whole-file read-modify-write with no synchronisation, andpkc-curate.shfires a catalog refresh from aPostToolUsehook on every edit. Takes an advisoryflockon the target file itself, so no sidecar.lockis left in the bundle.O_APPENDis not usable: entries are inserted under today's heading mid-file. (#37)
Added
write_concept(..., create_only=True).mergeprotects frontmatter, never the body — correct for re-capture, and the reason a scaffolding pass re-run after enrichment flattens concepts back to stubs. Default behaviour unchanged and now pinned by a test. (#35)
Changed
write_conceptnow returns"refused"for atruth_statebarrier, distinct from"skipped". Previously a rejected write was indistinguishable from a byte-identical no-op, so a caller reported success having written nothing. Breaking: the documented return contract, the test that pinned it, andpkc_materialize's counts all move with it. CI's0 createdassertion is unaffected — identical content still returns"skipped". This mirrors the existingunchanged/skippedsplit, whichCLAUDE.mdalready calls load-bearing. (#36)
0.5.0 — 2026-08-06
Two new concept types. Minor bump: the plugin tree gains types, templates, and capture subcommands.
Added
Riskconcept type (risks/) — what could go wrong, with aseverityof low/medium/high/critical. New relationsexposes(Risk → Feature) andmitigates(Decision → Risk).Acceptanceconcept type (acceptance/) — one atomic, checkable condition for calling a Feature done, linked bysatisfiesto the Feature andverified_byto whatever proves it.pkc_capture.py riskandpkc_capture.py acceptance, plustemplates/risk.mdandtemplates/acceptance.md.- Both types on the sample auth chain, so they are covered by validate, doctor, search, and the golden pack like every other type.
concept_ref()inpkc_common.py— normalizes a concept reference that may be an absolute path, a relative path, or a bare title.- Incremental materialize is now verifiable. Fingerprint short-circuits report a distinct
unchangedaction, separate fromwrite_concept()'sskipped. CI asserts every action isunchangedon a second run. tools/ci-local.shandnpm run ci— 19 steps mirroring both CI workflows.
Fixed
- Relative concept paths were mangled. Ten call sites did
t if t.startswith("/") else f"/{dir}/{slugify(t)}.md";slugifystrips/and., so--decides features/user-auth.mdproduced/features/featuresuser-authmd.md. All sites now useconcept_ref(). Affects--decides,--informs,--links-to,--for,--originates-from,--blocks, andpkc_pr_capture's--implements. - Capture warns on stderr when an inverse-edge target does not exist, instead of dropping the edge silently.
Notes
- Edge direction is not symmetric.
Decision --mitigates--> Risk, never the reverse.--mitigated-bytherefore writes its edge on the decision. pack()walks outbound edges only, so a concept pointing at a Feature does not appear in that Feature's pack unless the Feature points back.capture_acceptancewrites the inverseFeature --verified_by--> Acceptancefor this reason. The general fix — a reverse index inpack()— is tracked, not shipped.- The golden auth pack grows from 9 to 12 nodes. The tiny pack now sits exactly on its 8-node ceiling.
- Stories still open under the v0.5.0 milestone (agent auto-context injection, MCP server mode) were re-milestoned to v0.6.0 rather than left claiming a shipped version.
- GitHub Actions resumed producing runs during this release, after the blackout recorded in 0.4.2. This release is verified by both CI and
npm run ci.
0.4.2 — 2026-08-06
Documentation release. No plugin code changed.
Added
- Six wiki pages, each with a source of truth in
docs/:User-Guide— install, the 20 commands grouped by intent, context packs, concept types, typed edges, privacy, troubleshootingCLI-Reference— every script's flags, extracted from each script's own--helpPlugin-Guide— packaging and extension: the skill/command/script split,${CLAUDE_PLUGIN_ROOT}, hook rules, adding a capability or concept typeDesign-Doc— architecture and the four decisions that shape itCode-Walkthrough— a read-the-code tour with line citations computed against the treeWorklog-Spec— how this repo runs WikiTicket SDD
tools/wiki-publish.py— the wiki publisher. The wiki-publish skill describes the ledger rules but ships no implementation. Placed intools/rather thanscripts/deliberately:scripts/ships inside the plugin, and a wiki publisher is repo tooling.
Fixed
docs/worklog-spec.mduseddoc_type: spec, which is not in the IA schema (valid: plan, roadmap, roadmap-snapshot, status, design, adr, guide). Re-keyed asguide/worklog-spec.
Known gap
-
GitHub Actions produced no workflow runs for this release, or for v0.4.1. The last run was 2026-08-05 18:59 UTC; PRs #21, #22, and #23 each have zero check-runs despite Actions being enabled and both workflows
active. The cause appears to be org-level and is not visible from this repository.Both releases were verified by running the CI suite locally instead — unit tests,
py_compileover every script, validate and doctor onsample-knowledge, the golden pack assertions, materialize idempotency, andcommit-msgover every commit in each PR. All passed. This is self-reported rather than independently verified, and is recorded here rather than left in a terminal.Related:
merge-when-green.shtreats no checks as all checks green. PR #22 merged that way. Worth a guard.
0.4.1 — 2026-08-06
Added
- Incremental materialize — materialized concepts carry
source_fingerprint, a hash of only the worklog fields that reach the rendered output. A matching fingerprint short-circuits before the frontmatter and body are built, so unchanged items never reachwrite_concept(). Re-materialize is now O(changed work) instead of O(all work). adapters/github/adapter— the GitHub ticket adapter, soworklog synccan push work items to GitHub Issues. worklog's owninit.shscaffoldsbin/andhooks/but notadapters/.docs/vision.md— themes, brainstorm, non-goals, and how to contribute an idea.
Changed
- This repo now runs WikiTicket SDD on itself.
bin/, git hooks viacore.hooksPath, a.work/event log, and CI invariants.pkc_materialize.pyis exercised against a real fold instead of only a four-item fixture. docs/roadmap.mdis generated from.work/bybin/worklog roadmap-renderand is read-only for humans. Its narrative half moved todocs/vision.md; the shipped table was dropped as a duplicate of this changelog.CLAUDE.mdrewritten for accuracy: the zero-dependency mini-YAML parser, thewrite_concept()merge andtruth_statecontract,--repo/--bundleresolution order, and thesource_fingerprintcontract.AGENTS.mdcarries the work-tracking policy as a real file, not a symlink — the Claude and Grok hosts get deliberately different content.
Notes
- Upgrade path is one migration pass: a bundle written before this release has no fingerprint, so the first materialize updates each concept once and every run after that skips.
- Contributors: commits no longer land on
main, and every commit message must reference a 26-char ULID or#123. Both enforced by git hooks and by CI on pull requests.
0.4.0 — 2026-08-04
Added
pkc_search.py//pkc-search— full-text AND search with ranked hitspkc_digest.py//pkc-digest— weekly/daily brief + needs-verification queuepkc_release_notes.py//pkc-release-notes— notes from Release/Feature/CodeChange edgespkc_thread.py//pkc-capture-thread— Slack/Discord paste → Meeting/Discoverypkc_federate.py//pkc-federate— multi-repo read-only roots + cross-searchpkc_adr_import.py//pkc-import-adr— MADR/adr-tools → DecisionRecords- Preview: Search + Digest views
- Federation config schema + example
- Sample digest + release-notes packs
- 24 unit tests
0.3.0 — 2026-08-04
Added
- Doctor, Assumption/Question, scrub, transcript, PR capture, tiny packs, mermaid, config schema
0.2.0 — 2026-08-04
Added
- Context packs, validate, action-item bridge, curate hook, CI, golden pack, roadmap
0.1.0 — 2026-08-03
Added
- Dual-host plugin, core capture/materialize skills, sample chain, templates