Skip to content

fix(trail): extract provenance per document for list results (closes #149) - #162

Merged
rajfirke merged 3 commits into
rajfirke:mainfrom
lavneethora:fix/149-per-document-provenance
Sep 3, 2026
Merged

fix(trail): extract provenance per document for list results (closes #149)#162
rajfirke merged 3 commits into
rajfirke:mainfrom
lavneethora:fix/149-per-document-provenance

Conversation

@lavneethora

Copy link
Copy Markdown
Contributor

What does this PR do?

_track_result called _extract_provenance on the whole result. When a decorated function
returns a list of Documents, the list itself has no .metadata, so provenance came back None
for every document in the batch.

Reproduced on main with two documents carrying distinct sources and dates:

prov=MISSING   fresh=UNKNOWN  json=null
prov=MISSING   fresh=UNKNOWN  json=null

After this change:

prov=VALID  fresh=FRESH  {"source_url": "https://example.com/a", "created_at": "2026-08-01T..."}
prov=VALID  fresh=STALE  {"source_url": "https://example.com/b", "created_at": "2020-01-01T..."}

The second document is from 2020 and now correctly flags STALE. Previously it passed silently,
which is the compliance consequence the issue describes.

Why not a plain zip(result, items)

The issue suggests zipping result with items. That is correct for the common case but
misbehaves with a custom content_extractor: if the extractor reshapes the batch, zip pairs
each content item with an unrelated document's provenance, attributing the wrong source to the
wrong content. zip also truncates silently when lengths differ.

So the pairing is guarded by an explicit rule in a new _extract_provenances helper:

  • If result is a list or tuple and extraction produced exactly one item per element, read
    provenance from each element.
  • Otherwise the mapping from item back to element is unknowable, so read provenance from
    result as a whole and share it, which is exactly the previous behaviour.

The helper always returns exactly one provenance per content item, so the loop uses
zip(..., strict=True). Any future mismatch raises instead of silently dropping records.

This means every non-list path keeps its current behaviour: a single Document still reads its own
.metadata, a str/dict/None result behaves as before, and an extractor that collapses a
batch into one string falls back rather than guessing. All four cases have tests.

Checklist

  • Tests added/updated for the change
  • ruff check src/ tests/ passes
  • ruff format --check src/ tests/ passes
  • mypy src/provena/ passes
  • pytest passes with no failures
  • CHANGELOG.md updated (if user-facing change)

Four new tests in TestContextTrailTrack: per-document provenance from a list, per-document
freshness (the FRESH/STALE split above), a single non-list Document still reading its own
metadata, and a reshaping extractor falling back without mispairing. The two list tests fail on
unfixed source; the other two are regression guards for the paths deliberately left unchanged,
so they pass either way. Full suite goes 514 to 518 passing, skips unchanged.

Related Issues

Fixes #149

_extract_provenance ran on the list itself rather than each element. Lists have no .metadata, so every document in a retrieved batch was logged with provenance_status=MISSING and no freshness check. Pair each content item with its own element when extraction produced one item per element, and fall back to the previous whole-result behaviour when the counts do not line up.
@lavneethora lavneethora changed the title Fix/149 per document provenance fix(trail): extract provenance per document for list results (closes #149) Sep 2, 2026
@rajfirke
rajfirke merged commit 7700438 into rajfirke:main Sep 3, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@trail.track loses per-document provenance when the decorated function returns a list of Documents

2 participants