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
27 changes: 18 additions & 9 deletions .agents/scripts/test_skill_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ def test_skill_frontmatter_declares_a_semver_release_version(self) -> None:
assert match is not None
self.assertRegex(match.group(1), SEMVER)

def test_readme_release_link_matches_the_skill_version(self) -> None:
skill = SKILL.read_text(encoding="utf-8")
version = re.search(r"^version:\s*([^\s#]+)\s*$", skill, re.MULTILINE)
self.assertIsNotNone(version, "SKILL.md must declare a release version")
assert version is not None
readme = (ROOT / "README.md").read_text(encoding="utf-8")
expected = f"releases/tag/v{version.group(1)}"
self.assertIn(expected, readme, "README GitHub Release link must match SKILL.md version")

def test_version_extraction_step_executes_and_writes_outputs(self) -> None:
workflow = WORKFLOW.read_text(encoding="utf-8")
match = re.search(
Expand Down Expand Up @@ -65,7 +74,7 @@ def test_version_extraction_step_executes_and_writes_outputs(self) -> None:
else:
os.environ["GITHUB_OUTPUT"] = previous_output

self.assertEqual(output_path.read_text(encoding="utf-8"), "skill_version=0.2.1\ntag=v0.2.1\n")
self.assertEqual(output_path.read_text(encoding="utf-8"), "skill_version=0.2.2\ntag=v0.2.2\n")

def test_version_extraction_step_rejects_invalid_numeric_prerelease_identifiers(self) -> None:
workflow = WORKFLOW.read_text(encoding="utf-8")
Expand All @@ -85,7 +94,7 @@ def test_version_extraction_step_rejects_invalid_numeric_prerelease_identifiers(
skill_path = root / "skills/api-to-typemcp/SKILL.md"
skill_path.parent.mkdir(parents=True)
skill_path.write_text(
original.replace("version: 0.2.1", f"version: {invalid_version}"),
original.replace("version: 0.2.2", f"version: {invalid_version}"),
encoding="utf-8",
)
previous_cwd = Path.cwd()
Expand Down Expand Up @@ -243,9 +252,9 @@ def test_existing_unpublished_skill_is_recovered_before_version_reconciliation(s
[
(200, {"data": [{"slug": "integration"}]}),
(200, {"slug": "api-to-typemcp", "status": "DRAFT"}),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.1"}),
(200, [{"version": "0.2.1"}]),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.1"}),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.2"}),
(200, [{"version": "0.2.2"}]),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.2"}),
]
)
calls: list[tuple[str, str]] = []
Expand All @@ -258,7 +267,7 @@ def mocked_request(

environment = {
"SKILLS_HUB_AI_API_KEY": "test-key",
"SKILL_VERSION": "0.2.1",
"SKILL_VERSION": "0.2.2",
"GITHUB_SHA": "test-sha",
"GITHUB_REPOSITORY": "Theorvane/type-mcp-api-agent-skill",
}
Expand All @@ -280,8 +289,8 @@ def test_version_conflict_is_reconciled_without_a_second_mutation(self) -> None:
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED"}),
(200, []),
(409, {"error": "version already exists"}),
(200, [{"version": "0.2.1"}]),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.1"}),
(200, [{"version": "0.2.2"}]),
(200, {"slug": "api-to-typemcp", "status": "PUBLISHED", "latestVersion": "0.2.2"}),
]
)
calls: list[tuple[str, str]] = []
Expand All @@ -294,7 +303,7 @@ def mocked_request(

environment = {
"SKILLS_HUB_AI_API_KEY": "test-key",
"SKILL_VERSION": "0.2.1",
"SKILL_VERSION": "0.2.2",
"GITHUB_SHA": "test-sha",
"GITHUB_REPOSITORY": "Theorvane/type-mcp-api-agent-skill",
}
Expand Down
15 changes: 13 additions & 2 deletions .agents/scripts/validate_release_promotion.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
"""Contract checks for safe `dev`/release-branch promotion into `main`."""

from pathlib import Path

workflow = Path(".github/workflows/release-promotion.yml").read_text(encoding="utf-8")

assert "branches: [main]" in workflow, "release gate must target main"
assert "HEAD_REF: ${{ github.head_ref }}" in workflow, "release gate must read the PR head"
assert 'test "$HEAD_REF" = "dev"' in workflow, "only dev may promote to main"
print("verified release promotion accepts only dev into main")
assert 'test "$HEAD_REF" = "dev"' not in workflow, "release branches must not be rejected by a dev-only guard"
assert '[[ "$HEAD_REF" = "dev" || "$HEAD_REF" =~ ^release/[1-9][0-9]*-[a-z0-9]+(-[a-z0-9]+)*$ ]]' in workflow, (
"promotion may accept only dev or a strict positive-issue release branch"
)
assert "actions/checkout@11d5960a326750d5838078e36cf38b85af677262" in workflow, "lineage guard needs a pinned checkout"
assert "fetch-depth: 0" in workflow, "lineage guard needs complete history"
assert 'git merge-base --is-ancestor origin/main HEAD' in workflow, "candidate must retain main ancestry"
assert 'git merge-base --is-ancestor origin/dev HEAD' in workflow, "candidate must retain dev ancestry"
assert 'test "$(git rev-list --parents -n 1 HEAD | awk' in workflow, "release branch must use a two-parent merge commit"
print("verified release promotion accepts dev or a strict lineage-preserving release branch into main")
64 changes: 64 additions & 0 deletions .agents/task-briefs/69-promote-api-to-typemcp-v0-2-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Task brief: 69 — promote api-to-typemcp v0.2.1 to main

**Status:** review
**Issue:** https://github.com/Theorvane/type-mcp-api-agent-skill/issues/69
**Branch:** `release/69-api-to-typemcp-v0-2-1`
**Owner:** Hermes Agent

## Goal

Promote the reviewed `api-to-typemcp` v0.2.1 release candidate from `dev` to release-only `main`, preserving current release ancestry and publishing the same immutable version to GitHub, ClawHub, and skills-hub.ai.

## Source references

- Release policy: `docs/guides/skill-release.md`
- Release checklist: `.agents/checklists/release-readiness.md`
- Security hardening: #65
- ClawHub public-confirmation: #61
- Release preparation: #67

## Scope

### Included

- Merge current `origin/dev` into a branch created from `origin/main` with a two-parent merge commit.
- Run release-candidate verification on that exact merge commit.
- Deliver a `main` PR with exact-head approval and merge it with a merge commit.
- Verify resulting v0.2.1 release identity and all public registries.

### Excluded

- New feature/security implementation changes.
- Manual tags, releases, or registry mutations outside the guarded main-push workflow.

## Safety and contract notes

- Source input: `origin/main` `699638c08dba58eb99c1cc54f6c0e193af5237ed` and `origin/dev` `2274a105e701a0a93a90a636b7273f7d21b9cfa8`.
- Secrets: only repository Actions secrets; no credential read, output, or persistence.
- Side effects: GitHub release and registry publication occur only after reviewed main merge.
- Compatibility: generated projects remain bound to published `@theorvane/type-mcp@0.2.0` while the skill artifact is v0.2.1.

## Test-first / merge-candidate evidence

| Stage | Command | Expected/observed result |
| --- | --- | --- |
| Candidate | `git show -s --format='%P' HEAD` | Exact release candidate has two parents: old main then reviewed dev. |
| Red | `python3 .agents/scripts/validate_release_promotion.py` | Failed at the obsolete dev-only guard, proving a lineage-preserving release branch could not be promoted. |
| Green | `python3 .agents/scripts/validate_release_promotion.py` | Passed after requiring strict `release/<positive-issue>-<kebab>` naming and main/dev ancestry. |
| Verification | full release and generated-project suite | Passed: release contract 15, docs 8, workspace 3, engine 98; generated project `npm ci`, build, Vitest, and audit (0 vulnerabilities). |
| Publication | guarded main-push workflow | Pending after reviewed merge. |

## Verification

- [x] Unit/integration tests
- [x] Generator or generated-project E2E test
- [x] Build/package validation
- [x] `git diff --check`
- [x] Documentation updated
- [ ] Independent specification review recorded
- [ ] Independent code-quality review recorded

## Review notes

- Main was not an ancestor of dev. The candidate merge commit is intentional lineage repair and must be merged into main using a merge commit, not squash or rebase.
- The former dev-only release guard rejected this candidate. It now accepts only `dev` or a strict `release/<positive-issue>-<kebab>` name and validates main/dev ancestry; arbitrary PR branches remain blocked.
58 changes: 58 additions & 0 deletions .agents/task-briefs/75-release-api-to-typemcp-v0-2-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Task brief: 75 — reconcile and publish api-to-typemcp v0.2.2

**Status:** in-progress
**Issue:** https://github.com/Theorvane/type-mcp-api-agent-skill/issues/75
**Branch:** `chore/75-release-api-to-typemcp-v0-2-2`
**Owner:** sjungwon03

## Goal

Publish the reviewed Hermes and Claude Code MCP-registration capability as public `api-to-typemcp` version `0.2.2` through the protected `dev` → `main` release path.

## Source references

- Release contract: `docs/guides/skill-release.md`
- Release checklist: `.agents/checklists/release-readiness.md`
- Reviewed feature PR: https://github.com/Theorvane/type-mcp-api-agent-skill/pull/74

## Scope

### Included

- Preserve the divergent `main` release automation history by merging it into the current `dev` lineage with a no-fast-forward reconciliation commit.
- Bump the skill frontmatter version from `0.2.1` to `0.2.2`.
- Update the release-contract expected output and verify the bundled skill, generated-project E2E, and public release artifacts.

### Excluded

- New MCP engine behavior beyond reviewed `dev` content.
- Manual GitHub release, tag, ClawHub, or skills-hub.ai mutation.

## Safety and contract notes

- Source input: release promotion starts only from the current reviewed `dev` branch and preserves both release histories.
- Secrets: registry credentials remain GitHub Actions secrets only; no credential is read, printed, or committed.
- Side effects: GitHub Release and registry publication occur only after a reviewed `dev` → `main` merge.
- Compatibility: this is a backward-compatible feature release; generated projects continue to use the reviewed public TypeMCP runtime contract.

## Test-first evidence

| Stage | Command | Expected/observed result |
| --- | --- | --- |
| Red | `python3 .agents/scripts/test_skill_release.py` | Failed as expected after the contract expected `0.2.2` while `SKILL.md` remained `0.2.1`. |
| Green | `python3 .agents/scripts/test_skill_release.py` | Passed: 16 tests after the frontmatter, public README release link, and release-publisher fixture version were updated to `0.2.2`. |
| Regression | repository verification baseline plus generated-project E2E | Passed: 140 engine tests, 8 documentation tests, 3 workspace tests, 16 release-contract tests, documentation validation, Python compile, and `git diff --check`. |

## Verification

- [ ] Unit/integration tests
- [ ] Generator or generated-project E2E test
- [ ] Build/package validation
- [ ] `git diff --check`
- [ ] Documentation updated
- [ ] Independent specification review recorded
- [ ] Independent code-quality review recorded

## Review notes

- Pending exact-head independent review after PR creation.
18 changes: 16 additions & 2 deletions .github/workflows/release-promotion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,19 @@ jobs:
env:
HEAD_REF: ${{ github.head_ref }}
steps:
- name: Require the dev integration branch
run: test "$HEAD_REF" = "dev"
- name: Require dev or a strictly named release candidate
run: |
[[ "$HEAD_REF" = "dev" || "$HEAD_REF" =~ ^release/[1-9][0-9]*-[a-z0-9]+(-[a-z0-9]+)*$ ]]

- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0

- name: Require release-lineage ancestry
run: |
git fetch --no-tags origin main dev
git merge-base --is-ancestor origin/main HEAD
git merge-base --is-ancestor origin/dev HEAD
if [[ "$HEAD_REF" = release/* ]]; then
test "$(git rev-list --parents -n 1 HEAD | awk '{print NF - 1}')" -eq 2
fi
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Tasks 1–7 of embedded-engine delivery are implemented on `dev`: structured Ope

- [ClawHub (`@sjungwon03/api-to-typemcp`)](https://clawhub.ai/sjungwon03/api-to-typemcp)
- [skills-hub.ai (`api-to-typemcp`)](https://skills-hub.ai/skills/api-to-typemcp)
- [GitHub Release v0.2.1](https://github.com/Theorvane/type-mcp-api-agent-skill/releases/tag/v0.2.1)
- [GitHub Release v0.2.2](https://github.com/Theorvane/type-mcp-api-agent-skill/releases/tag/v0.2.2)

## Layout

Expand Down
2 changes: 1 addition & 1 deletion skills/api-to-typemcp/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: api-to-typemcp
description: Use when turning supplied API sources into a safe TypeMCP project.
version: 0.2.1
version: 0.2.2
category: integration
license: MIT
metadata:
Expand Down
Loading