feat: fail-fast guardrail for schema file changes without rebuild#46189
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Add TestEmbeddedSchemasAreValid in pkg/parser/schema_embed_test.go to verify all four embedded schemas are valid JSON and compilable; since go:embed re-reads files at compile time, running go test ./pkg/parser/... is equivalent to make build for schema-embedding purposes - Add scripts/check-stale-schema-binary.sh to detect when pkg/parser/schemas/ files are modified (via git diff) but the binary has not been rebuilt - Add check-stale-schema-binary Makefile target (mirrors check-stale-lock-files pattern) and wire it into the lint target so agent-report-progress catches it - Document the rebuild requirement in DEVGUIDE.md under Common Error Scenarios Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
1 similar comment
|
Hey
|
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
✅ Test Quality Sentinel completed test quality analysis. |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #46189 does not have the 'implementation' label and has only 69 new lines of code in business logic directories (threshold: 100). |
There was a problem hiding this comment.
Pull request overview
Adds guardrails to detect stale embedded schemas and validate schema integrity.
Changes:
- Adds embedded-schema compilation tests.
- Adds a stale-binary detection script integrated with linting.
- Documents schema rebuild requirements and refreshes generated workflow metadata.
Show a summary per file
| File | Description |
|---|---|
scripts/check-stale-schema-binary.sh |
Detects schema changes newer than the binary. |
pkg/parser/schema_embed_test.go |
Validates all embedded schemas. |
Makefile |
Integrates the guard into linting. |
DEVGUIDE.md |
Documents schema rebuilding and validation. |
.github/workflows/skillet.lock.yml |
Refreshes generated action annotations. |
.github/workflows/release.lock.yml |
Refreshes generated action annotations. |
.github/workflows/hourly-ci-cleaner.lock.yml |
Refreshes generated action annotations. |
.github/workflows/avenger.lock.yml |
Refreshes generated action annotations. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 8/8 changed files
- Comments generated: 4
- Review effort level: Medium
There was a problem hiding this comment.
Clean, well-reasoned addition. The two-layer approach (unit test + mtime script) is sound. No blocking issues found.
🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 23.9 AIC · ⌖ 4.24 AIC · ⊞ 5K
🧪 Test Quality Sentinel Report✅ Test Quality Score: 100/100 — Excellent
📊 Metrics (4 subtests across 1 test function)
Verdict
References: §29632343424 Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
There was a problem hiding this comment.
REQUEST_CHANGES — 3 high/medium correctness issues in the new guardrail script
The goal is sound, but the shell script has two silent-failure modes in CI and one ergonomic regression that collectively undermine the guardrail this PR is meant to provide.
🔴 Blocking issues (3)
-
check-stale-schema-binary.shline 396 — mtime check is a no-op in CI (HIGH):git checkoutresets all timestamps;[ "$f" -nt "$BINARY" ]is always false in CI. The script itself acknowledges this, making the core check a local-only check that provides false confidence in CI pipelines. -
check-stale-schema-binary.shline 347 — shallow-clone silent pass (MEDIUM):git diff BASE...HEADreturns empty on shallow clones and the|| trueswallows the error — the check silently exits 0 even when it can't determine what changed. -
Makefileline 176 —make lintnow fails without binary (MEDIUM):check-stale-schema-binaryerrors with "binary not found" ifmake buildhasn't run first, turningmake lintinto a two-step process with no documentation or graceful handling.
🟡 Non-blocking observations (2)
schema_embed_test.goline 250: intermediate JSON unmarshal intovar doc anyis dead code —CompileSchemaalready validates JSON syntax internally.check-stale-schema-binary.shline 354: local fallback misses brand-new untracked schema files;git diff --name-only HEADonly covers tracked changes.
🔎 Code quality review by PR Code Quality Reviewer · 54.4 AIC · ⌖ 4.91 AIC · ⊞ 5.6K
Comment /review to run again
Comments that could not be inline-anchored
scripts/check-stale-schema-binary.sh:396
Stale-binary mtime check is a no-op in CI: after a fresh git checkout, schema files and the binary share the same checkout timestamp, so [ "$f" -nt "$BINARY" ] is always false — the check silently passes even when schemas are stale.
<details>
<summary>💡 Details and suggested fix</summary>
The script acknowledges this limitation on lines 389–391 and defers to CI
scripts/check-stale-schema-binary.sh:347
Shallow-clone silent pass: git diff BASE...HEAD returns empty output (or errors) on shallow clones, and the || true swallows the error — so the check silently reports no modified schemas when it actually can't determine anything.
<details>
<summary>💡 Details</summary>
CI runners commonly use --depth=1 or similar shallow fetches. When the merge base between BASE_REF and HEAD doesn't exist locally, git diff BASE...HEAD either errors or returns empty. Because the result is piped…
scripts/check-stale-schema-binary.sh:354
Local fallback misses new untracked schema files: git diff --name-only HEAD only lists modified tracked files; a brand-new schema file that hasn't been git added yet won't appear, so the check passes even though the binary can't embed a file it doesn't know about.
<details>
<summary>💡 Suggested fix</summary>
Supplement with untracked file detection for the schemas directory:
# In addition to git diff HEAD, also check untracked files in the schemas dir
git ls-files --others -…
</details>
<details><summary>pkg/parser/schema_embed_test.go:250</summary>
**Redundant JSON unmarshal before CompileSchema**: step 2 unmarshals into `var doc any` to validate JSON, but `CompileSchema` already parses the JSON internally — the `doc` variable is never used after assignment (dead code).
<details>
<summary>💡 Suggested fix</summary>
Remove the intermediate unmarshal and collapse to two steps:
```go
// 1. Non-empty check
if len(tc.schemaVar) == 0 { ... }
// 2. Compile (validates both JSON syntax and schema semantics)
if _, err := CompileSchema(tc.schema…
</details>
<details><summary>Makefile:176</summary>
**`make lint` now requires the binary to exist**: adding `check-stale-schema-binary` to the `lint` target means `make lint` fails with a confusing "binary not found" error if a developer hasn't run `make build` first — that's unexpected for a target that should be runnable any time.
<details>
<summary>💡 Suggested fix</summary>
Either:
1. Make the script exit 0 with a warning (not error) when the binary doesn't exist and no schema files are modified — the rebuild guard is moot if nothing chan…
</details>There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /tdd and /diagnosing-bugs — the guardrail is well-structured and fills a real gap. Three issues worth addressing before merge.
📋 Key Themes & Highlights
Issues Found
- Placeholder schema URLs (
(contoso.com/redacted) in the test table may cause ID mismatches if schemas declare a real$id` — see inline comment on line 22. - Silent CI pass-through: the mtime staleness check in base-ref mode can silently pass because
git checkoutdoes not preserve file timestamps — see inline comment on line 396 of the script. - Empty schema not caught: valid JSON
{}compiles as a vacuous schema and would pass the current test — see inline comment on line 56.
Positive Highlights
- ✅ Clean parallel structure mirroring
check-stale-lock-files.sh— immediately navigable. - ✅ Wired into
make lint(and thusagent-report-progress) — the check runs automatically. - ✅ Table-driven, parallel test with clear
Fatalfmessages pointing tomake build. - ✅ Color/NO_COLOR/TERM=dumb handling is correct and CI-friendly.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 49.8 AIC · ⌖ 4.77 AIC · ⊞ 6.7K
Comment /matt to run again
Comments that could not be inline-anchored
pkg/parser/schema_embed_test.go:22
[/tdd] The schemaURL values use the placeholder domain (contoso.com/redacted) — if any of the embedded schemas declare a $idfield pointing to a different URL, the compiler may silently register them under two different IDs, making$ref` resolution behave differently in the test than in production.
<details>
<summary>💡 Suggestion</summary>
Read the $id field from each schema file and use that as the schemaURL, or at minimum assert that the compiled schema is registered under th…
scripts/check-stale-schema-binary.sh:396
[/diagnosing-bugs] The mtime-based staleness check (line 396: if [ "$f" -nt "$BINARY" ]) is documented as unreliable in CI because git checkout does not preserve timestamps. However, the collect_modified_files function in base-ref mode uses git diff --name-only rather than mtime — so in CI the mtime path is never reached when a base-ref is available. If --base-ref is absent in CI, the script falls back to git diff HEAD for file discovery but still uses mtimes to decide sta…
pkg/parser/schema_embed_test.go:56
[/tdd] The test validates that each schema is valid JSON and compiles, but does not assert structural correctness (e.g. required top-level keywords like "$schema", "type", or "properties"). A schema file that is syntactically valid JSON but semantically empty (e.g. {}) would pass this test yet silently disable all validation.
<details>
<summary>💡 Suggestion</summary>
Add a minimal structural assertion for each schema — at least check that the top-level object contains "$schema"…
|
@copilot run pr-finisher skill |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…UIDE.md - Remove `|| true` from three-dot git diff so shallow-checkout failures propagate - Include untracked schema files in local working-tree check via git ls-files - Handle deleted schema paths using schemas directory mtime as proxy - Clarify DEVGUIDE.md: go test validates schemas but does not rebuild the gh-aw CLI Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Addressed all four review threads in commit
Local |
|
@copilot please run the
|
…st-schema-change Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
All three unresolved threads were addressed in |
Schema files under
pkg/parser/schemas/are embedded at compile time via//go:embed; editing them without rebuilding silently leaves the running binary with stale schemas. There was no targeted check to catch this class of drift.Changes
pkg/parser/schema_embed_test.go— NewTestEmbeddedSchemasAreValidtest that compiles all four embedded schemas (main_workflow_schema,mcp_config_schema,repo_config_schema,aw_manifest_schema) as part ofmake test-unit. Becausego testre-embeds files at compile time, running this test is equivalent tomake buildfor schema-embedding purposes — any malformed JSON or invalid schema breaks the test immediately.scripts/check-stale-schema-binary.sh— New script (mirrorscheck-stale-lock-files.sh) that usesgit diffto detect modifiedpkg/parser/schemas/*.jsonfiles and compares their mtimes against the binary, failing with a clearmake buildremediation message when stale.Makefile— Newcheck-stale-schema-binarytarget (supportsGITHUB_BASE_REF/CHECK_STALE_SCHEMA_BASE_REFfor CI base-ref mode) wired intolint, which is already part ofagent-report-progress.DEVGUIDE.md— New "Schema changes not taking effect / stale schema errors" entry under Common Error Scenarios documenting the rebuild requirement and pointing to the new guardrails.