Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/governance-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,10 @@ jobs:
# 1. `.hypatia-baseline.json` — array of acknowledged findings,
# shape mirrors the Hypatia findings themselves. Schema is
# `.machine_readable/hypatia-baseline.schema.json` in this
# repo. The validate-baseline job above schema-checks this.
# Added 2026-05-25 as part of the convergence on a single
# exemption mechanism.
# repo, enforced structurally by apply-baseline.sh itself
# (jq mirror; a malformed baseline exits 2 instead of
# silently matching nothing). Added 2026-05-25 as part of
# the convergence on a single exemption mechanism.
# 2. `.hypatia-ignore` flat-file — legacy single-rule-per-line
# format (`cicd_rules/banned_language_file:<relpath>`).
# Will be retired in a follow-up PR once .hypatia-baseline.json
Expand Down
33 changes: 0 additions & 33 deletions .github/workflows/push-email-notify.yml

This file was deleted.

3 changes: 3 additions & 0 deletions .github/workflows/scorecard-enforcer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ permissions:
jobs:
scorecard:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
security-events: write
id-token: write # For OIDC
Expand All @@ -39,6 +40,7 @@ jobs:
check-score:
needs: scorecard
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
Expand All @@ -61,6 +63,7 @@ jobs:

check-critical:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/signed-push-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ permissions:
jobs:
smoke:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
Expand Down
872 changes: 872 additions & 0 deletions .hypatia-baseline.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions .machine_readable/REGISTRY.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ name = "A2ML — Attested Markup Language"
stream = "foundation"
home = "a2ml/"
canonical_doc = "a2ml/README.adoc"
source_hash = "sha256:7c51aab6fe43b04bc795647ae9b3d4813ca82f6182399c2987e17edfd50b36f5"
source_hash = "sha256:11ffe50a8a15e22d800383589a947ca76a56d96d4f43f673b2d820ef64000436"
route = "the typed/verified machine-readable document format"

[[spec]]
Expand All @@ -54,7 +54,7 @@ name = "K9 Self-Validating Components"
stream = "foundation"
home = "k9-svc/"
canonical_doc = "k9-svc/README.adoc"
source_hash = "sha256:ddef46e42399af08488cea00f95515614f54cb1aea8b8990d4ef049e9f57f073"
source_hash = "sha256:7b851b1bd94b5561059bff1a21ebff2618d2bb12aa811f1c3329a199cd1fa79b"
route = "self-validating components with embedded contracts + deploy logic"

[[spec]]
Expand All @@ -72,7 +72,7 @@ name = "META.a2ml spec"
stream = "foundation"
home = "meta-a2ml/"
canonical_doc = "meta-a2ml/README.adoc"
source_hash = "sha256:bd0f10114fb753377708b2fa07c5995a000060aa50472d02e067f890f005ab53"
source_hash = "sha256:4b00ebf2fa1ab6aee46b67be34715b4198d4643ce2025c82882afda502d9c038"
route = "architecture decisions / governance metadata format"

[[spec]]
Expand Down Expand Up @@ -135,7 +135,7 @@ name = "0-AI Gatekeeper Protocol"
stream = "protocol"
home = "0-ai-gatekeeper-protocol/"
canonical_doc = "0-ai-gatekeeper-protocol/README.adoc"
source_hash = "sha256:264806b26e710c04adbe7db357f638fdef1bd24a1bae7be9d9d50742a1e535ea"
source_hash = "sha256:00d9193c6bd4409e9c9928133d57a8b5afd93a26797e957fbb6d189707c241f6"
route = "the AI-agent entry/gating protocol behind 0-AI-MANIFEST"

[[spec]]
Expand Down Expand Up @@ -180,7 +180,7 @@ name = "Consent-Aware HTTP"
stream = "protocol"
home = "consent-aware-http/"
canonical_doc = "consent-aware-http/README.adoc"
source_hash = "sha256:92e1d1c0252a69b8812cf0fa48bf2178f7f246314b1238a6d98eea9ed99fdb40"
source_hash = "sha256:7cb83ee0414539d8594b93a83ecf67fe5744b9f3d40b1f209946370dab423652"
route = "consent headers / AI-usage boundaries for HTTP"

[[spec]]
Expand Down Expand Up @@ -225,7 +225,7 @@ name = "RSR — Rhodium Standard Repositories"
stream = "governance"
home = "rhodium-standard-repositories/"
canonical_doc = "rhodium-standard-repositories/README.adoc"
source_hash = "sha256:02b75b57a41d213e4d3e7687e22252ba60bbef0923f75ebe18ef1a962c382f1e"
source_hash = "sha256:fa0fcde2d5be8ed8e4c7067f2a309a3ce424f6fafc942b7a10981546844a971d"
route = "the repository-compliance standard every repo is graded against"

