Skip to content

Add a reusable verify-decisions workflow for repository decision records #88

Description

@woksin

Summary

Add a reusable verify-decisions workflow (workflow_call, org-only guard like the other reusables) that checks a repository's decisions/ folder against the shared decision-record front-matter shape, so the convention is enforceable in any Cratis repository on the day it opts in, independent of corpus distribution.

Current behaviour

  • This repository ships seven workflow_call reusables (cleanup-pr-artifacts.yml, propagate-copilot-instructions.yml, sync-copilot-instructions.yml, trigger-documentation-build.yml, update-packages.yml, verify-no-work-records.yml, verify-release-intent.yml). The two governance gates, verify-no-work-records and verify-release-intent, are the organisation's proven way to put a rule in front of every PR.
  • No verify-decisions workflow exists (the string appears nowhere in the repository).
  • Decision records across the organisation have no shared checker: Cratis/Components keeps four tracked records under Documentation/decisions/ with Starlight-only front matter and status in body prose; other repositories keep decisions in .agents/PROJECT.md or in untracked work files. Nothing verifies that an accepted record names a decider and a date, that supersession pointers are two-way, or that the check a record claims to be enforced by still exists.
  • Corpus profiles cannot carry a checker (passive skills forbid scripts and hooks), so a checker as a reusable workflow is the only distribution path that reaches repositories today.

Desired behaviour

.github/workflows/verify-decisions.yml, workflow_call, with inputs:

  • decisions_path (default decisions)
  • schema_path (default: the schema vendored in this repository under .github/scripts/decision-record.frontmatter.schema.json)
  • require_census (boolean, default true)

The job runs .github/scripts/verify-decisions.mjs (Node, no network) which checks, per file under decisions_path:

  1. the filename prefix agrees with the front-matter status (proposed · returned · accepted · rejected · deferred · superseded);
  2. an accepted, rejected, deferred or superseded record carries a decided date and a decider that resolves to a named actor (a bare role fails);
  3. superseded-by and supersedes point both ways;
  4. stage: verified carries at least one evidence entry;
  5. every enforced-by path resolves in the repository (a workflow file is an acceptable target);
  6. the index's census line agrees with the files (when require_census);
  7. the front matter validates against the schema.

Checker conventions: exit 0 and print counts (records parsed, links resolved, checks run) when clean; exit 1 with rule id, file and line per defect; exit 2 with the missing prerequisite when it cannot run (no decisions_path, unreadable schema, git ls-files failure). --self-test builds a temporary fixture with one planted defect per rule plus a clean control and asserts exactly those defects; a workflow like verify-ai-profile-subscription-update.yml runs node --check, node --test and --self-test on every change to the script.

Rule 1 of the org guard applies: if: ${{ github.repository_owner == 'Cratis' }} as in propagate-copilot-instructions.yml:45.

Acceptance

Done when:

  • The workflow and script exist; README.md documents the three inputs and the wrapper a repository adds.
  • verify-decisions.mjs --self-test passes and is run by CI on changes to the script.
  • One repository with tracked decision records (Cratis/Components is the candidate) runs the wrapper green after adding the front matter fields (tracked in that repository).

Verify by:

node .github/scripts/verify-decisions.mjs --self-test          # prints "self-test: 7 planted, 7 red, control 0"
node .github/scripts/verify-decisions.mjs --decisions-path tests/fixtures/decisions-clean   # exit 0, prints counts
node .github/scripts/verify-decisions.mjs --decisions-path does-not-exist                    # exit 2, names the prerequisite

Mutation proof: in the clean fixture, delete the file named by one record's enforced-by; the run exits 1 naming rule 5 and the record.

Out of scope

The decision-record skill and vocabulary (Cratis/AI); repository-specific wrappers; making the check required (an org-admin ruleset action).

Dependencies

Source: AI-Learning F-75. Depends on: D-0001, D-0002 (decision ids for where records live and the single record format); the verify-no-work-records allowlist amendment in this repository (adopting repositories otherwise fight that gate); the exit-2/self-test convention issue in this repository (same conventions).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions