Skip to content

Commit b73c18e

Browse files
hyperpolymathclaude
andcommitted
feat: canonical A2ML dialect check (TOML), warn-by-default + enforce flag
Estate decision 2026-07-06: ONE canonical .a2ml dialect = TOML (key = value / [section]). The validator was deliberately polyglot (accepted TOML, S-expr, and YAML-ish identity), which is why the estate drifted into three dialects under one extension. Adds Check 5 (canonical dialect): flags repo-specific files still written as YAML or S-expression. Warning by default so the estate can migrate incrementally; new input enforce-canonical-dialect=true promotes it to an error once a repo's files are converted. AI-manifest templates are exempt (converted upstream in rsr-template-repo/standards). README declares TOML canonical. This is the source-of-truth step; template conversion + propagation follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7e1cd7e commit b73c18e

3 files changed

Lines changed: 46 additions & 0 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ RSR (Rhodium Standard Repository) projects to declare machine-readable
1212
metadata, AI agent instructions, and project state. This action scans
1313
for `.a2ml` files and validates their structure and required fields.
1414

15+
# Canonical dialect: TOML
16+
17+
Estate decision (2026-07-06): the **one canonical A2ML dialect is TOML**
18+
(`key = value` / `[section]`). Files written in YAML (`key:`) or S-expression
19+
(`(form ...)`) are non-canonical and should be converted. Check 5 flags them as
20+
a **warning** by default; set `enforce-canonical-dialect: true` to make it a
21+
hard **error** once a repository's `.a2ml` files are all converted. AI-manifest
22+
templates are exempt from this check until they are converted upstream in
23+
`rsr-template-repo` / `standards`.
24+
1525
# Checks Performed
1626

1727
1. **SPDX header** — Verifies `SPDX-License-Identifier` is present in

action.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ inputs:
2929
will fail on any validation issue. Defaults to false.
3030
required: false
3131
default: 'false'
32+
enforce-canonical-dialect:
33+
description: >-
34+
When true, files written in a non-canonical A2ML dialect (YAML or
35+
S-expression instead of canonical TOML) are reported as errors rather
36+
than warnings. Defaults to false so the estate can migrate incrementally.
37+
Canonical A2ML dialect: TOML (key = value / [section]).
38+
required: false
39+
default: 'false'
3240
paths-ignore:
3341
description: >-
3442
Newline-separated path fragments to skip. Each line is matched as a
@@ -69,6 +77,7 @@ runs:
6977
env:
7078
INPUT_PATH: ${{ inputs.path }}
7179
INPUT_STRICT: ${{ inputs.strict }}
80+
INPUT_ENFORCE_CANONICAL_DIALECT: ${{ inputs.enforce-canonical-dialect }}
7281
INPUT_PATHS_IGNORE: ${{ inputs.paths-ignore }}
7382
run: |
7483
"${GITHUB_ACTION_PATH}/validate-a2ml.sh"

validate-a2ml.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set -euo pipefail
2626

2727
SCAN_PATH="${INPUT_PATH:-.}"
2828
STRICT="${INPUT_STRICT:-false}"
29+
ENFORCE_DIALECT="${INPUT_ENFORCE_CANONICAL_DIALECT:-false}"
2930
PATHS_IGNORE_RAW="${INPUT_PATHS_IGNORE:-}"
3031
GITHUB_OUTPUT_FILE="${GITHUB_OUTPUT:-/dev/null}"
3132

@@ -278,6 +279,32 @@ validate_a2ml() {
278279
fi
279280
fi
280281
done < "$file"
282+
283+
# --- Check 5: Canonical dialect (TOML) ---
284+
# Estate canon (2026-07-06): .a2ml is ONE dialect — TOML (`key = value` /
285+
# `[section]`). Flag repo-specific files still written as YAML (`key:`) or
286+
# S-expression (`(form ...)`). AI manifests are template-sourced (converted
287+
# upstream in rsr-template-repo/standards), so they are exempt here to avoid
288+
# estate-wide noise before that conversion lands. Warning by default so the
289+
# estate can migrate incrementally; set enforce_canonical_dialect=true to
290+
# make it a hard error once conversion is complete.
291+
if [[ "$is_manifest" == "false" ]]; then
292+
local _sexpr _toml _yaml _dialect=""
293+
_sexpr=$(grep -cE '^[[:space:]]*\([a-zA-Z]' "$file" 2>/dev/null || true)
294+
_toml=$(grep -cE '^[[:space:]]*(\[[^]]+\]|[A-Za-z0-9_.-]+[[:space:]]*=)' "$file" 2>/dev/null || true)
295+
_yaml=$(grep -cE '^[[:space:]]*[A-Za-z0-9_-]+:[[:space:]]' "$file" 2>/dev/null || true)
296+
if [[ "${_sexpr:-0}" -gt 2 && "${_toml:-0}" -eq 0 ]]; then
297+
_dialect="S-expression"
298+
elif [[ "${_yaml:-0}" -gt 2 && "${_toml:-0}" -le 1 ]]; then
299+
_dialect="YAML"
300+
fi
301+
if [[ -n "$_dialect" ]]; then
302+
local _sev="warning"
303+
[[ "$ENFORCE_DIALECT" == "true" ]] && _sev="error"
304+
report_issue "$_sev" "$file" "1" \
305+
"Non-canonical A2ML dialect ($_dialect). Canonical A2ML dialect is TOML (key = value / [section]); convert this file."
306+
fi
307+
fi
281308
}
282309

283310
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)