Skip to content

fix: key known-goodness on the instance state, not on the feed list - #262

Merged
sbneto merged 11 commits into
developfrom
dn-8425-known-good-status-signal
Aug 3, 2026
Merged

fix: key known-goodness on the instance state, not on the feed list#262
sbneto merged 11 commits into
developfrom
dn-8425-known-good-status-signal

Conversation

@sbneto

@sbneto sbneto commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

TL;DR

  • is_known_good was state == 'KNOWN_GOOD' or bool(known_good_sources). The state alone now decides.
  • Without this, a fully scanned malicious sample whose hash is catalogued would render as "known-good … it is not scanned", replacing its real detections.
  • A known-good instance that does carry results now reports them (N/M engines reported malicious) — in red when any engine reported malicious, so the colour is no weaker than before the sample was reconciled.
  • SDK floor raised to polyswarm_api>=4.2.0. Unrelated to known-goodness; two other behaviours the CLI depends on fail silently on 4.1.0.

Context

The flag drives the Detections line, and when it is set the CLI replaces the engine verdicts with a known-good sentence.

The feed-list fallback was harmless only because the server emitted that list exclusively for instances already in the known-good state. It no longer does: the list is now present whenever the server's current understanding of the file is known-good, including on a fully scanned instance with real detections and a PolyScore. The state is the single reliable signal for "known-good, bytes withheld", so it alone decides and the feed list only shapes the message.

Re-verified against artifact-index's known-good model rework (#1919, DN-8425 v2): no CLI change is needed. state still reports KNOWN_GOOD exactly while the file is currently known-good, and known_good_sources still accompanies it, so both reads this PR makes are unchanged. The rework also hands the CLI a case it now renders correctly for free: a submission the server declined as known-good whose hash has since been un-flagged reports the new NOT_STORED state, which the CLI prints as an ordinary state string rather than claiming "known-good … it is not scanned" about a file that is scannable again.

Changes

Renderingsrc/polyswarm/formatters/text.py

  • The flag derives from state only; known_good_sources is read after it and short-circuits to [] when not known-good. Keeps the defensive getattr for older SDKs.
  • The trailing clause reports preserved results instead of claiming "it is not scanned": N/M engines reported malicious when there are valid assertions and the window is closed, "its scan has not finished running yet" when the window is open (parity with every other branch, which guards its counts on window_closed), "it is not scanned" otherwise.
  • Colour follows the verdict, not the status: red when there are malicious assertions and a closed window, green otherwise. Green on "40/50 engines reported malicious" would be a weaker warning than the same instance gave before it was reconciled. Status: Known good stays green — it labels the catalogue status, not the verdict.

A second, unrelated fix: --no-color was a no-op for text output. TextOutput assigned
self.color and read it nowhere, so every _white/_red/… helper styled unconditionally; the
log prefix did the same. Both now honour the flag (formatters/text.py _paint,
client/polyswarm.py setup_logging(verbosity, color=…)), covered by two CliRunner tests
(tests/cli_test.py). It rides along because the colour rule this PR documents is
unenforceable while the flag is dead — but it is a user-visible change to every command run with
--no-color on a tty, so it belongs in a release note rather than being discovered later.

Dependencypyproject.toml

  • polyswarm_api>=4.0.0>=4.2.0. The known-good attributes ship in 4.1.0, so this is not what the fix needs; the floor is set by llm_report_create sending the client's community, and a streaming 204 No Content raising NoResultsException (the no-results exit code 1). Both are silent no-ops on 4.1.0. Verified before raising it: 4.2.0 is published to PyPI, and the SDK's develop declares 4.2.0, so CI's archive install is not silently upgraded over by PyPI.

Docsspecs/03-formatters.md, specs/05-sdk-contract.md, AGENTS.md

  • 03: the state as the signal, the feed list as message-shaping, the three-way results clause, and the colour rule.
  • 05: a ## Current floor section explaining the two behaviours above, plus the two preconditions any floor bump has to satisfy (published on PyPI; declared by the SDK's develop archive).
  • AGENTS.md + specs/04-testing.md: Style 3 — pure rendering logic unit-tested against the formatter directly, with the rule that a Style-3-covered command still needs one CliRunner test proving the command reaches the formatter.

Teststests/known_good_field_test.py

  • Drops the test that pinned the removed fallback; adds ones that pin the opposite (feeds without the state are ignored; a scanned instance with feeds reports its detections; a known-good instance with results reports them).
  • Two tests render with TextOutput(color=True) and assert the ANSI wrapper — every other test unstyles, which made the green/red decision (the point of the known-good rendering) unobservable.

Tests

pytest -s -v — 115 passed, 0 failed.

Requires

  • artifact-index https://github.com/polyswarm/artifact-index/pull/1919 (the field's new emission rule).
    Traceability, not merge ordering: this change is correct both before and after that one lands — against an
    older server state is simply absent and the instance renders as an ordinary one, which a test now pins.
  • polyswarm-api feat: typed exception for a withheld known-good binary polyswarm-api#316traceability, not a dependency. CI
    installs this branch's SDK archive because the branch names match (.gitlab-ci.yml tries
    $CI_COMMIT_BRANCH.zip before develop.zip), so a reviewer should be able to see what is actually being
    tested against. The CLI itself needs only released SDK surface: the floor is >=4.2.0, satisfied by both
    PyPI and the SDK's develop, and none of that PR's new surface is used here.

Merging

Squash-merge, or rename the branch — either is fine. The branch name carries an internal
ticket ID, so a default merge commit (whose subject embeds it) would put that ID in this public
repo's permanent history. The commits, title and description are clean (AGENTS.md §Commit +
hygiene).

To be precise about the branch name, because an earlier revision of this section overstated it:
CI does install the same-named polyswarm-api branch archive (.gitlab-ci.yml tries
$CI_COMMIT_BRANCH.zip before falling back to develop.zip), so while both branches exist this
PR is tested against the paired SDK — which is useful. But nothing here requires it. The
floor this PR sets (>=4.2.0) is satisfied by the SDK's develop and by PyPI, both verified,
and none of the SDK's new surface (KnownGoodWithheldException) is used by the CLI. So the name
is a convenience, not a dependency, and renaming the branch is a valid way to solve the
public-history concern outright.

`is_known_good` was `state == 'KNOWN_GOOD' or bool(known_good_sources)`, and the
Detections line it drives replaces the engine verdicts with "this artifact is a
known-good binary … it is not scanned".

That was harmless only because the server emitted the feed list exclusively for
instances already in the known-good state. It no longer does: the list is now
present whenever the sha256 matches a known-good record, including on a fully
scanned instance with real detections and a PolyScore, so the fallback would have
reported such a sample as never scanned.

