CR-DIST-06 part 2: guard that every referenced listing asset exists - #249
Merged
Conversation
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>
🛡️ GraQle PR Guardian💥 Blast Radius: 2 modules affected
Total blast radius: 2 🏛️ Governance Verdict
|
| 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
approved these changes
Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CR-DIST-06 part 2 — guard that every referenced listing asset exists
Public port of private #329 (
491ea174+b4e82dd0), merged 2026-08-03. Both filesbyte-identical to
private/master.The problem
plugin.jsondeclares a logo and screenshots by relative path, and nothing verifiedthose 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:interface.logo/interface.screenshotsentry must resolve to a real file.pngmust carry PNG magic bytes — a JPEG named.pngfails silently in a listingscreenshots: []is allowed (assets not supplied yet); a listed-but-missing entry is notProven 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 zeroscreenshots and only a human reviewer would catch it.
test_screenshots_populated_before_submissionis skipped by default and armed with
GRAQLE_SUBMISSION_CHECK=1before packaging.Not always-on deliberately: failing today would redden the suite for an asset still being produced.
Sentinel BLOCKED pass 1 — fixed
assert checked > 0failed in a legitimate environment (sparse checkout withoutplugins/) — cryptic red CI for a non-defect. Nowpytest.skipnaming 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, sopath.parent.parentiscorrect for both.
Evidence on this branch
tests/test_packaging— 61 passed, 1 skipped, 0 failedprivate/masterAlso included
plugins/codex/graqle/assets/README.md— the three required screenshots, their specs, anda 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 -vrun before push. Public cherry-pick authorised only because private#329 is merged (
2026-08-03T09:16:53, base advanced to9db717db).🤖 Generated with Claude Code