From 7eb4a21049457ed400e5c953cd6aaec133965311 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 16 Aug 2026 17:45:12 +0000 Subject: [PATCH 1/3] style: strip AI slop from authored skills and tests Co-authored-by: Ben Sykes --- .github/workflows/pr-title-lint.yml | 6 +- skills/agentsmd-generator/SKILL.md | 135 ++++++------------ .../agentsmd-generator/scripts/repo-inventory | 2 +- skills/work-breakdown/SKILL.md | 15 +- tests/test_assess_linear_sdd_state.py | 21 +-- tests/test_install_linear_agent.py | 6 +- tests/test_jj_clone_diagnose.py | 3 +- tests/test_repo_inventory.py | 2 +- tests/test_research_metadata.py | 6 +- tests/test_upstream_catalog.py | 2 +- 10 files changed, 60 insertions(+), 138 deletions(-) diff --git a/.github/workflows/pr-title-lint.yml b/.github/workflows/pr-title-lint.yml index 63d5fcd..28e6e52 100644 --- a/.github/workflows/pr-title-lint.yml +++ b/.github/workflows/pr-title-lint.yml @@ -38,15 +38,12 @@ jobs: didn't match the configured pattern. Please ensure that the subject doesn't start with an uppercase character. - # Add comment when PR title is invalid - uses: marocchino/sticky-pull-request-comment@v2 if: always() && (steps.lint_pr_title.outputs.error_message != null) with: header: pr-title-lint-error message: | - ๐Ÿ‘‹ Thank you for opening this pull request! - - We require PR titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/). + PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/). **Error details:** @@ -60,7 +57,6 @@ jobs: Please update your PR title and the check will run automatically. - # Delete comment when PR title is fixed - uses: marocchino/sticky-pull-request-comment@v2 if: ${{ steps.lint_pr_title.outputs.error_message == null }} with: diff --git a/skills/agentsmd-generator/SKILL.md b/skills/agentsmd-generator/SKILL.md index a85e274..4331cbc 100644 --- a/skills/agentsmd-generator/SKILL.md +++ b/skills/agentsmd-generator/SKILL.md @@ -3,78 +3,41 @@ name: agentsmd-generator description: Generate project-level AGENTS.md guides that capture conventions, workflows, and required follow-up tasks. Use when a repository needs clear agent onboarding covering structure, tooling, testing, task flow, README expectations, and conventional commit summaries. license: MIT allowed-tools: Read Write Edit Bash(ls:*) Bash(git:*) Bash(just:*) Bash(make:*) Bash(tree:*) Bash(scripts/repo-inventory:*) -metadata: - generated-at: "2026-01-10T00:00:00Z" - group: "enablement" - category: "documentation" - difficulty: "intermediate" - step-count: "4" --- # Agent Context Generator -## What You'll Do -- ๐Ÿ” Inventory the repository's structure, capture a `.gitignore`-aware `tree` output, and record automation entry points (preferring `just`/`make` tasks when available) -- ๐Ÿงญ Capture coding conventions, directory ownership, testing expectations, and review workflows so future agents can navigate confidently -- ๐Ÿงฉ Produce an `AGENTS.md` file following the opinionated section order below, honoring scope rules for nested directories -- โœ… Embed universal wrap-up tasks: ensure the README is updated after significant code changes and summarize changes per conventional commits while resolving any open questions with the developer +Inventory the repo from source (not docs), then write an `AGENTS.md` that future agents can follow. Treat README/CONTRIBUTING/`docs/` as hints only: verify every claim against code, configs, scripts, CI, and manifests. When they disagree, the code wins โ€” flag the discrepancy. ---- +## Phase 1 ยท Understand the repository -## Phase 1 ยท Understand the Repository - -> **CRITICAL: The codebase is the sole source of truth.** Never trust repo documentation (README, CONTRIBUTING, docs/, etc.) as authoritative. Treat all documentation as potentially stale or wrong. Always validate claims by inspecting actual source files, configs, scripts, CI pipelines, and dependency manifests. When documentation contradicts the code, the code wins. Flag discrepancies for the developer. - -1. **Run the bundled inventory script first** - - Run [`scripts/repo-inventory`](scripts/repo-inventory) from the repo root. It performs the deterministic data-gathering so you don't run a dozen probes by hand: a `.git`/`.jj`-pruned, gitignore-aware `tree` (with `tree --prune` and `git ls-files` fallbacks), detected `languages`/`package_managers`, automation `runners` plus `make_targets`/`just_recipes`, `ci_files`, and `env_files`. - - Read its `key=value` lines as the **factual baseline** for the steps below. Use `-C ` to scope a subdirectory or `--depth ` to widen/trim the tree. - - This is fact-gathering only; everything below adds the judgment the script cannot infer (ownership, intent, stale-doc reconciliation). -2. **Check for existing AGENTS.md** - - Use `glob` or the inventory tree to discover current files. Determine scope inheritance so you can update or extend instead of duplicating. -3. **Skim Docs as Hints Only** - - Skim `README.md`, `CONTRIBUTING.md`, and other onboarding docs for clues about project philosophy, setup, and workflows. - - **Do NOT accept doc claims at face value.** Cross-reference every stated convention, command, tool, or workflow against the actual codebase (and the inventory) before including it in AGENTS.md. - - If `docs/` or `documentation/` exists, scan for references but verify each against the code. -4. **Survey Project Layout** - - Start from the inventory `[tree]` and `languages`. Add primary build targets and ownership the script can't infer (e.g., "`src/ui` maintained by Frontend team"). - - Check for `plans/`, `docs/`, or other knowledge directories. Flag must-read files (ADR indexes, architecture overviews, runbooks) to reference later in AGENTS.md. - - If `tree` is unavailable, the script falls back automatically; trim the captured tree to the top 2โ€“3 levels and note omissions for brevity. -5. **Confirm Automation Runners** - - The inventory reports `runners`, `make_targets`, and (when `just` is installed) `just_recipes`. Confirm which commands are canonical for linting, testing, building, and syncing data; note the definitive task names for inclusion later. - - If `just` is not installed, inspect the `Justfile` directly for recipe names. -6. **Catalog Tooling & Environment** - - The inventory gives `languages`, `package_managers`, and `env_files`. Add required runtimes, secrets handling, and local services it can't detect. - - Review any `.env.example`, `config/`, or secrets documentation surfaced in `env_files` that agents must read. -7. **Clarify Testing & Quality Gates** - - Start from the inventory `ci_files`, then identify test suites, coverage expectations, linting, and formatting. -8. **Resolve Ambiguities Early** - - Whenever conventions, ownership, or workflows seem unclear, prompt the developer with focused questions before drafting the guide. - - Ask explicitly whether existing `plans/` or documentation directories are authoritative or stale, and clarify what canon to reference. - -> **Outcome:** A structured notes list describing layout, tooling, commands, testing, release process, documentation references, pending questions, and update expectations. +1. **Run [`scripts/repo-inventory`](scripts/repo-inventory)** from the repo root. It emits `key=value` facts (`languages`, `package_managers`, `runners`, `make_targets`/`just_recipes`, `ci_files`, `env_files`) plus a gitignore-aware `[tree]` (with `tree --prune` and `git ls-files` fallbacks). Use `-C ` to scope a subdirectory or `--depth ` to widen/trim the tree. Everything below is judgment the script cannot infer (ownership, intent, stale-doc reconciliation). +2. **Existing AGENTS.md** โ€” find current files and their scope inheritance so you update instead of duplicating. +3. **Docs as hints** โ€” skim README, CONTRIBUTING, and other onboarding docs. Cross-check every stated convention, command, tool, or workflow against the inventory and the code before including it. +4. **Layout** โ€” start from `[tree]` and `languages`. Add ownership the script cannot infer (e.g. "`src/ui` maintained by Frontend"). Flag must-read files (ADR indexes, architecture overviews, runbooks). If `tree` was unavailable, the script already fell back; trim to the top 2โ€“3 levels. +5. **Automation** โ€” confirm which `runners` / `make_targets` / `just_recipes` are canonical for lint, test, build, and data sync. If `just` is not installed, read the `Justfile` for recipe names. +6. **Tooling & environment** โ€” add required runtimes, secrets handling, and local services on top of `languages`, `package_managers`, and `env_files`. +7. **Testing & quality** โ€” start from `ci_files`, then name test suites, coverage, lint, and format expectations. +8. **Ambiguities** โ€” ask before drafting when ownership, workflows, or whether `plans/`/`docs/` are canonical is unclear. ---- +## Phase 2 ยท Plan the structure -## Phase 2 ยท Plan the AGENTS.md Structure -Follow this opinionated order to keep files consistent and scannable: +Use this order: -1. **Header** โ€” Title + short purpose statement. -2. **Context Marker** โ€” Emoji marker (๐Ÿง ) so agents signal they have loaded project context. -3. **Quick Facts** โ€” Table or bullet summary (languages, package manager, key scripts, CI). -4. **Repository Tour** โ€” High-level directory map with responsibilities and ownership hints. -5. **Tooling & Setup** โ€” Required runtimes, package managers, environment variables, secrets. -6. **Common Tasks** โ€” Lint/test/build/deploy commands. Prefer listing `just` recipes first, then `make` targets, then raw commands. -7. **Testing & Quality** โ€” When and how to run tests, linting, formatting, coverage, and CI expectations. -8. **Workflow Expectations** โ€” Branching model, review norms, feature flagging, deployment cadence. -9. **Documentation Duties** โ€” When to update `README.md`, architecture diagrams, or other docs. -10. **Finish the Task** โ€” Mandatory wrap-up checklist for every agent task. +1. **Header** โ€” Title + short purpose. +2. **Context Marker** โ€” Emoji marker (๐Ÿง ) so agents signal they loaded project context. +3. **Quick Facts** โ€” Languages, package manager, key scripts, CI. +4. **Repository Tour** โ€” Directory map with responsibilities and ownership. +5. **Tooling & Setup** โ€” Runtimes, package managers, env vars, secrets. +6. **Common Tasks** โ€” Lint/test/build/deploy. Prefer `just` recipes, then `make` targets, then raw commands. +7. **Testing & Quality** โ€” When and how to run tests, lint, format, coverage, CI. +8. **Workflow Expectations** โ€” Branching, review, feature flags, deploy cadence. +9. **Documentation Duties** โ€” When to update README, diagrams, or other docs. +10. **Finish the Task** โ€” Mandatory wrap-up checklist. -For deeper directories (e.g., `services/api/`), include a "Scope" note at the top clarifying inheritance from parent AGENTS instructions. Always confirm with the developer before drafting new per-directory AGENTS files so you do not duplicate existing guidance or create unnecessary overhead. - ---- +For nested directories (e.g. `services/api/`), add a Scope note at the top describing inheritance. Confirm with the developer before creating per-directory AGENTS files. ## Phase 3 ยท Compose AGENTS.md -Use the template below and adapt each section to the project: ```markdown # Project Agent Guide @@ -120,11 +83,12 @@ The marker for this instruction is: ๐Ÿง  - List other docs to refresh (architecture, ADRs, etc.) ## Finish the Task Checklist -- [ ] Update relevant docs (& `README.md` if significant changes landed) -- [ ] Summarize changes in conventional commit format (e.g., `feat: ...`, `fix: ...`) +- [ ] Update relevant docs (`README.md` if the change is significant) +- [ ] Summarize changes in conventional commit format (e.g. `feat: ...`, `fix: ...`) ``` -### Subdirectory Template (Use Only with Developer Approval) +Subdirectory template (only with developer approval): + ```markdown # Agent Guide @@ -147,34 +111,21 @@ The marker for this instruction is: ๐Ÿง  - Docs or runbooks to reference - Open questions captured during discovery ``` -Only create these per-directory guides after confirming with the developer which areas need dedicated context and what information should be emphasized. - -**Writing Notes:** -- The Context Marker section signals to agents (and users) that project-specific context is loaded. Agents following emoji marker conventions will prepend ๐Ÿง  to responses, confirming AGENTS.md was read. -- Keep language direct and actionable. Agents should follow commands verbatim. -- Mention the preferred order of operations (e.g., "Always run `just format` before opening a PR"). -- When referencing scripts, include relative paths so agents can jump quickly (e.g., ``scripts/bootstrap.sh``). -- Incorporate a trimmed `tree --gitignore` snapshot (or link to the saved artifact) so readers grasp layout quickly. -- In the Repository Tour, highlight where `plans/`, `docs/`, design docs, or ADRs live if present. -- Call out any unanswered questions as action items, and confirm with the developer before creating any per-directory AGENTS overlays. -- If the project mixes languages/platforms, add subsections per component but keep global guidance first. ---- +Writing notes: + +- Agents following emoji-marker conventions prepend ๐Ÿง  to responses after loading this file. +- Keep language direct. Commands should be copyable. +- Prefer relative paths for scripts (e.g. `scripts/bootstrap.sh`). +- Include a trimmed `tree --gitignore` snapshot (or a link to it) in the Repository Tour. +- Call out unanswered questions as action items. +- Mixed-language repos: subsections per component, global guidance first. + +## Phase 4 ยท Validate + +- Scope rules stated (inheritance / overrides). +- Canonical automation commands present. +- README update and conventional-commit reminders in the wrap-up checklist. +- Per-directory files only exist if the developer approved them. -## Phase 4 ยท Validate & Wrap Up -1. **Self-review** - - Does the file respect AGENTS scope rules? (Mention inheritance or overrides.) - - Are all critical commands documented, especially automation entry points? - - Is the README update expectation explicit? - - Did you obtain developer approval before adding any per-directory AGENTS files, and is that approval reflected in the write-up? - - Does the "Finish the Task" checklist include the conventional commit summary reminder? -2. **Formatting** - - Ensure headings use Title Case, commands are wrapped in backticks, and lists are concise. - - Keep sections under ~8 bullets unless a table is clearer. -3. **Handoff Summary** - - When delivering the AGENTS.md to the user, include: - - A short summary of major sections added/updated. - - Confirmation that README and conventional commit reminders are present. - - Any follow-up suggestions (e.g., missing tests or outdated scripts). - -Use this skill whenever a repo lacks AGENTS context or when existing instructions are incomplete or outdated. The goal is to leave future agents with a single, trustworthy map of the project, its tooling, and the expectations for finishing tasks responsibly. +When handing off, summarize what was added or updated, confirm the wrap-up reminders, and list leftover gaps (missing tests, stale scripts). diff --git a/skills/agentsmd-generator/scripts/repo-inventory b/skills/agentsmd-generator/scripts/repo-inventory index 9670b48..5d8d3bf 100755 --- a/skills/agentsmd-generator/scripts/repo-inventory +++ b/skills/agentsmd-generator/scripts/repo-inventory @@ -114,7 +114,7 @@ for f in Justfile justfile Makefile makefile GNUmakefile Taskfile.yml Taskfile.y done if [[ -n "$makefile" ]]; then - # Same `## help`-style convention used by this repo's own Makefile. + # Parse `target: ## description` help comments. # `|| true` keeps a no-match `grep` (exit 1, with pipefail) from aborting. make_targets="$(grep -E '^[a-zA-Z0-9_.-]+:.*?## ' "$makefile" 2>/dev/null \ | sed -E 's/^([a-zA-Z0-9_.-]+):.*/\1/' | sort -u | paste -sd, - || true)" diff --git a/skills/work-breakdown/SKILL.md b/skills/work-breakdown/SKILL.md index d9d2800..bc1692d 100644 --- a/skills/work-breakdown/SKILL.md +++ b/skills/work-breakdown/SKILL.md @@ -9,18 +9,17 @@ Analyze the provided features/issues and produce a breakdown of work suitable fo ## Process -1. **Gather context** โ€” Fetch issue details, read relevant source code using the `codebase-exploration` skill and the `research_codebase` skill (for more detailed analysis - when required) -2. **Identify work units** โ€” Break input into discrete, independently deliverable units - these units of work should be independently verifiable, either manually or using automated testing. +1. **Gather context** โ€” Fetch issue details and read relevant source using the `codebase-exploration` and `research_codebase` skills. +2. **Identify work units** โ€” Split into independently deliverable units that can be verified manually or with tests. 3. **Map dependencies** โ€” Which units depend on others? What must be sequenced vs parallelized? -4. **Assess risks** โ€” Flag units of work that touch the same file, projects/repositories, entities, or layers (merge conflict risk, network layer, data layer, etc.) across multiple systems -5. **Recommend execution order** โ€” Suggest which units can run in parallel and which should be sequenced +4. **Assess risks** โ€” Flag units that touch the same files, repos, entities, or layers (merge conflicts, network, data, etc.). +5. **Recommend execution order** โ€” Which units can run in parallel, which must be sequenced. ## Output -Present a concise breakdown to the user: -- List of work units with one-line descriptions -- Dependency graph (which blocks which) - this should be a minimal visual representation +- Work units with one-line descriptions +- Dependency graph (minimal visual: which blocks which) - Risk flags - Recommended implementation strategy -Do NOT make requirements or design decisions โ€” just identify the shape of the work. +Do not make requirements or design decisions โ€” identify the shape of the work. diff --git a/tests/test_assess_linear_sdd_state.py b/tests/test_assess_linear_sdd_state.py index 36d905f..581f58b 100644 --- a/tests/test_assess_linear_sdd_state.py +++ b/tests/test_assess_linear_sdd_state.py @@ -11,7 +11,7 @@ import json -from conftest import requires, run_script +from conftest import run_script SKILL = "sdd-linear" SCRIPT = "assess-linear-sdd-state.py" @@ -29,7 +29,6 @@ def decide(snapshot: dict) -> dict: AUDIT_PASS = {"present": True, "status": "PASS"} -@requires("python3") def test_s1_start_when_no_spec_issue() -> None: result = decide({}) assert result["phase"] == 1 @@ -37,28 +36,24 @@ def test_s1_start_when_no_spec_issue() -> None: assert result["spec"] is None -@requires("python3") def test_s1_questions_when_scratch_questions_file_present() -> None: result = decide({"questions_file_present": True}) assert result["phase"] == 1 assert result["detailed_state"] == "S1_QUESTIONS" -@requires("python3") def test_s1_when_issue_exists_but_description_missing() -> None: result = decide({"spec_issue": {"identifier": "ENG-1", "description_present": False}}) assert result["phase"] == 1 assert result["detailed_state"] == "S1_START" -@requires("python3") def test_s2_start_when_task_list_attachment_missing() -> None: result = decide({"spec_issue": SPEC, "task_list_attachment_present": False}) assert result["phase"] == 2 assert result["detailed_state"] == "S2_START" -@requires("python3") def test_s2_parents_done_when_no_subissues_yet() -> None: result = decide( {"spec_issue": SPEC, "task_list_attachment_present": True, "subissues": []} @@ -67,7 +62,6 @@ def test_s2_parents_done_when_no_subissues_yet() -> None: assert result["detailed_state"] == "S2_PARENTS_DONE" -@requires("python3") def test_s2_subtasks_done_when_audit_missing() -> None: result = decide( { @@ -81,7 +75,6 @@ def test_s2_subtasks_done_when_audit_missing() -> None: assert result["detailed_state"] == "S2_SUBTASKS_DONE" -@requires("python3") def test_s2_audit_failed_on_fail_status() -> None: result = decide( { @@ -95,7 +88,6 @@ def test_s2_audit_failed_on_fail_status() -> None: assert result["detailed_state"] == "S2_AUDIT_FAILED" -@requires("python3") def test_s2_audit_failed_on_unknown_status() -> None: # A present-but-unverified audit must not advance the workflow. result = decide( @@ -110,7 +102,6 @@ def test_s2_audit_failed_on_unknown_status() -> None: assert result["detailed_state"] == "S2_AUDIT_FAILED" -@requires("python3") def test_s3_midflight_when_a_subissue_is_incomplete() -> None: result = decide( { @@ -124,7 +115,6 @@ def test_s3_midflight_when_a_subissue_is_incomplete() -> None: assert result["detailed_state"] == "S3_MIDFLIGHT" -@requires("python3") def test_s4_start_when_all_done_and_validation_missing() -> None: result = decide( { @@ -139,7 +129,6 @@ def test_s4_start_when_all_done_and_validation_missing() -> None: assert result["detailed_state"] == "S4_START" -@requires("python3") def test_s4_failed_when_validation_fails() -> None: result = decide( { @@ -154,7 +143,6 @@ def test_s4_failed_when_validation_fails() -> None: assert result["detailed_state"] == "S4_FAILED" -@requires("python3") def test_s4_complete_when_validation_passes() -> None: result = decide( { @@ -169,7 +157,6 @@ def test_s4_complete_when_validation_passes() -> None: assert result["detailed_state"] == "S4_COMPLETE" -@requires("python3") def test_canceled_subissue_counts_as_terminal() -> None: # A canceled executable task should not block progression to validation. result = decide( @@ -185,10 +172,9 @@ def test_canceled_subissue_counts_as_terminal() -> None: assert result["detailed_state"] == "S4_START" -# --- compact subissue_counts form (avoids enumerating every sub-issue) --- +# Compact `subissue_counts` form. -@requires("python3") def test_compact_counts_parents_done_when_total_zero() -> None: result = decide( { @@ -201,7 +187,6 @@ def test_compact_counts_parents_done_when_total_zero() -> None: assert result["detailed_state"] == "S2_PARENTS_DONE" -@requires("python3") def test_compact_counts_midflight_when_some_incomplete() -> None: result = decide( { @@ -215,7 +200,6 @@ def test_compact_counts_midflight_when_some_incomplete() -> None: assert result["detailed_state"] == "S3_MIDFLIGHT" -@requires("python3") def test_compact_counts_validation_when_all_terminal() -> None: result = decide( { @@ -230,7 +214,6 @@ def test_compact_counts_validation_when_all_terminal() -> None: assert result["detailed_state"] == "S4_START" -@requires("python3") def test_compact_counts_take_precedence_over_subissues_list() -> None: # When both forms are present, the compact counts are authoritative. result = decide( diff --git a/tests/test_install_linear_agent.py b/tests/test_install_linear_agent.py index b5b63f3..4c8db96 100644 --- a/tests/test_install_linear_agent.py +++ b/tests/test_install_linear_agent.py @@ -9,14 +9,13 @@ from pathlib import Path -from conftest import parse_kv, requires, run_script +from conftest import parse_kv, run_script SKILL = "sdd-linear" SCRIPT = "install-linear-agent.sh" AGENT_FILE = "linear-project-manager.md" -@requires("bash") def test_installs_into_empty_dest(tmp_path: Path) -> None: dest = tmp_path / "agents" @@ -30,7 +29,6 @@ def test_installs_into_empty_dest(tmp_path: Path) -> None: assert "name: linear-project-manager" in target.read_text(encoding="utf-8") -@requires("bash") def test_is_idempotent_and_non_destructive(tmp_path: Path) -> None: dest = tmp_path / "agents" dest.mkdir() @@ -41,11 +39,9 @@ def test_is_idempotent_and_non_destructive(tmp_path: Path) -> None: assert proc.returncode == 0, proc.stderr assert parse_kv(proc.stdout)["status"] == "exists" - # Existing agent is left untouched. assert target.read_text(encoding="utf-8") == "custom user content\n" -@requires("bash") def test_force_overwrites_existing(tmp_path: Path) -> None: dest = tmp_path / "agents" dest.mkdir() diff --git a/tests/test_jj_clone_diagnose.py b/tests/test_jj_clone_diagnose.py index 7bdbada..9ad37f8 100644 --- a/tests/test_jj_clone_diagnose.py +++ b/tests/test_jj_clone_diagnose.py @@ -68,8 +68,7 @@ def test_url_mode_detects_packed_collision(tmp_path: Path) -> None: assert "release" in proc.stdout -def test_jj_clone_help_still_works_after_refactor() -> None: - # jj-clone now sources lib.sh; make sure that wiring didn't break startup. +def test_jj_clone_help() -> None: proc = run_script(SKILL, "jj-clone", "--help") assert proc.returncode == 0 diff --git a/tests/test_repo_inventory.py b/tests/test_repo_inventory.py index 3e4d682..24ad944 100644 --- a/tests/test_repo_inventory.py +++ b/tests/test_repo_inventory.py @@ -9,7 +9,7 @@ from pathlib import Path -from conftest import git, init_repo, parse_kv, requires, run_script +from conftest import init_repo, parse_kv, run_script SKILL = "agentsmd-generator" SCRIPT = "repo-inventory" diff --git a/tests/test_research_metadata.py b/tests/test_research_metadata.py index 7e2b2c2..b8aa263 100644 --- a/tests/test_research_metadata.py +++ b/tests/test_research_metadata.py @@ -1,9 +1,7 @@ """Behavior tests for the research_codebase `spec_metadata.sh` script. -The script prints the deterministic frontmatter metadata block (date, -researcher, git_commit, branch, repository, last_updated*) for a research -document. It is self-contained and bundled with the skill, replacing the -previously-external `hack/spec_metadata.sh`. +The script prints the frontmatter metadata block (date, researcher, +git_commit, branch, repository, last_updated*) for a research document. """ from __future__ import annotations diff --git a/tests/test_upstream_catalog.py b/tests/test_upstream_catalog.py index 5d41e42..68a52b4 100644 --- a/tests/test_upstream_catalog.py +++ b/tests/test_upstream_catalog.py @@ -43,7 +43,7 @@ def has_license(folder: Path) -> bool: def test_catalog_and_lock_agree() -> None: assert catalog_names() == set(lock_skills()), ( "upstream-skills.toml and upstream-skills.lock.json disagree; " - "run `make sync-upstream-skills`" + "run `task sync-upstream-skills`" ) From f2e2f425f0e8a11b978d8a9921d3ffa01a85702e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 16 Aug 2026 17:48:03 +0000 Subject: [PATCH 2/3] test: track inventory fixture so tree fallback sees files Co-authored-by: Ben Sykes --- tests/test_repo_inventory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_repo_inventory.py b/tests/test_repo_inventory.py index 24ad944..2122668 100644 --- a/tests/test_repo_inventory.py +++ b/tests/test_repo_inventory.py @@ -9,7 +9,7 @@ from pathlib import Path -from conftest import init_repo, parse_kv, run_script +from conftest import commit_file, init_repo, parse_kv, run_script SKILL = "agentsmd-generator" SCRIPT = "repo-inventory" @@ -76,7 +76,7 @@ def test_detects_ci_and_env_files(tmp_path: Path) -> None: def test_emits_tree_section(tmp_path: Path) -> None: repo = init_repo(tmp_path / "proj") - (repo / "README.md").write_text("# hi\n", encoding="utf-8") + commit_file(repo, "README.md", "# hi\n", "docs: readme") out = run_script(SKILL, SCRIPT, cwd=repo).stdout From cbc52d57273b28d3b53bab3d6263611189d6c3db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 17 Aug 2026 01:05:08 +0000 Subject: [PATCH 3/3] revert: keep deslop scoped to authored skills Co-authored-by: Ben Sykes --- .github/workflows/pr-title-lint.yml | 6 +++++- tests/test_assess_linear_sdd_state.py | 21 +++++++++++++++++++-- tests/test_install_linear_agent.py | 6 +++++- tests/test_jj_clone_diagnose.py | 3 ++- tests/test_repo_inventory.py | 4 ++-- tests/test_research_metadata.py | 6 ++++-- tests/test_upstream_catalog.py | 2 +- 7 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr-title-lint.yml b/.github/workflows/pr-title-lint.yml index 28e6e52..63d5fcd 100644 --- a/.github/workflows/pr-title-lint.yml +++ b/.github/workflows/pr-title-lint.yml @@ -38,12 +38,15 @@ jobs: didn't match the configured pattern. Please ensure that the subject doesn't start with an uppercase character. + # Add comment when PR title is invalid - uses: marocchino/sticky-pull-request-comment@v2 if: always() && (steps.lint_pr_title.outputs.error_message != null) with: header: pr-title-lint-error message: | - PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/). + ๐Ÿ‘‹ Thank you for opening this pull request! + + We require PR titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/). **Error details:** @@ -57,6 +60,7 @@ jobs: Please update your PR title and the check will run automatically. + # Delete comment when PR title is fixed - uses: marocchino/sticky-pull-request-comment@v2 if: ${{ steps.lint_pr_title.outputs.error_message == null }} with: diff --git a/tests/test_assess_linear_sdd_state.py b/tests/test_assess_linear_sdd_state.py index 581f58b..36d905f 100644 --- a/tests/test_assess_linear_sdd_state.py +++ b/tests/test_assess_linear_sdd_state.py @@ -11,7 +11,7 @@ import json -from conftest import run_script +from conftest import requires, run_script SKILL = "sdd-linear" SCRIPT = "assess-linear-sdd-state.py" @@ -29,6 +29,7 @@ def decide(snapshot: dict) -> dict: AUDIT_PASS = {"present": True, "status": "PASS"} +@requires("python3") def test_s1_start_when_no_spec_issue() -> None: result = decide({}) assert result["phase"] == 1 @@ -36,24 +37,28 @@ def test_s1_start_when_no_spec_issue() -> None: assert result["spec"] is None +@requires("python3") def test_s1_questions_when_scratch_questions_file_present() -> None: result = decide({"questions_file_present": True}) assert result["phase"] == 1 assert result["detailed_state"] == "S1_QUESTIONS" +@requires("python3") def test_s1_when_issue_exists_but_description_missing() -> None: result = decide({"spec_issue": {"identifier": "ENG-1", "description_present": False}}) assert result["phase"] == 1 assert result["detailed_state"] == "S1_START" +@requires("python3") def test_s2_start_when_task_list_attachment_missing() -> None: result = decide({"spec_issue": SPEC, "task_list_attachment_present": False}) assert result["phase"] == 2 assert result["detailed_state"] == "S2_START" +@requires("python3") def test_s2_parents_done_when_no_subissues_yet() -> None: result = decide( {"spec_issue": SPEC, "task_list_attachment_present": True, "subissues": []} @@ -62,6 +67,7 @@ def test_s2_parents_done_when_no_subissues_yet() -> None: assert result["detailed_state"] == "S2_PARENTS_DONE" +@requires("python3") def test_s2_subtasks_done_when_audit_missing() -> None: result = decide( { @@ -75,6 +81,7 @@ def test_s2_subtasks_done_when_audit_missing() -> None: assert result["detailed_state"] == "S2_SUBTASKS_DONE" +@requires("python3") def test_s2_audit_failed_on_fail_status() -> None: result = decide( { @@ -88,6 +95,7 @@ def test_s2_audit_failed_on_fail_status() -> None: assert result["detailed_state"] == "S2_AUDIT_FAILED" +@requires("python3") def test_s2_audit_failed_on_unknown_status() -> None: # A present-but-unverified audit must not advance the workflow. result = decide( @@ -102,6 +110,7 @@ def test_s2_audit_failed_on_unknown_status() -> None: assert result["detailed_state"] == "S2_AUDIT_FAILED" +@requires("python3") def test_s3_midflight_when_a_subissue_is_incomplete() -> None: result = decide( { @@ -115,6 +124,7 @@ def test_s3_midflight_when_a_subissue_is_incomplete() -> None: assert result["detailed_state"] == "S3_MIDFLIGHT" +@requires("python3") def test_s4_start_when_all_done_and_validation_missing() -> None: result = decide( { @@ -129,6 +139,7 @@ def test_s4_start_when_all_done_and_validation_missing() -> None: assert result["detailed_state"] == "S4_START" +@requires("python3") def test_s4_failed_when_validation_fails() -> None: result = decide( { @@ -143,6 +154,7 @@ def test_s4_failed_when_validation_fails() -> None: assert result["detailed_state"] == "S4_FAILED" +@requires("python3") def test_s4_complete_when_validation_passes() -> None: result = decide( { @@ -157,6 +169,7 @@ def test_s4_complete_when_validation_passes() -> None: assert result["detailed_state"] == "S4_COMPLETE" +@requires("python3") def test_canceled_subissue_counts_as_terminal() -> None: # A canceled executable task should not block progression to validation. result = decide( @@ -172,9 +185,10 @@ def test_canceled_subissue_counts_as_terminal() -> None: assert result["detailed_state"] == "S4_START" -# Compact `subissue_counts` form. +# --- compact subissue_counts form (avoids enumerating every sub-issue) --- +@requires("python3") def test_compact_counts_parents_done_when_total_zero() -> None: result = decide( { @@ -187,6 +201,7 @@ def test_compact_counts_parents_done_when_total_zero() -> None: assert result["detailed_state"] == "S2_PARENTS_DONE" +@requires("python3") def test_compact_counts_midflight_when_some_incomplete() -> None: result = decide( { @@ -200,6 +215,7 @@ def test_compact_counts_midflight_when_some_incomplete() -> None: assert result["detailed_state"] == "S3_MIDFLIGHT" +@requires("python3") def test_compact_counts_validation_when_all_terminal() -> None: result = decide( { @@ -214,6 +230,7 @@ def test_compact_counts_validation_when_all_terminal() -> None: assert result["detailed_state"] == "S4_START" +@requires("python3") def test_compact_counts_take_precedence_over_subissues_list() -> None: # When both forms are present, the compact counts are authoritative. result = decide( diff --git a/tests/test_install_linear_agent.py b/tests/test_install_linear_agent.py index 4c8db96..b5b63f3 100644 --- a/tests/test_install_linear_agent.py +++ b/tests/test_install_linear_agent.py @@ -9,13 +9,14 @@ from pathlib import Path -from conftest import parse_kv, run_script +from conftest import parse_kv, requires, run_script SKILL = "sdd-linear" SCRIPT = "install-linear-agent.sh" AGENT_FILE = "linear-project-manager.md" +@requires("bash") def test_installs_into_empty_dest(tmp_path: Path) -> None: dest = tmp_path / "agents" @@ -29,6 +30,7 @@ def test_installs_into_empty_dest(tmp_path: Path) -> None: assert "name: linear-project-manager" in target.read_text(encoding="utf-8") +@requires("bash") def test_is_idempotent_and_non_destructive(tmp_path: Path) -> None: dest = tmp_path / "agents" dest.mkdir() @@ -39,9 +41,11 @@ def test_is_idempotent_and_non_destructive(tmp_path: Path) -> None: assert proc.returncode == 0, proc.stderr assert parse_kv(proc.stdout)["status"] == "exists" + # Existing agent is left untouched. assert target.read_text(encoding="utf-8") == "custom user content\n" +@requires("bash") def test_force_overwrites_existing(tmp_path: Path) -> None: dest = tmp_path / "agents" dest.mkdir() diff --git a/tests/test_jj_clone_diagnose.py b/tests/test_jj_clone_diagnose.py index 9ad37f8..7bdbada 100644 --- a/tests/test_jj_clone_diagnose.py +++ b/tests/test_jj_clone_diagnose.py @@ -68,7 +68,8 @@ def test_url_mode_detects_packed_collision(tmp_path: Path) -> None: assert "release" in proc.stdout -def test_jj_clone_help() -> None: +def test_jj_clone_help_still_works_after_refactor() -> None: + # jj-clone now sources lib.sh; make sure that wiring didn't break startup. proc = run_script(SKILL, "jj-clone", "--help") assert proc.returncode == 0 diff --git a/tests/test_repo_inventory.py b/tests/test_repo_inventory.py index 2122668..3e4d682 100644 --- a/tests/test_repo_inventory.py +++ b/tests/test_repo_inventory.py @@ -9,7 +9,7 @@ from pathlib import Path -from conftest import commit_file, init_repo, parse_kv, run_script +from conftest import git, init_repo, parse_kv, requires, run_script SKILL = "agentsmd-generator" SCRIPT = "repo-inventory" @@ -76,7 +76,7 @@ def test_detects_ci_and_env_files(tmp_path: Path) -> None: def test_emits_tree_section(tmp_path: Path) -> None: repo = init_repo(tmp_path / "proj") - commit_file(repo, "README.md", "# hi\n", "docs: readme") + (repo / "README.md").write_text("# hi\n", encoding="utf-8") out = run_script(SKILL, SCRIPT, cwd=repo).stdout diff --git a/tests/test_research_metadata.py b/tests/test_research_metadata.py index b8aa263..7e2b2c2 100644 --- a/tests/test_research_metadata.py +++ b/tests/test_research_metadata.py @@ -1,7 +1,9 @@ """Behavior tests for the research_codebase `spec_metadata.sh` script. -The script prints the frontmatter metadata block (date, researcher, -git_commit, branch, repository, last_updated*) for a research document. +The script prints the deterministic frontmatter metadata block (date, +researcher, git_commit, branch, repository, last_updated*) for a research +document. It is self-contained and bundled with the skill, replacing the +previously-external `hack/spec_metadata.sh`. """ from __future__ import annotations diff --git a/tests/test_upstream_catalog.py b/tests/test_upstream_catalog.py index 68a52b4..5d41e42 100644 --- a/tests/test_upstream_catalog.py +++ b/tests/test_upstream_catalog.py @@ -43,7 +43,7 @@ def has_license(folder: Path) -> bool: def test_catalog_and_lock_agree() -> None: assert catalog_names() == set(lock_skills()), ( "upstream-skills.toml and upstream-skills.lock.json disagree; " - "run `task sync-upstream-skills`" + "run `make sync-upstream-skills`" )