From 9431d559e3818c34bee645b8b778433d5c5712a5 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:03:36 -0400 Subject: [PATCH 1/6] chore(tasks): add Aether task composition root --- Taskfile.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Taskfile.yml diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..a8d2d08 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,11 @@ +--- +# yaml-language-server: $schema=https://taskfile.dev/schema.json + +version: "3" + +# Keep Taskfile as an ergonomic orchestration layer. Canonical implementation +# remains in ./aether, GitHub CLI, and the repository's versioned build scripts. +includes: + skills: + taskfile: ./.tasks/skills.yml + flatten: true From ef0c9e091509f3db818653c2d13ddba764f24db0 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:03:44 -0400 Subject: [PATCH 2/6] chore(tasks): wrap skill build and publish flows --- .tasks/skills.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .tasks/skills.yml diff --git a/.tasks/skills.yml b/.tasks/skills.yml new file mode 100644 index 0000000..89df132 --- /dev/null +++ b/.tasks/skills.yml @@ -0,0 +1,38 @@ +--- +# yaml-language-server: $schema=https://taskfile.dev/schema.json + +version: "3" + +vars: + DIST_DIR: '{{default "dist" .DIST_DIR}}' + +tasks: + skills:build: + desc: Build deterministic Aether distributions + cmds: + - ./aether distribution build --output-directory "{{.DIST_DIR}}" + + skills:publish:dry-run: + desc: Build and validate the gh skill publish payload without publishing + preconditions: + - sh: command -v gh >/dev/null 2>&1 + msg: GitHub CLI (`gh`) is required for skill publish validation. + cmds: + - task: skills:build + vars: + DIST_DIR: "{{.DIST_DIR}}" + - gh skill publish "{{.DIST_DIR}}" --dry-run + + skills:publish: + desc: Publish a tagged Aether skill release after the dry-run gate + requires: + vars: + - RELEASE_TAG + preconditions: + - sh: command -v gh >/dev/null 2>&1 + msg: GitHub CLI (`gh`) is required for skill publication. + cmds: + - task: skills:publish:dry-run + vars: + DIST_DIR: "{{.DIST_DIR}}" + - gh skill publish "{{.DIST_DIR}}" --tag "{{.RELEASE_TAG}}" From c45e609df6a108bfee050fef90e9692e43b956a6 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:03:54 -0400 Subject: [PATCH 3/6] docs(tasks): document skill publish wrappers --- docs/taskfile-workflows.md | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/taskfile-workflows.md diff --git a/docs/taskfile-workflows.md b/docs/taskfile-workflows.md new file mode 100644 index 0000000..62623bc --- /dev/null +++ b/docs/taskfile-workflows.md @@ -0,0 +1,62 @@ +# Aether Taskfile workflows + +Taskfile is an optional developer-experience layer over Aether's canonical command surfaces. The repository does not require Taskfile for CI, release automation, or direct use of `./aether` and GitHub CLI. + +## Build distributions + +Convenience command: + +```sh +task skills:build +``` + +Direct equivalent: + +```sh +./aether distribution build --output-directory "dist" +``` + +Override the output directory when needed: + +```sh +task skills:build DIST_DIR="/tmp/aether-dist" +``` + +## Validate a skill publication + +Preferred local convenience command: + +```sh +task skills:publish:dry-run +``` + +This performs the deterministic distribution build first and then runs: + +```sh +gh skill publish "dist" --dry-run +``` + +No release is created by the dry-run task. + +## Publish an explicit tagged release + +Publication is intentionally gated on an explicit `RELEASE_TAG` value: + +```sh +task skills:publish RELEASE_TAG="v1.2.3" +``` + +The task runs the complete `skills:publish:dry-run` path before invoking: + +```sh +gh skill publish "dist" --tag "v1.2.3" +``` + +The GitHub Actions release workflow remains the protected production publication path. This local task is an explicit convenience wrapper, not a replacement for repository release policy or environment review gates. + +## Authority boundary + +- `./aether distribution build` owns deterministic distribution generation. +- `gh skill publish` owns GitHub CLI validation/publication behavior. +- `Taskfile.yml` only sequences those commands for developer ergonomics. +- Credentials remain in the developer or CI environment; Taskfiles must never contain token values. From 848dfa08bcaeeb0b1496688d5f4513af4a0de2a4 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:14:02 -0400 Subject: [PATCH 4/6] test(tasks): lock skill publish wrapper contract --- tests/test_taskfile_workflows.py | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_taskfile_workflows.py diff --git a/tests/test_taskfile_workflows.py b/tests/test_taskfile_workflows.py new file mode 100644 index 0000000..46a1484 --- /dev/null +++ b/tests/test_taskfile_workflows.py @@ -0,0 +1,51 @@ +"""Contract tests for Aether's optional Taskfile workflow wrappers.""" + +from __future__ import annotations + +from pathlib import Path +import unittest + +import yaml + + +ROOT = Path(__file__).resolve().parents[1] + + +class TaskfileWorkflowTests(unittest.TestCase): + def test_root_taskfile_composes_skill_tasks(self) -> None: + root = yaml.safe_load((ROOT / "Taskfile.yml").read_text(encoding="utf-8")) + self.assertEqual(str(root["version"]), "3") + self.assertEqual(root["includes"]["skills"]["taskfile"], "./.tasks/skills.yml") + self.assertTrue(root["includes"]["skills"]["flatten"]) + + def test_publish_tasks_delegate_to_canonical_commands(self) -> None: + workflow = yaml.safe_load( + (ROOT / ".tasks" / "skills.yml").read_text(encoding="utf-8") + ) + tasks = workflow["tasks"] + self.assertEqual( + tasks["skills:build"]["cmds"], + ['./aether distribution build --output-directory "{{.DIST_DIR}}"'], + ) + dry_run = tasks["skills:publish:dry-run"]["cmds"] + self.assertEqual(dry_run[0]["task"], "skills:build") + self.assertEqual(dry_run[1], 'gh skill publish "{{.DIST_DIR}}" --dry-run') + publish = tasks["skills:publish"] + self.assertEqual(publish["requires"]["vars"], ["RELEASE_TAG"]) + self.assertEqual(publish["cmds"][0]["task"], "skills:publish:dry-run") + self.assertEqual( + publish["cmds"][1], + 'gh skill publish "{{.DIST_DIR}}" --tag "{{.RELEASE_TAG}}"', + ) + + def test_taskfiles_do_not_embed_credentials(self) -> None: + content = "\n".join( + path.read_text(encoding="utf-8") + for path in (ROOT / "Taskfile.yml", ROOT / ".tasks" / "skills.yml") + ).lower() + for forbidden in ("github_token:", "gh_token:", "personal_access_token:", "bearer "): + self.assertNotIn(forbidden, content) + + +if __name__ == "__main__": + unittest.main() From a4b403a63ece1832c94c88234a78e30f539f88fd Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:22:59 -0400 Subject: [PATCH 5/6] chore(tasks): align defaults with Task syntax --- .tasks/skills.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tasks/skills.yml b/.tasks/skills.yml index 89df132..bd55f0c 100644 --- a/.tasks/skills.yml +++ b/.tasks/skills.yml @@ -4,7 +4,7 @@ version: "3" vars: - DIST_DIR: '{{default "dist" .DIST_DIR}}' + DIST_DIR: '{{.DIST_DIR | default "dist"}}' tasks: skills:build: From 3effe0476dcb8a5d4bfa4a100d3daccdca36584d Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 23 Aug 2026 16:23:35 -0400 Subject: [PATCH 6/6] docs(tasks): expose publish convenience commands --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 2136198..96f26dc 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Machine-readable catalog and provenance: - Python 3.12+ - Git - GitHub CLI (`gh`) 2.96+ for skill publish/install flows +- Task 3 (optional) for convenience wrappers in `Taskfile.yml` Install dev dependencies: @@ -141,6 +142,16 @@ Validate publishability (no release write): gh skill publish "dist" --dry-run ``` +Optional Taskfile convenience wrappers build first and delegate to the same canonical command surface: + +```sh +task skills:build +task skills:publish:dry-run +task skills:publish RELEASE_TAG="v1.0.0" +``` + +The live Taskfile publish path requires an explicit release tag and runs the dry-run task before publishing. See `docs/taskfile-workflows.md` for direct-command equivalents and ownership boundaries. + ## 9) Install locally with GitHub CLI Install from local build output: