Skip to content

fix(text): render NOT_STORED instead of claiming a finished scan - #273

Closed
mjbradford89 wants to merge 1 commit into
developfrom
dn-8558-known-good-refusal-messages
Closed

mjbradford89 wants to merge 1 commit into
developfrom
dn-8558-known-good-refusal-messages

Conversation

@mjbradford89

Copy link
Copy Markdown
Contributor

TL;DR

  • An artifact whose bytes were declined as known-good at submission rendered as Status: Assertion window closed — a completed scan that found nothing — for something that was never scanned and holds no bytes to scan.
  • Give NOT_STORED its own branch, ahead of window_closed, naming the cause and the remedy.
  • Also corrects a comment describing a server contract that no longer holds.
  • 2 files, +55/-5. No SDK floor change — state already ships well below the pin.

The bug

These records carry window_closed=True, so the status ladder fell straight through the known-good branch to the ordinary closed-window line:

Status: Assertion window closed

…on a record with polyscore: None and no assertions. It reads as "we scanned it and nothing came back", which is the opposite of what happened: the platform knows the hash, deliberately never stored its bytes, and never scanned it at all. Resubmitting the file scans it normally.

Now:

Status: Not stored. Its bytes were declined as a known-good binary when submitted; resubmit the file to scan it.

Placed ahead of window_closed deliberately — a comment records why, since the ordering is the whole fix.

The comment correction

The known_good_sources note claimed the field is "emitted for any instance whose sha256 matches a known-good record, including a fully scanned one". That described the server before it gated the field. It now gates known_good on the same predicate it reports KNOWN_GOOD from, so the feeds cannot arrive without the state — the scenario the comment warned about can't occur.

Worth keeping, and now stated: the implication runs one way only. state == 'KNOWN_GOOD' does not guarantee non-empty sources (the server can render the state with no feeds if the catalogue entry stops resolving between gate and render), which is exactly why the attribution stays guarded on known_good_sources rather than assuming it is populated. Left as-is, that comment would have justified "simplifying" the guard away.

reported_state is now read once and compared twice, rather than a second getattr for the same attribute — the file's own note warns against adding those.

Test plan

Two new rendering tests in tests/known_good_field_test.py (Style 3 — formatter-direct, no CliRunner): the status line and its wording, and that a NOT_STORED instance is never reported as known-good even when the server sent a feed list.

Full suite: 198 passed.

Note for local runs: this repo's .venv has stale non-editable installs of both polyswarm and polyswarm_api in site-packages shadowing the working trees. Without PYTHONPATH=src:<sdk>/src, pytest exercises the installed copies and reports 11 phantom failures. Worth a uv pip install -e ..

Companion PRs

Same branch name, independently mergeable — no ordering dependency:

  • polyswarm-api — exposes the server's errors.code on NotFoundException. This PR does not depend on it; it reads the instance state that already ships.
  • A server-side PR reworks the refusal message a rescan/download returns for these same artifacts.

An artifact whose bytes were declined as known-good at submission carries
window_closed=True, so it fell through to "Status: Assertion window closed" —
which reads as a completed scan that produced nothing, for an artifact that was
never scanned and holds no bytes to scan. Give NOT_STORED its own branch, ahead
of window_closed, naming both the cause and the remedy (resubmit the file).

Also corrects the known_good_sources comment, which described a server contract
that no longer holds. The server gates that field on the same predicate it
reports KNOWN_GOOD from, so the feeds cannot arrive without the state. The
converse does not hold — the state can arrive with no feeds — which is why the
attribution stays guarded rather than assuming the list is populated.
@claude

claude Bot commented Sep 16, 2026

Copy link
Copy Markdown

Review — 2 blocking, 3 smaller

Gitflow is clean (base develop, no version bump, no floor change — state ships in 4.1.0 per specs/05 §Current floor).

1. Correctness — the Detections line still claims a finished scan and offers a rescan that can't happen

The fix only touches the Status ladder. The Detections ladder is untouched, and a NOT_STORED instance falls through it identically to before: is_known_good is False, community != 'stream', valid_assertions == [], window_closed=True, failed=False → text.py:180:

Detections: No engines responded to this scan. You can trigger a rescan now.
...
Status: Not stored. Its bytes were declined as a known-good binary when submitted; resubmit the file to scan it.

That's the pre-existing test test_feeds_without_any_state_render_as_an_ordinary_instance (same field set) pinning the exact line these records get. So the block now says both "no engines responded to this scan" and "never scanned, no bytes to scan", and tells the user to trigger a rescan the PR body says the server refuses for these very artifacts.

It's also the same mis-signal specs/03-formatters.md names as the reason KNOWN_GOOD got its own Detections branch, not just a Status line: "instead of the misleading 'no engines responded — rescan now' a window-closed/no-assertion instance would otherwise get". NOT_STORED needs the matching Detections branch (ahead of the valid_assertions == 0 and window_closed branch, for the same ordering reason the Status branch is ahead of window_closed).

Test gap, same item: the new tests assert only the Status line. The KNOWN_GOOD siblings assert 'trigger a rescan' not in text and 'No engines responded' not in text — those two assertions on _instance(state='NOT_STORED') are what would have caught this.

2. Spec drift — specs/03-formatters.md not updated, and the comment change contradicts it

AGENTS.md: "Update the spec in the same PR as the code change; if a PR drifts from a spec, the spec is wrong until proven otherwise." Two drifts:

  • The NOT_STORED branch and its deliberate ordering ahead of window_closed are documented nowhere. §Known-good artifact instances still describes the ladder as Failed → Known good → window_closed (specs/03-formatters.md:135).
  • The comment edit retires a claim the spec still makes verbatim (specs/03-formatters.md:70-73): "the server emits the feed list for any instance whose sha256 matches a known-good record, including a fully scanned one with real detections/PolyScore" — and the derived note that the if is_known_good ternary is "a statement of that coupling, not a guard". If the server now gates known_good on the same predicate, that paragraph and the TestKnownGoodFeedsAreNotTheSignal class docstring ("served for every instance whose sha256 matches a known-good record") are both stale. Update them here, or leave the comment alone.

3. The new comment's invariant is contradicted by the PR's own test

text.py:132 — "the feeds can never arrive without the state" — but test_not_stored_is_not_reported_as_known_good constructs exactly that (state='NOT_STORED', known_good=FEEDS), as does the pre-existing test_feeds_without_known_good_state_are_ignored (state='SETTLED'). Since that sentence is the sole justification for edit #2, it should say what it means: the server never emits feeds for an instance it would report KNOWN_GOOD for, which is not the same as "never without the state".

4. 'NOT_STORED' is a new load-bearing wire literal with no provenance

specs/03-formatters.md §"The wire dependency, and where it is actually pinned" exists because a Style-3 test compares against a dict the test wrote itself, so the KNOWN_GOOD literal was "read off the server's serializer, not inferred" (ArtifactInstanceSerializer emits instance.state.name; BountyState.KNOWN_GOOD.name). 'NOT_STORED' gets the same exposure and none of that treatment — a typo or a rename renders nothing and no test fails. State where the literal was read from, same as its sibling.

5. Dangling spec citation

tests/known_good_field_test.py:248 cites "specs/05 case 2b". specs/05-sdk-contract.md has no numbered cases. If that's the SDK repo's spec, name the repo.

@sbneto

sbneto commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

When the platform refuses a download or rescan for a hash whose bytes it deliberately never stored, it said "the file does not exist" — the same words a genuinely absent artifact gets. The reporter read that as data loss and spent an investigation hunting a retention bug across 297 files in 47 container images. The server side splits the refusal so it names the cause; this PR is the CLI half, giving NOT_STORED its own status line instead of letting it fall through to "Assertion window closed". 3 PRs on dn-8558-known-good-refusal-messages; 10 files, +170/-13 across the set.

Severity: 0 HIGH · 3 MODERATE · 5 LOW. Prior feedback: 9 checked · 9 open.
Objective: met, with gaps — names the cause for the ticket's 297-file bucket, not its 10-file one.

  • missing: "should search return a record with polyscore=None and last_scanned=None at all?" → F3

