JSON-L export/import — Implementation Plan
See jsonl-export-design.md for
the full design and confirmed decisions. TDD throughout, per project
convention: _test.go first, confirm red, then implement.
Commit after each work item.
W1 — Record types +
ExportJSONL
File: jsonl.go,
jsonl_test.go (package knowledge)
- Seven record structs (
projectRecord,conceptRecord,sourceRecord,observationRecord,observationConceptRecord,projectConceptRecord,observationSourceRecord), each with aType string \json:“type”`` field set to a constant on construction, plus the fields listed in the design doc (uuid-keyed references, no local ids). ExportJSONL(kb *KnowledgeBase, w io.Writer, projectName string) error: whole-db whenprojectName == ""; scoped per the design doc’s reachability rule otherwise. Onejson.Marshal+ newline per record, dependency order.- Tests: whole-db export against a small fixture produces valid, ordered JSONL with expected line counts; project-scoped export includes only reachable rows (including a concept reachable only via an observation, and excluding an unrelated project’s data); empty database exports zero lines without error; unknown project name returns an error.
W2 — ImportJSONL
File: jsonl.go (continued),
jsonl_test.go (continued)
type ImportTableSummary struct { Table string; Read, Imported, Skipped int }ImportJSONL(kb *KnowledgeBase, r io.Reader) ([]ImportTableSummary, error): buffer all lines bytype(unknowntypevalues are skipped and counted, not fatal — forward-compat with a future record kind), then apply in phase order (projects/concepts/sources → observations → the three join types), buildinguuid -> local idmaps as each phase completes.- Tests: round-trip (
Exportone kb,Importinto a fresh empty kb, contents match — projects, observations, concept links, source links); re-import of the same file is a no-op (Skippedcounts equalRead,Importedall zero, row counts unchanged); importing into a kb that already has a same-named project merges observations under the local project row (verifies the uuid-mismatch case the design doc calls out); a join record whoseuuidreference is missing from the file is skipped, not fatal; malformed JSON on one line errors out with the line number.
W3 — CLI verbs
File: cmd/kb/jsonl.go,
cmd/kb/jsonl_test.go
verbs["export"],verbs["import"], both operating on the ambientkb(nomerge-style special case inmain.go).export:-project NAME(optional),-out PATH(optional, default stdout).--jsonmode wraps the line count in a{"lines_written": N}envelope instead of interleaving JSONL with a JSON summary object.import:-in PATH(optional, default stdin).--jsonmode prints[]ImportTableSummaryas JSON; text mode prints the same table-format summary style asmerge’s output.logKBCall/dl.Logwiring matching every other verb.- Add both to
jsonaudit_test.go’s case table (text +--json, using the existing fixture;importcase reads from a small embedded JSONL fixture via-in, not stdin, to keep the test hermetic).
W4 — Docs, decisions, version
helptext.go:ExportHelpText,ImportHelpTextconsts (man-page style, matchingMergeHelpText);HelpText’s VERBS section gains an entry.help_dispatch.go: route"export"/"import"topics.Makefile: addexport importtoKB_TOPICS(hand-edited, not viacmt codemeta.json Makefile— that target is hand-customized andcmtwould blow awayKB_TOPICS/kb-topics-help, see the 2026-07-27 Makefile-regen gotcha in DECISIONS.md).- Run
make kb-topics-help(or the equivalent manual./bin/kb export -help > kb-export.1.md/ same forimport) to generate the checked-in.1.mdsources, thenmake manifpandocis available locally. user_manual.md: a section describingexport/import, mirroring the existingmergesection.codemeta.json: bumpversion(0.0.2 → 0.0.3) andreleaseNotes; runcmt codemeta.json version.go CITATION.cff about.md README.md.DECISIONS.md: entry after everything is verified, following the existing format (context/decision/rejected/consequences), including any real bugs found along the way.agents/knowledge.db(Laboratory root): onedecisionobservation on theknowledgeproject summarizing the shipped feature, linking thecross-machine-syncconcept — closes out the step-4 deferred item.
Non-goals for this pass
- A
-formatflag or any second export format. - Streaming import (buffer-then-phase only, per the design doc).
- Compression/encryption of the JSONL file — it’s plain text, same
trust model as the
.dbfile itself. - Wiring
export/importinto harvey’s/kbcommands — this plan covers theknowledgemodule and its ownkbCLI only.