Skip to content

CR-DIST-06 part 2: guard that every referenced listing asset exists - #249

Merged
harishquantamix merged 2 commits into
masterfrom
cr-dist-06-assets-public
Aug 3, 2026
Merged

CR-DIST-06 part 2: guard that every referenced listing asset exists#249
harishquantamix merged 2 commits into
masterfrom
cr-dist-06-assets-public

Conversation

@quantamixsol

Copy link
Copy Markdown
Owner

CR-DIST-06 part 2 — guard that every referenced listing asset exists

Public port of private #329 (491ea174 + b4e82dd0), merged 2026-08-03. Both files
byte-identical to private/master.

The third commit on the private branch (a72b0282) was itself a back-port of public
e38ed344
, so it is deliberately not cherry-picked here — public already has it.

The problem

plugin.json declares a logo and screenshots by relative path, and nothing verified
those paths resolved
. A typo — or a screenshot referenced before the file was added —
ships a manifest pointing at nothing. A directory reviewer fetching a 404 image is a
rejection
, and it is invisible until someone else looks.

The guard

tests/test_packaging/test_codex_plugin_assets.py:

  • every interface.logo / interface.screenshots entry must resolve to a real file
  • anything named .png must carry PNG magic bytes — a JPEG named .png fails silently in a listing
  • screenshots: [] is allowed (assets not supplied yet); a listed-but-missing entry is not
  • accepts both plain-string and object entry shapes, so it survives whichever schema OpenAI publishes

Proven the honest way: the three screenshot paths were wired first, the guard failed on
all three missing files, and the entries were removed rather than ship a red test. They
go back in when the images land.

Pre-submission gate

screenshots: [] being legal creates a gap — a submission could go out with zero
screenshots and only a human reviewer would catch it. test_screenshots_populated_before_submission
is skipped by default and armed with GRAQLE_SUBMISSION_CHECK=1 before packaging.

Not always-on deliberately: failing today would redden the suite for an asset still being produced.

Sentinel BLOCKED pass 1 — fixed

assert checked > 0 failed in a legitimate environment (sparse checkout without
plugins/) — cryptic red CI for a non-defect. Now pytest.skip naming what was missing.
Verified by running in a temp dir with no plugins/: skips.

Advisory on base-path depth was verified rather than assumed — both manifests sit at
plugins/<channel>/graqle/.<channel>-plugin/plugin.json, so path.parent.parent is
correct for both.

Evidence on this branch

  • tests/test_packaging61 passed, 1 skipped, 0 failed
  • Mutation-tested here, not just inherited: a dangling screenshot reference makes the guard fail; restoring makes it pass
  • Armed gate verified firing on the current empty list
  • Both files byte-identical to private/master

Also included

plugins/codex/graqle/assets/README.md — the three required screenshots, their specs, and
a pre-commit checklist (no keys, no client code, nothing from the CrawlQ side, no
trade-secret thresholds), since these ship publicly and permanently.

Rule #0

git remote -v run before push. Public cherry-pick authorised only because private
#329 is merged (2026-08-03T09:16:53, base advanced to 9db717db).

🤖 Generated with Claude Code

quantamixsol and others added 2 commits August 3, 2026 16:14
plugin.json declares a logo and screenshots by relative path, and nothing
verified those paths resolved. A typo — or a screenshot referenced before the
file was added — would ship a manifest pointing at nothing. A directory
reviewer fetching a 404 image is a rejection, and it is invisible until
somebody else looks.

The guard allows `screenshots` to be EMPTY (assets not supplied yet) but
requires every entry that IS listed to resolve. That distinction matters: it
lets paths be wired ahead of the images without a false failure, while still
catching the case the guard exists for.

Proven by doing exactly that: I wired the three screenshot paths first, the
guard failed on all three missing files, and I removed the entries again rather
than ship a red test. The paths go back in when the images land.

Also checks PNG magic bytes — a .png that is not a PNG fails silently in a
listing.

Mutation-tested: pointing a screenshot at a non-existent file makes the guard
fail; restoring makes it pass. tests/test_packaging 61 passed, 4 skipped.

assets/README.md records the exact three screenshots needed, their specs, and a
pre-commit checklist (no keys, no client code, nothing from the CrawlQ side, no
trade-secret thresholds) since these ship publicly and permanently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… gate

Sentinel BLOCKED pass 1 on one defect and raised two advisories. All addressed.

BLOCKER — `assert checked > 0` failed in a legitimate environment. A sparse
checkout without plugins/ is not a defect, but the assert turned it into a
cryptic red CI with no diagnostic. Now pytest.skip with a message saying
exactly what was not found. A test that fails for environmental reasons rather
than code defects is a bad test.

ADVISORY 1 (base-path depth) — VERIFIED, no change needed. Both manifests sit
at plugins/<channel>/graqle/.<channel>-plugin/plugin.json with assets/ beside
the plugin dir, so path.parent.parent is correct for both. Added a comment
recording the expected structure so the next reader does not have to re-derive
it.

ADVISORY 2 (empty screenshots could reach submission unnoticed) — this was the
real gap allowing screenshots:[] creates. Added
test_screenshots_populated_before_submission, SKIPPED by default and armed with
GRAQLE_SUBMISSION_CHECK=1 immediately before packaging a submission. Not
always-on deliberately: failing today would make the suite red for an asset
still being produced.

ADVISORY 3 (guessed schema) — accepted, no code change. plugin.json keeps
screenshots:[] rather than shipping a guessed entry shape; the guard already
accepts both plain-string and object forms, so it survives either.

Verified in all three states: default 4 passed/1 skipped; armed, the gate
correctly FAILS on the empty list; with no plugins/ present, it skips instead
of failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🛡️ GraQle PR Guardian

💥 Blast Radius: 2 modules affected

Module Files Changed Risk Level Impact Radius
plugins 1 🟡 T2 1
tests 1 🟢 T1 1

Total blast radius: 2


🏛️ Governance Verdict

⚠️ WARN

  • T2: Advisory warnings detected. Review recommended.

🔍 SHACL Violations

No SHACL violations detected.


🔐 Approval Requirements

This PR requires approval from: T2

  • A Senior Engineer or above must approve.

Approval requirement NOT yet satisfied.


Metric Value
Blast Radius 2
Files Analyzed 2
Blocked 0
SHACL Violations 0
Verdict WARN

🔬 Powered by GraQle PR Guardian v0.1.0 · Scan completed 2026-08-03T14:16:05.664589+00:00

@harishquantamix
harishquantamix merged commit 597a7c4 into master Aug 3, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants