Ky's AI governance, tests, and CI rules live here and apply to every repo AI agents work in (Kiro, Codex, Cursor, Grok Bot, Claude Code, Copilot). The rules come from KyLamportLogic/LamportLogic.
The gate: agent code cannot merge to main (and so cannot deploy) unless the repo's own CI and the ai-governance / verdict check pass. Checks are the gate, and only Ky merges.
| Path | Purpose |
|---|---|
.github/workflows/governance.yml |
Reusable workflow (on: workflow_call) that runs the gate in any repo |
AGENTS.governance.md |
Canonical AI-GOVERNANCE:v1 section every repo's AGENTS.md must contain |
templates/ai-governance.yml |
Caller workflow that goes in each repo |
scripts/optin.sh |
One command to opt a repo in, plus --protect to apply the protection model |
scripts/sweep.sh |
Finds every owned/admin repo without the gate; report-only by default, --apply opens opt-in PRs and protects repos whose verdict has passed. Optional scripts/sweep.skip (owner/repo reason per line, kept out of git) lists exclusions |
.github/workflows/self-test.yml |
This repo dogfoods its own gate and runs tests/ (behavioral tests of the policy script) |
roles.yml |
The agent role map (spec-author / coder / approver) and spec paths; read by every gate run from main |
templates/spec/ |
Spec templates: requirements.md, design.md, tasks.md |
specs/ai-governance-gate/ |
The gate's own spec (the requirements the tests in tests/ trace to) |
All jobs run on pull_request and on push to main.
| Job | Checks | LamportLogic source |
|---|---|---|
quality |
Detects pnpm / yarn / npm (via corepack) or Python. Runs install (frozen lockfile), then lint, type-check/typecheck, test, build if those scripts exist; Python uses ruff, mypy, and pytest when configured. Missing tests fail unless require-tests: false. Optional extra-command runs repo-specific gates, e.g. pnpm validate:all. |
ci.yml, .claude/hooks/verify-before-stop.mjs |
policy |
AGENTS.md contains the AI-GOVERNANCE:v1 section. CODEOWNERS has a global * owner. Every workflow has a permissions: block and SHA-pinned uses:. On PRs: the title follows Conventional Commits, and the body references an issue. A missing reference is a warning by default and an error for code PRs or when require-issue-reference: true. The spec gate (below) runs on every PR. Structural changes need a valid SYSTEMS_THINKING_CONTRACT covering every structural path (auto = enforced when .systems-thinking/contracts/ exists). If governance files change, a notice flags that code-owner review is needed. |
AGENTS.md, CLAUDE.md, CODEOWNERS, .husky/commit-msg, PR template, validate-security-baseline.js, .systems-thinking/ |
security |
zizmor (workflow static analysis), shellcheck on changed scripts, osv-scanner dependency review of newly introduced vulnerabilities; all pinned by hash/checksum. | security-lint.yml, ci.yml, dependency-review.yml |
secret-scan |
gitleaks 8.30.1 binary, pinned by checksum, over the whole PR commit range (not just the tip). Uses the repo's .gitleaks.toml if it has one. Fails closed. |
secret-scan.yml, .gitleaks.toml |
verdict |
A single aggregate check that fails closed. Make this the one required status check. It also publishes the scorecard (see below). | n/a |
External actions are pinned to full commit SHAs. The scoped-evidence helper is maintained in this repository and pinned independently by consumers. Repositories with selected-action allowlists must explicitly permit the reviewed helper before adoption; do not broaden or bypass their allowlists automatically.
The maintained eight-dimension evidence contract
adds explicit acceptance criteria, real command receipts and visible remaining
gaps. Consumers pin .github/actions/system-quality to a reviewed full commit
SHA, or opt in through system-quality-definition. A passing scoped CI result
does not establish whole-system readiness or change Ky's merge authority.
For external stack jobs, pass their actual needs result through the reusable workflow's new prerequisite-result input under an always-run caller. Its verdict accepts only exact success, including when external quality is explicitly selected with run-quality: false. Review the required-check binding; a standalone optional check or a literal success is insufficient.
roles.yml is the single role map. Ky owns it (CODEOWNERS) and only Ky merges it. The policy job reads it from KyPython/ai-governance@main on every run, in every repo, and fails closed if it can't. Callers have no input to change roles or spec paths.
| Role | Who | May |
|---|---|---|
spec-author |
Kiro (kiro-agent[bot]); KyPython as owner override |
write specs and requirements in spec-only PRs |
coder |
Kiro's builders (kiro-agent[bot]), Codex (chatgpt-codex-connector[bot], codex), Cursor (cursor[bot]), Copilot, Claude, Grok Bot, … |
implement specs that already exist on main; a code PR never touches spec paths, even when Kiro writes it |
approver |
KyPython | review and merge (enforced by branch protection: agents can't merge) |
Every PR that changes a code path fails policy (and therefore verdict) unless all of these hold:
- It links an issue in the body:
Closes #N,Relates to #N, or an issue URL. - It references an existing spec. The body has a
Spec: specs/<slug>(or.kiro/specs/<slug>) line, and that spec already exists on the base branch. Itsrequirements.mdcontains at least one numbered EARS acceptance criterion: a stable ID<PREFIX>-<n>.<m>on a line containingSHALL, e.g.1. GOV-1.1 WHEN … THEN the system SHALL …. - It does not touch spec paths. Specs come first, in their own spec-only PR.
- It traces tests to the spec. It adds or changes a test file (
*.test.*,*.spec.*,test_*.py,*_test.*,tests/,__tests__/) whose path or content cites one of those criterion IDs (GOV-1.1, orGOV_1_1in identifiers) or requirement IDs (GOV-1).
Every PR that touches spec paths (spec_paths in roles.yml) fails unless the PR opener and the author and committer of every commit match a spec-author identity. The committer check is skipped for GitHub's own web-flow committer (noreply@github.com). In KyPython/ai-governance, a change to roles.yml additionally needs every identity to be the owner.
What the role gate can and cannot prove.
- The PR opener's login comes from GitHub, so it is reliable.
- Commit author and committer names and emails are self-declared git metadata. Anyone can write any name.
- Many agents push with Ky's token, so their commits and PRs appear as KyPython. The owner override then accepts them.
- So the gate reliably blocks spec changes that show a coder or unknown identity anywhere: a
cursor[bot]commit, a Codex-opened PR, or abox@…committer. It cannot tell an agent using Ky's token from Ky himself. - Closing that gap needs each agent to have its own GitHub identity (app or bot account), with no shared token. Signed-commit verification would be a further step.
- Ky's merge remains the real approval for any spec.
Exempt by path only. A PR is exempt from the code rules when every changed path is one of these:
- docs (
*.md,docs/, images,.cursorrules,.cursor/rules/); - spec and contract files (spec paths are still subject to the role gate);
- repo metadata (
.gitignore,.gitattributes,.editorconfig, prettier config,.npmrc,.nvmrc/.node-version/.python-version,CODEOWNERS,LICENSE, file-purpose inventories (purpose-inventory.json),.github/workflows/*.yml, issue/PR templates,dependabot.yml). Other config such astsconfig.jsonoreslint.config.*changes behavior, so it counts as code; - lockfiles and
requirements*.txt; package.jsonwhere only dependency keys changed (dependencies,devDependencies,peerDependencies,optionalDependencies,packageManager,overrides,resolutions,pnpm).
Labels, branch names (including dependabot/*), and authors never exempt a PR, so a dependabot lockfile/manifest bump passes by path while a dependabot PR touching code does not. The rules live in governance.yml (policy sections 0, 8, 9) and are covered by tests/test_policy.py.
Format and where it came from. Specs follow Kiro's layout: requirements.md (Spec ID, issue, user stories, numbered EARS criteria), optional design.md and tasks.md; see templates/spec/. The ID-to-test traceability copies what Ky already does:
- LamportLogic
packages/auth/src/requirement-to-test.ts: theAUTH-REQ-001→ test-case matrix; - ZeroAPI
docs/specs/EMAIL_OUTCOME_REQUIREMENTS.md:EO-1..EO-4with a tests section; - park-city-studios
REQUIREMENTS_TRACEABILITY.md; - human-rebuild-bootcamp
GOLDEN_SPEC.md:[R-SPEC-F1]requirement IDs plus acceptance oracles; - ky-cloud-control-plane's Kiro-first spec gate: Kiro drafts requirements, Ky approves the exact sentences.
KyLamportLogic/LamportLogic, human-rebuild-bootcamp and ky-cloud-control-plane are the strictest repos and the source of truth. Every control they enforce is listed below with its ai-governance status.
- had: the gate already did this.
- added: the
securityjob and policy sections 10–13 (requirements GATE-5). - repo-specific: not generic. It stays in that repo's own required CI, which the gate runs alongside and never replaces.
| Control | Source | ai-governance |
|---|---|---|
| Install, lint, type-check, test, build | LamportLogic ci.yml (verify); bootcamp ci.yml (npm run ci:github); control-plane pnpm build/pnpm test |
had (quality) |
| Gitleaks over the PR commit range | LamportLogic secret-scan.yml |
had (secret-scan) |
Workflow SHA pinning and permissions: |
LamportLogic validate-security-baseline.js |
had (policy; workflow-baseline: all matches LamportLogic's full-repo scope) |
| Workflow static analysis | LamportLogic security-lint.yml (zizmor) |
added: zizmor 1.30.1, hash-pinned, --min-severity medium on in-scope workflows |
| Shellcheck on changed scripts | LamportLogic ci.yml |
added: shellcheck --severity=error |
| Dependency review | LamportLogic RAG dependency-review.yml; validate-dependency-floor.js |
added: osv-scanner 2.6.0 (checksum-pinned). Fails on vulnerabilities the PR introduces (head vs. base), so no paid GitHub Advanced Security is needed |
| No hand-rolled auth, no direct DB client | LamportLogic validate-constitution.sh (pre-commit) |
added: fails when added lines use jwt.sign/jwt.verify/jsonwebtoken or new PrismaClient outside `packages/auth |
| No destructive commands | LamportLogic .claude/hooks/pre-tool-use.mjs |
added: force-push, reset --hard, git clean -fdx, DB reset/drop, terraform destroy and rm -rf / fail when added to scripts, workflows, Dockerfiles, Makefiles, .husky/ or package.json |
| Deploy only after CI | LamportLogic deploy-all.yml (workflow_run + success); AGENTS rule |
added: changed deploy/release/publish workflows fail if they deploy on PR events, use workflow_run without a success check, or deploy on push to any branch |
| Container hardening | LamportLogic validate-container-hardening.js |
added (subset): changed Dockerfiles need a versioned, non-latest base and a non-root final USER. The full rules (digest pin, HEALTHCHECK, ports, volumes) stay in LamportLogic |
| CODEOWNERS | all three | had (global owner required) |
| PR template | LamportLogic, bootcamp PULL_REQUEST_TEMPLATE.md |
added (warning) |
| Dependabot | LamportLogic dependabot.yml |
added (warning) |
| Conventional Commits | LamportLogic .husky/commit-msg |
had (PR title) |
| Systems-thinking contract | LamportLogic, bootcamp .systems-thinking/ |
had |
| Requirements before code; Kiro authors specs | control-plane KIRO_SPEC_AND_MONETIZATION_GATE.md; LamportLogic AGENTS |
had (spec gate + role gate) |
| AI monetization / mutation gate | LamportLogic validate-ai-monetization-gate.js, ai:mutation:check |
repo-specific (the generic analog is the spec + role gates) |
validate:all suite (env vars, RLS, auth coverage, provider SDK, workspace sync, path integrity) |
LamportLogic | repo-specific |
| Snyk code, open-source and IaC scans | LamportLogic snyk-security.yml |
repo-specific (needs a Snyk account token). osv-scanner and zizmor cover the free part |
| PCS Godot tests, model-verification plane, mutation testing | LamportLogic | repo-specific |
OPA policy tests, TLA+ model check, cdk synth --strict with cdk-nag |
control-plane | repo-specific. The gate's quality job runs its pnpm build and pnpm test, which include the OPA and TLA+ suites |
Bootcamp grading parity (ci:github) |
bootcamp | repo-specific |
| Scheduled full-history gitleaks | LamportLogic secret-scan.yml (schedule) |
not added; the strict repos keep their own |
- Kiro writes the spec (
requirements.md,design.md,tasks.md) in a spec-only PR. Ky merges it. - Each task in
tasks.mdgoes to one coder, in its own PR that citesSpec: specs/<slug>and the task's criterion IDs. - A different agent reviews that PR: for example, Codex reviews Cursor's PR and Cursor reviews Codex's. The gate checks the code; the reviewer checks intent against the requirement sentences.
- Ky merges once
verdictand the repo's CI are green and review threads are resolved.
Coders cooperate rather than compete. Sometimes the same task is built head-to-head by two coders, purely for learning; the scorecard below makes that comparison cheap.
Every run's verdict job publishes the same JSON (schema ai-governance-summary/v1) in three places:
- the run's job summary, as a one-row table plus the JSON;
- a
ai-governance-summaryartifact (ai-governance-summary.json, kept 90 days); - the reusable workflow's
summaryoutput, also printed as a log line startingAI_GOVERNANCE_SUMMARY.
{
"schema": "ai-governance-summary/v1",
"repo": "KyPython/effectproof", "pr": 21, "pr_author": "cursor[bot]", "head_sha": "…",
"verdict": "pass", "jobs": {"quality": "success", "policy": "success", "secret-scan": "success"},
"failed_checks": 0, "failed_check_names": [], "policy_errors": 0,
"tests": {"passed": 46, "failed": 0, "total": 46},
"spec": ["specs/ai-governance-gate"], "requirement_ids": ["GOV-1.1", "GOV-1.2", "GOV-1.3", "GOV-2.1", "GOV-3.1"],
"changed_lines": {"added": 50, "deleted": 0, "total": 50}, "files_changed": 2,
"run_url": "https://github.com/…/actions/runs/…"
}What each field means:
testsis parsed from the test output of the quality job and theextra-command. Supported runners: Jest, Vitest, pytest, unittest, node:test and Mocha; counts are summed across monorepo packages. The values arenullwhen the runner's output isn't recognized, or when tests didn't run (for example, lint failed first).requirement_idsare the spec IDs cited by the PR's changed tests.failed_checkscounts the gate jobs that didn't succeed, andpolicy_errorscounts policy findings.changed_linescounts the PR diff against the merge-base, excluding lockfiles.
The scorecard only reports; the pass/fail decision never reads it. Use it to see at a glance what a PR covered, to check a reviewer's claims, or to compare an occasional head-to-head. Example: gh run download <run-id> -n ai-governance-summary.
node-version ("22"), python-version ("3.12"), working-directory ("."), run-quality (true; set false only when the repo's own required CI already runs install/lint/test/build on every PR), workflow-baseline ("changed" = only workflows touched by the PR/push must be pinned and permissioned; "all"; "off"), require-tests (true), extra-command (""), require-issue-reference (false; code PRs always need it), enforce-conventional-title (true), systems-thinking-contract ("auto" | "required" | "off").
# 1. Open the opt-in PR. It adds the caller workflow, the AGENTS.md section,
# CODEOWNERS if missing, and a systems-thinking contract if the repo uses them.
GITHUB_TOKEN=<pat with repo+workflow> ./scripts/optin.sh <repo-name>
# 2. Wait until the PR shows the "ai-governance / verdict" check, then require it on main:
./scripts/optin.sh --protect <repo-name>
# 3. Ky reviews and merges the opt-in PR.To do it by hand: copy templates/ai-governance.yml to .github/workflows/ai-governance.yml, replace __AI_GOVERNANCE_SHA__ with the current main SHA of this repo, paste AGENTS.governance.md near the top of AGENTS.md, and add * @KyPython to .github/CODEOWNERS. Then add the branch protection on main (the "checks are the gate" model):
- require a PR, with 0 required approvals (no code-owner review, no last-push approval)
- required checks: the repo's own CI that runs on every PR, plus
ai-governance / verdict - strict (branch must be up to date)
- conversation resolution required
- linear history
- no force pushes, no deletions
- enforced on admins
Access: this repo is public, so any repo can call the gate: KyPython repos, KyLamportLogic org repos, private or public. A private reusable workflow can only be shared with repos owned by the same account. The repo holds no secrets; that was checked with gitleaks before publishing.
Updating the gate: callers pin a commit SHA, so a change here affects nobody until each repo bumps its SHA in a PR that Ky approves. That stops an agent from weakening the gate for every repo in one edit.
Gate deploys on governance. Deploy only from main (which only advances through governed PRs), or chain off the gate the way LamportLogic's deploy-all.yml does:
on:
workflow_run:
workflows: ["AI Governance"]
types: [completed]
branches: [main]
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_repository.full_name == github.repositoryFor hosted deploys (Vercel, Cloudflare, etc.), restrict production deploys to the main branch.
- The protection model has no approval requirement on purpose: agents push with Ky's account, and GitHub never lets authors approve their own PRs. The checks are the gate. Branch protection cannot stop a holder of Ky's admin token from merging their own PR once checks pass. That rule lives in AGENTS.md, so keep agents' tokens scoped where possible.
- (Historical) GitHub enforces "Ky approves" only when the PR author is not Ky's own account, because GitHub never lets authors approve their own PRs. If agents push and open PRs with Ky's personal token, the PR is authored by
KyPython. Withenforce_adminson, it can then never collect the required approval. With admin bypass on, anything holding Ky's admin token, agents included, can bypass. The robust fix is to give agents their own identity (a GitHub App / bot account, or the Cursor/Codex GitHub apps) with write access but not admin, so Ky's review is the real gate. - Branch protection and rulesets on private repos need a paid plan. The KyPython account is on Pro, so its private repos work. The
KyLamportLogicorg is on Free, so its private repos return403 Upgrade to GitHub Pro or make this repository public; fixing that needs GitHub Team, $4/user/month. Org-wide rulesets also need Team, and "require workflows" rulesets need Enterprise. - The role gate proves declared identities only. See "What the role gate can and cannot prove" above. While agents push with Ky's token, the gate cannot tell an agent from Ky. Giving each agent its own GitHub identity is what makes
roles.ymlfully enforceable. - Cross-review by a different agent is a workflow rule, not a gate check. The gate does not verify who reviewed a PR.
- The scorecard is best-effort. Test counts depend on recognizing the runner's output; anything else reports
null. It never affects the verdict. roles.ymlis read frommainat run time, unlike the SHA-pinned workflow. A role change by Ky applies everywhere at once. If raw.githubusercontent.com is unreachable, verdicts fail closed.