Files
playbook/docs/superpowers/plans/2026-07-13-sync-tsl-playbook-workflow-upgrade.md
T

8.8 KiB

Sync TSL Playbook Workflow Upgrade Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make the tsl-playbook workflow update only the generated TSL playbook paths while preserving every unrelated target-branch file.

Architecture: Keep the existing temporary clone and external bundle. Replace the broad AGENTS.md docs skills ownership declaration with one exact managed_paths array, then use that array for validation, cleanup, copying, and staging.

Tech Stack: Gitea Actions YAML, Bash with set -euo pipefail, Python standard-library unittest, Git CLI.

Global Constraints

  • Own only AGENTS.md, docs/tsl/, skills/tsl-syntax-reference/, and skills/tsl-api-reference/.
  • Preserve root files, non-TSL docs, unrelated Skills, and all other unmanaged target-branch paths.
  • Do not special-case docs/tsl/syntax; the source tree and builder determine bundle contents.
  • Do not merge source and target branches.
  • Keep GIT_ASKPASS; never embed credentials in the repository URL.
  • Build outside the repository before checking out the target branch.
  • Do not modify TSL content, the builder, or either Skill.
  • Do not execute a TSL interpreter.

Task 1: Enforce exact workflow ownership

Files:

  • Modify: test/test_build_tsl_playbook.py
  • Modify: .gitea/workflows/sync-tsl-playbook.yml

Interfaces:

  • Consumes: bundle paths AGENTS.md, docs/tsl, skills/tsl-syntax-reference, and skills/tsl-api-reference.

  • Produces: one managed_paths Bash array used by validation, removal, copy, and git add -A.

  • Preserves: every target-branch path absent from managed_paths.

  • Step 1: Write the failing static contract assertions

Replace test_sync_workflow_does_not_remove_entire_target_branch with:

    def test_sync_workflow_does_not_remove_entire_target_branch(self):
        text = SYNC_WORKFLOW.read_text(encoding="utf-8")

        self.assertNotRegex(text, r"git rm -rf --quiet\s+\.")
        self.assertIn("managed_paths=(", text)
        for path in (
            "AGENTS.md",
            "docs/tsl",
            "skills/tsl-syntax-reference",
            "skills/tsl-api-reference",
        ):
            self.assertIn(f'"{path}"', text)
        self.assertNotIn("generated_paths=(AGENTS.md docs skills)", text)
        self.assertIn('rm -rf -- "${managed_paths[@]}"', text)
        self.assertIn('git add -A -- "${managed_paths[@]}"', text)
        self.assertNotIn('cp -R "$bundle"/. "$REPO_DIR"/', text)
        self.assertNotIn(".gitea/ci/", text)
        self.assertNotIn("https://oauth2", text)
        self.assertNotIn("oauth2:${TOKEN}", text)
        self.assertNotRegex(text, r"REPO_URL=.*(TOKEN|WORKFLOW)")
        self.assertNotIn("git remote set-url", text)
        self.assertIn("GIT_ASKPASS", text)
        self.assertIn('REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"', text)
  • Step 2: Extend the existing-target integration fixture

In test_sync_preserves_files_outside_generated_paths, replace the standalone README setup with:

            unmanaged_files = {
                "README.md": "manual branch note\n",
                "docs/python/index.md": "manual python docs\n",
                "skills/manual-skill/SKILL.md": "manual skill\n",
            }
            for relative, content in unmanaged_files.items():
                path = repo / relative
                path.parent.mkdir(parents=True, exist_ok=True)
                path.write_text(content, encoding="utf-8", newline="\n")

            stale_managed_files = (
                "docs/tsl/stale.md",
                "skills/tsl-syntax-reference/stale.md",
                "skills/tsl-api-reference/stale.md",
            )
            for relative in stale_managed_files:
                path = repo / relative
                path.parent.mkdir(parents=True, exist_ok=True)
                path.write_text("stale\n", encoding="utf-8", newline="\n")

            git(repo, "add", ".")
            git(repo, "commit", "-m", "manual target branch content")
            git(repo, "push", "-u", "origin", "tsl-playbook")
  • Step 3: Assert preservation, cleanup, and idempotence