[[spec]]
Expand Down
4 changes: 2 additions & 2 deletions .machine_readable/hypatia-baseline.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"pattern": "^[a-z][a-z0-9_]*$"
},
"type": {
"description": "Hypatia finding type within the rule module (e.g. `banned_language_file`, `obj_magic`, `deprecated_api`).",
"description": "Hypatia finding type within the rule module. Two shapes exist: lowercase snake_case (`banned_language_file`, `secret_detected`) and uppercase mnemonic codes emitted by structural_drift/git_state/code_scanning_alerts (`SD022`, `GS007`, `CSA001`). Entries must use the literal value the finding carries (standards#477).",
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
"pattern": "^([a-z][a-z0-9_]*|[A-Z]{2,3}[0-9]{3})$"
},
"file": {
"description": "Repo-relative path to a single file the entry exempts. Mutually exclusive with `file_pattern`.",
Expand Down
13 changes: 0 additions & 13 deletions 0-ai-gatekeeper-protocol/.github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ on:
permissions: read-all

jobs:
trufflehog:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
with:
fetch-depth: 0 # Full history for scanning

- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@116e7171542d2f1dad8810f00dcfacbe0b809183 # v3
with:
extra_args: --only-verified --fail

gitleaks:
runs-on: ubuntu-latest
timeout-minutes: 20
Expand Down
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,28 @@ Look for issues labelled:

## Development Workflow

### First: install the pre-commit guard

```sh
just hooks-install
```

This installs `hooks/pre-commit`, which catches the most common CI failure
before it leaves your machine: **registry drift**. The spec registry
(`.machine_readable/REGISTRY.a2ml`) records a content hash of every tracked
file under a spec home, so *any* edit under one (including
`.machine_readable/` itself) must be followed by:

```sh
just registry # regenerates REGISTRY.a2ml + TOPOLOGY.md
git add .machine_readable/REGISTRY.a2ml TOPOLOGY.md
```

If you skip this, the required "Registry + topology in sync" check fails —
and because it is a required check, stale registry hashes on `main` block
*every* open PR, not just yours (see #381). The hook fails the commit with
the exact fix commands whenever the registry is stale.

### Branch Naming
```
docs/short-description # Documentation (P3)
Expand Down
13 changes: 0 additions & 13 deletions a2ml/.github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ on:
permissions: read-all

jobs:
trufflehog:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
with:
fetch-depth: 0 # Full history for scanning

- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@116e7171542d2f1dad8810f00dcfacbe0b809183 # v3
with:
extra_args: --only-verified --fail

gitleaks:
runs-on: ubuntu-latest
timeout-minutes: 20
Expand Down
87 changes: 66 additions & 21 deletions a2ml/actions/validate/validate-a2ml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,32 @@
# validate-a2ml.sh — A2ML manifest validation script
#
# Scans for .a2ml files and validates:
# 1. Required fields: agent-id or pedigree name, version
# 1. Identity presence (warning — see below)
# 2. SPDX-License-Identifier header presence
# 3. Attestation block structure (if present)
# 4. Section heading syntax ([section] or ## section)
#
# On identity (standards#435): the A2ML SPEC's only identity requirement is
# per-record (`@record` needs author/tool/kind, SPEC §7) — there is NO
# normative file-level "must have agent-id/name" rule. An earlier version of
# this script required `agent-id|name|project =` as a hard error and flagged
# the majority of canonical estate files (scorecards identify via `spec_id`,
# contractile Xfiles via `@abstract` + filename, the six-file set via
# `[metadata]`). Identity is therefore checked as a lint WARNING against the
# real estate shapes, and skipped entirely for classes that are identity-free
# by design:
# - AI manifests (AI-MANIFEST*.a2ml, AI.a2ml): markdown prose
# - design-rationale/example trees (INPUT_DESIGN_TREES)
# - templates/scaffolds: basename contains "template", or the body carries
# {{PLACEHOLDER}} markers — a scaffold cannot validate as concrete
#
# Environment variables:
# INPUT_PATH — Directory to scan (default: .)
# INPUT_STRICT — Promote warnings to errors (default: false)
# INPUT_PATH — Directory to scan (default: .)
# INPUT_STRICT — Promote warnings to errors (default: false)
# INPUT_DESIGN_TREES — Space-separated path fragments exempt from
# identity/version checks (default:
# "machine-readable-design/ self-validating/examples/
# docs/templates/")
#
# Exit codes:
# 0 — All files valid (or only warnings in non-strict mode)
Expand All @@ -26,6 +44,12 @@ set -euo pipefail

SCAN_PATH="${INPUT_PATH:-.}"
STRICT="${INPUT_STRICT:-false}"
DESIGN_TREES="${INPUT_DESIGN_TREES:-machine-readable-design/ self-validating/examples/ docs/templates/}"

# Outside GitHub Actions GITHUB_OUTPUT is unset; under `set -u` an unset
# expansion inside a redirection aborts the whole script (the `|| true`
# cannot catch an expansion error). Default to /dev/null for local runs.
GITHUB_OUTPUT="${GITHUB_OUTPUT:-/dev/null}"

# Counters
FILES_SCANNED=0
Expand Down Expand Up @@ -90,44 +114,65 @@ validate_a2ml() {
"Missing SPDX-License-Identifier in first 10 lines"
fi

# --- Check 2: Required identity fields ---
# A2ML files must contain either:
# - agent-id = "..." or agent_id = "..."
# - pedigree block with name field
# - name = "..." at top level (for AI manifests)
# - project = "..." (for STATE.a2ml)
# --- Check 2: Identity presence (lint warning; see header) ---
# Identity shapes actually used across the estate:
# - agent-id / agent_id / name / project / spec_id = "..." (TOML-ish)
# - name: "..." (colon dialect)
# - a [metadata] or [scorecard] section (the six-file set and
# scorecards: the filename + section carry the identity)
# - an @abstract directive (contractile Xfile dialect)
local has_identity=false
local has_version=false
local has_placeholders=false
line_num=0

while IFS= read -r line; do
line_num=$((line_num + 1))

# Check for identity fields (various A2ML patterns)
if [[ "$line" =~ ^[[:space:]]*(agent[-_]id|name|project)[[:space:]]*= ]]; then
if [[ "$line" =~ ^[[:space:]]*(agent[-_]id|name|project|spec_id)[[:space:]]*= ]] \
|| [[ "$line" =~ ^[[:space:]]*name[[:space:]]*: ]] \
|| [[ "$line" =~ ^\[(metadata|scorecard)\] ]] \
|| [[ "$line" =~ ^@abstract ]]; then
has_identity=true
fi
# Check for version field
if [[ "$line" =~ ^[[:space:]]*(version|schema_version)[[:space:]]*= ]]; then
# Check for version field (either separator)
if [[ "$line" =~ ^[[:space:]]*(version|schema_version)[[:space:]]*[=:] ]]; then
has_version=true
fi
# Template placeholder marker ({{PROJECT_NAME}}, {{VERSION}}, …)
if [[ "$line" == *"{{"*"}}"* ]]; then
has_placeholders=true
fi
done < "$file"

# AI manifest files (0-AI-MANIFEST.a2ml, 0.1-AI-MANIFEST.a2ml, etc.)
# use markdown-style headers and free text, so identity check is relaxed
# Classes that are identity-free by design (see header):
local basename
basename="$(basename "$file")"
local is_manifest=false
if [[ "$basename" == *"AI-MANIFEST"* ]]; then
is_manifest=true
local identity_exempt=false
# AI manifests: markdown prose (0-AI-MANIFEST.a2ml, AI.a2ml, …)
if [[ "$basename" == *"AI-MANIFEST"* || "$basename" == "AI.a2ml" ]]; then
identity_exempt=true
fi
# Templates/scaffolds
if [[ "${basename,,}" == *"template"* || "$has_placeholders" == "true" ]]; then
identity_exempt=true
fi
# Design-rationale / example trees (standards#435 option a)
local tree
for tree in $DESIGN_TREES; do
if [[ "$file" == *"$tree"* ]]; then
identity_exempt=true
break
fi
done

if [[ "$has_identity" == "false" && "$is_manifest" == "false" ]]; then
report_issue "error" "$file" 1 \
"Missing required identity field (agent-id, name, or project)"
if [[ "$has_identity" == "false" && "$identity_exempt" == "false" ]]; then
report_issue "warning" "$file" 1 \
"No identity found (agent-id/name/project/spec_id field, [metadata] or [scorecard] section, or @abstract directive)"
fi

if [[ "$has_version" == "false" && "$is_manifest" == "false" ]]; then
if [[ "$has_version" == "false" && "$identity_exempt" == "false" ]]; then
report_issue "warning" "$file" 1 \
"Missing version or schema_version field"
fi
Expand Down
13 changes: 0 additions & 13 deletions consent-aware-http/.github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ on:
permissions: read-all

jobs:
trufflehog:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0 # Full history for scanning

- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3
with:
extra_args: --only-verified --fail

gitleaks:
runs-on: ubuntu-latest
timeout-minutes: 20
Expand Down
Loading
Loading