The state is the single reliable signal for "known-good, bytes withheld", so it
alone decides; the feed list only shapes the message. Keeps the defensive getattr
for older SDKs — one consequence, now documented: an SDK without `.state` renders
a known-good instance as an ordinary one rather than mislabelling a scanned one.
@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review — fix: key known-goodness on the instance state, not on the feed list

The core change is right: the feed list is a sha256-match signal, not a state signal, and state == 'KNOWN_GOOD' is the only thing that licenses "it is not scanned". Base is develop, no version bump — both correct. A few things need action.

1. The SDK pin is now under-floored (pyproject.toml:25)

specs/05-sdk-contract.md §Invariants: "The SDK version pin in pyproject.toml is the compatibility contract. Floor it at the lowest SDK version that exposes every method/behaviour the CLI relies on."

Before this PR, known_good_sources was a fallback, so known-good rendering worked on an SDK that predated ArtifactInstance.state. Now .state is load-bearing with no fallback — and the pin is still polyswarm_api>=4.0.0,<5.0.0. A user on an SDK in the allowed range that lacks .state gets a known-good instance rendered as an ordinary window-closed one ("No engines responded… trigger a rescan"), silently.

The PR body/spec frame that as an accepted consequence ("renders a known-good instance as an ordinary one rather than mislabelling a scanned one"). That's the right behaviour, but it isn't a substitute for the pin: the documented contract is that the floor excludes SDKs missing behaviour the CLI relies on. Please raise the floor to the SDK release that ships ArtifactInstance.state, or state explicitly in specs/03-formatters.md why the degraded rendering is preferred over a pin bump.

2. specs/04-testing.md wasn't updated, and its invariant now has two more counterexamples

specs/04-testing.md §Invariants: "Tests drive the CLI through click.testing.CliRunner — they invoke the real command tree, not internal functions."

tests/known_good_field_test.py constructs a TextOutput and calls artifact_instance() directly. The drift arrived with the earlier known-good PR, but this one adds two more tests in that style (TestKnownGoodFeedsAreNotTheSignal) and updates only 03-formatters.md. Per AGENTS.md ("If a spec is thin or missing for an area you're changing, fill it in in the same PR"), 04-testing.md should either sanction "Style 3 — pure-unit formatter rendering" and say when it's the right choice, or narrow the invariant. As written, the file reads as forbidding the tests this PR adds.

3. ## Requires is an unresolved placeholder

## Requires reads artifact-index AI_PR (the field's new emission rule). specs/05-sdk-contract.md §Coordinated changes step 2 wants the dependency linkedAI_PR is a literal placeholder; substitute the real PR link. (Naming artifact-index is fine; it's a public companion repo.)

Worth stating for the merge decision: the CLI change is correct both before and after the server-side emission change, so this isn't a merge-order blocker — the link is traceability only. Say so if that's the intent.

4. known_good_sources gating is dead logic (src/polyswarm/formatters/text.py:88)

known_good_sources = (getattr(instance, 'known_good_sources', None) or []) if is_known_good else []

known_good_sources is read in exactly two places (text.py:91, text.py:92), both inside if is_known_good and not instance.failed:. The if is_known_good else [] tail can never change an observable outcome. It's defensible as intent-documentation, but specs/03-formatters.md:63 ("It is therefore read as [] unless is_known_good") states it as a load-bearing rule, which overstates it. Either drop the ternary and keep the comment, or keep it and soften the spec sentence — as written a future reader may assume the guard protects something.

5. Missing test case: state == 'KNOWN_GOOD' with failed=True

text.py:90 guards the known-good Detections branch with and not instance.failed, and text.py:129-134 orders Status: Failed ahead of Status: Known good. That's the one branch where the flag has to lose, and nothing in the suite pins it:

def test_failed_known_good_reports_failure(self):
    text = _render(_instance(state='KNOWN_GOOD', known_good=FEEDS, failed=True,
                             failed_reason='...'))
    assert 'Detections: This scan has failed' in text
    assert 'Status: Failed' in text
    assert 'it is not scanned' not in text

Secondary: no test covers state absent + feeds present — the older-SDK path the PR body calls out as an intentional behaviour change. test_normal_instance_unchanged covers absent-state-without-feeds, so the risk is low now that is_known_good never reads the feeds, but that's precisely the assertion that would catch the fallback being reintroduced.

6. Branch name carries a ticket ID

AGENTS.md: "Don't reference ticket IDs or internal project codes in commit messages, PR titles, or PR descriptions." The branch is dn-8425-known-good-status-signal, and GitHub's default merge-commit subject is Merge pull request #262 from polyswarm/dn-8425-…, so the ID lands in develop's history on a plain merge. Override the merge commit message (or squash with a clean subject) when merging.

One open question, not a blocker

For a scanned instance whose sha256 matches a known-good record, the CLI now says nothing at all about the match — the feed list is fetched and discarded. Strictly better than the old mislabelling, but if the match is useful analyst signal, a separate non-verdict line (e.g. Known-good feeds matching this sha256: commercial, nsrl) would surface it without touching the Detections line. Out of scope here; worth a 99-open-questions.md entry if it's a real product question.

…tate

