# Ingest specification — connectors and bundle materialization (framework-neutral) > **Status:** normative. This document specifies the **ingest layer** both reference > implementations build: a deterministic step that connects the framework to real data sources > and materializes the extract as an OKF knowledge bundle, which the existing 8-step loop > ([method-spec.md](method-spec.md)) then consumes unchanged. It is written so the layer can be > implemented **from this spec alone** — without reverse-engineering any existing > implementation. The prose is framework-neutral by rule: it never names a concrete agent > toolkit or vendor stack, and a guard test keeps it that way. > > The key words MUST, MUST NOT, SHOULD, and MAY are to be interpreted as in RFC 2119. > Requirements are labelled normative; anything marked *(reference)* documents the reference > implementation's concrete choice and is informative, not binding — except where a golden > extraction (§11) freezes it. ## 1. Scope and conformance Ingest is an **addition in front of** the loop: nothing in this document changes the method spec, the golden suite, or agent behaviour. - The method spec (§3 Step 1) forbids query-time retrieval against the bundle. The consequence is architecture-defining: **data reaches the model ONLY via OKF bundles.** Connectors therefore live in a deterministic ingest step that runs BEFORE the optimiser and materializes the extract as a bundle — not RAG, not live lookups inside the agent loop, no retrieval tools in the run path. - Ingest makes **zero model calls** and is deterministic end to end (§11). - **Conformance:** a conforming implementation MUST implement the `file` and `sql` source types (§4) with the materialization contract of §5–§7 and the gates of §8–§9, and MUST reproduce the shared golden extractions (§11) byte for byte. The `http` source type is an OPTIONAL extension point: implementing it (e.g. against a local mock, or via an MCP-based connector) does not require any change to this spec, and NOT implementing it does not break conformance. - **Honesty rule (unwaivable, method spec §1):** a machine-generated bundle is labelled as such (`generated: true` plus a manifest reference, §7) everywhere it is presented. - **Boundary:** the deploying organisation owns processing purposes and impact assessments; ingest provides only the technical prerequisites (local-only default, provenance, no silent egress). ## 2. Architecture ``` manifest → connector → mapping → OKF bundle (incl. index generation) → [existing loop, unchanged] ``` An expert's coupling to a source is one JSON **manifest** (§4). A **connector** executes the manifest's extractions against the source. **Materialization** (§5) maps each extraction to an OKF concept file with a provenance frontmatter layer (§7) and generates or updates the bundle's `index.md` (§6). The loop then reads the bundle exactly as it reads a hand-curated one — by navigation, never retrieval. **Index generation is part of the contract.** A bundle without `index.md` is an error, and navigation follows ONLY index cross-links (method spec §3 Step 1) — a generated concept file without an index link is unreachable. The materializer therefore creates or updates `index.md` (§6); navigability is a load-bearing requirement (§11), not a detail. ## 3. Layer separation in the bundle (unwaivable) The bundle is not only input — it is also the wiki the promotion gate (method spec §6) lifts approved knowledge into. Two rules keep ingest and the learning loop apart: - **The verdict layer is RESERVED.** A manifest mapping MUST NOT produce `type: verdict` files, and generated filenames MUST NOT fall in the `promoted-verdict-*` namespace (nor be `index.md`, which is managed per §6). The promotion gate is the ONLY path into the verdict layer. Rationale: seeding (method spec §3 Step 1) turns every `type: verdict` file into a store entry with decision default `approved` — an ingest that could write verdict-typed files would inject machine-generated "approved" verdicts around the gate, exactly the self-contamination the gate exists to prevent. This MUST be enforced fail-fast at manifest validation (before any source call) and proven by a load-bearing test (§11). - **Ingest owns only its own files.** Re-materialization replaces EXACTLY the files carrying the ingest stamp (`generated: true` plus an `ingest_manifest` reference, §7) and MUST NOT touch curated or promoted files. If a generated filename collides with an existing file that does NOT carry the stamp, materialization MUST fail — never overwrite curated content. Index updating is idempotent and preserves curated links (§6). ## 4. The ingest manifest (the contract) One JSON file per source coupling. The manifest MUST be schema-validated fail-fast BEFORE any source call — a malformed manifest never starts a run (the startup-contract discipline of method spec §10). Queries are **configuration, not code**: declarative strings the connector interprets; a connector MUST NOT evaluate manifest content as program code. Top-level fields (all required): | Field | Meaning | |---|---| | `manifest_version` | Integer schema version; this spec defines version `1`. | | `source` | The source description, polymorphic on `source.type` (below). | | `bundle_summary` | Prose used as the index body when the materializer creates a fresh `index.md` (§6). | | `extractions` | Non-empty list of extraction descriptions (below). | **`source` — polymorphic on `type`** (common field: `id`, the source-system identifier matching `[a-z0-9][a-z0-9-]*`, stamped as `source_system` in §7): - `type: "file"` — a local file catalogue. Field `root`: the directory the extraction paths resolve against. Path resolution MUST be boundary-checked against `root`, fail-closed (the OKF path rule) — an extraction can never read outside the catalogue. - `type: "sql"` — a SQL database. Field `connection_ref`: the NAME of a runtime-resolved reference (an environment variable) whose value is the connection string or database path. - `type: "http"` — a remote endpoint (OPTIONAL extension point, §1). Field `base_url`: the endpoint base; it MUST NOT embed credentials. Optional field `credential_ref`: the NAME of a runtime-resolved secret reference. An MCP-based connector is an extension of this family and MUST honour the same extraction, materialization, and gate contracts. **Credentials never live in the manifest.** The manifest carries only references by name (`connection_ref`, `credential_ref`); the secret is resolved at run time from the environment. A manifest is therefore versionable and shareable without secrets. **`extractions` — each entry** (all fields required unless marked optional): | Field | Meaning | |---|---| | `id` | Unique within the manifest, matching `[a-z0-9][a-z0-9-]*`; names the generated file (§5). | | `title` | Human-readable title; becomes the `title` frontmatter and the index link label. Single-line. | | `query` | The extraction query, polymorphic on the source type (below). | | `okf_type` | The generated file's OKF `type`. MUST NOT be `verdict` (case-insensitive; §3). | | `max_rows` | Required positive integer cap on the extraction size (§8). | `query` by source type: for `file`, a relative path (within `root`) to a CSV file whose first row is the header; for `sql`, a single read-only SELECT statement (one statement, no data modification — the connector SHOULD enforce read-only access, and the query SHOULD carry an explicit ORDER BY, since golden conformance requires a stable row order); for `http`, a path joined to `base_url`, fetched with GET. ## 5. Materialization (deterministic) Materialization takes three EXPLICIT inputs: the manifest, the target bundle directory, and `ingested_at` — plus the network opt-in flag (§8) when the source type requires it. - **`ingested_at` is an explicit required argument** — there is NO wall-clock default (mirroring the promotion gate's timestamp rule, method spec §6). Format: ISO-8601 UTC with a `Z` suffix (e.g. `2026-07-03T12:00:00Z`), stamped verbatim. This is what makes golden extractions bit-deterministic. - **One concept file per extraction**, named `ingest-{extraction id}.md` (the extraction-id grammar in §4 makes the name path-safe; the `ingest-` prefix keeps the namespace disjoint from `index.md` and `promoted-verdict-*`). Splitting one extraction into per-row files is an extension point, not version 1. - **Frontmatter** — exactly these keys, in exactly this order (OKF line-oriented `key: value`; §7 defines the provenance semantics): `type`, `title`, `source_system`, `source_query`, `ingested_at`, `ingest_manifest`, `generated`. All values MUST be single-line; the materializer MUST collapse whitespace runs (including newlines) in `source_query` to single spaces. - **Body rendering** — for `file` and `sql`: a markdown table, header row = column names, data rows in source order. Cell values: text verbatim with `\` escaped as `\\`, `|` escaped as `\|`, and newlines replaced by single spaces; integers in plain decimal; non-integral numbers in their shortest round-trip decimal form; SQL NULL as the empty string; any other value type MUST fail (never silent coercion). For `http`: the response body verbatim inside a fenced code block. Files are LF-only and end with exactly one trailing newline. - **`ingest_manifest`** is stamped as `{manifest filename stem}@{h}` where `{h}` is the first 16 hex characters of the SHA-256 of the manifest file's raw bytes — so every generated file points at the exact manifest version that produced it. - **Replacement semantics (§3):** materialization first removes every file in the bundle whose frontmatter carries the ingest stamp, then writes the new set, then updates the index (§6). Version 1 assumes ONE manifest per bundle; multiple manifests feeding one bundle is an extension point. ## 6. Index generation - If `index.md` is missing, the materializer creates it with `bundle_summary` as the body; if it exists, every line it does not itself manage is preserved byte for byte. - Each generated file gets one index cross-link, `- [{title}](ingest-{id}.md)`, appended in manifest extraction order. Linking is **idempotent by target**: a link whose target is already present in the index is never added twice *(reference: the reference implementation's existing index-linking primitive has exactly these semantics)*. - On re-materialization, index links whose target is an ingest-owned file removed in this run (§5) MUST be removed; ALL other links — curated and promoted — are preserved verbatim. A promoted verdict's index link therefore survives re-ingest (load-bearing, §11). - The link label is the extraction `title` — generated files are context-layer content, so a descriptive label is correct here. (The fixed-neutral-label rule of method spec §6 protects the VERDICT layer and is untouched by this spec.) - *(reference limitation)* the index read-modify-write is not atomic — single-process use is assumed, as in the promotion gate. ## 7. Provenance — a separate layer, an unbroken chain Ingest provenance is its OWN frontmatter layer on generated concept files — NOT an extension of the method spec's §9 proposal provenance. §9 links a proposal to a bundle file and text span; ingest provenance links the bundle file to the source system. Two separate contracts that are never mixed — the same discipline as the two falsifiers. | Field | Meaning | |---|---| | `source_system` | The `source.id` from the manifest. | | `source_query` | The query that fetched the content (whitespace-collapsed, §5). | | `ingested_at` | The explicit timestamp argument, verbatim (§5). | | `ingest_manifest` | The manifest reference `{stem}@{hash16}` (§5). | | `generated` | Literally `true` — the machine-generated marker (§1 honesty rule). | - OKF consumers preserve unknown frontmatter fields, so this layer rides through navigation and context rendering unchanged. - **Chain integrity:** the chain expert → proposal → bundle file + span → source + query + timestamp holds GIVEN versioned bundles (the OKF premise: curated, version-controlled). A re-materialization is a new bundle version carrying the new stamp, so a run's citations can be tied to the concrete materialization. Bundle versioning is a documented deployer prerequisite — no claim of "unbroken" is made beyond it. ## 8. Security frame - **Local-only default, no silent egress.** Network sources (`http`, and any other non-local transport) require an EXPLICIT per-run opt-in flag; without the flag the connector MUST refuse fail-fast. The flag is a run argument, never a manifest field — the manifest cannot grant itself network access. - **Source calls are logged** (which source, when, row count). - **Extraction size caps:** each extraction's `max_rows` (§4) is enforced fail-fast — an extraction exceeding its cap is an ERROR, never a silent truncation. - Impact assessments and processing purposes stay with the deployer (§1). ## 9. The HITL gate Two layers, both required: 1. **Procedural:** ingest NEVER runs automatically — always an explicit operator/expert command with a manifest path. No scheduler, no implicit re-ingest. 2. **Technical:** fail-fast schema validation before any source call (§4), plus the network opt-in flag (§8). Richer approval mechanics (a signed `approved_by`, an approval registry) are an extension point — not version 1. ## 10. Re-ingest and versioning Version 1 re-ingest is **full re-materialization of the ingest-owned files** (§5): given the same source content, manifest, and `ingested_at`, the result is byte-identical and repeated runs are idempotent. Curated and promoted content ALWAYS survives (§3, §6). Incremental re-ingest is an extension point. ## 11. Determinism, golden extractions, and load-bearing tests Connectors are tested ONLY against fixtures and golden extractions — never against live sources. Test suites MUST run without credentials and without network access. **Golden extraction format:** one directory per case, convention `examples/ingest-golden-{source type}/`, containing: | Entry | Meaning | |---|---| | `manifest.json` | The manifest under test. | | `fixture/` | The source content (the file catalogue, the database file, or the mock payloads). | | `ingested-at.txt` | The fixed timestamp, one line, §5 format. | | `expected-bundle/` | The expected materialized bundle, compared file by file, byte for byte. | **Load-bearing conformance tests** (a test MUST fail when its seam is detached — the method spec §11 regime): | Seam | The test MUST fail when… | |---|---| | Provenance stamping | a generated file no longer carries the §7 layer | | Navigability | the generated bundle stops being consumable by the UNCHANGED bundle-navigation code, index links included | | Verdict reservation | a manifest mapping to `type: verdict` (or the reserved filename namespace) stops being rejected | | Re-ingest layer safety | re-materialization over a bundle with a promoted verdict deletes the verdict file or its index link | | Golden regression | any byte of a golden extraction's expected bundle diverges | | Network gate | an `http` source stops refusing fail-fast without the opt-in flag | | Spec integrity | this spec goes missing, names a concrete agent toolkit, or stops documenting a contract field | ## 12. Cross-check table Every field of the machine-readable contracts, mapped to its normative section (completeness is enforced by the spec-integrity test): | Field | Contract | Section | |---|---|---| | `manifest_version` | manifest (top level) | §4 | | `source` | manifest (top level) | §4 | | `bundle_summary` | manifest (top level) | §4, §6 | | `extractions` | manifest (top level) | §4 | | `type` | manifest source discriminator / OKF frontmatter | §4, §5 | | `id` | manifest source and extraction identifiers | §4 | | `root` | manifest source (`file`) | §4 | | `connection_ref` | manifest source (`sql`) | §4 | | `base_url` | manifest source (`http`) | §4 | | `credential_ref` | manifest source (`http`) | §4 | | `title` | manifest extraction / frontmatter / index label | §4, §5, §6 | | `query` | manifest extraction | §4 | | `okf_type` | manifest extraction | §4, §3 | | `max_rows` | manifest extraction | §4, §8 | | `source_system` | provenance frontmatter | §7 | | `source_query` | provenance frontmatter | §5, §7 | | `ingested_at` | provenance frontmatter / materialization argument | §5, §7 | | `ingest_manifest` | provenance frontmatter | §5, §7 | | `generated` | provenance frontmatter | §3, §7 | | `manifest.json`, `fixture/`, `ingested-at.txt`, `expected-bundle/` | golden extraction case | §11 |