After the first run_sync(repo), add:

            for relative, expected in unmanaged_files.items():
                result = run(
                    ["git", "show", f"HEAD:{relative}"],
                    cwd=repo,
                    check=False,
                )
                self.assertEqual(result.returncode, 0, msg=result.stderr)
                self.assertEqual(result.stdout, expected)

            for relative in stale_managed_files:
                result = run(
                    ["git", "cat-file", "-e", f"HEAD:{relative}"],
                    cwd=repo,
                    check=False,
                )
                self.assertNotEqual(result.returncode, 0, msg=relative)

            first_publish = git(repo, "rev-parse", "HEAD").stdout.strip()
            git(repo, "checkout", "main")
            run_sync(repo)
            second_publish = git(repo, "rev-parse", "HEAD").stdout.strip()
            self.assertEqual(second_publish, first_publish)

Keep the existing assertions that generated AGENTS.md, docs/tsl/index.md, and both TSL Skill entrypoints exist.

  • Step 4: Run the focused suite and verify RED
python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v

Expected: FAIL because the current workflow has no managed_paths array and deletes the whole target docs and skills directories.

  • Step 5: Define the exact managed paths in the workflow

Replace generated_paths=(AGENTS.md docs skills) with:

          managed_paths=(
            "AGENTS.md"
            "docs/tsl"
            "skills/tsl-syntax-reference"
            "skills/tsl-api-reference"
          )

Use it for pre-checkout validation:

          for path in "${managed_paths[@]}"; do
            if [ ! -e "$bundle/$path" ]; then
              echo "ERROR: bundle is missing expected path: $path" >&2
              exit 1
            fi
          done
  • Step 6: Implement exact cleanup, copying, and staging

Replace the broad removal, bundle-root copy, and staging commands with:

          rm -rf -- "${managed_paths[@]}"
          for path in "${managed_paths[@]}"; do
            mkdir -p "$(dirname "$path")"
            cp -R -- "$bundle/$path" "$path"
          done

          git add -A -- "${managed_paths[@]}"

Do not add a docs/tsl/syntax condition or any other path exception.

  • Step 7: Run the focused suite and verify GREEN
python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v

Expected: all 7 tests pass. The integration test proves unmanaged root/docs/skills files survive, stale managed files disappear, and a second identical publication creates no commit.

  • Step 8: Inspect and commit the implementation
git diff --check
git diff -- .gitea/workflows/sync-tsl-playbook.yml test/test_build_tsl_playbook.py
git add .gitea/workflows/sync-tsl-playbook.yml test/test_build_tsl_playbook.py
git commit -m ':wrench: chore(ci): scope tsl playbook sync paths'

Expected: the commit contains only the workflow and its regression tests.


Task 2: Final workflow verification

Files:

  • Verify: .gitea/workflows/sync-tsl-playbook.yml
  • Verify: test/test_build_tsl_playbook.py

Interfaces:

  • Consumes: Task 1's exact managed-path publication flow.

  • Produces: fresh evidence that the workflow is scoped, secure, idempotent, and compatible with new target branches.

  • Step 1: Run the full focused suite in a clean process

python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v

Expected: 7 tests pass with OK and no errors or failures.

  • Step 2: Verify the ownership contract directly
rg -n 'managed_paths|generated_paths|rm -rf|cp -R|git add -A' .gitea/workflows/sync-tsl-playbook.yml

Expected: one four-entry managed_paths definition; no broad generated_paths; removal, copy, and staging derive from managed_paths; no whole-bundle copy.

  • Step 3: Verify repository state
git status --short
git show --stat --oneline HEAD

Expected: the implementation commit contains only .gitea/workflows/sync-tsl-playbook.yml and test/test_build_tsl_playbook.py, with a clean working tree.

  • Step 4: Avoid a verification-only commit

If verification passes without corrections, stop. If it exposes a defect, return to Task 1's RED → GREEN cycle and commit only the correction:

git commit -m ':bug: fix(ci): close tsl playbook sync verification gap'