The change made `.state` load-bearing with no fallback, but the dependency floor
still allowed 4.0.0, which predates it — so a user on an in-range SDK would get a
known-good instance rendered as an ordinary window-closed one ("No engines
responded… trigger a rescan"), silently. `.state` shipped in 4.1.0, and the SDK
contract says the floor must exclude releases missing behaviour the CLI relies on.

Also: specs/04-testing.md's invariant said tests drive the CLI through CliRunner
and never call internal functions, which as written forbade the formatter-unit
tests this change adds — the spec now sanctions that style and says when it is the
right choice. specs/03-formatters.md no longer overstates the feed-list guard as
load-bearing. And two branches where the flag has to lose or be ignored are now
pinned: a failed known-good instance reports the failure, and an instance with
feeds but no state renders as an ordinary one — the assertion that would catch the
removed fallback being reintroduced.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review. The formatter change itself is right, and I checked the load-bearing assumptions rather than taking them on trust: in SDK 4.1.0 both state (self.state = content.get('state')) and known_good_sources are plain instance attributes set in ArtifactInstance.__init__, so the getattr reads, the state=None path and the del older_sdk.state in the new test all behave as the tests assume; valid_assertions is [a for a in assertions if a.mask], so 1/2 is correct; and JSONOutput.artifact_instance really does dump result.json, as specs/03-formatters.md now claims. Base is develop, no CLI version bump, Requires section present, spec updates land with the code. One actionable issue, one minor.

1. The SDK floor should be 4.2.0, not 4.1.0pyproject.toml:25

specs/05-sdk-contract.md §Version pin: "Floor it at the lowest SDK version exposing everything the CLI uses", and the second commit states the rule as excluding "releases missing behaviour the CLI relies on". 4.1.0 does expose .state, but it is still below what this CLI needs today:

  • src/polyswarm/client/report.py:131 calls api.llm_report_download(...). Through 4.1.0 that method builds the download request from report.download_url, which ReportLLMPostProcessing never sets — the call raises AttributeError before any HTTP happens. Fixed only in 4.2.0 ("llm_report_download() now reads the report's url field, fixing an AttributeError from the previously-referenced non-existent download_url" — polyswarm-api#314, which even carried an explicit NOTE (issue 4) docstring saying so).
  • Same release: llm_report_create only started sending the client's community (report.py:109 depends on it), and streaming downloads started raising NoResultsException on a 204 instead of silently writing an empty file — the CLI's documented exit-code-1 no-results contract for polyswarm download (client/download.py:26).

polyswarm-api#314 "Release 4.2.0" merged to the SDK's master on 2026-07-23, so >=4.2.0,<5.0.0 is installable. Since this PR is the one that touches the pin — and argues the floor must exclude releases missing relied-on behaviour — raising it to 4.2.0 here is the consistent move; if you would rather keep the PR scoped to state, the llm_report_download gap needs its own issue, because a user on an in-range 4.1.0 hits it as a hard traceback.

2. Minor — the assertion loop is not gated by is_known_good (src/polyswarm/formatters/text.py:113)

The for assertion in instance.assertions loop runs unconditionally, so an instance arriving with state == 'KNOWN_GOOD' and assertions would render "…it is not scanned." immediately followed by per-engine verdicts. Unreachable if the server never pairs the two — but "the server only emits X together with Y" is exactly the assumption this PR just found to be wrong for known_good. test_scanned_instance_with_feeds_reports_its_detections pins the SETTLED direction; nothing pins this one.

Nothing else: the known_good_sources ternary is inert today (the list is only read inside the known-good branch), which specs/03-formatters.md already says out loud, and the existing test_search_hash_with_text_output cassette satisfies the new Style 3 rule that a Style-3-covered command still needs one CliRunner test reaching the formatter.

…ample is unscanned

Two behaviours the CLI relies on arrived in 4.2.0, not 4.1.0: llm_report_create
only started sending the client's community then (and `report llm-create` does not
pass one itself, so on 4.1.0 a private-community report is created without it), and
streaming downloads only started raising NoResultsException on a 204 then, which
the download command's no-results exit code depends on.

The assertion loop was not gated by the known-good flag, so an instance carrying
that state *and* assertions rendered "it is not scanned" immediately followed by
per-engine verdicts. That pairing is no longer hypothetical: the server now
reconciles a previously scanned instance into the known-good state while
deliberately preserving its assertions, polyscore and detections, so it is exactly
what a client receives. The rendering now says both things without contradicting
itself — the bytes are withheld, and here are the results we already had.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review — keying known-goodness on the instance state

The core change (is_known_good = state == 'KNOWN_GOOD') is right, and the feeds-are-not-the-signal tests pin it well. Five things need action, roughly in severity order.

1. A known-good instance with malicious detections now renders green.
text.py:95-101 builds the whole known-good Detections line through self._green, including the new N/M engines reported malicious clause, and text.py:137 still emits a green Status: Known good. The ordinary branch (text.py:107-110) renders the same count red when malicious_assertions is non-empty.

So the scenario in the TL;DR — a fully scanned malicious sample whose hash is catalogued — still gets a benign-coloured presentation once the server reconciles it to KNOWN_GOOD: Detections: This artifact is a known-good binary (flagged by: nsrl); 40/50 engines reported malicious. in green, above Status: Known good. specs/03 asserts this is deliberate ("The line stays green both ways"), but green on a 40/50 line is a weaker signal than the pre-PR code gave for that same instance, and it is the same class of mis-signal this PR is fixing. Either colour the line with self._red when malicious_assertions is non-empty (and update the spec paragraph), or state in the spec why withheld bytes outrank a majority-malicious verdict.

Related coverage gap: no test can observe the colour. Every case goes through TextOutput(color=False) plus click.unstyle, so the green/red decision — the whole point of the known-good rendering per specs/03 §Known-good artifact instances ("rendering a green … signal") — is unpinned. One TextOutput(color=True) case asserting the ANSI wrapper on the malicious-plus-known-good line would settle both this and the ambiguity above.

2. del older_sdk.state reaches into SDK internals (tests/known_good_field_test.py:153).
specs/05 invariant: "The CLI consumes only the SDK's public surface … It does not reverse-engineer private internals." Deleting an attribute off a parsed ArtifactInstance depends on state living in the instance __dict__; the day the SDK derives it (property over bounty_state, descriptor, __slots__) this test errors with AttributeError: state for a reason unrelated to the CLI.

It also pins a configuration this PR explicitly declares unsupported — specs/03:97-99: "That degradation is belt-and-braces, not a supported configuration." The no_state half of the loop already covers the real case (server omits state). Drop the del half, or stand up a minimal stub instead of mutating an SDK resource.

3. The SDK floor bump is undisclosed and unverified (pyproject.toml).
polyswarm_api>=4.0.0>=4.2.0 appears in neither the TL;DR nor ## Changes nor ## Requires, and specs/03:99 states the known-good attributes ship in 4.1.0 — so this fix does not need it. Two things to confirm before merge:

  • Is 4.2.0 published to PyPI? If not, pip install polyswarm-cli breaks for every consumer the moment this reaches master. No SDK PR is linked under ## Requires to indicate otherwise.
  • Does the SDK's develop archive declare >= 4.2.0? .gitlab-ci.yml does pip install $ARCHIVE/$CI_COMMIT_BRANCH.zip || …/develop.zip and then pip install .[tests]. If the archive build reports a lower version, that second install silently upgrades the SDK from PyPI over the archive build — CI stops testing SDK develop, which is the mechanism specs/05's invariant ("CI installs the SDK from the SDK repo's branch archive") and §Coordinated changes rest on. The new specs/05:72 ("A pin change is a one-file change") glosses over exactly this interaction; worth a sentence there.

If the floor genuinely needs raising for the report-community / 204-streaming reasons the new §Current floor documents, that is a legitimate fix — but it is a separate concern from known-goodness and should at least be called out in the description.

4. The PR body understates the change.
The N/M engines reported malicious rendering is a second, user-visible behaviour change, and ## Changes describes only the flag derivation. The AGENTS.md, specs/04, specs/05 and pyproject.toml edits are also unlisted. The specs themselves are updated correctly — it is the description that is incomplete, and on a public repo that is the review artefact.

5. window_closed is not consulted in the new branch.
Every other Detections branch guards on instance.window_closed (text.py:107,113). A KNOWN_GOOD instance with valid assertions and an open window now renders a final-looking 1/2 engines reported malicious. where an ordinary instance would say "This scan has not finished running yet." Probably unreachable if reconciliation only happens post-settlement — if so, one line in specs/03 §"A known-good instance that also carries results" saying that, so the missing guard reads as intentional.

Clean

  • Base is develop; the CLI's own version is untouched (AGENTS.md §Gitflow).
  • JSONOutput correctly needs no change — it dumps .json, which carries raw state/known_good.
  • Style 3 in specs/04 is well-scoped, and its "still needs at least one CliRunner test proving the command reaches the formatter" requirement is already met by tests/cli_test.py's text-format search hash / scan file cases.
  • test_known_good_with_only_non_responding_engines_is_still_not_scanned keys on valid_assertions rather than assertions — right call.

Review follow-ups on the known-good rendering:

- A known-good instance carrying malicious detections rendered the whole
  Detections line green, including the "N/M engines reported malicious"
  clause the ordinary branch renders red. Green on 40/50 is a weaker
  warning than the same instance gave before it was reconciled, which is
  the class of mis-signal this rendering exists to fix. Red when there
  are malicious assertions and the window is closed; green otherwise.
- The counts are now guarded on window_closed like every other branch, so
  an open window's numbers are never presented as final.
- Pin the colour decision against TextOutput(color=True): every existing
  test unstyles its output, so the green/red choice was unobservable.
- Drop the `del instance.state` half of the no-state test — it mutates an
  SDK resource's internals, which specs/05 forbids, and pins a
  configuration specs/03 declares unsupported. The server-omits-state
  case it shared covers the real behaviour.
- Document the floor's two preconditions (published on PyPI, and declared
  by the SDK's develop archive) — both verified for 4.2.0.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review — clean against the documented conventions

Checked the diff against AGENTS.md and specs/03-formatters.md, 04-testing.md, 05-sdk-contract.md. No correctness, spec-drift, downstream-contract or gitflow defects found. (No code spans below — the review tooling here rejects backticks.)

Verified specifically:

  • Logic. is_known_good keys on .state only; the Detections branch stays gated on 'not instance.failed' and the Status block still orders Failed ahead of Known good, so a failed known-good instance cannot render "not scanned" — pinned by test_failed_instance_reports_the_failure_not_known_good. The three-way clause switch is on valid_assertions, not assertions, so all-declined engines still read "it is not scanned" — also pinned. known_good_sources is read nowhere else in the tree (client/kgb.py and client/search.py hit different endpoints), so dropping the fallback leaves no second call site keyed on the feed list.
  • No cassette drift. No tests/vcr/*.vcr instance response carries known_good, so no .click expectation depended on the removed fallback.
  • Formatters. JSONOutput and the hash formatters dump .json / .sha256 and need no change, as specs/03 says.
  • Gitflow. Base is develop; pyproject.toml's own version is untouched — only the SDK floor moved, which is what AGENTS.md §Gitflow requires of a feature PR.
  • Specs updated in the same PR for every area touched, including the 04-testing.md invariant that as written previously forbade the Style-3 tests this change relies on.

Two minor items, both optional:

  1. specs/03-formatters.md:100 — "Status: Known good" staying green while the Detections line goes red is stated as a rule, but no test observes it: _detections_line() filters the styled render down to the Detections line only. One extra assertion in test_malicious_detections_outrank_the_known_good_colour — click.style('Status: Known good', fg='green') in the styled output — would pin it.
  2. src/polyswarm/formatters/text.py:88 — the trailing 'if is_known_good else []' is a no-op, as specs/03-formatters.md:63-66 concedes ("the ternary cannot change what renders today"). Fine to keep as a statement of the coupling, but the spec spends more words defending it than the guard costs; an inline comment would carry the same intent more cheaply.

One precondition I could not verify from this checkout (no network access in the review environment): the two your own new specs/05 §Version pin bullet adds — polyswarm-api 4.2.0 published on PyPI, and the SDK's develop declaring at least 4.2.0. Your commit message says both were checked; worth a second pair of eyes before merge, since the failure mode (CI silently installing PyPI's SDK over the branch archive) is invisible.

…t goes red

specs/03 states it as a rule; _detections_line() filters the styled render down
to the Detections line, so nothing observed it.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Reviewed against AGENTS.md and specs/03, 04, 05. The core change is correct: state alone gating is_known_good, the three-way results clause, and the and instance.window_closed conjunct on the red/green choice all hold up; malicious_assertions ⊆ valid_assertions keeps the colour condition and the count condition in step, and no tests/vcr/*.click renders an artifact_instance this would shift. Gitflow is clean — base develop, the CLI's own version untouched, no ticket IDs or AI trailers in the commits. specs/04's new Style-3 rule ("still needs at least one CliRunner test") is satisfied by test_search_hash_with_text_output / test_submission_lookup_text. Three things to fix, all small.

1. TextOutput(color=True) is not what makes the colour observable — self.color is never read. src/polyswarm/formatters/text.py:909-926: _green/_red/_white call click.style(...) unconditionally; self.color is assigned at text.py:33 and again at :35, and consulted nowhere. So _render_styled and _render construct formatters that emit identical strings — the new tests pass because _render calls click.unstyle and _render_styled doesn't, not because of the color= argument.

Two places state the wrong mechanism:

  • specs/03-formatters.md: "The colour decision is invisible to a test that unstyles its output, so it is pinned directly against TextOutput(color=True)."
  • tests/known_good_field_test.py:51-53 docstring: "Keeps the ANSI wrapper … every other test here unstyles".

The second half of that docstring is right; the color=True framing in both is not. Worth correcting so nobody later "fixes" _render_styled to color=False and assumes the colour assertions still mean anything.

Adjacent and pre-existing, but this PR is the one that promotes colour to a documented spec rule: because self.color is dead, --no-color is a no-op for text output (client/polyswarm.py:197,229 threads it in; JSONOutput at json.py:189-193 does honour it), while specs/03 still says TextOutput "honours --color/--no-color". Click strips ANSI on a non-tty, which is why this hasn't bitten anyone. Separate fix, but that spec claim is currently false.

2. The spec header says "majority-malicious"; the code reddens on any malicious assertion. specs/03-formatters.md: "Colour: a majority-malicious verdict outranks the withheld-bytes signal." The body sentence right after it is accurate ("except when the instance has malicious assertions and a closed window"), and text.py:111 is if instance.malicious_assertions and instance.window_closed — so 1/50 goes red. One word, but as written the header would mislead anyone implementing to the spec, and the "40/50" example two sentences later reinforces the wrong reading.

3. Test gap: the open-window colour is not pinned. test_open_window_does_not_present_counts_as_final renders through _render, i.e. unstyled, so state=KNOWN_GOOD + non-empty malicious_assertions + window_closed=False carries no colour assertion. That is exactly the combination where the and instance.window_closed conjunct in the colour check is load-bearing: drop it and that line turns red while reading "its scan has not finished running yet", and the whole suite still passes. test_malicious_detections_outrank_the_known_good_colour and test_clean_known_good_stays_green only cover the closed-window pair. One _detections_line(_render_styled(...)) == click.style(..., fg='green') on that instance closes it.

Nothing else. The known_good_sources ternary is dead by construction, but specs/03 explicitly argues for keeping it as a statement of the coupling. The >=4.2.0 floor rationale in specs/05 matches .gitlab-ci.yml (archive install followed by pip install .[tests], so a below-floor archive really does get silently overridden from PyPI) and client/report.py:109 passing no community of its own; the two SDK-internal claims behind the floor aren't checkable from this repo, so I'm taking the PR's verification at its word.

Review follow-ups.

`self.color` was assigned twice in __init__ and read nowhere: every
_white/_red/_green helper called click.style() unconditionally, so
`--no-color` was a no-op for text output while JSONOutput honoured it and
specs/03 claimed TextOutput did too. Masked in practice because click strips
ANSI when stdout is not a tty. All five helpers now go through one `_paint`,
which is the only place the flag is read.

That also corrects what this PR's own colour tests pin: it is the absent
click.unstyle, not the `color=` argument, that made the green/red decision
observable. The spec and the helper docstring said otherwise.

Also:
- The spec header said "majority-malicious" while the code reddens on any
  malicious assertion — the same threshold the ordinary branch uses.
- Pin the open-window colour. That is the one combination where the
  `and window_closed` conjunct is load-bearing: the instance has a malicious
  assertion, so dropping it turns the line red while it reads "its scan has
  not finished running yet", and every existing test still passed.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Reviewed against AGENTS.md and specs/03, /04, /05. Gitflow is clean — base develop, no [project] version touched. The new specs/05 §Current floor checks out: .gitlab-ci.yml:28-29 really does install the SDK archive and then pip install .[tests], and client/report.py:109 really passes no community to llm_report_create. Two things worth acting on.

1. state is now load-bearing with no fallback, and nothing in the suite pins its wire shape.
No recorded response under tests/vcr/ carries a state key on an artifact-instance payload — window_closed, polyscore, assertions are all there, state and bounty_state are not. So KNOWN_GOOD is only ever compared against a dict known_good_field_test.py writes itself, and Style 3 by construction cannot see the transport boundary (specs/04: "Argument parsing, SDK calls, generator consumption … are command behaviour: a formatter unit test cannot observe them"). If artifact-index #1919 serialises that field under a different key, or with a label other than the exact string KNOWN_GOOD, is_known_good is permanently False and a withheld artifact renders Detections: No engines responded to this scan. You can trigger a rescan now. — the precise bug this branch exists to prevent, with the feed-list fallback (the last thing that would have masked it) now removed. One cli_test.py cassette over a known-good hash closes it; short of that, state in the PR that the key and the label were read off #1919 serializer rather than inferred.

2. The three-way switch assumes a reconciled row keeps window_closed: true, and the tests cannot check that.
text.py:95 and text.py:111 gate both the counts and the red on instance.window_closed, and specs/03 defends the open-window branch as "[u]nreachable through the server reconciliation (it moves only STORED rows, which carry no assertions, and SETTLED ones, which have a closed window)". That is a claim about artifact-index behaviour asserted in a CLI spec, and _instance() sets window_closed by hand — so a real payload of state: KNOWN_GOOD + window_closed: false + preserved malicious assertions would render green "its scan has not finished running yet" with nothing in the suite failing. Green on a sample with a malicious detection is the same mis-signal class the colour rule was added to fix, so this is the one assumption in the PR I would want confirmed against #1919. If a reconciled row can carry an open window, redden on malicious_assertions alone and keep only the text guarded on the window.

Non-blocking: _paint is a real fix (self.color was assigned twice and read nowhere), but it changes text output for every command when --no-color is passed on a tty — worth a release-note line on the eventual develop → master PR, since it is unrelated to known-goodness. TestColorFlag also lives in a module whose docstring scopes it to the known_good field.

…inned

`state` is load-bearing with no fallback, and a Style-3 formatter test cannot
see the transport boundary. Rather than leave that inferred: the key and the
label were read off the server's ArtifactInstanceSerializer
(`'state': instance.state.name`, and BountyState.KNOWN_GOOD.name is exactly
'KNOWN_GOOD'), and the thing that would catch a future rename is the server's
own suite — this repo replays frozen cassettes with no VCR-off e2e job, so a
recorded body would keep replaying the old shape.

Same treatment for the open-window branch: the claim that reconciliation
cannot produce one is now stated against the mechanism it rests on
(RECONCILABLE_STATES, and SETTLED requiring window_closed), with the fix to
apply if that ever stops holding.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review

The known-good rendering change is correct and matches the specs it updates. is_known_good = state == 'KNOWN_GOOD' with known_good_sources demoted to message-shaping is the right fix; the three-way results clause and the malicious_assertions and window_closed colour rule line up with the ordinary window-closed branch's own threshold (malicious ⊆ valid is already assumed by the N/M render at text.py:120, so the colour and text guards can't disagree). Gitflow is clean — base develop, no CLI version bump.

Four things that need action, roughly by severity.

1. --no-color has no CliRunner test — and it's the flag, not the painting, that was broken

tests/known_good_field_test.py:189 (TestColorFlag) constructs TextOutput(color=False) directly. But the bug this PR fixes is that the option was a no-op, and the wiring it travels through is formatters[output_format](color=color, output=output_file) (client/polyswarm.py:229). This PR's own amendment to specs/04-testing.md says exactly that this can't be a Style-3 test:

Argument parsing, SDK calls, generator consumption, ctx.obj wiring and exit codes are command behaviour: a formatter unit test can't observe them, so those need Style 1 or Style 2.

TestColorFlag pins _paint, not --no-color. Nothing in the suite would catch color being dropped from that constructor call. It is testable — CliRunner.invoke takes color=True, which stops click stripping ANSI off the non-tty:

res_color   = CliRunner().invoke(polyswarm_cli, ['--color',    'search', 'hash', SHA], color=True)
res_nocolor = CliRunner().invoke(polyswarm_cli, ['--no-color', 'search', 'hash', SHA], color=True)
assert '\x1b[' in res_color.output and '\x1b[' not in res_nocolor.output

Any existing search hash mock/cassette works as the vehicle.

2. --no-color is still a no-op for log output

setup_logging(verbosity) (client/polyswarm.py:95) never receives color, and NamedColorFormatter.format calls click.style(...) unconditionally at line 108. On a tty, polyswarm --no-color -v ... still emits a green info [polyswarm]: prefix. specs/03-formatters.md scopes its claim to TextOutput, so this isn't drift — but the PR bills itself as "honour --no-color", and half of it is still masked by the same non-tty stripping the commit message calls out. Either thread the flag into setup_logging, or say in the spec that the flag governs formatter output only.

3. The 4.2.0 floor precondition — was the archive's declared version checked, dev suffix included?

The new specs/05-sdk-contract.md bullet is a good catch, and the hazard it describes has a sharper edge than stated: PEP 440 orders 4.2.0.dev1 < 4.2.0, so if the SDK's develop head carries a dev/pre-release suffix (plausible — this repo's own pyproject.toml has [tool.bumpversion.parts.dev]), then pip install .../develop.zip followed by pip install .[tests] resolves >=4.2.0 against PyPI and replaces the archive build. Silent, and CI stops testing the SDK branch entirely. The PR body says develop "declares 4.2.0" — please confirm that was read off the archive build's version string, not the last release tag.

4. Ticket ID will land in git history via the branch name

AGENTS.md §Commit + PR hygiene:

Don't reference ticket IDs or internal project codes in commit messages, PR titles, or PR descriptions. This repo is public; published artefacts shouldn't leak internal references.

The commits and title are clean, but the branch is dn-8425-known-good-status-signal, and a merge commit's default message is Merge pull request #262 from polyswarm/dn-8425-... — the ID ends up permanently in public history. Squash-merge with a rewritten subject, or rename the branch before merge. Worth extending the AGENTS.md rule to branch names either way.

Non-blocking

  • known_good_sources = (...) if is_known_good else [] (text.py:87) is dead by construction, and specs/03 says so ("the ternary can't change what renders today"). Fine as a documented statement of coupling; just noting it is not load-bearing.
  • specs/03-formatters.md:23 — "so a new one cannot style unconditionally" overstates it. Nothing stops a new helper from calling click.style directly; _paint is a convention, not an enforcement point.
  • test_known_good_with_results_reports_them_instead_of_not_scanned asserts 'PolyScore: 0.9' in text, which passes only as a prefix of the :.20f render (0.90000000000000002220). Harmless, but the assertion is weaker than it reads.

…o end

Review follow-ups.

- The previous commit made TextOutput read self.color, but setup_logging never
  received the flag, so `polyswarm --no-color -v …` still emitted a green log
  prefix on a tty — the same half-honoured flag, one layer over. Threaded
  through, and the spec now states the flag's scope.
- TestColorFlag pinned _paint, not the option: the flag travels through
  `formatters[output_format](color=color, …)` in the command group, which a
  formatter unit test cannot observe (specs/04 says so itself). Added a
  CliRunner test with color=True, which stops click stripping ANSI off the
  non-tty capture so the two runs differ only in the flag.
- specs/03 claimed _paint means "a new helper cannot style unconditionally".
  It is a convention, not an enforcement point — reworded.
- specs/05: the floor precondition needs the version read off the archive's own
  tree, because PEP 440 orders 4.2.0.dev1 < 4.2.0 and the SDK has a bumpversion
  dev part. Both files on origin/develop say 4.2.0 with no suffix.
- Anchor the PolyScore assertion on the parsed line: 'PolyScore: 0.9' also
  matches 0.95, and the :.20f render is not the literal '0.9'.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review — PR #262

Base is develop, no CLI version bump, ## Requires present: gitflow clean. The known-good logic itself is correct and consistent with the updated specs/03-formatters.mdstate alone decides, the feed list only shapes the message, not instance.failed still outranks it (both Detections and Status), the results clause matches the ordinary window-closed branch's count and reddening threshold, and JSONOutput genuinely needs nothing (it dumps .json). No other call site treats the feed list as a signal — grep known_good finds only kgb.py / known_good(), a different resource.

Four things worth acting on, none of them in the known-good rendering.

1. Undisclosed second change: --no-color was a no-op for text output, and this PR fixes it. formatters/text.py gains _paint (previously self.color was assigned and never read) and client/polyswarm.py:95 gains setup_logging(verbosity, color=True) for the log prefix. That is a real user-visible behaviour change across two files the PR body's Changes section never mentions — it lists only formatters/text.py, pyproject.toml, the specs and known_good_field_test.py; client/polyswarm.py and cli_test.py don't appear. Landing it alongside a PR whose whole point is a red-vs-green decision is defensible (the colour rule is unenforceable while the flag is dead), but AGENTS.md §Commit + PR hygiene wants small, independently reviewable scoped changes. Either split it out or state it in the description — a reviewer reading the TL;DR won't know --no-color behaviour changed.

2. Test gap: the log-prefix half of that fix is untested. test_color_flag_reaches_the_text_formatter runs at default verbosity, so log_level is WARNING (client/polyswarm.py:128) and no record ever reaches NamedColorFormatter — the assertions only observe the formatter path. The new color= branch at client/polyswarm.py:112-113 is never executed by the suite. Per specs/04-testing.md ("argument parsing, the SDK call, the wiring … exercise the real command tree"), the missing case is concrete: -v --no-color over a cassette asserting no \x1b[ on the Running polyswarm-cli version … info prefix, plus the --color counterpart asserting there is one.

3. tests/known_good_field_test.py:50-55_render_styled's docstring is now wrong. "It is the absent click.unstyle, not the color= argument, that makes the difference; color=True … is passed only for emphasis" described the code before _paint. color=True is now load-bearing: flip it to False and every styled assertion in test_malicious_detections_outrank_the_known_good_colour / test_clean_known_good_stays_green / test_open_window_does_not_present_counts_as_final fails. Both are required; the comment tells a future editor one of them is decorative. specs/03-formatters.md inherits the same half-statement ("read without click.unstyle, which is what _render_styled … exists for").

4. tests/cli_test.py:326 — the borrowed cassette couples two tests through the record workflow. @vcr.use_cassette('test_search_hash_with_text_output', allow_playback_repeats=True) is neither a cp nor a hand-edit, so no specs/04 invariant breaks, but unittest orders methods alphabetically and test_color_flag_… sorts ahead of test_search_hash_with_text_output. So the documented re-record path (delete the .vcr, run against a live stack) has the colour test author the owner test's cassette, with two duplicate GETs in it. Cheap fix: give it its own cassette name, or record the ownership in the comment so whoever re-records isn't surprised.

Minor: the branch name carries an internal ticket ID (dn-8425-…). AGENTS.md scopes that prohibition to commit messages, PR titles and descriptions — all clean here — but a develop merge commit usually embeds the branch name, which lands it in public history anyway. Squash-merge or rename.

Not flagged, for the record: the >=4.2.0 floor. The new §Current floor in specs/05-sdk-contract.md states both preconditions correctly against .gitlab-ci.yml's archive-then-pip install .[tests] ordering, and the .devN-suffix reasoning is right. I could not verify from this environment that 4.2.0 is on PyPI or that the SDK's develop declares an unsuffixed 4.2.0; the PR says both were checked, and if either is false CI silently stops testing the SDK branch (or pip install polyswarm-cli breaks at master). Worth one confirming glance before merge.

Review follow-ups.

- The colour test ran at default verbosity, so the level was WARNING and no
  record ever reached NamedColorFormatter: the log-prefix half of the fix was
  untested. Added the -v pair.
- Both colour tests now mock `Polyswarm.search_hashes` (specs/04 Style 2)
  instead of replaying a cassette. The response content is irrelevant to
  whether the flag reaches the renderer, so a cassette would have to be
  recorded against a live stack for a test that never exercises the server —
  and borrowing another test's cassette coupled the two through the re-record
  path, since unittest orders methods alphabetically and this one sorts first.
- `_render_styled`'s docstring said `color=True` was "passed only for
  emphasis". That was true before `_paint`; it is now load-bearing, and saying
  otherwise invites a future editor to drop it and silently disarm every colour
  assertion. Corrected in the docstring and in specs/03.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review

Correctness of the known-good rendering itself looks right, and it matches specs/03-formatters.md as rewritten: is_known_good keys on state alone, known_good_sources is read only inside the branch, the three-way results clause and the malicious_assertions and window_closed colour rule are exactly what the spec now states, and the not instance.failed / Status: Failed ordering is preserved. Gitflow is fine — base is develop, no CLI version bump, and the SDK floor is a dependency pin (specs/05 §Version pin: "Bumping the pin is a normal code change"), not a release step. The Requires link is present.

Two things to fix.

1. The new colour tests do not mock at the SDK boundary (tests/cli_test.py)

_run_color_pair patches polyswarm.polyswarm.Polyswarm.search_hashes, and the _one_instance docstring cites this as "specs/04 Style 2". It is not: specs/04-testing.md defines that style as mock.patch("polyswarm_api.api.PolyswarmAPI.<method>"), with the invariant "Mock at the SDK boundary, or replay HTTP with VCR … The CLI's own code is exercised either way." Polyswarm.search_hashes is CLI code (src/polyswarm/polyswarm.py:161) — patching it cuts out utils.parallel_executor_iterable_results, so the run no longer exercises the fan-out the SDK boundary would leave in place.

The seam one level down works identically here: patch polyswarm_api.api.PolyswarmAPI.search with return_value=iter([self._one_instance()]) (one hash, one call). Same assertions, correct boundary, and the docstring's spec citation becomes true. Either that, or drop the citation and say why the deviation is warranted.

2. JSONOutput does not honour --no-colorPrettyJSONOutput does

Both specs/03-formatters.md §"Scope of --no-color" ("It governs the text formatter (via _paint), JSONOutput, and the log prefix") and the _paint docstring ("because JSONOutput does honour the flag") name the wrong class. JSONOutput (formatters/json.py) has no __init__, never stores color, and emits plain click.echo(self._to_json(...)) — there is no colour there to honour. The flag is read by PrettyJSONOutput.__init__ / _to_json (json.py:183-194), which is what routes through ClickFormatter. Worth correcting in both places, since the stated rationale for _paint rests on that contrast ("--no-color was a no-op for text output while JSONOutput honoured it").

Notes, no action needed

  • The --no-color fix and the 4.2.0 floor are independent of the known-good change; they are in their own commits, which satisfies AGENTS.md §"Small, scoped commits". Squash-merging (as the description recommends) collapses that — renaming the branch and taking a merge commit keeps the three independently revertable, if that matters to you.
  • The floor's two preconditions are documented and claimed verified; PyPI and the SDK archive are unreachable from this environment, so that one is on the merger to re-confirm at merge time.

Review follow-ups.

- _run_color_pair patched Polyswarm.search_hashes, which is CLI code, so it cut
  utils.parallel_executor_iterable_results out of the run — and the docstring
  cited it as "specs/04 Style 2", which defines that style as patching
  polyswarm_api.api.PolyswarmAPI.<method>. Now patches PolyswarmAPI.search: same
  assertions, correct seam, and the citation is true.
- specs/03 and _paint's docstring both credited JSONOutput with honouring
  --no-color. It has no __init__ and emits plain JSON; PrettyJSONOutput is the
  one that reads the flag. The contrast is the stated rationale for _paint, so
  naming the wrong class undercut it.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review

Clean against the documented conventions on gitflow (base develop, no pyproject.toml version bump, ## Requires present, no ticket IDs in the title/body/commits) and on specs/03/04/05 — the spec updates land in the same PR as the code, as AGENTS.md requires. Three things worth action, none blocking.

1. The known-good colour predicate does not match the text predicate — src/polyswarm/formatters/text.py:95-114

The text switches on len(instance.valid_assertions) > 0 and instance.window_closed; the colour switches on instance.malicious_assertions and instance.window_closed, with no valid_assertions conjunct. The ordinary branch's red check (text.py:119-124) is nested inside valid_assertions > 0 and window_closed, so specs/03's "the same threshold and the same colour the ordinary branch gives that count" is not quite what the code does.

Concretely: an instance whose only assertion has verdict=True, mask=False renders Detections: This artifact is a known-good binary; it is not scanned. in red — if malicious_assertions is not a strict subset of valid_assertions in the SDK. Worth confirming; the guard is free either way if the colour is decided in the same if/elif that picked the clause rather than in a second, differently-shaped condition. Two conditions that must agree but are written differently is the drift risk, independent of today's SDK semantics.

test_known_good_with_only_non_responding_engines_is_still_not_scanned deliberately uses verdict=None, mask=False, so it does not reach this case.

2. The floor precondition names the wrong archive — specs/05-sdk-contract.md

The new Current floor bold line is right ("must be satisfied by the SDK archive CI installs, and by PyPI"), but it then narrows the precondition to "the SDK's develop declares at least that version". .gitlab-ci.yml:28 installs $POLYSWARM_API_ARCHIVE/$CI_COMMIT_BRANCH.zip || .../develop.zip — while a paired SDK branch exists, the branch archive is what gets installed, and pip install .[tests] on the next line will silently replace it from PyPI if that branch's own pyproject.toml declares < 4.2.0 (or carries the .devN suffix the spec correctly warns about). Since this PR resolves an SDK branch by $CI_COMMIT_BRANCH, the check needs to be on that branch's tree, not only develop. Reword the precondition to "the branch archive CI resolves (falling back to develop)".

3. --no-color reaching setup_logging is documented in the wrong spec

specs/03-formatters.md documents setup_logging(verbosity, color=...), but 03's Scope is output rendering and client/polyswarm.py belongs to specs/01-architecture.md, which lists --color/--no-color among the global options (line 19) without noting it now also governs the log prefix. A one-line cross-reference in 01 would put it where a reader of the group module looks.

Not flagged

  • Bundling the --no-color fix is justified as written — the colour rule specs/03 now states is unenforceable while the flag is dead. It is a user-visible behaviour change to every text command, and the PR body already routes it to a release note; AGENTS.md Gitflow puts the CLI's own version bump at the develop -> master step, so leaving pyproject.toml's version alone here is correct.
  • _run_color_pair patching polyswarm_api.api.PolyswarmAPI.search is the right seam — Polyswarm.search_hashes (src/polyswarm/polyswarm.py:161-172) still runs, so utils.parallel_executor_iterable_results stays in the path, and the specs/04 Style 2 citation is accurate.
  • No cassette pins the state wire shape, but specs/03 says so explicitly and points at where it is verified; with no VCR-off e2e job here, a recorded body would replay a stale shape after a rename, so that is the correct call rather than a coverage gap.
  • Grepped for other unconditional styling: click.style/secho now appear only in _paint, setup_logging, and ClickFormatter (reached only via PrettyJSONOutput when the flag is on), so specs/03's scope statement is complete.
  • I could not verify from this checkout that 4.2.0 is on PyPI or what the paired SDK branch declares — taking the PR body's verification at its word.

Merge

Squash-merge, per the PR body: the branch name carries an internal ticket ID and a default merge commit would embed it in this public repo's history.

Review follow-up. The text keyed on `valid_assertions and window_closed`; the
colour keyed on `malicious_assertions and window_closed`. Two differently-shaped
conditions that had to agree, held together only by the SDK guaranteeing
malicious_assertions is a subset of valid_assertions (both filter on `mask`, one
additionally on `verdict`). It is a subset today — verified — so there is no live
defect, but a rendering rule in this repo should not depend on a property of
another repo's resource class to stay coherent.

Each branch now names its own paint function, so the count branch is the only one
that can redden and the clause it renders is the one being coloured.
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review — PR #262

Checked the diff against AGENTS.md and specs/03-formatters.md, 04-testing.md, 05-sdk-contract.md.

The rendering change is correct. is_known_good keys on state alone; known_good_sources is only read inside the known-good branch; the three-way results clause and the red/green choice are decided in the same if/elif, so the text and the colour cannot disagree; not instance.failed still gates the Detections branch and Status: Failed still precedes Status: Known good. _paint is the single read of self.color, and no other module reaches for the colour helpers (grep over client/, utils.py, polyswarm.py is empty), so --no-color is now honoured for every text-rendered resource, and setup_logging(verbose, color=color) closes the log-prefix half. No cassette under tests/vcr/ carries a state key on an artifact-instance payload, so no .click expectation shifts. Gitflow is clean: base develop, [project] version untouched, conventional commit prefixes, no ticket IDs or AI-attribution trailers.

One thing needs action.

1. ## Requires is missing the paired SDK PR — or the branch-name rationale is stale

The Merging section asserts a paired, unlanded SDK branch:

The branch name itself is load-bearing while the PR is open: CI resolves the paired SDK archive by $CI_COMMIT_BRANCH, so it has to match the SDK branch until both land.

specs/05-sdk-contract.md §Coordinated changes is explicit about what that obliges — step 2: "Open the CLI PR with a ## Requires section linking the SDK PR." Step 3: "The CLI PR must not merge until the SDK surface it depends on is on the SDK’s develop (or released)…"

## Requires links only artifact-index#1919. So one of two things is true, and both need a change:

  • A polyswarm-api branch is genuinely being consumed — link its PR under ## Requires per step 2 and state the merge ordering. As it stands a reviewer cannot see what CI is actually installing, which is the exact opacity step 2 exists to close.
  • It isn’t — and the new §Version pin text says as much: the floor was verified against origin/develop (version = "4.2.0" / __version__ = 4.2.0, no suffix), so develop.zip already satisfies >=4.2.0 and the .gitlab-ci.yml:28 fallback is sufficient. Then the branch name is not load-bearing, and the right fix for the public-history concern is to rename the branch and drop the internal ID outright rather than rely on squash-merge discipline — the ref is visible in this public repo regardless of how it merges.

Worth resolving before merge, because the second reading also changes the merge instruction the PR gives.

@sbneto

sbneto commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

This one ran against an earlier revision of the description — the current body already takes reading B, and explicitly.

## Requires has linked polyswarm-api#316 since before this review, framed as "traceability, not a dependency," precisely so a reviewer can see what CI installs (step 2's purpose) without implying a merge order that doesn't exist. And the Merging section already says the earlier revision overstated the branch name, that nothing here requires it, and that renaming is therefore a valid way to close the public-history concern outright.

Re-verified the premise rather than taking the body's word for it, since it's the whole basis for reading B: SDK origin/develop carries both symbols this PR consumes — known_good_sources (resources.py:275) and ArtifactInstance.state (:281) — at version = "4.2.0", satisfying the >=4.2.0 floor. The diff against origin/develop uses known_good_sources (6×) and .state (4×) and nothing else from the SDK; KnownGoodWithheldException, the only surface unique to #316, is unused here. So the develop.zip fallback at .gitlab-ci.yml:28 is sufficient and the same-named branch install is a convenience.

One correction on the rename remedy: a ticket ID in a branch ref is explicitly permitted by the workspace convention for these public repos — what must stay clean is the published commit/PR text, which is why the ID would only become a problem via a default merge commit's subject. Hence squash-merge, with rename as the equally valid alternative. Leaving the choice with the maintainer.

No code change needed; 118 passed, CI green.

@sbneto
sbneto merged commit cb019be into develop Aug 3, 2026
2 checks passed
@sbneto
sbneto deleted the dn-8425-known-good-status-signal branch August 3, 2026 15:40
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