diff --git a/.github/workflows/example-metadata-check.yml b/.github/workflows/example-metadata-check.yml index af34535..80b0510 100644 --- a/.github/workflows/example-metadata-check.yml +++ b/.github/workflows/example-metadata-check.yml @@ -19,7 +19,10 @@ on: - "snippets/**" - "scenarios/**/pyproject.toml" - "scenarios/**/package.json" + - "scenarios/**/go.mod" + - "scenarios/**/Dockerfile" - "scenarios/**/README.md" + - "scripts/test_generate_example_metadata.py" - ".github/workflows/example-metadata-check.yml" push: branches: @@ -60,3 +63,9 @@ jobs: echo "::error:: python scripts/extract_snippets.py" exit 1 fi + + - name: Audit for orphan SDK-version literals + run: python scripts/generate_example_metadata.py --check + + - name: Run generator unit tests + run: python -m unittest scripts.test_generate_example_metadata diff --git a/go/README.md b/go/README.md index 89a586d..20dd522 100644 --- a/go/README.md +++ b/go/README.md @@ -30,7 +30,7 @@ All examples use the [`github.com/ai-agent-assembly/go-sdk`](https://pkg.go.dev/ ## Prerequisites - Go >= 1.26 -- Agent Assembly Go SDK v0.0.1-rc.3 +- Agent Assembly Go SDK v0.0.1-rc.5 A live gateway is **not required** to run any of these examples — each uses an offline mock `GovernanceClient` by default so you can explore governance behavior diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..5d1e02e --- /dev/null +++ b/scripts/__init__.py @@ -0,0 +1,7 @@ +"""Repo tooling package. + +Exists so the metadata generator's unit tests are importable as +``scripts.test_generate_example_metadata`` (``python -m unittest``). The +generator and snippet extractors are also runnable directly by path, which does +not depend on this marker. +""" diff --git a/scripts/generate_example_metadata.py b/scripts/generate_example_metadata.py index 9a55ecf..d3835a3 100755 --- a/scripts/generate_example_metadata.py +++ b/scripts/generate_example_metadata.py @@ -22,6 +22,15 @@ Historical release notes and example-provenance content (e.g. "written against 0.0.1-rc.3") are out of scope and must not be touched. + +Audit mode (``--check``): instead of rewriting, scan the same bounded set of +human-authored surfaces (manifest pins under ``python/ node/ go/ scenarios/`` +and SDK-version prose in READMEs / ``docs/*.md`` — never lockfiles or vendored +trees) and exit non-zero, naming each ``file:line``, if any SDK-version literal +has drifted from the SoT. This is the anti-recurrence invariant: a newly added +stale surface fails CI. Legitimate historical/provenance text is exempted per +line by the inline marker ``sdk-version-exempt``; such a line is never rewritten +and never audited. """ from __future__ import annotations @@ -251,6 +260,32 @@ def _sub(match: re.Match[str]) -> str: return _write_if_changed(path, new_text) +# --------------------------------------------------------------------------- +# Dockerfile pin rewrites +# --------------------------------------------------------------------------- +# +# A handful of scenario Dockerfiles ``pip install`` the published Python SDK by +# an exact ``agent-assembly==`` pin (the live-core-enforcement +# python-agent image). Left to hand-maintenance these drift on every SDK bump — +# AAASM-4702 had to hand-correct one. The generator now owns that pin too so +# future bumps (and the --check audit) cover it. The negative lookbehind anchors +# the match to the standalone ``agent-assembly`` project name so it can never +# fire on ``@agent-assembly/sdk`` or ``github.com/ai-agent-assembly/...``. +_DOCKERFILE_PIN_RE = re.compile( + r'(?agent-assembly==)(?P[^"\s]+)' +) + + +def rewrite_dockerfile(path: Path, sdk: PythonSdk) -> bool: + text = path.read_text(encoding="utf-8") + + def _sub(match: re.Match[str]) -> str: + return f"{match.group('pre')}{sdk.version}" + + new_text = _DOCKERFILE_PIN_RE.sub(_sub, text) + return _write_if_changed(path, new_text) + + # --------------------------------------------------------------------------- # README bounded-block rewrites # --------------------------------------------------------------------------- @@ -420,6 +455,41 @@ def _sub(match: re.Match[str]) -> str: return _write_if_changed(path, new_text) +# Some landing-page READMEs state the same requirement as a Markdown *bullet* +# instead of a table row — ``go/README.md`` carries ``- Agent Assembly Go SDK +# `` under ``## Prerequisites`` (AAASM-4717). The bullet is anchored to +# a list marker (``-``/``*``), so it can never collide with the table row (which +# starts with ``|``) nor with the generated sdk-install block. Only the first +# version token in the value is rewritten, so a trailing note is preserved +# verbatim, exactly like the table-row pass. +def _prereq_bullet_re(label: str) -> re.Pattern[str]: + return re.compile( + r"^(?P
[ \t]*[-*][ \t]+Agent Assembly "
+        + re.escape(label)
+        + r" SDK[ \t]+)(?P.*)$",
+        re.MULTILINE,
+    )
+
+
+def rewrite_prereq_bullet(path: Path, label: str, version: str) -> bool:
+    """Align a bullet-form ``- Agent Assembly