Skip to content

Add the exclude-superseded search filter (Q3, #108) - #115

Merged
joefeser merged 4 commits into
devfrom
zcode/q3-exclude-superseded
Aug 28, 2026
Merged

Add the exclude-superseded search filter (Q3, #108)#115
joefeser merged 4 commits into
devfrom
zcode/q3-exclude-superseded

Conversation

@joefeser

@joefeser joefeser commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Summary

Implements Q3 of the corrected query-surface epic (audit finding F5): an --exclude-superseded filter on both search and search-receipt, with receipt binding. Closes #108.

Superseded records remain first-class hits by default; the flag is the caller's opt-in and drops matches whose lifecycle column is superseded. The filter runs inside the same gated read transaction as the match (same verified snapshot), composes with both query grammars, and search receipts record exclude_superseded (optional boolean in the v1 schema, always set on new receipts) beside query_mode and the query digest — so every result-affecting parameter is bound and filtered results are replayable.

Explicitly a read-time lifecycle filter, not revocation: revocation suppression remains a projection-build input (M6 stays open as separate work).

Contract and compatibility impact

  • Additive: defaulted keyword argument, CLI flag, optional receipt field; no schema/output/fixture shape changes; default search behavior unchanged.
  • The pinned scan-projection fixture, vertical slice, and WITS conformance tests are untouched and green.
  • Only lifecycle superseded is excluded; draft/rejected/sealed are unaffected.

Security and privacy impact

  • Supersession becomes expressible from search, and receipts state which mode ran, instead of superseded records being indistinguishable hits.
  • No new echo surfaces; all fixtures synthetic.

Validation

  • python3 -m unittest discover -s tests — 610 tests OK (3 new).
  • ./scripts/run_conformance.sh — OK, including the new run_search_supersession_slice.py --check replay (5 operations: both-lifecycles default, filtered survivor, literal composition, receipt binding).

Tracking

Summary by Sourcery

Add opt-in superseded-record filtering to search while preserving default results and making filtered receipts replayable.

New Features:

  • Add an opt-in --exclude-superseded filter to search and search-receipt, supporting both raw and literal query modes.
  • Bind the active supersession filter state into search receipts for replayable filtered results.

Enhancements:

  • Preserve existing default search behavior while excluding only records with a superseded lifecycle when requested.

CI:

  • Add the supersession search slice to the conformance workflow.

Documentation:

  • Document the supersession filter contract, receipt behavior, and its distinction from revocation.
  • Add a decision record and replayable synthetic evidence for the new search behavior.

Tests:

  • Add unit, CLI, and conformance coverage for default results, lifecycle filtering, query-mode composition, and receipt binding.

Implement Q3 of the corrected query-surface epic (audit finding F5):
search and search-receipt accept --exclude-superseded, dropping matches
whose record lifecycle is superseded while the default keeps them as
first-class hits. The filter runs inside the same gated read transaction
as the match, composes with both query grammars, and search receipts
record exclude_superseded beside query_mode and the query digest so every
result-affecting parameter is bound and filtered results are replayable.
This is a read-time lifecycle filter, not revocation; revocation
suppression remains a projection-build input.

Security: supersession becomes expressible from search and receipts
state which mode ran, instead of superseded records being
indistinguishable hits. Compatibility: additive flag, defaulted keyword,
and optional receipt field; default behavior and all pinned fixtures
unchanged. Proven end to end with a synthetic slice replayed by
run_conformance.sh. For issue #108; decision recorded in
docs/decisions/0026.
@joefeser

Copy link
Copy Markdown
Owner Author

@codex review

@sourcery-ai sourcery-ai 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.

Sorry @joefeser, this account has used its review budget of 1,500,000 diff characters for the last 7 days.

You can request another review in 9 hours and 13 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces an additive --exclude-superseded filter for both search commands and their Python APIs, applies it against lifecycle data in the gated read snapshot for raw and literal queries, and records the option in replayable receipts. New tests, synthetic fixtures, documentation, and conformance tooling verify default compatibility, filtering behavior, and receipt binding without changing revocation semantics.

Sequence diagram for gated supersession-filtered search

sequenceDiagram
    participant Caller
    participant CLI
    participant Projection
    participant SQLite
    Caller->>CLI: search_records(index_path, query, exclude_superseded)
    CLI->>Projection: _read_index(index_path)
    Projection->>SQLite: Match query in verified read transaction
    SQLite-->>Projection: Matching record IDs
    opt exclude_superseded
        Projection->>SQLite: SELECT record_id FROM records WHERE lifecycle = 'superseded'
        SQLite-->>Projection: Superseded record IDs
        Projection-->>Projection: Remove superseded matches
    end
    Projection-->>CLI: record_ids
    CLI-->>Caller: Search results
Loading

Sequence diagram for replayable search receipt binding

sequenceDiagram
    participant Caller
    participant Projection
    participant SQLite
    Caller->>Projection: search_receipt(index_path, query, literal, exclude_superseded)
    Projection->>SQLite: Match query in verified read transaction
    SQLite-->>Projection: Filtered record IDs
    Projection->>SQLite: SELECT source_record_set_digest FROM projection_metadata
    SQLite-->>Projection: Source digest
    Projection-->>Caller: Receipt with query_mode, query_digest, exclude_superseded, record_ids
Loading

Flow diagram for default and filtered search behavior

flowchart LR
    A[Search query] --> B{--exclude-superseded?}
    B -->|No| C[Return all matching lifecycles]
    B -->|Yes| D[Drop lifecycle = superseded]
    D --> E[Return remaining matches]
    C --> F[Receipt records exclude_superseded = false]
    E --> G[Receipt records exclude_superseded = true]
Loading

File-Level Changes

Change Details Files
Added opt-in superseded-record filtering across the search APIs and CLI while preserving existing default results.
  • Added --exclude-superseded to search and search-receipt.
  • Added defaulted API parameters and filtered matched IDs by the records.lifecycle column.
  • Applied filtering for both raw and literal query modes within the existing read transaction.
  • Kept draft, rejected, and sealed records unaffected.
artifact_memory/cli.py
artifact_memory/projection.py
tests/test_cli.py
tests/test_projection.py
Bound the lifecycle filter state into search receipts and documented the additive contract and non-revocation semantics.
  • Added exclude_superseded to newly issued v1 receipts while retaining schema compatibility.
  • Documented replayability, default behavior, and the distinction from projection-time revocation suppression.
  • Recorded the design decision and evidence locations.
artifact_memory/schemas/core/search-receipt.v1.schema.json
docs/contracts/v0-filesystem-and-projections.md
docs/decisions/0026-exclude-superseded-search-filter.md
Added a deterministic synthetic conformance slice proving filtering, grammar composition, receipt binding, and replay.
  • Created synthetic accepted/superseded records and an expected receipt.
  • Implemented slice execution and --check replay validation.
  • Added slice schema validation, unit coverage, and conformance-runner integration.
artifact_memory/schemas/core/search-supersession-slice-receipt.v1.schema.json
artifact_memory/search_supersession_slice.py
fixtures/synthetic/search-supersession/v1/expected-receipt.json
fixtures/synthetic/search-supersession/v1/records/record-0001.json
fixtures/synthetic/search-supersession/v1/records/record-0002.json
scripts/run_search_supersession_slice.py
scripts/run_conformance.sh
tests/test_search_supersession_slice.py

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add opt-in superseded filtering to search

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds opt-in superseded-record filtering to raw and literal search flows.
• Binds exclusion state into replayable, integrity-gated search receipts.
• Documents and proves compatibility through schemas, synthetic fixtures, and conformance tests.
Diagram

graph TD
  CLI["Search commands"] --> API["Search APIs"] --> TX["Gated transaction"] --> FTS["FTS match"] --> DEC{"Exclude superseded?"}
  DEC -->|Enabled| FILTER["Lifecycle filter"] --> OUT["Record IDs"] --> RECEIPT["Bound receipt"]
  DEC -->|Disabled| OUT
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Filter within FTS SQL
  • ➕ Avoids loading every superseded record ID for each filtered query
  • ➕ Lets SQLite apply lifecycle filtering directly to matched rows
  • ➖ Requires parallel raw and literal query variants or more complex shared SQL
  • ➖ Couples FTS queries more tightly to the records table and projection schema

Recommendation: Keep the shared post-match filter for this additive contract change: it composes consistently with both grammars, preserves result ordering, and remains inside the verified transaction. If filtered-search performance becomes material on large projections, move the lifecycle predicate into joined SQL after benchmarking.

Files changed (15) +506 / -19

Enhancement (2) +60 / -18
cli.pyExpose superseded filtering on both search commands +4/-2

Expose superseded filtering on both search commands

• Adds the --exclude-superseded option to search and search-receipt. Forwards the parsed boolean to the corresponding projection APIs without changing default behavior.

artifact_memory/cli.py

projection.pyFilter superseded matches and bind receipt state +56/-16

Filter superseded matches and bind receipt state

• Extends raw and literal searches with an optional read-time lifecycle filter inside the gated SQLite transaction. Newly issued search receipts always record the exclusion boolean alongside query mode and digest.

artifact_memory/projection.py

Tests (3) +112 / -0
test_cli.pyTest superseded filtering through the CLI +41/-0

Test superseded filtering through the CLI

• Verifies unchanged default search output, filtered record IDs, and exclusion-state binding in search-receipt JSON output.

tests/test_cli.py

test_projection.pyTest lifecycle filtering across search grammars +42/-0

Test lifecycle filtering across search grammars

• Covers raw and literal exclusion, unchanged default behavior, and receipt binding to exclusion state, query digest, and source snapshot.

tests/test_projection.py

test_search_supersession_slice.pyTest exact synthetic slice replay +29/-0

Test exact synthetic slice replay

• Confirms generated evidence matches the pinned receipt, reports success, contains expected result counts, and validates against its schema.

tests/test_search_supersession_slice.py

Documentation (2) +72 / -1
v0-filesystem-and-projections.mdSpecify superseded search filtering contract +14/-1

Specify superseded search filtering contract

• Documents opt-in semantics, receipt replay binding, compatibility boundaries, and the distinction from projection-time revocation suppression. Adds the replay command for the synthetic slice.

docs/contracts/v0-filesystem-and-projections.md

0026-exclude-superseded-search-filter.mdRecord the additive filter design decision +58/-0

Record the additive filter design decision

• Captures rationale, compatibility constraints, authority limits, and evidence for retaining superseded hits by default while offering explicit read-time exclusion.

docs/decisions/0026-exclude-superseded-search-filter.md

Other (8) +262 / -0
search-receipt.v1.schema.jsonAllow exclusion state in v1 search receipts +1/-0

Allow exclusion state in v1 search receipts

• Adds an optional boolean exclude_superseded property while retaining compatibility with existing v1 receipts.

artifact_memory/schemas/core/search-receipt.v1.schema.json

search-supersession-slice-receipt.v1.schema.jsonDefine supersession slice evidence schema +50/-0

Define supersession slice evidence schema

• Introduces a strict schema for synthetic projection, filtering, receipt-binding, authority, and limitation evidence.

artifact_memory/schemas/core/search-supersession-slice-receipt.v1.schema.json

search_supersession_slice.pyImplement deterministic supersession filter proof +84/-0

Implement deterministic supersession filter proof

• Projects synthetic records and verifies default, filtered, literal, and receipt-bound search behavior. Produces and validates a canonical digest-bearing evidence receipt.

artifact_memory/search_supersession_slice.py

expected-receipt.jsonPin expected supersession slice evidence +52/-0

Pin expected supersession slice evidence

• Records deterministic outcomes for projection, default and filtered searches, literal composition, and receipt binding.

fixtures/synthetic/search-supersession/v1/expected-receipt.json

record-0001.jsonAdd accepted survivor search fixture +18/-0

Add accepted survivor search fixture

• Provides a synthetic accepted ledger record expected to remain after superseded filtering.

fixtures/synthetic/search-supersession/v1/records/record-0001.json

record-0002.jsonAdd superseded search fixture +18/-0

Add superseded search fixture

• Provides a synthetic superseded ledger record expected by default and removed only when filtering is requested.

fixtures/synthetic/search-supersession/v1/records/record-0002.json

run_conformance.shRun supersession proof in conformance suite +1/-0

Run supersession proof in conformance suite

• Adds deterministic supersession slice replay to the repository-wide conformance command.

scripts/run_conformance.sh

run_search_supersession_slice.pyAdd supersession slice replay command +38/-0

Add supersession slice replay command

• Runs the provider-free synthetic proof in a temporary workspace and optionally compares it with checked-in evidence.

scripts/run_search_supersession_slice.py

@qodo-code-review

qodo-code-review Bot commented Aug 27, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Filter scans all superseded records ✓ Resolved 🐞 Bug ➹ Performance
Description
Every non-empty filtered search materializes the complete set of superseded record IDs before
filtering the matches, so a highly selective query can still incur time and memory proportional to
every superseded record in the projection. This can make the opt-in search path unnecessarily slow
or memory-heavy on large indexes.
Code

artifact_memory/projection.py[R353-356]

+        superseded = {
+            row[0]
+            for row in connection.execute("SELECT record_id FROM records WHERE lifecycle = 'superseded'")
+        }
Relevance

●●● Strong

Recent accepted precedent supports fixing unbounded memory work in large-file paths; this
selective-query optimization is similarly concrete.

PR-#79

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed code executes an unrestricted lifecycle query and constructs a Python set containing
every superseded ID (artifact_memory/projection.py:352-357) after the FTS query has already
produced its much smaller match list (artifact_memory/projection.py:342-350). The schema provides
keyed/indexed columns that allow SQLite to apply this predicate while matching instead
(artifact_memory/schemas/core/index-sqlite.v1.sql:12-21).

artifact_memory/projection.py[342-357]
artifact_memory/schemas/core/index-sqlite.v1.sql[12-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`_matched_record_ids` loads every superseded record ID into a Python set for each non-empty filtered search, even when the FTS query matches only a few records. Apply the lifecycle predicate in SQLite so work is bounded by the matched rows and no projection-wide Python set is allocated.

## Issue Context
The `records` table has a primary key on `record_id` and an index on `(lifecycle, record_id)`, while both FTS queries return `record_id`. Join or correlate the FTS result with `records` and predicate on `lifecycle != 'superseded'`; preserve existing ordering and literal-mode post-filter behavior.

## Fix Focus Areas
- artifact_memory/projection.py[314-357]
- artifact_memory/schemas/core/index-sqlite.v1.sql[12-21]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread artifact_memory/projection.py Outdated

@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: eea8b1027b

ℹ️ 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 artifact_memory/projection.py Outdated
…hanged

Process the current-head required-reviewer batch from PR #115 (Qodo,
Codex P2):

- Raw-mode exclusion now predicates lifecycle in SQL alongside the FTS
  match, and literal-mode exclusion bounds its lookups by the
  post-filtered candidates, so no filtered search materializes every
  superseded record ID or does work proportional to the projection.
- Default search receipts omit exclude_superseded entirely, restoring
  the exact pre-filter v1 shape for consumers pinned to the earlier
  schema; only filtered receipts carry the field.

Validated on this head: 610 tests OK and run_conformance.sh OK with the
regenerated supersession slice. For issue #108; decisions recorded in
docs/decisions/0026.

@sourcery-ai sourcery-ai 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.

New security issues found

Comment thread artifact_memory/projection.py Outdated
… exclusion

Address the Sourcery security-lint thread on PR #115: replace the
placeholder-count f-string IN query with one parameterized primary-key
lifecycle lookup per post-filtered candidate. No search path builds SQL
from string concatenation, and the work stays bounded by matched rows
rather than the projection. Validated: 610 tests OK, run_conformance.sh
OK.
sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Aug 27, 2026

@sourcery-ai sourcery-ai 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.

Sourcery assessment

Approved.

@joefeser

Copy link
Copy Markdown
Owner 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: 718ec1f224

ℹ️ 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 artifact_memory/schemas/core/search-receipt.v1.schema.json Outdated
Address the current-head Codex P2 on PR #115: the optional
exclude_superseded property accepted false from external producers even
though omission is the contract's only representation of the default,
and such a receipt would also lose the pre-filter shape pinned v1
consumers expect. The property is now const true; a receipt carrying
exclude_superseded: false fails schema validation. Validated: 610 tests
OK, run_conformance.sh OK.

@sourcery-ai sourcery-ai 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.

Sourcery assessment

Approved.

@joefeser

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 5f96b55152

ℹ️ 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".

@joefeser
joefeser merged commit 1b4b91a into dev Aug 28, 2026
6 checks passed
@joefeser
joefeser deleted the zcode/q3-exclude-superseded branch August 28, 2026 15:48
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.

1 participant