Skip to content

feat(core): add portable document ingestion contracts - #1178

Open
phernandez wants to merge 1 commit into
mainfrom
codex-document-ingestion
Open

feat(core): add portable document ingestion contracts#1178
phernandez wants to merge 1 commit into
mainfrom
codex-document-ingestion

Conversation

@phernandez

@phernandez phernandez commented Aug 1, 2026

Copy link
Copy Markdown
Member

Why

Hosted document ingestion needs a portable contract before Cloud can safely compose PDF extraction, accepted note writes, and later agent enrichment. Without a shared schema, provenance fields, run identity, lifecycle rules, and raw semantic parsing behavior would drift between runtimes.

This is the Core dependency for basicmachines-co/basic-memory-cloud#1648 and records the parser-neutral portion of #1006. Historical revision indexing remains separate in #1177.

What Changed

  • add strict, versioned document and document_ingestion_run Pydantic contracts
  • add deterministic document/run identities, .pdf.md sidecar paths, canonical Markdown assembly/parsing, and SHA-256 helpers
  • bind stored run IDs and output note identities back to their trusted source, extraction profile/options, and pipeline inputs
  • add a bounded enrichment output contract whose entire assembled Markdown must exactly match its declared structured observations and relations
  • require strict integer OCR page numbers at the trusted extraction boundary; booleans, floats, and strings cannot be coerced into diagnostics
  • correlate ingestion-run stage with the current revision kind so raw and successful run states cannot contradict their output
  • let generated raw documents opt out of observation/relation parsing with bm_parse_semantics: false while retaining searchable body content
  • add document_ingestion as an explicit accepted-note object source
  • preserve lightweight CLI registration by loading the Markdown parser only when agent semantics are validated

Implementation Details

  • src/basic_memory/schemas/document.py owns parser-neutral source, extraction, ingestion, revision, run-note, and agent-output models.
  • Nested source, extraction, ingestion, and document frontmatter is strict, frozen, extra-forbidden, and fail-fast.
  • Run UUID5 identity includes source entity/checksum, pipeline, extractor engine/version, extraction profile, extraction-options hash, and prompt version; document and run-note validation recompute that identity instead of trusting the supplied UUID.
  • Run-note output identity and path are recomputed from the source entity and source path.
  • Raw extraction and enriched revisions are deliberately distinct. enrich_document_markdown accepts only a raw note, requires its exact canonical Markdown checksum, and reconstructs trusted provenance instead of accepting it from the agent.
  • The agent contract parses the entire assembled body and compares exact parser output with the declared structured fields. Unmatched fences, unclosed HTML comments, wikilinks, hashtags, context, category tricks, and extra semantics cannot hide or forge graph data.
  • The agent validators import entity_parser lazily. Public schema exports therefore remain stable without pulling the heavy Markdown stack into lightweight CLI registration.
  • EntityParser only suppresses semantic parsing for an explicit scalar false opt-out. Malformed list/dict settings no longer crash arbitrary Markdown parsing.
  • The original binary, parser execution, queues, storage versions, provider calls, and historical indexing remain runtime concerns outside Core.

Testing

Automated

  • just fast-check: passed Ruff fix/check, formatting, and ty type checking.
  • uv run pytest -q tests/schemas/test_document.py tests/markdown tests/test_runtime.py::TestRuntimeContracts::test_note_object_metadata_parses_safe_values_only: 125 passed; src/basic_memory/schemas/document.py reached 100% statement coverage.
  • uv run pytest -q --no-cov tests/cli/test_cli_exit.py::test_bm_cli_import_does_not_load_heavy_stack: 1 passed.
  • git diff --check: passed before the amend.

Manual

  • No manual UI testing applies to this parser-neutral contract change.