Fixes are proposed, not applied; nothing was run.

Cross-repo coordination

Member PR State Role
artifact-index polyswarm/artifact-index#1978 open API (producer)
polyswarm-api polyswarm/polyswarm-api#326 closed, unmerged SDK
polyswarm-cli #273 closed, unmerged; branch deleted CLI ← you are here

Merge order: artifact-index#1978 → polyswarm-cli#273 (§14: API is the contract). Technically this PR is independently mergeable — reported_state/state already ship on artifact-index master — but §14 still orders the capability.

Contracts crossing the set: errors.code on the 404 refusal — producer artifact-index, consumers polyswarm-api (branches on KNOWN_GOOD only) and this repo (reads no code at all). Nothing here changes as a result.

Coherence: fixes are independent; no cross-repo adjustment needed.

Gaps: this PR's branch dn-8558-known-good-refusal-messages is gone from origin — head 142578d survives only as refs/pull/273/head. It is the one piece of this set that exists on no branch anywhere, and F3's fix needs it back before that ref is pruned → also F1 on the server PR.

Findings (round 1)

Every finding below is work for this change set; each entry's Lands in: names the repo whose PR carries the fix, on the branch name every member already shares.

[MODERATE] F3. The CLI offers a rescan the server always refuses

What happens: One block of CLI output tells the user two incompatible things: that no engines responded and they can trigger a rescan now, and — once this PR lands — that the bytes were never stored and the file must be resubmitted. The rescan it invites returns 404. The user retries an operation that can never succeed, which is the "is my data gone?" confusion the ticket was opened to remove.
When: Any CLI render of a case-2b instance — the reported 297-file bucket — via polyswarm lookup <scan_id> on a declined submission, or polyswarm search hash on a catalogued reference instance. Today, with this PR closed, the status line reads "Assertion window closed" as well.
Why:

  • This PR derives is_not_stored and consumes it in the Status ladder only (src/polyswarm/formatters/text.py:143, :215).
  • The Detections ladder is byte-identical to develop, and its third arm matches this payload exactly (src/polyswarm/formatters/text.py:180).
  • window_closed=True rides on that payload for these rows (artifact-index/src/artifact_index/services/known_good.py:215, artifact-index/src/artifact_index/models/instances.py:630).
  • The rescan route gates on check_binary(action='rescan'), which raises a 404 for a never-stored row (artifact-index/src/artifact_index/views/v3/consumer.py:198).
  • This PR's own tests assert only the Status line, so the contradiction renders green (tests/known_good_field_test.py:243).

Lands in: polyswarm-cli
Also touches: artifact-index/src/artifact_index/views/v3/consumer.py:198
Proposed fix (untested): Insert a NOT_STORED arm immediately ahead of the "No engines responded" arm, guarded on len(instance.valid_assertions) == 0 so a reconciled row that kept its results still reports them below, stating the fact only and leaving the remedy sentence on the Status line:

        elif is_not_stored and not instance.failed and len(instance.valid_assertions) == 0:
            # Ahead of the "no engines responded" arm for the same reason the Status
            # branch sits ahead of window_closed: these rows carry window_closed=True
            # with no assertions, so that arm offered a rescan the server refuses with
            # a 404 (check_binary) for bytes it never stored.
            output.append(self._white(
                'Detections: This artifact has not been scanned; no bytes were stored '
                'to scan.'))

This needs the branch back: restore dn-8558-known-good-refusal-messages from refs/pull/273/head and reopen under that identical name, since this repo's CI resolves the paired SDK by $CI_COMMIT_BRANCH. Update specs/03-formatters.md in the same PR — AGENTS.md requires it and this PR omitted it for the Status branch too — and assert the Detections line in TestNotStoredRendering, plus a case with valid assertions proving the counts arm still wins.

  • [LOW] F7. specs/03-formatters.md is not updated for the NOT_STORED Status branch or its deliberate ordering ahead of window_closed (specs/03-formatters.md:135), and the new comment's claim that the server gates known_good on the same predicate retires a paragraph the spec still states verbatim (specs/03-formatters.md:70); separately the new comment's "the feeds can never arrive without the state" is contradicted by this PR's own test_not_stored_is_not_reported_as_known_good, the 'NOT_STORED' wire literal gets none of the provenance note its KNOWN_GOOD sibling carries, and a test cites "specs/05 case 2b" against a repo spec that has no numbered cases (tests/known_good_field_test.py:248). Fix (untested): update both spec passages, narrow the comment to "never for an instance it would report KNOWN_GOOD for", record where the literal was read from, and name the repo in the citation.

