Everything built so far targets OKF v0.1. Upstream published v0.2 on 2026-07-25, so the plan records how the library relates to it and, more importantly, who owns each decision. Read from the spec itself rather than secondhand, which corrected two readings that a summary had gotten wrong: - STRICT_V1's `timestamp` is NOT a defect. Section 13.1 grants consumers a documented fallback to legacy `timestamp` precisely when `generated` is absent, and STRICT_V1 emits no `generated`. Nothing is asked of the wiki. - The one measured shape problem is DEFAULT's `generated: "true"`, because v0.2 requires `generated.by` within `generated`. That key was not reserved in v0.1, so it was legal when written; v0.2 claimed the name. DEFAULT states commons' ingest-spec section 5 layer, so the fix is commons' call and is raised there as open question V1 rather than patched locally. Two findings shrink the work. The canonical form for `generated` and a single `verified` is an inline flow mapping, which the existing scalar parser already round-trips as an opaque string, so block-list support is only needed for `sources` and multi-verifier `verified` -- and only if a named consumer asks. And the collision degrades safely: `_is_ingest_owned` returns False for a v0.2 mapping, so a foreign concept is refused rather than overwritten. The track sits between Phases 3 and 4 because Phase 4 freezes the cross-runtime contract. Freezing a v0.1 shape into two runtimes would let the shared fixture suite certify the drift instead of catching it. Self-imposed rule, since the spec does not require it: conform first, claim after. Declaring `okf_version: "0.2"` is a MAY with no conformance checkpoint, so claiming it early would be permitted -- and would be the same class of true-sounding misleading claim as reporting a 0.2.0 measurement under a 0.3.1 heading. Also moves the guard 0.3.1 measurement procedure out of session state and into execution-order.md, where it belongs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut
151 lines
7.5 KiB
Markdown
151 lines
7.5 KiB
Markdown
# llm-ingestion-okf
|
|
|
|
## Context
|
|
|
|
Shared OKF (Open Knowledge Format) ingestion library. Three entry doors,
|
|
one boundary rule:
|
|
|
|
- **Door A — spec-based ingestion:** implements the normative
|
|
`ingest-spec.md` owned by `portfolio-optimiser-commons` (manifest →
|
|
`file`/`sql`/`http` connector → deterministic materialization of
|
|
`ingest-{id}.md` → index generation; zero model calls). This repo
|
|
IMPLEMENTS the spec; commons keeps authorship. Spec changes the library
|
|
needs go via commons, never edited locally. The library ships the §11
|
|
golden fixtures (byte-exact) for the three door-A source types
|
|
(`ingest-golden-{file,sql,http}/`, shipped in `9dd86b1`).
|
|
- **Door B — bundle inbox:** converts dropped files to OKF concepts. All
|
|
file-type→text extraction lives HERE (the guard is text-only). v1 core:
|
|
`md`, `txt`, `csv`, `json`, `html` (stdlib). `pdf`/`docx`/`xlsx` only via
|
|
the optional `[extract]` extra; without it those types are rejected
|
|
fail-fast.
|
|
- **Door C — external bundle import:** third-party OKF bundles are assessed
|
|
per concept via the guard's `okf.import_bundle`; only concepts clearing the
|
|
guard's non-blocking floor are merged/indexed here. Two invariants, both
|
|
load-bearing: a merged concept is written **verbatim** (this library's
|
|
line-oriented frontmatter parser cannot round-trip the block lists the
|
|
guard's parser accepts, so stamping an external concept would destroy sender
|
|
data and persist bytes the guard never screened), and ownership is therefore
|
|
proven by **content identity** — an occupied target name is re-used only
|
|
when the bytes there are already identical, never overwritten otherwise.
|
|
|
|
**Boundary rule (non-negotiable, zero overlap):** `llm-ingestion-guard`
|
|
(pinned `>=0.2,<0.3`) answers "is this content safe to persist?" —
|
|
scan/sanitize/quarantine/fail-secure/provenance-stamp. This library is
|
|
plumbing: connect source → materialize deterministic OKF bundle → generate
|
|
index. Never reimplement security; call the guard at persist gates
|
|
(`prepare_input`/`screen_output`, `okf.import_bundle`). When in doubt which
|
|
side of the boundary something belongs on: ask the operator.
|
|
|
|
**Implementation baseline:** the stricter behaviors from
|
|
`portfolio-optimiser` (streaming row caps, utf-8-sig, in-memory staging with
|
|
pre-mutation collision gate, validated `ingested_at`, typed `IngestError`)
|
|
are the library baseline. First consumer: `portfolio-optimiser-claude`.
|
|
|
|
### Roadmap (committed phases — all known OKF surfaces get covered)
|
|
|
|
1. **Phase 1 — Door A (Python).** ingest-spec implementation + the §11
|
|
golden fixtures. Consumers: `portfolio-optimiser-claude` first, then
|
|
`portfolio-optimiser`.
|
|
2. **Phase 2 — Doors B/C (Python).** Bundle inbox and external-bundle
|
|
import, guard-gated.
|
|
3. **Phase 3 — Configurable bundle contract.** Types, layers, frontmatter
|
|
sets, index shape, and reserved-file policy become config instead of
|
|
constants; proving consumer is `claude-code-llm-wiki` (`strict-v1`
|
|
profile). Two consumers hold opposite postures on whether an index is
|
|
authored or directory-derived, so neither is a library invariant and
|
|
nothing here enumerates a directory unless the profile says derived.
|
|
4. **Phase 4 — Node half (`node/`).** Zero-dependency Node/ESM package
|
|
(importable *and* CLI-invokable, vendorable per plugin — matching the
|
|
marketplace precedent) for the second-brain world: bundle check, index
|
|
generation, inbox split/frontmatter/write, and doc conversion
|
|
(docx/pdf/eml/html → md). Covers okr, linkedin-studio, ms-ai-architect,
|
|
and the marketplace catalog.
|
|
|
|
The two halves share the OKF contract and fixture suite, **not code**.
|
|
|
|
**Upstream version: everything above targets OKF v0.1.** Google published v0.2
|
|
on 2026-07-25 (two supersessions, both with documented consumer fallbacks; all
|
|
new fields optional). Alignment is a policy track between Phases 3 and 4, not a
|
|
phase: `docs/plan/okf-v0.2-alignment.md`. Two constraints bind here — the
|
|
`generated` marker `DEFAULT` emits sits in commons' ingest-spec §5 layer, so its
|
|
shape is commons' call, not ours; and `okf_version`'s value belongs to catalog
|
|
(decision E1). No profile may hard-code an upstream version, and no bundle
|
|
claims a version its shape has not earned.
|
|
|
|
Phase 4 preconditions (coordination, not unilateral moves):
|
|
- Lifts okr's reference implementations (`okf-check.mjs`, `okf-index.mjs`,
|
|
innboks libs) in agreement with okr and the marketplace catalog; the
|
|
catalog remains the convention owner and re-pins its shared gate here.
|
|
- linkedin-studio's `ingest/published/` provenance-record grammar stays
|
|
plugin-local by design (different lifecycle) — do not normalize it.
|
|
- The Node-side persist gate remains security territory: guard-as-contract
|
|
(per okr's adoption doc) until a Node guard exists in the security repo.
|
|
No security reimplementation here, in either runtime.
|
|
|
|
### Non-goals (all phases)
|
|
|
|
- Verdict/feedback machinery (method-spec) — stays in consumer repos.
|
|
- Embedding/RAG/retrieval layers.
|
|
- Security functionality — always the guard's domain.
|
|
|
|
## Stack
|
|
|
|
Python 3.10+. Package `llm_ingestion_okf` (src layout, hatchling).
|
|
**Exactly one runtime dependency, ever:** `llm-ingestion-guard>=0.2,<0.3`
|
|
(itself zero-dep), landed with the Door B/C persist gates. Everything else is
|
|
stdlib, and a packaging test enforces it. Only `guard_adapter.py` imports the
|
|
guard; importing the package does not. Install channel until the package
|
|
index exists (a direct reference is a channel, not the pin):
|
|
`pip install "llm-ingestion-guard @ git+https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git@v0.2.0"`.
|
|
Binary extraction parsers live behind the `[extract]` extra only.
|
|
|
|
Phase 4 adds a `node/` half: Node/ESM with zero npm dependencies
|
|
(`node:` builtins only), both importable and CLI-invokable, consumed by
|
|
vendoring per plugin rather than npm publishing. The halves share contract
|
|
and fixtures, never code.
|
|
|
|
## Conventions
|
|
|
|
- Type hints everywhere; `mypy --strict` target.
|
|
- Determinism is bit-exact: `ingested_at` is an explicit required argument
|
|
(no wall-clock defaults); LF-only output; golden fixtures compared
|
|
byte-for-byte.
|
|
- No model calls anywhere in the run path.
|
|
- Credentials only as env-var *references* resolved at runtime; never in
|
|
manifests, logs, or frontmatter.
|
|
- Network access requires an explicit per-run opt-in flag; refuse fail-fast
|
|
otherwise.
|
|
- Conventional Commits: `type(scope): description`.
|
|
- English for all code, docs, and commit messages (public repo).
|
|
|
|
## Commands
|
|
|
|
- Test: `pytest`
|
|
- Lint: `ruff check .` + `ruff format --check .`
|
|
- Type check: `mypy --strict src/`
|
|
|
|
## Workflow
|
|
|
|
- TDD: no production code without a failing test first.
|
|
- This repo is published PUBLICLY (`open/` namespace on Forgejo). `STATE.md`
|
|
and `docs/oppstartsprompt.md` are LOCAL-ONLY (gitignored) — never commit
|
|
session state or internal briefs. No secrets, sober English prose, no
|
|
marketing language.
|
|
- After `git commit`: push to Forgejo (`git push origin`) immediately.
|
|
Never GitHub.
|
|
|
|
## Communication patterns
|
|
|
|
### Linking to local files
|
|
|
|
When pointing to local files in responses, always use markdown link syntax
|
|
with a descriptive name:
|
|
|
|
- Use `[Human-friendly name](file:///absolute/path)` — never bare
|
|
`file:///...` URLs or autolinks `<file://...>`.
|
|
- Always use absolute paths. Never `~/` or relative paths.
|
|
- For multiple files, render as a bullet list of named markdown links.
|
|
|
|
Why: bare `file://` URLs only render the first as clickable across multiple
|
|
lines. Named markdown links make each entry independently clickable and look
|
|
cleaner.
|