Risks / Follow-ups

  • The raw sidecar title currently follows the source basename so the existing entity path contract preserves an exact .pdf.md path.
  • Agent enrichment is contract-only here; Cloud will keep it disabled until exact raw Tigris materialization provenance is available (basicmachines-co/basic-memory-cloud#1649).
  • Current search/graph behavior remains head-only. Opt-in historical revision indexing is tracked separately in Opt-in historical revision indexing for the Basic Memory data lake #1177.
  • The commit is SSH-signed and DCO-signed-off. GitHub reports unknown_key because the available public key is registered for authentication but not as a GitHub signing key.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5810e4dca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/markdown/entity_parser.py Outdated
@phernandez
phernandez force-pushed the codex-document-ingestion branch from e5810e4 to 5a0ad61 Compare August 1, 2026 04:00

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a0ad615fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/schemas/document.py
@phernandez phernandez changed the title Add portable document ingestion contracts feat(core): add portable document ingestion contracts Aug 1, 2026
@phernandez phernandez added enhancement New feature or request cloud Basic Memory Cloud labels Aug 1, 2026
@phernandez
phernandez force-pushed the codex-document-ingestion branch from 5a0ad61 to cbd903c Compare August 1, 2026 04:43

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbd903c254

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/schemas/document.py
Comment thread src/basic_memory/schemas/document.py
@phernandez
phernandez force-pushed the codex-document-ingestion branch from cbd903c to fca36b5 Compare August 1, 2026 05:34

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fca36b555a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/schemas/document.py
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@phernandez
phernandez force-pushed the codex-document-ingestion branch from fca36b5 to fa57ffe Compare August 1, 2026 05:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa57ffe030

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/schemas/document.py Outdated
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@phernandez
phernandez force-pushed the codex-document-ingestion branch from fa57ffe to bc13b3d Compare August 1, 2026 06:17
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc13b3d444

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +193 to +196
schema_ref: Literal["schema/document-extraction"] = Field(
default="schema/document-extraction",
validation_alias="schema",
serialization_alias="schema",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Permit field-name validation for schema aliases

When downstream code serializes either frontmatter model with the standard model_dump() and reconstructs it with model_validate(), the dump contains schema_ref, but this validation_alias accepts only schema; because extras are forbidden, the model rejects its own serialized payload. The parallel ingestion-run field has the same problem, so enable validation by field name or accept both names.

Useful? React with 👍 / 👎.

Comment thread src/basic_memory/schemas/document.py Outdated
schema_version: Literal["1"] = "1"
title: NonEmptyText
body: StrictStr
tags: tuple[NonEmptyText, ...] = ()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize agent tags before materializing them

When an agent returns a structured tag such as "finance,secret" or "#hidden", this tuple preserves that literal value in the document contract, but EntityParser later runs parse_tags and indexes it as finance/secret or hidden. The accepted Markdown contract and its indexed metadata therefore disagree; validate tags with a canonical single-tag grammar or normalize them before merging into frontmatter.

AGENTS.md reference: AGENTS.md:L251-L251

Useful? React with 👍 / 👎.

Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez
phernandez force-pushed the codex-document-ingestion branch from bc13b3d to 61d7b0d Compare August 1, 2026 06:35

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61d7b0dd55

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

validation_alias=AliasChoices("schema", "schema_ref"),
serialization_alias="schema",
)
tags: tuple[NonEmptyText, ...] = ("document",)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Canonicalize document frontmatter tags before indexing

Fresh evidence beyond the prior agent-tag report is that raw/trusted DocumentNoteFrontmatterV1.tags still accepts any nonempty text: values such as finance,secret or #hidden survive this contract and are propagated by enrichment, but EntityParser later turns them into finance/secret and hidden. The accepted document contract therefore disagrees with the metadata indexed from its canonical Markdown; apply the same single-tag grammar used for agent tags here.

AGENTS.md reference: AGENTS.md:L251-L251

Useful? React with 👍 / 👎.

Comment on lines +389 to +391
elif self.ingestion.stage is DocumentIngestionStage.failed:
if self.failure is None:
raise ValueError("failed ingestion run requires structured failure details")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require extraction metadata once a failed run has raw output

When a post-extraction failure such as an agent timeout carries output.raw but omits extraction, this branch accepts the run even though a raw extraction revision was already produced. Fresh evidence beyond the earlier run-ID report is that this post-extraction failure shape bypasses _require_deterministic_run_id, so it can record an arbitrary UUID while losing the extractor engine, profile, version, and options that shaped the output; require extraction whenever a failed run has output.

Useful? React with 👍 / 👎.

raise ValueError("successful ingestion run requires extraction metadata")
if self.output is None or self.output.current is None:
raise ValueError("successful ingestion run requires its current document revision")
if self.output.current.kind is DocumentRevisionKind.raw_extraction:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject identical raw and current revision checksums

When a ready or needs-review worker changes current.kind but accidentally reuses raw.checksum, this validation accepts the audit record because it checks only the kind. These checksums represent exact Markdown revisions, and a successful document necessarily changes at least the ingestion stage and bm_parse_semantics, so the same checksum cannot identify both revisions; otherwise history consumers fetch raw bytes labeled as enriched or human-edited.

AGENTS.md reference: AGENTS.md:L355-L359

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cloud Basic Memory Cloud enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant