Skip to content

feat(agent-email): single-source version stamping + publish-time --check (bump 0.2.0)#1822

Merged
kovtcharov merged 1 commit into
mainfrom
feat/agent-email-version-stamp
Jun 23, 2026
Merged

feat(agent-email): single-source version stamping + publish-time --check (bump 0.2.0)#1822
kovtcharov merged 1 commit into
mainfrom
feat/agent-email-version-stamp

Conversation

@kovtcharov

Copy link
Copy Markdown
Collaborator

Why this matters

The email package's version lived in eight files of six different types (Python, YAML, TOML, JSON, Markdown, HTML) with no tool to keep them in sync, so references drifted silently. On main right now, binaries.lock.json still pins both agentVersion and baseUrl to …/agents/email/0.1.0 while every other file already says 0.2.0 — a static pointer to a prior hub deployment that no test caught. After this change, AGENT_VERSION in version.py is the one source of truth, a stamp script syncs every other reference from it, and a --check mode fails the build loudly on any mismatch — so a stale version reference can never ship again.

Mirrors the Agent UI's existing pattern (installer/version/bump-ui-version.mjs: one source → stamps dependents → --check gated in CI).

Stamped file types (all driven from AGENT_VERSION): the YAML manifest, pyproject.toml, npm package.json, the lock's agentVersion + baseUrl, the two README image URLs, and the architecture.html version badge. API_VERSION (the REST/contract version) is deliberately not touched — it's the contract version, independent of the package build version.

Cross-branch skip-with-warning: three npm-side targets (README image URLs, assets/architecture.html) don't exist on main yet — they live on in-flight branches (#1776, #1814). The script skips them with a warning rather than failing, so it works across the partial state today and will stamp them correctly once those branches merge. This PR only touches version strings + the new script + the two workflows + the new test; it does not touch the playground HTML, the /v1/email/init endpoint, or the npm client (owned by #1814/#1813/#1776).

This PR also fixes the existing binaries.lock.json drift (0.1.0 → 0.2.0) as the first run of the new stamper.

Test plan

  • python hub/agents/python/email/packaging/stamp_version.py --check passes on the post-bump tree (exit 0)
  • Mutating any target to a wrong version makes --check exit non-zero (covered by test_stamp_version.py)
  • python -m pytest hub/agents/python/email/tests/test_stamp_version.py — 10 passed (hermetic, no network)
  • Version-contract tests green with the bump: test_agent_version_matches_package_export + test_agent_version_matches_package_metadata (pyproject + in-code AGENT_VERSION both 0.2.0)
  • black + isort clean on the new files
  • --check wired into release_agent_email.yml (before publish) and test_email_agent_unit.yml (early PR drift gate; npm-side paths added to its triggers)

…eck (bump 0.2.0)

The email package version lived in eight files of six types (Python, YAML,
TOML, JSON, Markdown, HTML) with no sync tool, so references drifted: on main
binaries.lock.json still pointed agentVersion + baseUrl at email/0.1.0 while
every other file had already moved to 0.2.0 — a stale static reference to a
prior hub deployment that nothing caught.

Make AGENT_VERSION in gaia_agent_email/version.py the one source of truth and
add packaging/stamp_version.py to stamp every downstream reference from it
(mirrors installer/version/bump-ui-version.mjs). Default mode stamps; --check
verifies and exits non-zero on any drift. Wired --check into the release job
(before publish) and the unit-test PR workflow (early drift detection). Targets
absent on main (npm README image URL, assets/architecture.html — they land on
other in-flight branches) are skipped-with-warning so the script works across
that partial state and stamps them once those branches merge.

API_VERSION (the REST/contract version) is deliberately untouched — it is the
contract version, independent of the package build version.
@github-actions github-actions Bot added the devops DevOps/infrastructure changes label Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Summary

Solid, tightly-scoped DevOps PR that closes a real gap: the email package's version lived in eight files across six formats with no sync tool, and main was already shipping a binaries.lock.json whose baseUrl pointed at a stale …/agents/email/0.1.0 hub directory. This makes AGENT_VERSION in version.py the single source of truth, adds a stdlib-only stamp/--check script (mirroring the Agent UI's bump-ui-version.mjs), wires --check into both the PR unit workflow and the pre-publish release job, and fixes the existing 0.1.0 drift. I verified it end-to-end locally: --check exits 0 on the current tree, the 10 hermetic tests all pass, and the gate sits correctly in the publish job after the main-branch guard. The one thing worth a second look before merge: the uniform skip-on-no-match policy can let a mandatory target silently pass the gate it's meant to enforce.

Issues Found

🟡 Important

Skip-on-no-match can mask drift in always-present targets (packaging/stamp_version.py:237-246)

Every rule is treated identically: if its regex finds no match, the target is skipped-with-warning and --check still returns 0. That's exactly right for the three cross-branch npm targets (README images, architecture.html) — but four targets are always on main and gen-controlled: gaia-agent.yaml, pyproject.toml, npm package.json, and the two binaries.lock.json fields. For those, "regex matched nothing" doesn't mean "absent on another branch" — it means the file was reformatted in a way the pattern no longer recognizes, and the gate goes green while a real drift ships. That's precisely the failure mode this PR exists to prevent.

The patterns are format-fragile in ways that make this concrete, not hypothetical — e.g. the package.json rule hardcodes a two-space indent (^( "version":); a reformat to 4-space/tabs makes it silently un-matched rather than caught.

Cheap fix: mark the always-present rules required and fail (not skip) when a required rule matches nothing.

@dataclass
class Rule:
    label: str
    path: Path
    pattern: re.Pattern
    field: str
    required: bool = False  # always-present targets: no-match is a FAILURE, not a skip

Then in process(), when required and not matches, append to result.mismatches instead of result.skipped. The yaml/pyproject/package.json/lock rules get required=True; the README-image and architecture.html rules stay optional. Keeps the cross-branch tolerance you designed for while restoring the guarantee for targets that can't legitimately be absent.

🟢 Minor

Test coverage gap mirrors the issue above (tests/test_stamp_version.py) — there's no test asserting that a present-but-unmatchable mandatory target fails --check. If you adopt the required flag, add a case that reformats package.json (e.g. 4-space indent) and asserts --check exits non-zero. Without it, the existing suite would stay green through exactly the regression the script is meant to catch.

Strengths

  • Genuinely follows an existing repo pattern rather than inventing one — single source → stamps dependents → --check gated in CI, explicitly mirroring installer/version/bump-ui-version.mjs. The deliberate carve-out of API_VERSION (contract version, independent of build version) is correct and tested.
  • Tests are hermetic and meaningful — synthesized temp tree, no network, no dependence on the repo's live versions; they cover consistency, drift-detection, idempotency (byte-identical no-op), and the cross-branch skip behavior. Stamping is minimal-diff by design (only group 2 rewritten), which the idempotency test verifies.
  • Stamp/check parity is realprocess() drives both modes off the same rule set, so the gate can't validate a different shape than the stamper writes. Both workflow wirings are placed well: early PR drift gate plus a pre-publish gate behind the agent-publish environment's human approval.

Verdict

Approve with suggestions. No blocking defects — the script does what it claims and is well-tested. The 🟡 is a hardening of the PR's own central guarantee (don't let a mandatory target's drift pass silently) with a low-cost fix; reasonable to land in this PR or a fast follow-up, author's call.

@kovtcharov kovtcharov added this pull request to the merge queue Jun 23, 2026
Merged via the queue into main with commit a8f414e Jun 23, 2026
27 checks passed
@kovtcharov kovtcharov deleted the feat/agent-email-version-stamp branch June 23, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devops DevOps/infrastructure changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants