feat(GH-63): machine-enforceable decision-record quality (schemas, validator, index, CI gate)#76
Open
juliusz-cwiakalski wants to merge 20 commits into
Open
Conversation
Incorporates pre-delivery red-team (#1) review of change artifacts — resolving AC-GH63-5↔DEC-13 contradiction (C1: non-negotiable-violation reclassified as non-blocking warning per AC-GH63-6/DEC-13), time-dependent index-determinism issue (M1→DEC-15: committed index=time-independent only; overdue reviews to stdout), version-check↔NFR-1 conflict (M3→DEC-14: repo-internal tools omit network version-check), plus adding decision-record template to CI path filter (M4), schema-driven coverage check (M5), minor reconciliations (DEC-12/16, lifecycle matrix DM-5, flags), and PDR-0001 formalizing §28.3 scope interpretation (SD-2) for human sign-off at PR review. Refs: GH-63
…n_date pattern, coverage strength)
…id/date hardening, LC_ALL pin)
Closed
8 tasks
There was a problem hiding this comment.
Pull request overview
This PR makes the decision-record standards introduced in GH-46 machine-enforceable by adding JSON Schemas, a stdlib-only validator, a deterministic index generator, and a path-filtered CI gate that blocks malformed decision records and stale indexes.
Changes:
- Add JSON Schemas for decision-record front matter and decision planning summaries, plus fixtures and tests to keep schema and validator behavior aligned.
- Introduce
tools/validate-decision-record(front matter + cross-field rules, plus non-blocking heuristics) andtools/generate-decision-index(generateddoc/decisions/00-index.md+ health view). - Add a
verify-decision-recordsGitHub Actions job that validates records, checks index drift, and runs the tool test suites when relevant paths change.
Reviewed changes
Copilot reviewed 54 out of 54 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/validate-decision-record | New validator CLI (front matter + cross-field rules, --summary mode, --coverage coupling) |
| tools/test-all.sh | Adds license header metadata |
| tools/generate-decision-index | New deterministic index generator with committed vs advisory health split |
| tools/.tests/test-zclaude-unit | Adds license header metadata |
| tools/.tests/test-validate-decision-record.sh | New validator test suite (fixtures, coverage, regression guards) |
| tools/.tests/test-generate-decision-index.sh | New generator test suite (determinism, health signals, flags) |
| tools/.tests/fixtures/positive/ADR-9005-empty-links-flow-map.md | Positive fixture covering links: {} parsing regression |
| tools/.tests/fixtures/positive/ADR-9004-unclassified-r2.md | Positive fixture for default-rigor behavior |
| tools/.tests/fixtures/positive/ADR-9003-accepted-r3-with-reviewers.md | Positive fixture for Accepted R3 reviewers requirement |
| tools/.tests/fixtures/positive/ADR-9002-accepted-r2-with-decider.md | Positive fixture for Accepted R2 decider requirement |
| tools/.tests/fixtures/positive/ADR-9001-template-instantiated-r2.md | Positive fixture covering many schema enum fields |
| tools/.tests/fixtures/positive/ADR-0001-snapshot.md | Snapshot fixture of ADR-0001 for dogfooding validation |
| tools/.tests/fixtures/planning-summary/legacy-alias-summary.json | Planning-summary fixture for legacy adr.* alias normalization |
| tools/.tests/fixtures/planning-summary/generic-summary.json | Planning-summary fixture for canonical shape |
| tools/.tests/fixtures/negative/summary-non-negotiable-violation.json | Negative fixture for heuristic warning (non-blocking) |
| tools/.tests/fixtures/negative/summary-constraint-driver-overlap.json | Negative fixture for hard-fail constraint/driver overlap |
| tools/.tests/fixtures/negative/ADR-9027-accepted-unclassified-missing-decider.md | Negative fixture for default-R2 Accepted decider requirement |
| tools/.tests/fixtures/negative/ADR-9026-invalid-calendar-date.md | Negative fixture for semantic date validation |
| tools/.tests/fixtures/negative/ADR-9022-invalid-rigor.md | Negative fixture for invalid nested rigor enum |
| tools/.tests/fixtures/negative/ADR-9021-malformed-id.md | Negative fixture for id pattern enforcement |
| tools/.tests/fixtures/negative/ADR-9020-malformed-dates.md | Negative fixture for date pattern enforcement (multiple fields) |
| tools/.tests/fixtures/negative/ADR-9019-malformed-decision-date.md | Negative fixture for decision_date pattern enforcement |
| tools/.tests/fixtures/negative/ADR-9018-accepted-missing-verification-criteria.md | Negative fixture for Accepted VC heuristic warning (non-blocking) |
| tools/.tests/fixtures/negative/ADR-9017-accepted-r3-missing-reviewers.md | Negative fixture for Accepted R3 reviewers hard-fail |
| tools/.tests/fixtures/negative/ADR-9016-accepted-missing-decision-date.md | Negative fixture for Accepted decision_date non-null hard-fail |
| tools/.tests/fixtures/negative/ADR-9015-accepted-r2-missing-decider.md | Negative fixture for Accepted R2 decider hard-fail |
| tools/.tests/fixtures/negative/ADR-9014-missing-owners.md | Negative fixture for owners minItems violation |
| tools/.tests/fixtures/negative/ADR-9013-supersedes-mismatch.md | Negative fixture for Superseded lifecycle consistency |
| tools/.tests/fixtures/negative/ADR-9012-impossible-transition.md | Negative fixture for impossible lifecycle state |
| tools/.tests/fixtures/negative/ADR-9011-invalid-status.md | Negative fixture for invalid status enum |
| tools/.tests/fixtures/negative/ADR-9010-invalid-decision-type.md | Negative fixture for invalid decision_type enum |
| tools/.tests/fixtures/negative/ADR-00010-filename-prefix-collision.md | Negative fixture for id/filename boundary matching |
| tools/.tests/fixtures/coverage-map.json | Schema-rule to fixture mapping used by --coverage |
| tools/.lib/frontmatter.sh | Shared stdlib-only front-matter parser used by validator + index generator |
| schemas/decision-record-frontmatter.schema.json | JSON Schema for decision-record front matter + x-coverage rules |
| schemas/decision-planning-summary.schema.json | JSON Schema for planning-summary artifacts + x-coverage rules |
| doc/tools/validate-decision-record.md | User guide for the validator CLI |
| doc/tools/generate-decision-index.md | User guide for the index generator + DEC-15 split |
| doc/spec/features/feature-decision-records.md | Spec updates documenting generated index + machine-enforceable gate |
| doc/guides/decision-records-management.md | Guide updates: generated index workflow + validator/index usage |
| doc/decisions/README.md | README updates: index generation + validation pointers |
| doc/decisions/PDR-0001-decision-record-validator-scope.md | New PDR documenting validator negative-case scope interpretation |
| doc/decisions/00-index.md | Updated generated index content (table + health section) |
| doc/changes/2026-06/2026-06-25--GH-63--machine-enforceable-decision-records/code-review/review-iter-2.md | Review artifact documenting iteration-2 PASS |
| doc/changes/2026-06/2026-06-25--GH-63--machine-enforceable-decision-records/code-review/findings-iter-2.json | Structured record of iteration-2 findings |
| doc/changes/2026-06/2026-06-25--GH-63--machine-enforceable-decision-records/chg-GH-63-pm-notes.yaml | PM notes capturing scope decisions + phase results |
| AGENTS.md | Documents the new /decision-index command |
| .opencode/README.md | Adds /decision-index to the command inventory |
| .opencode/command/decision-index.md | New command definition wrapping tools/generate-decision-index |
| .github/workflows/ci.yml | Adds verify-decision-records job (schema validity, validator, drift check, tests) |
| .ados-claude/skills/decision-index/SKILL.md | Generated Claude plugin artifact for /decision-index |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+203
to
+206
| except Exception as exc: | ||
| sys.stderr.write("frontmatter parse error: %s\n" % exc) | ||
| print(json.dumps({})) | ||
| sys.exit(0) |
Comment on lines
+165
to
+170
| # owners: required + minItems 1 | ||
| if [[ "$(_jq -r 'has("owners")' <<<"$json")" == "false" ]]; then | ||
| VALIDATION_ERRORS+=("[ERROR] ${LOG_TAG} ${fname} (id ${id:-<missing>}): required field 'owners' is missing (minItems: 1).") | ||
| elif [[ "$(_jq -r '.owners | length' <<<"$json")" -lt 1 ]]; then | ||
| VALIDATION_ERRORS+=("[ERROR] ${LOG_TAG} ${fname} (id ${id:-<missing>}): field 'owners' must have at least one entry (minItems: 1).") | ||
| fi |
Comment on lines
+349
to
+354
| for rf in decision_id decision_type title hard_requirements decision_drivers alternatives; do | ||
| if [[ "$(_jq -r --arg k "$rf" 'has($k)' <<<"$json")" == "false" ]]; then | ||
| VALIDATION_ERRORS+=("[ERROR] ${LOG_TAG} ${fname}: planning-summary required field '${rf}' is missing.") | ||
| fi | ||
| done | ||
|
|
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.
Summary
Makes the decision-record quality invariants GH-46 established (single source of truth for body structure, rigor-aware required fields, lifecycle validity, constraint/driver discipline) machine-enforceable instead of prompt-only, and turns them into a CI gate. Adds a generated, auditable decision index with a health view. Closes #63.
What lands
schemas/—decision-record-frontmatter.schema.json+decision-planning-summary.schema.json(JSON Schema draft 2020-12), modeling the landed Decision-making refactor: unify agent, process-first guide, type-aware practice #46 nested front matter (classification/governance/ai_assistance/links), validated against the ADR-0001 dogfood record + template.tools/validate-decision-record— stdlib-only CLI (bash + python3/jq; nojsonschemapip dep, no network) enforcing rigor-aware required fields, lifecycle transitions, and the §28.3 negative cases expressible against landed artifacts, with actionable errors.--coverageproves schema↔validator parity by running the validator on each negative fixture.tools/generate-decision-index— deterministic index generator;doc/decisions/00-index.mdbecomes generated (replaces hand-maintenance). Committed health = time-independent (missing deciders, missing metrics, future-field waivers); overdue reviews are stdout-only so calendar time can't trip CI./decision-indexcommand (read-only w.r.t. records) + regenerated.ados-claude/.verify-decision-recordsjob alongside the preservedverify-claude-build; path-filtered ondoc/decisions/,schemas/, the template/spec, and the tools; runs the validator + schema self-validity + index-drift check + the suites. Failures block merge.PDR-0001— formalizes the §28.3 scope interpretation (see sign-off below).feature-decision-records.md,decision-records-management.md,doc/decisions/README.md,AGENTS.md,doc/tools/*.md.The ticket AC says the validator must "fail each negative case from §28.3." 4 of 14 cases require machinery this ticket lists as non-goals, so they are deferred to siblings with documented ownership (not silently dropped):
PDR-0001 (
doc/decisions/PDR-0001-decision-record-validator-scope.md) records this. If you'd rather land all 14 here, say so before merge and I'll expand scope. Otherwise, accepting the PR accepts the PDR.Review / quality gates (two red-team rounds + one reviewer iteration)
links: {}flow-map parser crash;decision_datepattern not enforced; dead code; mockability) → iter-2: PASS.LC_ALL=Cdeterminism pin; DM-4 default-rigor explanation).8c72277).Testing (all green, correct exit codes)
tools/.tests/test-validate-decision-record.sh→ 39/39 (exit 0)tools/.tests/test-generate-decision-index.sh→ 14/14 (exit 0)scripts/test-all.sh→ 5/5 (exit 0)validate-decision-record --coverage→ 0 uncovered, 18 pattern/enum rules enforcement-proven00-index.mdin sync + byte-deterministic across locales;.ados-claude/regen is a no-op; 0 forbidden deps / network calls; headers viascripts/add-header-location.shonly.Non-goals (sibling tickets)
Domain catalogs (GH-66), structured evidence ledger + R3 source verification (GH-65), and the verification/retrospective lifecycle agents (GH-64) reuse this schema/index foundation and own the 4 deferred §28.3 cases.
Refs: GH-63