elsewhere: F1, F2, F4, F8 → polyswarm/artifact-index#1978 · F5, F6 → polyswarm/polyswarm-api#326

Outstanding review feedback

Status Raised The ask Disposition
not addressed #273, round 1 Detections ladder still claims a finished scan → F3
not addressed #273, round 1 specs/03-formatters.md not updated → F7
not addressed #273, round 1 Comment invariant contradicted by this PR's own test → F7
not addressed #273, round 1 'NOT_STORED' literal has no provenance note → F7
not addressed #273, round 1 Dangling "specs/05 case 2b" citation → F7

Standards conformity

Set-level. §14 delivery order ✓ — the server PR ships no capability needing SDK or CLI support, since errors.code is byte-identical on both its arms and only prose moves, so "API, SDK and CLI land in one change set" does not bind a message rewording. Rule 6 name identity ◐ — the two surviving branches are byte-identical and tag-safe, but this repo's is deleted from origin, so the set can no longer resolve companion code by name → F1. ## Requires ✓ — correctly absent; nothing in the set gates anything else.

Change-level. §15 ✗ → F7.

Project-level. §16 ✓ — no floor change is owed: this PR reads ArtifactInstance.state, which ships in 4.1.0, below the declared floor, and it does not adopt the SDK's new .code.

Clean: §2, §16. Non-clean: §15 ✗. Not applicable: §3–§14, §17 — a client, so no queues, storage, AKM, charts, clusters or request paths.

@sbneto

sbneto commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

When the platform refuses a download or rescan for a hash whose bytes it deliberately never stored, it said "the file does not exist" — the same words a genuinely absent artifact gets, which sent the reporter hunting a retention bug across 297 files in 47 container images. The server now answers that row first, above deleted and expired, and names the cause; this PR is the CLI half, giving NOT_STORED its own status line instead of letting it fall through to "Assertion window closed". 3 PRs on dn-8558-known-good-refusal-messages; 10 files, +242/-16 across the set.

Severity: 0 HIGH · 1 MODERATE · 6 LOW. Prior feedback: 13 checked · 10 open.
Objective: met, with gaps — names the cause for the ticket's 297-file bucket, not its 10-file one.

  • missing: "should search return a record with polyscore=None and last_scanned=None at all?" → F1

Fixes are proposed, not applied; nothing was run.

Cross-repo coordination

Member PR State Role
artifact-index polyswarm/artifact-index#1978 open, head moved since round 1 API (producer)
polyswarm-api polyswarm/polyswarm-api#326 closed, unmerged — unchanged SDK
polyswarm-cli #273 closed, unmerged; branch still deleted — unchanged CLI ← you are here

Merge order: artifact-index#1978 → polyswarm-cli#273 (§14: API is the contract). Technically this PR is independently mergeable — reported_state/state already ship on artifact-index master — but §14 still orders the capability.

Contracts crossing the set: errors.code on the 404 refusal. The server PR now moves that value for one cohort and ships a consumer audit naming this repo as reading no code at all, so nothing here changes as a result.

Coherence: fixes are independent; no cross-repo adjustment needed.

Gaps: this PR's branch dn-8558-known-good-refusal-messages is still gone from origin — head 142578d survives only as refs/pull/273/head. It is the one piece of this set that exists on no branch anywhere, and F1's fix needs it back before that ref is pruned. The server PR's body now carries that warning.

Findings (round 2)

Every finding below is work for this change set; each entry's Lands in: names the repo whose PR carries the fix, on the branch name every member already shares.

[MODERATE] F1. The CLI offers a rescan the server always refuses

What happens: One block of CLI output tells the user two incompatible things: that no engines responded and they can trigger a rescan now, and — once this PR lands — that the bytes were never stored and the file must be resubmitted. The rescan it invites returns 404. The user retries an operation that can never succeed, which is the "is my data gone?" confusion the ticket was opened to remove.
When: Any CLI render of a case-2b instance — the reported 297-file bucket — via polyswarm lookup <scan_id> on a declined submission, or polyswarm search hash on a catalogued reference instance. Today, with this PR closed, the status line reads "Assertion window closed" as well.
Why:

  • This PR derives is_not_stored and consumes it in the Status ladder only (src/polyswarm/formatters/text.py:143, :215).
  • The Detections ladder is byte-identical to develop, and its third arm matches this payload exactly (src/polyswarm/formatters/text.py:180).
  • window_closed=True rides on that payload for these rows (artifact-index/src/artifact_index/services/known_good.py:215, artifact-index/src/artifact_index/models/instances.py:630).
  • The rescan route gates on check_binary(action='rescan'), which raises a 404 for a never-stored row (artifact-index/src/artifact_index/views/v3/consumer.py:198).
  • Both PRs are byte-identical to round 1, so this carries round 1's validation, where the formatter was executed against a NOT_STORED payload.

Lands in: polyswarm-cli
Also touches: artifact-index/src/artifact_index/views/v3/consumer.py:198
Proposed fix (untested): Unchanged from round 1 — insert a NOT_STORED arm immediately ahead of the "No engines responded" arm, guarded on len(instance.valid_assertions) == 0 so a reconciled row that kept its results still reports them below, stating the fact only and leaving the remedy sentence on the Status line:

        elif is_not_stored and not instance.failed and len(instance.valid_assertions) == 0:
            # Ahead of the "no engines responded" arm for the same reason the Status
            # branch sits ahead of window_closed: these rows carry window_closed=True
            # with no assertions, so that arm offered a rescan the server refuses with
            # a 404 (check_binary) for bytes it never stored.
            output.append(self._white(
                'Detections: This artifact has not been scanned; no bytes were stored '
                'to scan.'))

It needs the branch back first: restore dn-8558-known-good-refusal-messages from refs/pull/273/head and reopen under that identical name, since this repo's CI resolves the paired SDK by $CI_COMMIT_BRANCH. Update specs/03-formatters.md in the same PR (see F6) and assert the Detections line in TestNotStoredRendering, plus a case with valid assertions proving the counts arm still wins.

  • [LOW] F6. specs/03-formatters.md is not updated for the NOT_STORED Status branch or its deliberate ordering ahead of window_closed (specs/03-formatters.md:135), and the new comment's claim that the server gates known_good on the same predicate retires a paragraph the spec still states verbatim (specs/03-formatters.md:70); separately the new comment's "the feeds can never arrive without the state" is contradicted by this PR's own test_not_stored_is_not_reported_as_known_good, the 'NOT_STORED' wire literal gets none of the provenance note its KNOWN_GOOD sibling carries, and a test cites "specs/05 case 2b" against a repo spec that has no numbered cases (tests/known_good_field_test.py:248). Fix (untested): update both spec passages, narrow the comment to "never for an instance it would report KNOWN_GOOD for", record where the literal was read from, and name the repo in the citation. Lands in: polyswarm-cli

elsewhere: F2, F3, F7 → polyswarm/artifact-index#1978 · F4, F5 → polyswarm/polyswarm-api#326

Outstanding review feedback

Status Raised The ask Disposition
not addressed #273, round 1 Detections ladder still claims a finished scan → F1
not addressed #273, round 1 specs/03 not updated; comment contradicts spec; literal has no provenance; dangling citation → F6

Standards conformity

Audited in round 1 for all three members; that block stands. No new change-level violation in this PR — it is byte-identical to round 1.

Set-level rows unchanged: §14 ✓, Rule 6 ◐ (this repo's branch is deleted from origin, so the set can no longer resolve companion code by name), ## Requires ✓. The server PR's body now states the non-dependency explicitly.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants