taskfile-ci-parity
Skill2 starsChanged 6 months agoApache-2.0View on GitHub
Wire validation so local == CI by construction: every check is a Taskfile target; CI and hooks invoke it rather than reimplementing it. Use when adding a validation gate, when local/CI disagree, or when a hook duplicates a command.
- Installs packages
- Commits and pushes
In this repository43 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- break-glassSkill
- claim-ledgerSkill
- contract-parity-auditSkill
- counterfactual-auditSkill
- design-doc-reviewSkill
- diagram-gen-emergentSkill
- diagram-gen-specSkill
- doc-triageSkill
- explain-workSkill
- feature-implSkill
- finding-triageSkill
- first-party-auditSkill
- gh-token-rotateSkill
- handoffSkill
- implementSkill
- jj-stackSkill
- linear-escalation-triageSkill
- mache-usageSkill
- park-workSkill
- pr-boardSkill
- pr-bucketsSkill
- prior-art-cartographerSkill
- problem-decomposerSkill
- pr-review-kitSkill
- pr-reviewSkill
- pr-suggestionSkill
- readme-restructureSkill
- repo-description-polishSkill
- repo-homepage-setSkill
- repo-social-previewSkill
- repo-topic-taggerSkill
- review-prepSkill
- review-queueSkill
- reviewSkill
- self-auditSkill
- structural-pr-reviewSkill
- surveySkill
- test-fidelitySkill
- workflow-auditSkill
- work-scopeSkill
- writing-pr-descriptionsSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 28
If CI says run: go test -race ./...
- Very specific: line 45
Use cmds: - task: X for serial steps, deps: [X, Y] for parallel (deps must not depend on each other).
- Very specific: line 89
Compose the gate: Add/confirm task ci composing the non-mutating targets.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- Author: jamestexas — drafted by claude-opus-4-8 (2026-06-22). Reference impl: agentic-research/mache PR #429. Provenance: mache memory feedback_validation_taskfile_ci_gate. -->/taskfile-ci-parity — Single-source validation across Taskfile, CI, and git hooks
The principle (one sentence)
Validation logic lives in Taskfile targets; CI workflows and git hooks INVOKE those targets — they never re-implement the underlying command. When that holds, "passes locally" and "passes CI" cannot disagree, because they run the same definition. The job of this skill is to make that true and keep it true.
The three layers (and the anti-pattern each one drifts into)
| Layer | Should do | Drifts into (the smell) |
| Taskfile | own every command (task test, task lint, task fmt:check) | a one-off command that exists nowhere as a target |
| CI workflow | run: task <target> | inlining go test … / gofumpt -l … / npm run … directly in the YAML |
| git hooks | invoke task <target> | .pre-commit-config.yaml entry: re-implementing the same flags |
If CI says run: go test -race ./... and the Taskfile says task test: go test -race ./..., you have two copies of the same command that will drift the first time someone tweaks one. Collapse to one: CI runs task test.
How to make Taskfiles modular (DRY) — composition vs includes
These solve different problems. Pick by scope:
- Composition (within one Taskfile, same repo) — one target calls others. This is the default dedup tool.
Usetasks: ci: # the single-source gate cmds: - task: fmt:check # serial; non-mutating verify, not `fmt` (-w) - task: vet - task: lint - task: test # heavy tests run here; sub-gates compose in - task: validatecmds: - task: Xfor serial steps,deps: [X, Y]for parallel (deps must not depend on each other). Parameterize withinternal: true+vars:to avoid copy-pasted near-identical tasks:_build-image: { internal: true, cmds: ["docker build -t {{.IMG}} ."] } build-prod: { cmds: [{ task: _build-image, vars: { IMG: prod } }] } - Includes (across files / across repos) — split a big Taskfile or share a gate between repos:
Reach for includes when the same gate should exist in many repos (e.g. hoist aincludes: common: ../shared/Taskfile.common.yml # tasks namespaced as `common:*` tasks: ci: { cmds: [{ task: common:lint }, { task: test }] }Taskfile.ci.ymlinto a shared location andincludes:it everywhere) — that's the cross-repo version of single-sourcing. Within one repo, prefer composition.
Two gate variants — know which you're wiring
task check— the dev gate. May MUTATE (fmtwith-w,lint --fix). Convenient locally; do NOT run in CI (CI must not rewrite the tree).task ci— the merge gate. NON-mutating:fmt:check(diff-and-fail, never write), no--fix. A dirty/unformatted tree must FAIL, not be silently fixed. This is the one CI and the pre-push hook run.
Keeping both, with ci non-mutating, is the load-bearing distinction. A mutating gate in CI hides the failure it should report.
The git-hook layer — catch CI failures before CI
Goal: a push that CI would reject fails locally first. Use pre-push, not pre-commit, for the full gate — running the whole -race suite on every commit is too slow; you push far less often. Keep pre-commit fast (format/lint per-file), put the 1:1 gate on pre-push.
With the pre-commit framework (.pre-commit-config.yaml), add a local hook on the pre-push stage that invokes the task — never re-implement it:
- repo: local
hooks:
- id: task-ci
name: task ci (1:1 with CI — full gate before push)
entry: task ci
language: system
pass_filenames: false
always_run: true
stages: [pre-push]One-time install of the pre-push stage: pre-commit install --hook-type pre-push. (Suggest the user run it via ! pre-commit install --hook-type pre-push so output lands in-session.) If git-lfs already owns .git/hooks/pre-push, the pre-commit framework chains correctly once installed; verify with a dry git push --dry-run.
Procedure
- Audit for drift.
grep -n "run:" .github/workflows/*.ymland read.pre-commit-config.yamlentry:lines. Any command not expressed astask <target>is a dedup target. Note them. - Make every check a target. For each drifted command, ensure a Taskfile target owns it; if missing, add one (smallest sensible unit, e.g.
fmt:check,parity,gen:server-json:check). - Compose the gate. Add/confirm
task cicomposing the non-mutating targets. Keepcheckas the mutating dev convenience. - Point CI at it. Replace inline
run:commands withrun: task <target>. Parallel CI jobs each calling a task is fine — the commands aren't duplicated, only the orchestration. (Pin the task version:go install github.com/go-task/task/v3/cmd/task@vX.) - Point hooks at it. pre-commit
entry:→task <fast-target>; add the pre-pushtask cihook above. - Verify.
task --list-all | grep -E 'ci|check|<new>'(targets parse), run the new target, and confirmtask ciand the CI workflow reference the same target names. - Record provenance. Note in the target's comment that it's the single source ("CI and the pre-push hook invoke THIS — never re-implement"). Link the ADR/memory if the repo has one.
Red flags (stop and dedup)
- A command appears in both a workflow
run:and a Taskfilecmds:→ CI should call the task. .pre-commit-config.yamlentry:spells out flags that atasktarget already owns → call the task.- CI runs a mutating formatter (
-w,--fix) → switch to the:checkvariant; CI must report, not fix. - "It passed locally but CI failed on formatting/lint" → the local path and CI path are different commands. Converge them.
- A new test/check added as a loose
go test -run …you run by hand → it isn't a gate until it's a target wired intoci(and thus CI + pre-push).
Definition of done
A check is done when: (1) it's a Taskfile target, (2) task ci includes it (so CI and the pre-push hook both run it), and (3) no workflow or hook re-implements its command. Until then it's a script someone has to remember to run.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

