
Mise Tasks
- 588 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
mise-tasks is a mise task runner skill that guides creation and execution of mise.toml task definitions for developers who need polyglot dev workflows and repeatable local commands.
About
mise-tasks is a CLI-oriented agent skill from terrilica/cc-skills focused on the mise polyglot tool version manager and its task runner. Although the bundled readme is empty, the skill name and repository context indicate guidance for authoring mise.toml tasks, wiring dependencies between tasks, and standardizing dev, test, and lint commands behind mise run. A developer reaches for mise-tasks when consolidating Node, Python, Rust, or other toolchain commands under one mise entrypoint instead of scattered Makefile or npm scripts. The skill fits teams already using mise for version pinning who want agent help generating or debugging task graphs.
- mise-tasks
Mise Tasks by the numbers
- 588 all-time installs (skills.sh)
- +27 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #687 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill mise-tasksAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 588 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
How do you configure mise tasks for polyglot repos?
Use mise-tasks for development tasks
Who is it for?
Developers using jdx mise who want agent help authoring or debugging mise.toml task workflows across multiple languages.
Skip if: Teams standardized on Make, npm scripts only, or GitHub Actions without local mise adoption should skip mise-tasks.
When should I use this skill?
A user asks to create, fix, or run mise tasks, mise.toml definitions, or mise run workflows in a repository.
What you get
mise.toml task definitions, dependency-linked task graphs, and documented mise run commands.
- mise.toml tasks
- mise run command docs
Files
mise Tasks Orchestration
<!-- ADR: 2025-12-08-mise-tasks-skill -->
Orchestrate multi-step project workflows using mise [tasks] section with dependency management, argument handling, and file tracking.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
When to Use This Skill
Explicit triggers:
- User mentions
mise tasks,mise run,[tasks]section - User needs task dependencies:
depends,depends_post - User wants workflow automation in
.mise.toml - User mentions task arguments or
usagespec
AI Discovery trigger (prescriptive):
Whenmise-configurationskill detects multi-step workflows (test suites, build pipelines, migrations), prescriptively invoke this skill to generate appropriate[tasks]definitions.
Quick Reference
Task Definition
[tasks.build]
description = "Build the project"
run = "cargo build --release"Running Tasks
mise run build # Run single task
mise run test build # Run multiple tasks
mise run test ::: build # Run in parallel
mise r build # Short formDependency Types
| Type | Syntax | When |
|---|---|---|
depends | depends = ["lint", "test"] | Run BEFORE task |
depends_post | depends_post = ["notify"] | Run AFTER task succeeds |
wait_for | wait_for = ["db"] | Wait only if running |
Key Task Properties
| Property | Purpose | Example |
|---|---|---|
description | AI-agent discoverability (CRITICAL) | "Run pytest with coverage. Exits non-zero on failure." |
alias | Short name | alias = "t" |
dir | Working directory | dir = "packages/frontend" |
env | Task-specific env vars (NOT passed to deps) | env = { LOG_LEVEL = "debug" } |
hide | Hidden from mise tasks output | hide = true |
sources | File tracking for caching | sources = ["src/**/*.rs"] |
outputs | Skip if newer than sources | outputs = ["target/release/myapp"] |
confirm | Prompt before execution | confirm = "Delete all data?" |
quiet | Suppress mise output | quiet = true |
silent | Suppress ALL output | silent = true |
raw | Direct stdin/stdout (disables parallelism) | raw = true |
tools | Task-specific tool versions | tools = { python = "3.9" } |
shell | Custom shell | shell = "pwsh -c" |
usage | Argument spec (preferred over Tera) | See Task Arguments |
Namespacing
mise run 'test:*' # All tasks starting with test:
mise run 'db:**' # Nested: db:migrate:up, db:seed:test
mise tasks --hidden # View hidden tasks (prefixed with _)For detailed examples and patterns for all levels, see Task Levels Reference.
---
Level 10: Monorepo (Experimental)
Requires: MISE_EXPERIMENTAL=1 and experimental_monorepo_root = true
mise run //projects/frontend:build # Absolute from root
mise run :build # Current config_root
mise run //...:test # All projects
mise run '//projects/...:build' # Build all under projects/Tasks in subdirectories are auto-discovered with path prefix (packages/api/.mise.toml tasks become packages/api:taskname).
For complete monorepo documentation, see: advanced.md
---
Level 11: Polyglot Monorepo with Pants + mise
For Python-heavy polyglot monorepos (10-50 packages), combine mise for runtime management with Pants for build orchestration and native affected detection.
| Tool | Responsibility |
|---|---|
| mise | Runtime versions (Python, Node, Rust) + environment variables |
| Pants | Build orchestration + native affected detection + dependency inference |
# Native affected detection (no manual git scripts)
pants --changed-since=origin/main test
pants --changed-since=origin/main lint
pants --changed-since=origin/main package| Scale | Recommendation |
|---|---|
| < 10 packages | mise + custom affected (Level 10 patterns) |
| 10-50 packages (Python-heavy) | Pants + mise (this section) |
| 50+ packages | Consider Bazel |
See polyglot-affected.md for complete Pants + mise integration guide and tool comparison.
---
Integration with [env]
Tasks automatically inherit [env] values. Use _.file for external env files and redact = true for secrets.
[env]
DATABASE_URL = "postgresql://localhost/mydb"
_.file = { path = ".env.secrets", redact = true }
[tasks._check-env]
hide = true
run = '[ -n "$API_KEY" ] || { echo "Missing API_KEY"; exit 1; }'
[tasks.deploy]
depends = ["_check-env"]
run = "deploy.sh" # $DATABASE_URL and $API_KEY availableFor full env integration patterns, see Environment Integration.
---
Anti-Patterns
| Anti-Pattern | Why Bad | Instead |
|---|---|---|
| Replace /itp:go with mise tasks | No TodoWrite, no ADR tracking, no checkpoints | Use mise tasks for project workflows, /itp:go for ADR-driven development |
| Hardcode secrets in tasks | Security risk | Use _.file = ".env.secrets" with redact = true |
| Giant monolithic tasks | Hard to debug, no reuse | Break into small tasks with dependencies |
Skip or minimal description | AI agents cannot infer task purpose from name alone | Write rich descriptions: what it does, requires, produces, when to run |
Publish without build depends | Runtime failure instead of DAG prevention | Add depends = ["build"] to publish tasks |
| Orchestrator without all phases | "Run X next" messages get ignored | Include all phases in release:full depends array |
For release-specific anti-patterns and patterns, see Release Workflow Patterns.
---
Cross-Reference: mise-configuration
Prerequisites: Before defining tasks, ensure [env] section is configured.
PRESCRIPTIVE: After defining tasks, invoke [`mise-configuration` skill](../mise-configuration/SKILL.md) to ensure [env] SSoT patterns are applied.
The mise-configuration skill covers:
[env]- Environment variables with defaults[settings]- mise behavior configuration[tools]- Version pinning- Special directives:
_.file,_.path,_.python.venv
---
Additional Resources
- Task Levels Reference - Levels 1-9: basic tasks, dependencies, hidden tasks, arguments, file tracking, advanced execution, watch mode
- Task Patterns - Real-world task examples
- Task Arguments - Complete usage spec reference
- Advanced Features - Monorepo, watch, experimental
- Environment Integration - [env] inheritance and credential loading
- Polyglot Affected - Pants + mise integration guide and tool comparison
- Bootstrap Monorepo - Autonomous polyglot monorepo bootstrap meta-prompt
- Release Workflow Patterns - Release task DAG patterns, build-before-publish enforcement
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Task not found | Typo or wrong mise.toml | Run mise tasks to list available tasks |
| Dependencies not run | Circular dependency | Check task depends arrays for cycles |
| Sources not working | Wrong glob pattern | Use relative paths from mise.toml location |
| Watch not triggering | File outside sources list | Add file pattern to sources array |
| Env vars not available | Task in wrong directory | Ensure mise.toml is in cwd or parent |
| Run fails with error | Script path issue | Use absolute path or relative to mise.toml |
Post-Execution Reflection
After this skill completes, check before closing:
1. Did the command succeed? — If not, fix the instruction or error table that caused the failure. 2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match. 3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.
Only update if the issue is real and reproducible — not speculative.
mise Tasks Advanced Features
Advanced features for watch mode, monorepo support, and experimental functionality.
Watch Mode
Overview
mise watch re-runs tasks automatically when source files change. Requires watchexec to be installed.
Install watchexec:
mise use -g watchexec@latestBasic Watch
mise watch build # Re-run build on changes
mise watch test # Re-run tests on changesWatch uses sources from task definition to determine which files to monitor:
[tasks.build]
sources = ["src/**/*.rs", "Cargo.toml"]
run = "cargo build"Watch Options
--debounce- Wait before re-running (e.g.,--debounce 500ms)--restart- Kill running task and restart--clear- Clear screen before each run--on-busy-update- Behavior when task is running (e.g.,--on-busy-update=queue)
On-Busy Behavior
Controls what happens when files change while a task is running:
# Queue changes and run after current execution
mise watch build --on-busy-update=queue
# Immediately restart (kill current)
mise watch build --on-busy-update=restart
# Ignore changes during execution (default)
mise watch build --on-busy-update=do-nothingInterruptible Tasks
For tasks that should be restartable mid-execution:
[tasks.dev-server]
run = "uvicorn app:main --reload"Use --restart with long-running processes:
mise watch dev-server --restartWatch with Multiple Tasks
mise watch 'test lint' # Watch and run both
mise watch 'test ::: lint' # Watch and run in parallel---
Monorepo Support (Experimental)
Requires: MISE_EXPERIMENTAL=1 environment variable.
Enable Monorepo Mode
# Root .mise.toml
[settings]
experimental_monorepo_root = truePath Syntax
Monorepo mode introduces path-prefixed task names:
//projects/frontend:build- Task in specific subproject:build- Task in currentconfig_root//...:test- Runtestin all projects//projects/...:lint- Runlintin all underprojects///projects/frontend:*- All tasks infrontend
Project Discovery
Tasks in subdirectories are auto-discovered with path prefixes:
project-root/
.mise.toml # Root config
packages/
api/
.mise.toml # Tasks become packages/api:*
web/
.mise.toml # Tasks become packages/web:*
shared/
.mise.toml # Tasks become packages/shared:*Running Monorepo Tasks
# Run specific project task
mise run //packages/api:test
# Run test in all packages
mise run '//packages/...:test'
# Run all tasks in one package
mise run '//packages/web:*'
# Run from package directory
cd packages/api
mise run :test # Runs packages/api:test
mise run build # Also runs local buildCross-Project Dependencies
# packages/web/.mise.toml
[tasks.build]
depends = ["//packages/shared:build"] # Depend on shared lib
run = "npm run build"Monorepo Patterns
Root orchestration task:
# Root .mise.toml
[tasks.test-all]
description = "Run all package tests"
run = "mise run '//packages/...:test'"
[tasks.build-all]
description = "Build all packages"
run = "mise run '//packages/...:build'"Selective execution:
# Test only changed packages (requires git integration)
git diff --name-only main | xargs -I{} mise run '//{}:test'---
Experimental Features
Features requiring MISE_EXPERIMENTAL=1:
Task Hierarchy
Nested task inheritance (experimental):
[tasks.base-test]
env = { LOG_LEVEL = "debug" }
run = "pytest"
[tasks."test:unit"]
inherits = "base-test"
run = "pytest tests/unit/"Remote Tasks
Import tasks from remote sources (experimental):
[tasks]
include = ["https://example.com/tasks.toml"]Task Aliases with Arguments
mise alias test "run test -v"
mise test # Runs: mise run test -v---
Shell Integration
Custom Shell per Task
[tasks.powershell-task]
shell = "pwsh -c"
run = "Get-Process | Select-Object -First 5"
[tasks.python-task]
shell = "python -c"
run = '''
import json
print(json.dumps({"status": "ok"}))
'''
[tasks.zsh-task]
shell = "zsh -c"
run = "setopt extended_glob && ls **/*.md"Default Shell Configuration
[settings]
task_default_shell = "bash -c"---
Parallel Execution
Parallel Operator
# Run tasks in parallel with :::
mise run lint ::: typecheck ::: test
# Sequential (default)
mise run lint test typecheckJobs Control
mise run --jobs 4 'test:*' # Limit concurrent tasks
mise run --jobs 0 'test:*' # Unlimited parallelismParallel in Task Definition
[tasks.validate]
# These run in parallel (no dependencies between them)
depends = ["lint", "typecheck", "format-check"]
run = "echo 'All validations passed'"Dependencies without inter-dependencies run in parallel automatically.
---
Environment Integration
Global vs Task Environment
[env]
# Global - available to all tasks
DATABASE_URL = "postgresql://localhost/dev"
[tasks.test]
# Task-specific - overrides global, not passed to depends
env = { DATABASE_URL = "postgresql://localhost/test" }
run = "pytest"Important: Task env is NOT inherited by dependency tasks.
Environment from File
[env]
_.file = ".env"
[tasks.deploy]
# Additional env file for deploy
env_file = ".env.deploy"
run = "deploy.sh"Conditional Environment
[env]
{% if env.CI %}
LOG_LEVEL = "error"
{% else %}
LOG_LEVEL = "debug"
{% endif %}---
Debugging Tasks
Verbose Output
mise run --verbose build # Show task execution details
mise run -v build # Short formDry Run
mise run --dry-run ci # Show what would runTask Information
mise tasks # List all tasks
mise tasks --hidden # Include hidden tasks
mise task info build # Show task detailsEnvironment Inspection
mise env # Show all env vars
mise env --json # JSON format---
Best Practices
Performance
1. Use `sources`/`outputs` - Skip unchanged builds 2. Parallel where possible - Use ::: operator 3. Limit watch scope - Specific globs in sources 4. Cache dependencies - Use depends to avoid redundant work
Organization
1. Namespace with colons - test:unit, test:e2e 2. Hide internal tasks - hide = true for helpers 3. Document with descriptions - Every task gets description 4. Keep tasks focused - Single responsibility
Monorepo Specific
1. Root orchestration - Global tasks in root .mise.toml 2. Explicit dependencies - Cross-project with //path:task 3. Consistent naming - Same task names across packages 4. Selective execution - Use wildcards for efficiency
mise Task Arguments
Complete reference for the usage specification in mise tasks.
Overview
The usage field defines task arguments and flags using a specialized DSL. Arguments become environment variables accessible in the run script.
DEPRECATION WARNING: The Tera template method ({{arg(name="...")}}) will be removed in mise 2026.11.0. Use usage spec exclusively.
---
Positional Arguments
Required Argument
[tasks.process]
usage = 'arg "<file>" help="Input file to process"'
run = 'cat "${usage_file}"'- Angle brackets
<file>= required - Task fails if not provided
Optional Argument
[tasks.compile]
usage = 'arg "[output]" default="a.out" help="Output filename"'
run = 'gcc main.c -o "${usage_output}"'- Square brackets
[output]= optional defaultprovides fallback value
With Choices
[tasks.deploy]
usage = '''
arg "<environment>" help="Target environment" {
choices "dev" "staging" "prod"
}
'''
run = 'deploy.sh "${usage_environment}"'Variadic Arguments
[tasks.concat]
usage = 'arg "<files>" var=#true help="Files to concatenate"'
run = 'cat ${usage_files}'With limits:
usage = 'arg "<files>" var=#true var_min=1 var_max=10'---
Flags
Boolean Flag
[tasks.build]
usage = 'flag "-v --verbose" help="Enable verbose output"'
run = '''
if [ "${usage_verbose:-false}" = "true" ]; then
set -x
fi
cargo build
'''Flag with Value
[tasks.server]
usage = 'flag "-p --port <port>" default="8080" help="Server port"'
run = 'uvicorn app:main --port "${usage_port}"'Environment-Backed Flag
[tasks.deploy]
usage = 'flag "--region <region>" env="AWS_REGION" default="us-east-1"'
run = 'aws --region "${usage_region}" ecs deploy'Flag value can come from:
1. Command line: --region eu-west-1 2. Environment variable: AWS_REGION=eu-west-1 3. Default value: us-east-1
Count Flag
[tasks.debug]
usage = 'flag "-v" count=#true help="Verbosity level"'
run = '''
case "${usage_v:-0}" in
0) LOG_LEVEL="error" ;;
1) LOG_LEVEL="warn" ;;
2) LOG_LEVEL="info" ;;
*) LOG_LEVEL="debug" ;;
esac
echo "Log level: $LOG_LEVEL"
'''Usage: mise run debug -vvv sets usage_v=3
Negation Flag
[tasks.build]
usage = 'flag "--color" negate="--no-color" default=#true'
run = '''
if [ "${usage_color}" = "true" ]; then
cargo build --color=always
else
cargo build --color=never
fi
'''---
Complex Examples
Multiple Arguments and Flags
[tasks.migrate]
description = "Run database migration"
usage = '''
arg "<direction>" help="Migration direction" {
choices "up" "down"
}
arg "[count]" default="1" help="Number of migrations"
flag "-f --force" help="Skip confirmation"
flag "--dry-run" help="Preview changes only"
'''
run = '''
#!/usr/bin/env bash
set -euo pipefail
DIR="${usage_direction}"
COUNT="${usage_count}"
FORCE="${usage_force:-false}"
DRY="${usage_dry_run:-false}"
if [ "$DRY" = "true" ]; then
echo "[DRY RUN] Would migrate $DIR by $COUNT"
exit 0
fi
if [ "$FORCE" != "true" ]; then
echo "Migrating $DIR by $COUNT. Press Enter to continue..."
read
fi
diesel migration "$DIR" --count "$COUNT"
'''Custom Completion
[tasks.deploy]
usage = '''
arg "<service>"
complete "service" run="kubectl get services -o name | sed 's|service/||'"
'''
run = 'kubectl rollout restart deployment/${usage_service}'Shell completion will show available Kubernetes services.
---
Accessing Arguments in Scripts
Environment Variable Pattern
Arguments become usage_<name> environment variables:
[tasks.example]
usage = '''
arg "<input>" help="Input file"
arg "[output]" default="out.txt"
flag "-v --verbose"
flag "-n --count <n>" default="10"
'''
run = '''
echo "Input: ${usage_input}"
echo "Output: ${usage_output}"
echo "Verbose: ${usage_verbose:-false}"
echo "Count: ${usage_count}"
'''Bash Variable Patterns
| Pattern | Meaning | Use Case |
|---|---|---|
${usage_var} | Variable value | When you're sure it's set |
${usage_var:-default} | Default if unset | Boolean flags |
${usage_var:?error} | Error if unset | Required validation |
${usage_var:+value} | Value if set | Conditional flags |
Conditional flag passing:
run = 'myapp ${usage_verbose:+--verbose} ${usage_debug:+--debug}'Only adds --verbose if usage_verbose is set.
---
Multi-line Usage Specification
For complex tasks, use multi-line format:
[tasks.complex]
usage = '''
arg "<environment>" help="Target environment" {
choices "dev" "staging" "prod"
}
arg "[version]" default="latest" help="Version to deploy"
flag "-f --force" help="Skip all confirmations"
flag "-n --dry-run" help="Preview without changes"
flag "--timeout <seconds>" default="300" help="Operation timeout"
flag "--region <region>" env="AWS_REGION" default="us-east-1"
complete "environment" run="echo 'dev\nstaging\nprod'"
'''
run = '''
# Script here
'''---
Validation
Required vs Optional
<arg>(angle brackets) = required, task fails if missing[arg](square brackets) = optional, uses default or empty
Type Coercion
All values are strings. Cast in script if needed:
[tasks.batch]
usage = 'flag "-n --count <n>" default="10"'
run = '''
COUNT="${usage_count}"
for i in $(seq 1 "$COUNT"); do
echo "Processing batch $i"
done
'''Environment Variable Satisfaction
If a flag has env="VAR", the environment variable satisfies required checks:
[tasks.deploy]
usage = 'flag "--token <token>" env="DEPLOY_TOKEN" help="Auth token"'Works with:
mise run deploy --token abc123DEPLOY_TOKEN=abc123 mise run deploy
---
Best Practices
1. Always add `help` text - Improves discoverability 2. Use `default` for optional flags - Avoids empty string issues 3. Use `env` for secrets - Don't require secrets on command line 4. Prefer `usage` over legacy methods - Future-proof your tasks 5. Validate early - Check arguments at script start
Meta-Prompt: Autonomous Polyglot Monorepo Bootstrap
⚠️ SUPERSEDED for greenfield repos (2026-06-12): the canonical bootstrap is now the
moon + proto + Bun (Nx-convergent) stack at
../../bootstrap-monorepo/references/bootstrap-monorepo.md.
This Pants + mise document remains valid ONLY for maintaining repos that already use it;
migrate per-repo, parity-first. The SR&ED section below remains current and is referenced
by the new document.
Table of Contents
- Tooling Stack: Pants + mise
- Phase 0: Pre-Flight Verification
- Phase 1: Foundational Structure
- Phase 2: Root CLAUDE.md — The Hub
- Phase 3: Configuration Files
- pants.toml
- mise.toml
- Root pyproject.toml (Workspace)
- Sub-Package pyproject.toml (core-python)
- BUILD Files (Auto-generated by `pants tailor`)
- .mcp.json
- .gitignore
- sgconfig.yml (ast-grep Configuration)
- Example ast-grep Rules
- Phase 4: Verification Checklist
- Phase 5: Post-Bootstrap Tasks
- Python Package
- Rust Package
- Bun Package
- Phase 6: Cross-Language Type Definitions
- JSON Schema Examples
- Code Generation Script
- Phase 7: GitHub Repository Setup
- Repository Creation and Decoration
- Standard Labels
- README Badge Patterns
- LICENSE File Template (MIT)
- git-town Configuration
- Professional README Structure
- Phase 8: Release Workflow Setup
- Root package.json
- .releaserc.yml Configuration
- mise Release Tasks
- Release Commands
- Update .gitignore
- First Release
- SR&ED Commit Integration
- Performance Insights: Language Selection
- SR&ED Commit Conventions (Canada CRA)
- CRA Eligibility Criteria
- SR&ED Commit Types
- Commit Message Examples
- SR&ED Documentation Structure
- GitHub Labels for SR&ED
Role: You are a Principal Software Architect specializing in AI-native monorepo design.
Mission: Construct a production-grade polyglot monorepo from scratch, optimized for agentic workflows with Claude Code.
Constraint: The human will not touch any code. You execute everything autonomously, verifying at each phase.
---
Tooling Stack: Pants + mise
This bootstrap uses Pants + mise for 10-50 Python-heavy polyglot packages:
| Tool | Responsibility |
|---|---|
| mise | Runtime versions (Python, Node, Rust) + environment variables |
| Pants | Build orchestration + native affected detection + dependency inference |
→ See polyglot-affected.md for tool comparison and scaling guidance
---
Phase 0: Pre-Flight Verification
Before creating any files, verify the environment:
# Check required tools exist
command -v mise && mise --version
command -v git && git --version
command -v cargo && cargo --version
command -v uv && uv --version
command -v bun && bun --version
command -v pants && pants --versionIf any tool is missing, install via mise (Pants via pip):
mise use -g rust@latest python@3.12 node@lts bun@latest uv@latest
pip install pantsbuild.pantsCreate project root and initialize git:
mkdir -p ~/projects/hft-monorepo && cd ~/projects/hft-monorepo
git init---
Phase 1: Foundational Structure
Create the canonical directory structure for a polyglot HFT monorepo:
hft-monorepo/
├── CLAUDE.md # Hub: Link Farm root (this file)
├── mise.toml # Orchestrator: tools + env vars
├── pants.toml # Build system: orchestration + affected
├── BUILD # Root BUILD file
├── .mise/ # Mise local config
├── .mcp.json # MCP server configuration
├── .claude/ # Claude Code configuration
│ └── skills/ # Project-local skill modules
│ ├── python/
│ │ └── SKILL.md
│ ├── rust/
│ │ └── SKILL.md
│ └── bun/
│ └── SKILL.md
├── docs/ # Deep documentation (spoke)
│ ├── ARCHITECTURE.md
│ ├── LOGGING.md
│ ├── TESTING.md
│ └── WORKFLOWS.md
├── packages/ # Polyglot packages
│ ├── core-python/ # Python: shared utilities
│ │ ├── CLAUDE.md # Child hub
│ │ ├── BUILD # Pants target: python_sources()
│ │ ├── pyproject.toml
│ │ └── src/
│ ├── core-rust/ # Rust: performance-critical
│ │ ├── CLAUDE.md # Child hub
│ │ ├── BUILD # Pants target: cargo_package()
│ │ ├── Cargo.toml
│ │ └── src/
│ ├── core-bun/ # Bun: async I/O, APIs
│ │ ├── CLAUDE.md # Child hub
│ │ ├── BUILD # Pants target: javascript_sources()
│ │ ├── package.json
│ │ └── src/
│ └── shared-types/ # Cross-language type definitions
│ ├── CLAUDE.md
│ ├── BUILD
│ └── schemas/
├── services/ # Deployable services
│ ├── data-ingestion/
│ │ ├── CLAUDE.md
│ │ └── BUILD
│ ├── strategy-engine/
│ │ ├── CLAUDE.md
│ │ └── BUILD
│ └── execution-gateway/
│ ├── CLAUDE.md
│ └── BUILD
├── rules/ # ast-grep rule directories
│ ├── general/ # Cross-language patterns (secrets, etc.)
│ ├── python/ # Python-specific rules
│ ├── rust/ # Rust-specific rules
│ └── typescript/ # TypeScript-specific rules
├── scripts/ # Automation scripts
│ └── generate-types.sh # Code generation from schemas
└── logs/ # Local log output (gitignored)Execute creation:
mkdir -p .claude/skills/{python,rust,bun} docs packages/{core-python/src,core-rust/src,core-bun/src,shared-types/schemas} services/{data-ingestion,strategy-engine,execution-gateway} rules/{general,python,rust,typescript} scripts logs
touch .gitignore BUILD sgconfig.yml---
Phase 2: Root CLAUDE.md — The Hub
Create the root CLAUDE.md as the Link Farm hub with Progressive Disclosure:
# HFT Polyglot Monorepo
> **Navigation**: This file is the single entry point. Each section links to deeper documentation. Child directories contain their own `CLAUDE.md` files that Claude loads on-demand.
## Quick Reference
| Action | Command |
| -------------- | --------------------------------------------- |
| Build affected | `pants --changed-since=origin/main package` |
| Test affected | `pants --changed-since=origin/main test` |
| Lint all | `pants lint ::` |
| Generate BUILD | `pants tailor` |
| Search code | Use `ck` MCP tool: `semantic_search("query")` |
## Architecture Overview
**Stack**: Python (uv) · Rust (cargo) · Bun · Pants (build) · Mise (runtimes)
**Pattern**: Polyglot monorepo with independent semantic versioning
**AI Interface**: Claude Code via MCP servers
→ Deep dive: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
## Package Map
| Package | Language | Purpose | Entry |
| -------------- | -------- | ----------------------------- | ------------------------------------------------------------------ |
| `core-python` | Python | Shared utilities, data models | [packages/core-python/CLAUDE.md](packages/core-python/CLAUDE.md) |
| `core-rust` | Rust | Performance-critical compute | [packages/core-rust/CLAUDE.md](packages/core-rust/CLAUDE.md) |
| `core-bun` | Bun/TS | Async I/O, HTTP APIs | [packages/core-bun/CLAUDE.md](packages/core-bun/CLAUDE.md) |
| `shared-types` | Multi | Cross-language schemas | [packages/shared-types/CLAUDE.md](packages/shared-types/CLAUDE.md) |
## Workflow Protocol
When modifying code in this repo:
1. **Explore** — Read the relevant `CLAUDE.md` in the target directory
2. **Search** — Use `semantic_search` MCP tool to find related code
3. **Affected** — Run `pants --changed-since=origin/main list` to identify impacted targets
4. **Plan** — State approach before editing (ultrathink if complex)
5. **Implement** — Make changes, running `pants lint` after each file
6. **Test** — Run `pants --changed-since=origin/main test` before committing
7. **Verify** — Confirm logs emit correctly to `logs/`
→ Deep dive: [docs/WORKFLOWS.md](docs/WORKFLOWS.md)---
Phase 3: Configuration Files
pants.toml
# SSoT-OK: placeholder versions for documentation
[GLOBAL]
pants_version = "<version>"
backend_packages = [
"pants.backend.python",
"pants.backend.python.lint.ruff",
"pants.backend.experimental.rust",
"pants.backend.experimental.javascript",
]
[python]
interpreter_constraints = [">=3.12"]
[source]
root_patterns = ["packages/*", "services/*"]
[python-bootstrap]
search_path = ["<PATH>"]
[anonymous-telemetry]
enabled = falsemise.toml
Do NOT put GitHub tokens in mise (ADR 2026-06-21). mise manages tool versions
and non-GitHub env only. GitHub auth is driven by the repo's origin host-alias(git@github.com-<account>:…); a token, when a script needs one, resolves freshvia~/.claude/tools/bin/gh-token-for-repo. The oldread_file(.secrets/gh-token-*)
injection and the .secrets files are retired/deleted.[env]
LOG_DIR = "{{config_root}}/logs"
ENV = "dev"
PANTS_CONCURRENT = "true"
# SSoT-OK: placeholder versions for documentation
[tools]
python = "<version>"
rust = "<version>"
node = "<version>"
bun = "<version>"
uv = "<version>"
"cargo:ast-grep" = "latest" # Structural code search
# Convenience wrappers for Pants commands
[tasks."test:affected"]
description = "Test affected packages via Pants"
run = "pants --changed-since=origin/main test"
[tasks."lint:affected"]
description = "Lint affected packages via Pants"
run = "pants --changed-since=origin/main lint"
[tasks.lint]
description = "Lint all packages"
run = "pants lint ::"
[tasks.test]
description = "Test all packages"
run = "pants test ::"
[tasks.affected]
description = "List packages affected by git changes"
run = "pants --changed-since=origin/main list"
[tasks."pants:tailor"]
description = "Generate BUILD files"
run = "pants tailor"Root pyproject.toml (Workspace)
Dev dependencies are hoisted to workspace root for single-command installation. This eliminates "unnecessary package" warnings from uv sync and provides a unified dev environment.
# SSoT-OK: example workspace root configuration
[project]
name = "hft-monorepo"
version = "<version>"
requires-python = ">=3.12"
# Only workspace orchestration deps at root
dependencies = []
[tool.uv.workspace]
members = ["packages/*"]
# PEP 735 dependency groups - hoisted from all workspace members
# All dev tools centralized here for `uv sync --group dev`
[dependency-groups]
dev = [
# Testing
"pytest>=9.0.0",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
"coverage>=7.0.0",
# Linting & formatting
"ruff>=0.1.0",
"mypy>=1.0.0",
# Jupyter/notebooks (if needed)
"ipykernel>=7.1.0",
"jupyterlab>=4.5.0",
]Sub-Package pyproject.toml (core-python)
Sub-packages define only runtime dependencies. No [dependency-groups] - dev deps are at workspace root.
# SSoT-OK: example sub-package configuration
[project]
name = "core-python"
version = "<version>"
requires-python = ">=3.12"
dependencies = [
"loguru",
"platformdirs",
"pydantic",
]
# NOTE: Dev dependencies hoisted to workspace root pyproject.toml
# Use `uv sync --group dev` from workspace rootWhy hoist dev dependencies? See uv Managing Dependencies - PEP 735[dependency-groups]in sub-packages are not automatically included byuv syncfrom root. Hoisting ensuresuv sync --group devinstalls all dev tools in one command.
BUILD Files (Auto-generated by pants tailor)
# packages/core-python/BUILD
python_sources()
python_tests()
# packages/core-rust/BUILD
cargo_package()
# packages/core-bun/BUILD
javascript_sources()
javascript_tests().mcp.json
{
"mcpServers": {
"mise": {
"command": "mise",
"args": ["mcp"],
"env": {
"MISE_EXPERIMENTAL": "1"
}
},
"code-search": {
"command": "ck",
"args": ["--serve"],
"cwd": "."
},
"shell": {
"command": "uvx",
"args": ["mcp-shell-server"],
"env": {
"ALLOW_COMMANDS": "mise,git,jq,pants,cargo,uv,bun,cat,ls,grep,head,tail,find"
}
}
}
}.gitignore
# Logs
logs/
*.jsonl
# Dependencies
node_modules/
target/
.venv/
__pycache__/
*.pyc
# Build outputs
dist/
build/
*.egg-info/
# Pants
.pants.d/
.pids/
# IDE
.idea/
.vscode/
*.swp
# OS
.DS_Store
Thumbs.db
# Mise
.mise.local.toml
# Secrets (never commit)
.env.local
*.key
*.pemsgconfig.yml (ast-grep Configuration)
# ast-grep rule configuration
ruleDirs:
- rules/general
- rules/python
- rules/rust
- rules/typescript
testConfigs:
- testDir: tests/rules
utilDirs:
- utils
languageGlobs:
typescript: ["*.ts", "*.tsx"]
javascript: ["*.js", "*.jsx", "*.mjs"]
python: ["*.py", "*.pyi"]Example ast-grep Rules
rules/general/no-hardcoded-secrets.yml — Detect hardcoded API keys:
id: no-hardcoded-api-key
language: python
message: Possible hardcoded API key or secret detected
severity: error
rule:
any:
- pattern: api_key = "$$$"
- pattern: API_KEY = "$$$"
- pattern: secret = "$$$"
note: |
Never hardcode secrets. Use environment variables or Doppler.rules/python/no-print-statements.yml — Enforce logging over print:
id: no-print-statements
language: python
message: Use logging instead of print statements
severity: hint
rule:
pattern: print($$$)
note: |
Use loguru for structured logging:
from loguru import logger
logger.info("message")rules/typescript/no-console-log.yml — Enforce proper logging:
id: no-console-log
language: typescript
message: Use a proper logger instead of console.log
severity: hint
rule:
pattern: console.log($$$)
note: |
Use pino for structured logging:
import pino from 'pino';
const logger = pino();
logger.info({ data }, "message");Run rules with: sg scan or use ast-grep MCP for interactive searches.
---
Phase 4: Verification Checklist
After creating all files, verify the setup:
# 1. Directory structure
find . -name "CLAUDE.md" -o -name "BUILD" | head -20
# 2. Mise configuration
mise doctor
mise tasks
# 3. Pants configuration
pants --version
pants tailor # Generate BUILD files if needed
pants list :: # List all targets
# 4. Affected detection (Pants native)
pants --changed-since=origin/main list
# 5. MCP configuration
cat .mcp.json | jq .
# 6. Log directory
mkdir -p logs
ls -la logs/
# 7. Git status
git status
git add -A
git commit -m "chore: initial monorepo scaffold with Pants + mise"---
Phase 5: Post-Bootstrap Tasks
Once the scaffold is complete, initialize each package:
Python Package
cd packages/core-python
uv init
uv add loguru platformdirs pydantic
# NOTE: Dev deps are hoisted to workspace root - don't add here
# Use `uv sync --group dev` from workspace root instead
pants tailor # Generate BUILD fileRust Package
cd packages/core-rust
cargo init --lib
# Add dependencies to Cargo.toml per skill guide
pants tailor # Generate BUILD fileBun Package
cd packages/core-bun
bun init -y
bun add pino zod
bun add -d @biomejs/biome @types/bun
pants tailor # Generate BUILD file---
Phase 6: Cross-Language Type Definitions
For polyglot monorepos, define types once in JSON Schema (Draft 2020-12) and generate for each language.
JSON Schema Examples
packages/shared-types/schemas/fitness-metrics.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "fitness-metrics.json",
"title": "FitnessMetrics",
"type": "object",
"required": ["sharpeRatio", "maxDrawdown", "totalReturn"],
"properties": {
"sharpeRatio": { "type": "number" },
"maxDrawdown": { "type": "number", "minimum": 0, "maximum": 1 },
"totalReturn": { "type": "number" },
"tradingDays": { "type": "integer", "minimum": 1 }
}
}Code Generation Script
scripts/generate-types.sh:
#!/usr/bin/env bash
set -euo pipefail
SCHEMAS_DIR="packages/shared-types/schemas"
generate_python() {
# Requires: uv pip install datamodel-code-generator
datamodel-codegen \
--input "$SCHEMAS_DIR" \
--output "packages/core-python/src/generated/models.py" \
--output-model-type pydantic_v2.BaseModel
}
generate_typescript() {
# Requires: bun add -D json-schema-to-zod
for schema in "$SCHEMAS_DIR"/*.json; do
local name
name=$(basename "$schema" .json | tr '-' '_')
bunx json-schema-to-zod -s "$schema" -o "packages/core-bun/src/generated/${name}.ts"
done
}
generate_rust() {
# Requires: cargo install typify-cli
for schema in "$SCHEMAS_DIR"/*.json; do
local name
name=$(basename "$schema" .json | tr '-' '_')
typify "$schema" > "packages/core-rust/src/generated/${name}.rs"
done
}
case "${1:-all}" in
python) generate_python ;;
typescript) generate_typescript ;;
rust) generate_rust ;;
all) generate_python; generate_typescript; generate_rust ;;
esacAdd mise task:
[tasks.generate-types]
description = "Generate types from JSON Schema"
run = "bash scripts/generate-types.sh all"---
Phase 7: GitHub Repository Setup
For public repositories, proper decoration improves discoverability and professionalism.
Repository Creation and Decoration
# Create repository (if needed)
gh repo create <owner>/<repo-name> --public --source=. --push
# Add description and topics
gh repo edit <owner>/<repo-name> \
--description "Polyglot monorepo for <domain> using Python, Rust, TypeScript" \
--add-topic python \
--add-topic rust \
--add-topic typescript \
--add-topic monorepo \
--add-topic polyglot
# Example topics for trading/finance projects
gh repo edit <owner>/<repo-name> \
--add-topic trading \
--add-topic quantitative-finance \
--add-topic backtesting \
--add-topic numba \
--add-topic financeStandard Labels
Create consistent labels for issue and PR management:
# Package labels (scoped by package name)
gh label create "pkg:core-python" --color "3572A5" --description "Python core package"
gh label create "pkg:core-rust" --color "DEA584" --description "Rust core package"
gh label create "pkg:core-bun" --color "F7DF1E" --description "TypeScript/Bun package"
gh label create "pkg:shared-types" --color "6E5494" --description "Cross-language schemas"
# Type labels
gh label create "type:bug" --color "D73A4A" --description "Something isn't working"
gh label create "type:feature" --color "0E8A16" --description "New feature or request"
gh label create "type:docs" --color "0075CA" --description "Documentation improvements"
gh label create "type:refactor" --color "FBCA04" --description "Code refactoring"
gh label create "type:perf" --color "7057FF" --description "Performance improvements"
gh label create "type:ci" --color "BFD4F2" --description "CI/CD pipeline changes"README Badge Patterns
Static badges using shields.io for consistent styling:
# Project Title
[](packages/core-python)
[](packages/core-rust)
[](packages/core-bun)
[](.)
[](LICENSE)Badge format: 
Common colors:
| Language/Tool | Color Code | Logo |
|---|---|---|
| Python | 3776AB | python |
| Rust | DEA584 | rust |
| TypeScript | 3178C6 | typescript |
| Node.js | 339933 | node.js |
| Bun | FBF0DF | bun |
| MIT License | blue | - |
| Tests passing | brightgreen | - |
LICENSE File Template (MIT)
Create LICENSE in root:
MIT License
Copyright (c) <YEAR> <OWNER>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.git-town Configuration
Configure git-town for streamlined branch workflows:
# Initialize git-town (one-time)
git-town config setup
# Or configure directly
git config git-town.main-branch main
git config git-town.perennial-branches ""
git config git-town.push-new-branches true
git config git-town.sync-feature-strategy rebase
# Verify configuration
git-town configKey settings:
| Setting | Value | Purpose |
|---|---|---|
main-branch | main | Primary integration branch |
push-new-branches | true | Auto-push new feature branches |
sync-feature-strategy | rebase | Keep linear history on features |
Professional README Structure
# Project Name
[...]
Short description of the project (1-2 sentences).
## Overview
Brief explanation of what the project does and its key value proposition.
## Quick Start
\`\`\`bash
# Prerequisites
brew install mise
# Setup
git clone https://github.com/<owner>/<repo>.git
cd <repo>
mise install
# Run
mise run <main-task>
\`\`\`
## Packages
| Package | Language | Tests | Purpose |
| ------------------------- | ---------- | ----- | ------------------- |
| [`pkg-a`](packages/pkg-a) | Python | 40 | Primary analysis |
| [`pkg-b`](packages/pkg-b) | Rust | 14 | Performance compute |
| [`pkg-c`](packages/pkg-c) | TypeScript | 32 | APIs, web |
## Performance
| Implementation | Key Metric | Baseline |
| -------------- | ---------- | ----------- |
| Python + Numba | X.X ms | baseline |
| Rust | X.X ms | Y.Yx faster |
## Architecture
\`\`\`
project/
├── packages/
│ ├── pkg-a/
│ └── pkg-b/
├── data/
└── artifacts/
\`\`\`
## Documentation
- [Architecture](docs/ARCHITECTURE.md)
- [Domain Concepts](docs/CONCEPTS.md)
## License
MIT---
Phase 8: Release Workflow Setup
Automate versioning and changelog generation using semantic-release.
Full documentation: See itp:semantic-release skill for comprehensive release workflow patterns, troubleshooting, and advanced configurations.Root package.json
Create package.json in the monorepo root for semantic-release:
{
"name": "<project-name>",
"version": "<version>",
"private": true,
"description": "Polyglot monorepo for <domain>",
"repository": {
"type": "git",
"url": "git+https://github.com/<owner>/<repo>.git"
},
"author": "<owner>",
"license": "MIT",
"devDependencies": {
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^11.0.1",
"semantic-release": "^25.0.0"
}
}Note: Setversionto"0.0.0"for new projects. Semantic-release will bump it on first release.
Install dependencies: npm install
.releaserc.yml Configuration
Create .releaserc.yml in the monorepo root:
Warning: The@semantic-release/execplugin uses Lodash templates which conflict with bash${VAR:-default}syntax. Use<%= %>for semantic-release variables or avoid bash default syntax. See Troubleshooting: Lodash Template Conflicts.
branches:
- main
plugins:
# Preflight: Block release if working directory is dirty
# NOTE: Avoid ${VAR:-default} bash syntax in exec commands (Lodash conflict)
- - "@semantic-release/exec"
- verifyConditionsCmd: |
if [ -n "$(git status --porcelain)" ]; then
echo "Working directory not clean"
exit 1
fi
- - "@semantic-release/commit-analyzer"
- releaseRules:
# All commit types trigger patch for consistent versioning
- { type: "docs", release: "patch" }
- { type: "chore", release: "patch" }
- { type: "style", release: "patch" }
- { type: "refactor", release: "patch" }
- { type: "test", release: "patch" }
- { type: "build", release: "patch" }
- { type: "ci", release: "patch" }
- { type: "revert", release: "patch" }
- "@semantic-release/release-notes-generator"
- "@semantic-release/changelog"
- - "@semantic-release/git"
- assets:
- CHANGELOG.md
- package.json
message: "chore(release): ${nextRelease.version} [skip ci]"
- "@semantic-release/github"mise Release Tasks
Create file-based tasks in .mise/tasks/release/:
mkdir -p .mise/tasks/release.mise/tasks/release/preflight:
#!/usr/bin/env bash
#MISE description="Phase 1: Validate prerequisites for release"
set -euo pipefail
echo "═══════════════════════════════════════════════════════════"
echo " Phase 1: PREFLIGHT"
echo "═══════════════════════════════════════════════════════════"
# Check 1: Working directory clean
echo "→ Checking working directory..."
if [[ -n "$(git status --porcelain)" ]]; then
echo " ✗ Working directory not clean"
git status --short
exit 1
fi
echo " ✓ Working directory clean"
# Check 2: GitHub authentication (no API calls - prevents process storms)
echo "→ Checking GitHub authentication..."
if [[ -z "${GH_TOKEN:-}" ]]; then
echo " ✗ GH_TOKEN not set"
echo " Ensure mise.toml uses read_file() pattern"
exit 1
fi
echo " ✓ GH_TOKEN present (${#GH_TOKEN} chars)"
# Check 3: On main branch
echo "→ Checking branch..."
BRANCH=$(git branch --show-current)
if [[ "$BRANCH" != "main" ]]; then
echo " ✗ Not on main branch (current: $BRANCH)"
exit 1
fi
echo " ✓ On main branch"
# Check 4: Releasable commits exist
echo "→ Checking for releasable commits..."
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [[ -n "$LATEST_TAG" ]]; then
COMMITS=$(git log "$LATEST_TAG"..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ')
if [[ "$COMMITS" -eq 0 ]]; then
echo " ✗ No commits since $LATEST_TAG"
exit 1
fi
echo " ✓ Found $COMMITS commits since $LATEST_TAG"
else
COMMITS=$(git log --oneline 2>/dev/null | wc -l | tr -d ' ')
echo " ✓ No previous tags, $COMMITS commits to release"
fi
# Check 5: Tests pass (optional but recommended)
echo "→ Running tests..."
if mise run test >/dev/null 2>&1; then
echo " ✓ Tests passed"
else
echo " ⚠ Tests failed (continuing anyway)"
fi
echo ""
echo "✓ All preflight checks passed"
echo "".mise/tasks/release/version:
#!/usr/bin/env bash
#MISE description="Phase 2: Run semantic-release (version bump + changelog)"
# Note: No dependency on preflight - release:full handles the chain
set -euo pipefail
echo "═══════════════════════════════════════════════════════════"
echo " Phase 2: VERSION (semantic-release)"
echo "═══════════════════════════════════════════════════════════"
# Ensure node_modules are installed
if [[ ! -d "node_modules" ]]; then
echo "→ Installing npm dependencies..."
npm install --silent
fi
# Run semantic-release
semantic-release --no-ci
echo ""
echo "✓ Version phase complete"
echo "".mise/tasks/release/full:
#!/usr/bin/env bash
#MISE description="Complete release workflow"
#MISE depends=["release:preflight"]
set -euo pipefail
echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ Full Release Workflow ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""
# Phase 2: Version
mise run release:version
echo ""
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ ✓ Release workflow complete! ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo ""Make tasks executable:
chmod +x .mise/tasks/release/*Release Commands
| Command | Purpose |
|---|---|
mise run release:full | Complete 2-phase release (preflight + version) |
mise run release:preflight | Validate prerequisites only |
mise run release:version | Run semantic-release only |
Update .gitignore
Add npm artifacts:
# npm
node_modules/
package-lock.json # Optional: some prefer to commit thisFirst Release
# 1. Install dependencies
npm install
# 2. Commit release infrastructure
git add package.json .releaserc.yml .mise/tasks/release/
git commit -m "build: add semantic-release configuration and mise tasks
SRED-Type: support-work
SRED-Claim: RELEASE-INFRA"
# 3. Run first release
mise run release:fullSR&ED Commit Integration
If using SR&ED commit conventions (see SR&ED section below), commits must include trailers:
<type>(<scope>): <description>
<body>
SRED-Type: <category>
SRED-Claim: <claim-id>The sred-commit-guard hook (from itp-hooks) validates this format. Install via:
/itp:tether install---
Performance Insights: Language Selection
Based on real benchmarks with 1M data points (trading fitness calculations):
| Implementation | ITH Analysis | Overall | Notes |
|---|---|---|---|
| Python + Numba JIT | 5.5 ms | Baseline | LLVM-compiled, competitive with native |
| Rust (native) | 4.0 ms | 1.4x faster | Best for complex algorithms |
| Bun/TypeScript | 10.3 ms | 1.9x slower | Good for APIs, async I/O |
Key Insights:
1. Numba JIT is remarkably competitive — For numerical code, Numba compiles to LLVM machine code at runtime, matching or exceeding Rust for simple operations.
2. Rust advantage is in algorithmic complexity — Rust shines with branching logic, state machines, and memory-intensive operations (1.4-3x faster on ITH epoch detection).
3. TypeScript is fast enough for most use cases — 10ms for 1M points is acceptable for APIs, dashboards, and batch processing.
When to use each:
| Scenario | Best Choice |
|---|---|
| Existing Python codebase | Keep Python + Numba |
| Performance-critical paths | Rust via PyO3 bindings |
| Web API / real-time dashboard | Bun/TypeScript |
| Batch processing > 10M points | Rust |
| Quick prototyping | Python |
---
SR&ED Commit Conventions (Canada CRA)
For projects claiming Scientific Research & Experimental Development (SR&ED) tax credits, commits must document work that maps to CRA's eligibility criteria.
CRA Eligibility Criteria
| Criterion | Description | Commit Evidence Needed |
|---|---|---|
| Technological Uncertainty | What couldn't be achieved using standard practice | uncertainty:, experiment: |
| Technological Advancement | New knowledge or capability gained | advancement:, benchmark: |
| Scientific Content | Systematic investigation or search | research:, hypothesis: |
| Experimental Development | Iterative testing to resolve uncertainty | experiment:, iteration: |
SR&ED Commit Types
Extend conventional commits with SR&ED-specific prefixes:
<type>(<scope>): <description>
[optional body with SR&ED context]
[optional footer: SR&ED-CLAIM: <claim-id>]Standard Types (conventional commits):
| Type | Purpose | SR&ED Relevance |
|---|---|---|
feat | New feature | May support advancement |
fix | Bug fix | Rarely eligible |
docs | Documentation | Supports systematic investigation |
refactor | Code restructuring | Rarely eligible |
test | Adding tests | Supports experimental development |
perf | Performance improvement | May support advancement |
chore | Maintenance | Not eligible |
SR&ED-Specific Types (CRA-aligned):
| Type | CRA Mapping | Description |
|---|---|---|
experiment | Experimental Development | Hypothesis testing, controlled experiments |
research | Scientific Content | Literature review, prior art analysis |
uncertainty | Technological Uncertainty | Document what standard practice couldn't solve |
advancement | Technological Advancement | Document new knowledge or capability achieved |
hypothesis | Scientific Content | Formulate and document testable hypotheses |
analysis | Scientific Content | Data analysis, results interpretation |
iteration | Experimental Development | Iterative cycles to resolve uncertainty |
benchmark | Technological Advancement | Quantitative proof of advancement |
Commit Message Examples
Documenting Technological Uncertainty:
uncertainty(ith-python): standard Sharpe ratio insufficient for epoch detection
The conventional Sharpe ratio calculation doesn't account for time-varying
volatility regimes. Standard practice (rolling windows) fails to identify
discrete fitness epochs where strategy performance exceeds drawdown-adjusted
thresholds.
Attempted approaches that failed:
- Rolling 30-day Sharpe windows: too noisy, false positives
- EWMA-weighted returns: loses epoch boundary precision
- Standard drawdown metrics: no TMAEG concept exists
SR&ED-CLAIM: 2026-Q1-ITHDocumenting Experimental Work:
experiment(core-rust): test SIMD vectorization for ITH epoch detection
Hypothesis: SIMD intrinsics can accelerate excess_gain_excess_loss by 4x+
over scalar implementation for datasets > 100K points.
Methodology:
- Control: scalar Rust implementation (current)
- Treatment: AVX2 vectorized implementation
- Dataset: synthetic NAV series, 1M points, 100 iterations
Expected outcome: Sub-linear scaling with data size due to cache efficiency.
SR&ED-CLAIM: 2026-Q1-ITHDocumenting Technological Advancement:
advancement(ith-python): Numba JIT achieves near-native performance
Technological advancement achieved: Python+Numba matches Rust performance
for numerical ITH calculations, eliminating need for FFI complexity.
Benchmark results (1M data points):
- Python+Numba: 5.5ms (ITH analysis)
- Rust native: 4.0ms (1.4x faster, within acceptable range)
- TypeScript: 10.3ms (baseline comparison)
This advances the state of practice by proving JIT-compiled Python is
viable for production trading fitness analysis, previously assumed to
require native code.
SR&ED-CLAIM: 2026-Q1-ITHDocumenting Hypothesis Testing:
hypothesis(core-bun): TypeScript strict mode improves type safety at runtime
Hypothesis: Enabling strict:true in tsconfig.json will catch array boundary
errors at compile time that currently cause silent NaN propagation.
Test plan:
1. Enable strict mode
2. Fix all compile-time errors
3. Run existing test suite
4. Measure NaN-related failures before/after
Expected outcome: Zero runtime NaN errors from array access patterns.
SR&ED-CLAIM: 2026-Q1-ITHSR&ED Documentation Structure
Create docs/SRED.md to aggregate claim evidence:
# SR&ED Claim Evidence
## Claim Period: 2026-Q1
### Project: ITH (Investment Time Horizon) Analysis
**Technological Uncertainty**:
- Standard fitness metrics (Sharpe, Sortino) don't capture epoch-based performance
- No existing solution for TMAEG (Target Maximum Acceptable Excess Gain) calculation
- Uncertainty in optimal JIT compilation strategy for numerical Python
**Technological Advancement**:
- Novel ITH epoch detection algorithm
- Proof that Numba JIT matches native Rust for trading calculations
- Cross-language type system via JSON Schema code generation
**Systematic Investigation**:
- Benchmark-driven development with controlled experiments
- Iterative refinement of TMAEG calculation methodology
- Comparative analysis across Python, Rust, TypeScript implementations
### Commit Log (SR&ED Tagged)
| Date | Commit Hash | Type | Description |
| ---------- | ----------- | ----------- | ------------------------------- |
| 2026-01-15 | abc123 | uncertainty | Standard Sharpe insufficient |
| 2026-01-16 | def456 | experiment | SIMD vectorization test |
| 2026-01-17 | ghi789 | advancement | Numba achieves near-native perf |
### Time Allocation
| Activity | Hours | % of Total |
| ------------------------ | ----- | ---------- |
| Experimental Development | 40 | 50% |
| Applied Research | 24 | 30% |
| Documentation & Analysis | 16 | 20% |Git Log Extraction for Claims
Extract SR&ED-tagged commits for claim preparation:
# List all SR&ED commits
git log --oneline --grep="SR&ED-CLAIM"
# Extract by claim ID
git log --grep="SR&ED-CLAIM: 2026-Q1-ITH" --format="%h|%ad|%s" --date=short
# Generate claim summary
git log --grep="SR&ED-CLAIM" --format="| %ad | %h | %s |" --date=short > docs/sred-commits.mdGitHub Labels for SR&ED
# SR&ED claim tracking labels
gh label create "sred:uncertainty" --color "D93F0B" --description "Documents technological uncertainty"
gh label create "sred:advancement" --color "0E8A16" --description "Documents technological advancement"
gh label create "sred:experiment" --color "1D76DB" --description "Experimental development work"
gh label create "sred:research" --color "5319E7" --description "Scientific research activity"
gh label create "sred:eligible" --color "FBCA04" --description "Potentially SR&ED eligible"SR&ED Commit Enforcement (Claude Code Hook)
Enforce dual commit types (conventional + SR&ED) via PreToolUse hook.
Recommended format (Git trailers for metadata):
<conventional-type>(<scope>): <description>
<body>
SRED-Type: <category>
SRED-Claim: <claim-id>Why Git trailers?
| Approach | Parser Support | Extractable | Recommendation |
|---|---|---|---|
feat/experiment: (dual prefix) | Breaks parsers | No | Avoid |
feat(sred): (scope) | Good | Partial | OK for categorization |
SRED-Type: (trailer) | Excellent | git interpret-trailers | Best for metadata |
Hook installation (add to ~/.claude/settings.json):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash $HOME/.claude/plugins/marketplaces/cc-skills/plugins/itp-hooks/hooks/sred-commit-guard.sh",
"timeout": 5000
}
]
}
]
}
}Git commit-msg hook (for non-Claude commits):
#!/bin/bash
# .githooks/commit-msg
exec bash /path/to/sred-commit-guard.sh --git-hook "$1"Enable with: git config core.hooksPath .githooks
Extract SR&ED data for claims:
# All SRED-Type values from commits
git log --format='%(trailers:key=SRED-Type,valueonly)' | grep -v '^$' | sort | uniq -c
# Export for claim reporting
git log --since="2026-01-01" --format="%ad|%s|%(trailers:key=SRED-Type,valueonly)|%(trailers:key=SRED-Claim,valueonly)" --date=short---
Success Criteria
The bootstrap is complete when:
Infrastructure (Phases 0-5):
- [ ] All
CLAUDE.mdfiles exist and link correctly - [ ]
pants list ::shows all targets (ormise run affectedfor lightweight variant) - [ ]
pants --changed-since=origin/main listruns without error - [ ]
mise tasksshows convenience wrappers - [ ]
.mcp.jsonis valid JSON - [ ] Each package has BUILD file (or package.json/Cargo.toml/pyproject.toml)
- [ ]
logs/directory exists (gitignored) - [ ] Initial commit is made
Types (Phase 6):
- [ ] JSON Schema files exist in
packages/shared-types/schemas/ - [ ]
scripts/generate-types.shgenerates valid code for all languages - [ ] Generated types are referenced in package CLAUDE.md files
GitHub (Phase 7):
- [ ] Repository has description set
- [ ] Repository has relevant topics (5-10 recommended)
- [ ] Standard labels created (pkg:_, type:_)
- [ ] LICENSE file exists in root
- [ ] README.md has badges, quick start, package table
- [ ] git-town configured with main branch
SR&ED Documentation (Optional):
- [ ]
docs/SRED.mdcreated with claim structure - [ ] SR&ED labels created (sred:uncertainty, sred:advancement, etc.)
- [ ] Commit message convention documented
- [ ] Git log extraction scripts available
Release Workflow (Phase 8):
- [ ]
package.jsonexists with semantic-release dependencies - [ ]
.releaserc.ymlconfiguration present - [ ]
.mise/tasks/release/{preflight,version,full}tasks created and executable - [ ]
npm installcompletes without errors - [ ]
mise run release:preflightpasses all checks - [ ] First release creates GitHub release with changelog
---
Maintenance Protocol
When adding new packages:
1. Create directory under packages/ or services/ 2. Add CLAUDE.md following existing pattern 3. Run pants tailor to generate BUILD file 4. Link from root CLAUDE.md package map 5. Run mise run reindex for code search
---
Variant: Lightweight (No Pants)
For smaller projects (< 10 packages), skip Pants and use simple scripts:
scripts/affected.sh
#!/usr/bin/env bash
# Detect affected packages via git diff
set -euo pipefail
BASE_BRANCH="${1:-origin/main}"
CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH"...HEAD)
# Map files to packages
for file in $CHANGED_FILES; do
if [[ "$file" == packages/* ]]; then
echo "$file" | cut -d'/' -f2 | sort -u
fi
donemise.toml (no Pants)
[tasks.test]
description = "Test all packages"
run = """
cd packages/core-python && uv run pytest
cd ../core-rust && cargo test
cd ../core-bun && bun test
"""
[tasks."test:affected"]
description = "Test affected packages"
run = "bash scripts/affected.sh | xargs -I{} bash -c 'cd packages/{} && mise run test'"This variant was used successfully for the trading-fitness monorepo.
---
Testing Patterns by Language
Python (pytest)
# packages/core-python/tests/conftest.py
import pytest
import numpy as np
@pytest.fixture
def sample_nav_data():
"""Generate synthetic NAV series for testing."""
rng = np.random.default_rng(42)
returns = rng.normal(0.0005, 0.02, 1000)
return 100 * np.cumprod(1 + returns)Run: uv run pytest or pants test packages/core-python::
Rust (built-in)
// packages/core-rust/src/lib.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_drawdown_uptrend() {
let nav = vec![1.0, 1.1, 1.2, 1.3];
assert_eq!(max_drawdown(&nav), 0.0);
}
}Run: cargo test or pants test packages/core-rust::
TypeScript (bun:test)
// packages/core-bun/src/metrics.test.ts
import { describe, expect, test } from "bun:test";
import { maxDrawdown } from "./metrics";
describe("maxDrawdown", () => {
test("returns 0 for uptrend", () => {
expect(maxDrawdown([100, 110, 120])).toBe(0);
});
});Run: bun test or pants test packages/core-bun::
TypeScript Strict Mode Gotcha
With strict: true, array access returns T | undefined. Handle explicitly:
// WRONG: TypeScript error "possibly undefined"
const value = array[i];
doSomething(value); // Error!
// CORRECT: Explicit undefined check
const value = array[i];
if (value === undefined) {
return defaultValue;
}
doSomething(value); // OK---
Related Resources
Build & Environment:
- polyglot-affected.md - Tool comparison and Pants + mise guide
- Level 11: Pants + mise - Quick reference
- Pants Documentation
- mise Documentation
Release Workflow:
itp:semantic-releaseskill - Comprehensive release automation guide- semantic-release Documentation - Official docs
- local-release-workflow.md - 4-phase workflow reference
GitHub & Workflow:
- git-town Documentation - Branch workflow automation
- Shields.io - Badge generation for READMEs
- Conventional Commits - Commit message specification
Type Systems:
- JSON Schema Draft 2020-12 - Cross-language type definitions
SR&ED (Canada):
- CRA SR&ED Program - Official program page
- SR&ED Eligibility Criteria - What qualifies
- SR&ED Claim Guide T4088 - Claiming procedures
Environment Integration
How mise tasks interact with [env] section configuration.
Automatic Inheritance
Tasks automatically inherit [env] values:
[env]
DATABASE_URL = "postgresql://localhost/mydb"
_.file = ".env" # Load additional env vars
[tasks.migrate]
run = "diesel migration run" # $DATABASE_URL availableCredential Loading Pattern
[env]
_.file = { path = ".env.secrets", redact = true }
[tasks._check-env]
hide = true
run = '[ -n "$API_KEY" ] || { echo "Missing API_KEY"; exit 1; }'
[tasks.deploy]
depends = ["_check-env"]
run = "deploy.sh"Cross-Reference: mise-configuration
Prerequisites: Before defining tasks, ensure [env] section is configured.
PRESCRIPTIVE: After defining tasks, invoke [`mise-configuration` skill](../../mise-configuration/SKILL.md) to ensure [env] SSoT patterns are applied.
The mise-configuration skill covers:
[env]- Environment variables with defaults[settings]- mise behavior configuration[tools]- Version pinning- Special directives:
_.file,_.path,_.python.venv
Evolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
mise Tasks Patterns
Table of Contents
- Hidden Helper Tasks
- Credential Check
- Destructive Operation Confirmation
- Environment Validation
- Database Migration Pattern
- CI/CD Pipeline Pattern
- Release Workflow Pattern
- Development Server Pattern
- Runtime-Native Watch (Preferred)
- External Watch (Polyglot / Non-Runtime)
- Watch Method Decision Table
- Parameterized Deployment Pattern
- File Tracking Pattern
- Pueue Pipeline Orchestration Pattern
- mise Entry Points
- Shell Script DAG Builder
- Key Principles
- Anti-Pattern: mise `depends` for Long-Running Remote Jobs
- When to Use
- Complete Project Template
Real-world task patterns for common workflows.
Hidden Helper Tasks
Internal utilities that support other tasks but shouldn't appear in mise tasks output.
Credential Check
[tasks._check-credentials]
description = "Verify required credentials are set"
hide = true
run = '''
#!/usr/bin/env bash
set -euo pipefail
missing=()
[ -z "${DATABASE_URL:-}" ] && missing+=("DATABASE_URL")
[ -z "${API_KEY:-}" ] && missing+=("API_KEY")
if [ ${#missing[@]} -gt 0 ]; then
echo "Missing required credentials:"
printf ' - %s\n' "${missing[@]}"
echo ""
echo "Copy .env.example to .env and fill in values"
exit 1
fi
echo "Credentials configured"
'''Destructive Operation Confirmation
[tasks._confirm-destructive]
description = "Confirm destructive operation"
hide = true
run = '''
echo ""
echo "This will DELETE existing data."
echo "Press Enter to continue or Ctrl+C to cancel..."
read
'''Environment Validation
[tasks._validate-env]
description = "Validate environment is ready"
hide = true
run = '''
#!/usr/bin/env bash
set -euo pipefail
# Check Python version
python_version=$(python3 --version 2>&1 | cut -d' ' -f2)
required="3.11"
if [ "$(printf '%s\n' "$required" "$python_version" | sort -V | head -n1)" != "$required" ]; then
echo "Python >= $required required (found $python_version)"
exit 1
fi
# Check database connection
if ! pg_isready -q 2>/dev/null; then
echo "Database not available"
exit 1
fi
echo "Environment validated"
'''---
Database Migration Pattern
Complete database migration workflow with safety checks.
[env]
_.file = ".env"
CLICKHOUSE_DATABASE = "myapp"
[tasks._check-credentials]
hide = true
run = '[ -n "$CLICKHOUSE_HOST" ] || { echo "Set CLICKHOUSE_HOST in .env"; exit 1; }'
[tasks._confirm-destructive]
hide = true
run = 'echo "Press Enter to continue or Ctrl+C to cancel..." && read'
[tasks.db-drop]
description = "Drop legacy database (destructive!)"
depends = ["_check-credentials", "_confirm-destructive"]
run = "clickhouse-client --query 'DROP DATABASE IF EXISTS legacy_db'"
[tasks.db-init]
description = "Create database and tables from schema"
depends = ["_check-credentials"]
depends_post = ["db-validate"]
usage = 'opt "--schema" default="main" help="Schema name"'
run = "uv run python -m schema.cli init --schema ${usage_schema}"
[tasks.db-validate]
description = "Validate schema against live database"
depends = ["_check-credentials"]
run = "uv run python -m schema.cli validate"
[tasks.db-migrate]
description = "Full migration: drop legacy + create new + validate"
depends = ["db-drop", "db-init"]
depends_post = ["test-e2e"]
run = "echo 'Migration complete'"Usage:
mise run db-migrate
# Executes: _check-credentials → _confirm-destructive → db-drop → db-init → db-validate → test-e2e---
CI/CD Pipeline Pattern
Comprehensive CI pipeline with parallel stages.
[tasks.lint]
description = "Run linters"
run = "ruff check . && ruff format --check ."
[tasks.typecheck]
description = "Run type checker"
run = "mypy src/"
[tasks.test]
description = "Run test suite"
alias = "t"
run = "pytest tests/ -v"
[tasks."test:unit"]
description = "Run unit tests only"
run = "pytest tests/unit/ -v"
[tasks."test:integration"]
description = "Run integration tests"
depends = ["_check-credentials"]
run = "pytest tests/integration/ -v"
[tasks.build]
description = "Build distribution"
depends = ["lint", "typecheck", "test"]
run = "uv build"
[tasks.ci]
description = "Full CI pipeline"
depends = ["lint", "typecheck", "test", "build"]
run = "echo 'CI passed'"Parallel execution:
# Run lint and typecheck in parallel
mise run lint ::: typecheck
# Then run tests
mise run test---
Release Workflow Pattern
Safe release workflow with pre-checks.
[tasks._check-clean]
description = "Verify working directory is clean"
hide = true
run = '''
if [ -n "$(git status --porcelain)" ]; then
echo "Working directory not clean. Commit or stash changes."
exit 1
fi
'''
[tasks._check-main]
description = "Verify on main branch"
hide = true
run = '''
branch=$(git branch --show-current)
if [ "$branch" != "main" ] && [ "$branch" != "master" ]; then
echo "Must be on main/master branch (on: $branch)"
exit 1
fi
'''
[tasks.release-dry]
description = "Dry-run release (no changes)"
depends = ["_check-clean", "_check-main", "test"]
run = '/usr/bin/env bash -c '\''GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci --dry-run'\'''
[tasks.release]
description = "Create release"
depends = ["_check-clean", "_check-main", "test"]
confirm = "This will create a new release. Continue?"
run = '/usr/bin/env bash -c '\''GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci'\'''---
Development Server Pattern
Development workflow with file watching.
Runtime-Native Watch (Preferred)
Use the runtime's built-in file watcher when available — zero extra processes, zero extra memory.
Bun/TypeScript (preferred for all Bun services):
[tasks.start]
description = "Start service (auto-restarts on file changes)"
run = "bun --watch run src/main.ts"
[tasks.start-plain]
description = "Start without file watching"
run = "bun run src/main.ts"Anti-pattern: Do NOT usebun --hot,nodemon,ts-node-dev,tsx watch, or
watchexecfor Bun/TypeScript projects.bun --watchuses the same kqueue/inotify
primitives built into the Bun runtime with zero overhead (tested: +0 MB RSS vs plain
bun run).bun --hotpreserves module state across reloads which causes stale state
bugs in long-running services.
Python (uvicorn built-in reload):
[tasks.dev]
description = "Start development server"
run = "uvicorn app:main --reload --port 8000"
[tasks.dev-db]
description = "Start database in Docker"
run = "docker compose up -d postgres"
[tasks.dev-full]
description = "Start full dev environment"
depends = ["dev-db"]
run = "mise run dev"External Watch (Polyglot / Non-Runtime)
Use mise watch or watchexec only when the runtime lacks a built-in watcher (Go, Rust, shell scripts, multi-language orchestration).
mise watch dev # Auto-restart on file changes (requires watchexec)Watch Method Decision Table
| Runtime | Method | Command | Extra overhead |
|---|---|---|---|
| Bun | Built-in --watch | bun --watch run src/main.ts | 0 MB, 0 processes |
| Python | uvicorn reload | uvicorn app:main --reload | 0 MB, 0 processes |
| Node.js | Built-in --watch | node --watch src/main.js | 0 MB, 0 processes |
| Go/Rust/other | watchexec | watchexec -w src -- cargo run | +10 MB, 1 process |
| Multi-language | mise watch | mise watch dev | +10 MB (uses watchexec) |
---
Parameterized Deployment Pattern
Environment-aware deployment with arguments.
[tasks.deploy]
description = "Deploy to environment"
depends = ["_check-credentials", "build"]
usage = '''
arg "<environment>" help="Target environment" {
choices "dev" "staging" "prod"
}
flag "-f --force" help="Skip confirmation"
flag "--dry-run" help="Show what would be deployed"
'''
run = '''
#!/usr/bin/env bash
set -euo pipefail
ENV="${usage_environment}"
FORCE="${usage_force:-false}"
DRY="${usage_dry_run:-false}"
echo "Deploying to: $ENV"
if [ "$DRY" = "true" ]; then
echo "[DRY RUN] Would deploy to $ENV"
exit 0
fi
if [ "$ENV" = "prod" ] && [ "$FORCE" != "true" ]; then
echo "Production deployment requires --force flag"
exit 1
fi
kubectl config use-context "$ENV"
kubectl apply -f "k8s/$ENV/"
'''Usage:
mise run deploy dev # Deploy to dev
mise run deploy staging # Deploy to staging
mise run deploy prod --force # Deploy to production
mise run deploy prod --dry-run # Preview production deploy---
File Tracking Pattern
Efficient builds with source/output tracking.
[tasks.compile]
description = "Compile TypeScript"
sources = ["src/**/*.ts", "tsconfig.json"]
outputs = ["dist/**/*.js"]
run = "tsc"
[tasks.bundle]
description = "Bundle for production"
depends = ["compile"]
sources = ["dist/**/*.js", "package.json"]
outputs = ["build/bundle.js"]
run = "esbuild dist/index.js --bundle --outfile=build/bundle.js"Behavior:
- First run: Both tasks execute
- Second run (no changes): Both tasks skip
- After editing
src/: Onlycompileandbundlerun - Force rebuild:
mise run bundle --force
---
Pueue Pipeline Orchestration Pattern
Delegate long-running, multi-step pipelines to pueue for SSH-safe persistence and dependency chaining. Battle-tested during rangebar-py Issue #88 production deployment (batch repopulation -> OPTIMIZE TABLE -> validation).
mise Entry Points
# .mise/tasks/cache.toml
# Individual steps (can run standalone)
["cache:detect-overflow"]
description = "Detect volume overflow (negative volumes) in ClickHouse cache"
run = "python scripts/detect_volume_overflow.py"
["cache:optimize"]
description = "Run OPTIMIZE TABLE FINAL on range_bars"
run = "./scripts/pueue-populate.sh optimize"
# Fully-chained pueue pipeline (recommended for production)
["cache:postprocess-all"]
description = "Full post-fix pipeline via pueue: repopulate → optimize → detect (auto-chained)"
run = "./scripts/pueue-populate.sh postprocess-all"Shell Script DAG Builder
The shell script captures pueue job IDs with --print-task-id and chains steps with --after:
postprocess_all() {
# Step 1: Queue batch jobs, capture IDs
JOB_IDS=()
for threshold in 250 500 750 1000; do
local job_id
job_id=$(pueue add --print-task-id --group postfix \
--label "SYMBOL@${threshold}" \
--working-directory "$PROJECT_DIR" \
-- uv run python scripts/populate.py --threshold "$threshold" --force-refresh)
JOB_IDS+=("$job_id")
done
# Step 2: Chain OPTIMIZE TABLE --after all batch jobs
local optimize_id
optimize_id=$(pueue add --print-task-id --group postfix \
--label "optimize-table" \
--after "${JOB_IDS[@]}" \
--working-directory "$PROJECT_DIR" \
-- clickhouse-client --query "OPTIMIZE TABLE mydb.mytable FINAL")
# Step 3: Chain validation --after optimize
pueue add --group postfix \
--label "detect-overflow" \
--after "$optimize_id" \
--working-directory "$PROJECT_DIR" \
-- uv run python scripts/detect_overflow.py
echo "Pipeline: ${#JOB_IDS[@]} batch jobs → optimize → detect"
}Key Principles
- mise provides the entry point:
mise run cache:postprocess-all— human-friendly, discoverable viamise tasks - Pueue provides the execution engine: Dependency resolution, SSH-safe persistence, group-based parallelism
- Shell script is the glue: Captures pueue job IDs with
--print-task-id, chains with--after - Each step also standalone:
mise run cache:optimizeworks independently for ad-hoc use
Anti-Pattern: mise depends for Long-Running Remote Jobs
# BAD: mise blocks waiting for each step
["cache:postprocess-all"]
depends = ["cache:repopulate", "cache:optimize", "cache:detect"]
# This runs synchronously — if SSH disconnects, everything dies
# GOOD: Delegate to pueue for persistence
["cache:postprocess-all"]
run = "./scripts/pueue-populate.sh postprocess-all"
# This queues everything in pueue and returns immediatelyWhen to Use
| Scenario | Use This Pattern | Use Plain mise depends |
|---|---|---|
| Long-running jobs (hours/days) on remote hosts | Yes | No |
| Multi-step pipelines with dependencies | Yes | No |
| Jobs that must survive SSH disconnects | Yes | No |
| Group-based parallelism limits needed | Yes | No |
| Fast local tasks (< 5 minutes) | No | Yes |
| CI/CD pipelines | No | Yes (with parallel operator) |
| Single-step operations | No | Yes |
---
Complete Project Template
Full .mise.toml template combining all patterns.
# .mise.toml - Complete project configuration
min_version = "2024.9.5"
[settings]
experimental = true
[tools]
python = "3.11"
node = "22"
uv = "latest"
[env]
_.python.venv = { path = ".venv", create = true }
_.file = [".env", { path = ".env.local", redact = true }]
_.path = ["{{config_root}}/bin", "node_modules/.bin"]
PROJECT_ROOT = "{{config_root}}"
PYTHONUNBUFFERED = "1"
# Hidden helpers
[tasks._check-env]
hide = true
run = '[ -f .env ] || { echo "Copy .env.example to .env"; exit 1; }'
[tasks._check-clean]
hide = true
run = '[ -z "$(git status --porcelain)" ] || { echo "Uncommitted changes"; exit 1; }'
# Development
[tasks.dev]
description = "Start development server"
depends = ["_check-env"]
run = "uvicorn app:main --reload"
# Testing
[tasks.test]
description = "Run tests"
alias = "t"
run = "pytest tests/ -v"
[tasks."test:cov"]
description = "Run tests with coverage"
run = "pytest tests/ --cov=src --cov-report=html"
# Code quality
[tasks.lint]
description = "Run linters"
run = "ruff check . && ruff format --check ."
[tasks.fix]
description = "Fix linting issues"
run = "ruff check --fix . && ruff format ."
# Build
[tasks.build]
description = "Build package"
depends = ["lint", "test"]
run = "uv build"
# Release
[tasks.release]
description = "Create release"
depends = ["_check-clean", "build"]
confirm = "Create new release?"
run = '/usr/bin/env bash -c '\''GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci'\'''Polyglot Monorepo Affected Detection
Guide for choosing the right tool for affected detection in polyglot monorepos (Python + Rust + TypeScript).
Why Pants + mise?
mise excels at runtime version management and environment configuration, but has no native affected detection. You need manual git scripts to detect which packages changed.
Pants provides:
- Native affected detection (
--changed-since=origin/main) - Auto-inferred dependencies (no manual BUILD file maintenance)
- Native Python support (uv, ruff, pytest integration)
- Excellent mise coexistence
Combination: mise handles runtimes, Pants handles builds.
---
Tool Comparison
Affected Detection Capabilities
| Tool | Affected Detection | How It Works |
|---|---|---|
| Nx | Native (graph-aware) | Analyzes project graph + git diff |
| Turborepo | Native (git-based) | --filter=...[origin/main] syntax |
| mise | None (manual) | Requires custom git scripts |
| Pants | Native (git-integrated) | --changed-since=origin/main |
| Bazel | Via bazel-diff | External tool required |
Language Support
| Tool | Python | Rust | TypeScript | Polyglot Friendliness |
|---|---|---|---|---|
| Nx | Plugin-based | New plugin | Native | Improving |
| Turborepo | Needs wrapper | Needs wrapper | Native | Poor |
| mise | Native | Native | Native | Excellent |
| Pants | Native (uv/ruff/pytest) | Community plugin | Native | Excellent |
| Bazel | rules_python | rules_rust (official) | rules_nodejs | Excellent |
Scaling & Complexity
| Tool | Learning Curve | Setup Time | Scalability | Remote Caching |
|---|---|---|---|---|
| Nx | Medium | 2-4 hours | 100+ packages | Native |
| Turborepo | Low | 1-2 hours | 50+ packages | Native |
| mise | Low | 30 min | 20 packages | None |
| Pants | Low | 2-4 hours | 200 packages | REAPI |
| Bazel | High | 1-2 weeks | 1000+ packages | Native |
---
Recommendation by Scale
| Scale | Tool | Rationale |
|---|---|---|
| < 10 packages | mise + custom git script | Minimal overhead |
| 10-50 packages (Python-heavy) | Pants + mise | Native Python, auto-inference, native affected |
| 50+ packages (balanced polyglot) | Bazel | Proven scale, remote execution |
| JS-only monorepo | Turborepo or Nx | Excellent JS tooling |
---
Pants + mise Integration Guide
Architecture
monorepo/
├── mise.toml # Runtime versions + env vars (SSoT)
├── pants.toml # Pants configuration
├── BUILD # Root BUILD file (minimal)
├── packages/
│ ├── core-python/
│ │ ├── mise.toml # Package-specific env (optional)
│ │ └── BUILD # Auto-generated: python_sources()
│ ├── core-rust/
│ │ └── BUILD # cargo-pants plugin
│ └── core-bun/
│ └── BUILD # pants-js pluginpants.toml Configuration
[GLOBAL]
pants_version = "<version>"
backend_packages = [
"pants.backend.python",
"pants.backend.python.lint.ruff",
"pants.backend.experimental.rust",
"pants.backend.experimental.javascript",
]
[python]
interpreter_constraints = [">=3.11"]
[source]
root_patterns = ["packages/*"]
[python-bootstrap]
# Use mise-managed Python (mise sets PATH)
search_path = ["<PATH>"]mise.toml Configuration
# Runtime versions - Pants inherits from PATH
[tools]
python = "<version>"
node = "<version>"
rust = "<version>"
[env]
PANTS_CONCURRENT = "true"
# Convenience wrappers for Pants commands
[tasks."test:affected"]
description = "Test affected packages via Pants"
run = "pants --changed-since=origin/main test"
[tasks."lint:affected"]
description = "Lint affected packages via Pants"
run = "pants --changed-since=origin/main lint"
[tasks.test-all]
description = "Test all packages"
run = "pants test ::"
[tasks."pants:tailor"]
description = "Generate BUILD files"
run = "pants tailor"
[tasks."pants:check"]
description = "Type-check all Python"
run = "pants check ::"BUILD File Patterns
Python package (auto-generated by pants tailor):
# packages/core-python/BUILD
python_sources()
python_tests()
# Pants auto-infers dependencies from imports - no manual deps!Rust package (cargo-pants plugin):
# packages/core-rust/BUILD
cargo_package()TypeScript package (pants-js plugin):
# packages/core-bun/BUILD
javascript_sources()
javascript_tests()---
Native Affected Commands
# Test only affected packages
pants --changed-since=origin/main test
# Lint only affected packages
pants --changed-since=origin/main lint
# Build only affected packages
pants --changed-since=origin/main package
# See what's affected (dry run)
pants --changed-since=origin/main list
# Test all packages
pants test ::
# Generate BUILD files
pants tailor---
Migration Paths
mise-only → Pants + mise
1. Keep mise.toml - continues to manage Python/Rust/Node versions 2. Add pants.toml - minimal config (see above) 3. Generate BUILD files - pants tailor auto-creates them 4. Replace affected.sh - use pants --changed-since=origin/main 5. Update CI - replace mise run test:affected with pants --changed-since test
Pants + mise → Bazel
If Rust becomes dominant (50%+ of codebase) or you scale beyond 200 packages:
1. Evaluate Bazel's rules_rust (official, mature) 2. Use Pants v2.23+ workspace environments to invoke Bazel for Rust 3. Consider full Bazel migration only if team can dedicate build infrastructure resources
---
Fallback: mise-only Affected Detection
For < 10 packages where Pants is overkill:
# mise.toml - manual git-based affected detection
[tasks."_get-changed-packages"]
description = "Get packages with changes since origin/main"
hide = true
run = '''
git diff --name-only origin/main 2>/dev/null | \
grep -E '^packages/[^/]+/' | \
cut -d/ -f2 | \
sort -u
'''
[tasks."test:affected"]
description = "Test only packages with changes"
run = '''
for pkg in $(mise run _get-changed-packages); do
echo "Testing: $pkg"
mise run "test:$pkg" || exit 1
done
'''Limitation: This doesn't understand transitive dependencies. If shared-types changes, packages depending on it won't be detected unless they also changed.
---
Why Not Nx/Turborepo for Polyglot?
Both require package.json wrapper files in non-JS packages:
// packages/core-python/package.json (REQUIRED by Turborepo)
{
"name": "core-python",
"scripts": { "test": "uv run pytest" }
}This adds friction and doesn't leverage language-native tooling. Pants and mise treat all languages as first-class citizens.
---
Related Resources
- Level 11: Pants + mise - Quick reference in main skill
- Bootstrap Monorepo - Autonomous polyglot monorepo bootstrap meta-prompt
- Pants Documentation
- mise Documentation
Skill: mise-tasks | Related: pypi-doppler
Release Workflow Patterns for mise Tasks
Patterns and anti-patterns for orchestrating multi-phase release workflows with mise [tasks]. Based on real-world failures in Rust+Python (maturin) projects.
---
The Core Problem: Unlinked Pipeline Stages
Release workflows have natural phases that must execute in order. When phases are defined as independent mise tasks without depends, nothing prevents running them out of order:
# ❌ BROKEN: publish has no dependency on build
[tasks."release:build-all"]
depends = ["release:version"]
run = "maturin build --release"
[tasks."release:pypi"]
# No depends! Can run before build-all completes
run = "./scripts/publish-to-pypi.sh"Failure mode: Running mise run release:pypi before mise run release:build-all fails with "no wheels found". The publish script has a runtime check, but the task system doesn't enforce ordering — the failure happens late instead of being prevented by the DAG.
---
Pattern 1: Full DAG with depends
Use when: You want a single command (mise run release:full) that does everything.
# Phase 1: Preflight
[tasks."release:preflight"]
description = "Validate prerequisites"
run = """
git update-index --refresh -q || true
[ -z "$(git status --porcelain)" ] || { echo "FAIL: dirty"; exit 1; }
[ "$(git branch --show-current)" = "main" ] || { echo "FAIL: not main"; exit 1; }
"""
# Phase 2: Sync
[tasks."release:sync"]
description = "Synchronize with remote"
depends = ["release:preflight"]
run = """
git pull --rebase origin main
git push origin main
"""
# Phase 3a: Version bump
[tasks."release:version"]
description = "Bump version via semantic-release"
depends = ["release:sync"]
run = "./scripts/semantic-release.sh"
# Phase 3b: Build (after version bump sets new version)
[tasks."release:build-all"]
description = "Build all platform artifacts"
depends = ["release:version"]
run = """
mise run release:macos-arm64
mise run release:linux
mise run release:sdist
# Consolidate artifacts to dist/
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*= "\\(.*\\)"/\\1/')
cp -n target/wheels/*-${VERSION}-*.whl dist/ 2>/dev/null || true
cp -n target/wheels/*-${VERSION}.tar.gz dist/ 2>/dev/null || true
"""
# Phase 4: Smoke test (runs after build)
[tasks.smoke]
description = "Verify built artifacts"
depends = ["smoke:import", "smoke:process"]
# Phase 5: Postflight verification
[tasks."release:postflight"]
description = "Verify release state"
depends = ["smoke", "release:build-all"]
run = """
echo "Found $(find dist/ -name '*.whl' | wc -l | tr -d ' ') wheel(s)"
"""
# Phase 6: Publish (depends on build — CRITICAL)
[tasks."release:pypi"]
description = "Publish to PyPI"
depends = ["release:build-all"]
run = "./scripts/publish-to-pypi.sh"
# Orchestrator: single command for everything
[tasks."release:full"]
description = "Full release: version → build → smoke → publish"
depends = ["release:postflight", "release:pypi"]
run = "echo 'Release complete and published!'"Dependency DAG:
preflight → sync → version → build-all → postflight ─┐
↓ ↓
release:pypi ────→ release:fullKey properties:
mise run release:fullruns everything in correct ordermise run release:pypialone still works — it triggers build-all firstmise run release:build-allalone still works — it triggers version first- Every standalone invocation is safe because
dependsenforces prerequisites
---
Pattern 2: Selective Re-Run with Shared Guards
Use when: You need to re-run individual phases (e.g., rebuild after fixing a compile error) without re-running the entire chain.
# Guard: check that version was bumped (artifact exists)
[tasks._guard-version-bumped]
hide = true
run = """
TAG=$(git describe --tags --exact-match HEAD 2>/dev/null || true)
[ -n "$TAG" ] || { echo "FAIL: HEAD is not tagged. Run release:version first."; exit 1; }
"""
# Guard: check that wheels exist
[tasks._guard-wheels-exist]
hide = true
run = """
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*= "\\(.*\\)"/\\1/')
COUNT=$(find dist/ -name "*-${VERSION}-*.whl" 2>/dev/null | wc -l | tr -d ' ')
[ "$COUNT" -gt 0 ] || { echo "FAIL: No wheels for v${VERSION}. Run release:build-all first."; exit 1; }
"""
# Publish with guard (not depends on build)
[tasks."release:pypi"]
description = "Publish to PyPI (requires pre-built wheels)"
depends = ["_guard-wheels-exist"]
run = "./scripts/publish-to-pypi.sh"When to use this instead of Pattern 1: When cross-platform builds are slow (e.g., remote Docker builds) and you want to re-run publish without rebuilding on every invocation.
---
Anti-Patterns
1. Publish Without Build Dependency
# ❌ No depends — can run in any order
[tasks."release:build-all"]
run = "maturin build"
[tasks."release:pypi"]
run = "./scripts/publish-to-pypi.sh"Fix: Add depends = ["release:build-all"] to release:pypi.
2. Missing sdist in Build Chain
# ❌ Only builds wheels, forgets source distribution
[tasks."release:build-all"]
run = """
mise run release:macos-arm64
mise run release:linux
"""Fix: Add mise run release:sdist and copy all artifacts to dist/.
PyPI requires either a wheel per platform or an sdist for source-only installs. Missing sdist means users on unsupported platforms can't pip install.
3. Artifact Scatter
# ❌ Wheels land in different directories
[tasks."release:macos-arm64"]
run = "maturin build" # → target/wheels/
[tasks."release:linux"]
run = "ssh remote 'maturin build' && scp remote:wheels/*.whl dist/" # → dist/
[tasks."release:pypi"]
run = "uv publish" # Looks in dist/ onlyFix: release:build-all should consolidate all artifacts into dist/ after building. The publish step should only need to look in one place.
4. Orchestrator as Pass-Through
# ❌ release:full just prints a message, doesn't enforce anything
[tasks."release:full"]
depends = ["release:postflight"]
run = "echo 'Done! Now run: mise run release:pypi'"Fix: Include release:pypi in the depends array so release:full is truly complete:
[tasks."release:full"]
depends = ["release:postflight", "release:pypi"]
run = "echo 'Released and published!'"---
Pattern 3: Native Workspace Publishing (Rust 1.90+)
Use when: Publishing multiple crates from a Cargo workspace to crates.io. Requires Rust 1.90+ (stable Sept 2025).
Use `cargo publish --workspace` — a single native command that:
- Auto-discovers all publishable crates (skips
publish = false) - Topologically sorts by dependency order
- Pre-validates the entire workspace builds correctly before publishing any crate
- Handles crates.io index propagation between dependent publishes
[tasks."release:crates"]
description = "Publish to crates.io (native workspace publish)"
run = """
# Native workspace publish (Rust 1.90+) — one command, zero maintenance
cargo publish --workspace
"""Preflight dry-run:
[tasks."release:crates-dry"]
description = "Dry-run crates.io workspace publish"
run = "cargo publish --workspace --dry-run"Why this supersedes all other approaches:
| Approach | Problem |
|---|---|
for crate_dir in crates/*/ | Filesystem alphabetical order ≠ dependency order |
| Hardcoded list in task file | Drifts when new crates are added — caused rangebar-py #113 |
Manual [workspace.metadata] list | Still requires human to update — redundant with cargo metadata |
cargo metadata + Python topo sort | Works but bespoke — superseded by native Cargo in 1.90 |
| `cargo publish --workspace` | Zero maintenance, native, pre-validates, handles ordering |
Setup: Mark internal/non-publishable crates with publish = false in their Cargo.toml. Everything else publishes automatically. The CARGO_REGISTRY_TOKEN env var provides authentication.
Legacy Fallback (Rust < 1.90)
For projects pinned below Rust 1.90, use cargo metadata with Kahn's topological sort as a fallback. See the Rust 1.90 release notes for migration guidance.
---
Checklist: Release Task Audit
When reviewing a release workflow in .mise.toml:
- [ ] Every phase task has
dependson its prerequisites - [ ]
release:pypi(or equivalent publish) depends on build - [ ]
release:build-allincludes sdist, not just wheels - [ ] All build artifacts are consolidated to a single directory (
dist/) - [ ]
release:fullincludes all phases including publish in its dependency chain - [ ] Standalone invocation of any task is safe (prerequisites enforced by DAG)
- [ ] Version bump happens before build (so artifacts have correct version)
- [ ] Cross-compilation tools have their helper binaries declared (e.g.,
cargo-zigbuildformaturin --zig) - [ ] Crates.io publish uses `cargo publish --workspace` (Rust 1.90+), not hardcoded lists
- [ ] Preflight includes `cargo publish --workspace --dry-run`
---
Real-World Example: rangebar-py
The rangebar-py project (Rust+Python via maturin) hit the "publish without build" anti-pattern in production:
1. mise run release:pypi was called before wheels were built 2. The publish script detected "no wheels found" and failed 3. Wheels were built manually, then publish was re-run successfully 4. Both success and failure notifications arrived, causing confusion
Root cause: release:pypi had no depends — it was designed as a "manual step after release:full" but nothing enforced that ordering.
Fix: Added depends = ["release:build-all"] to release:pypi and included release:pypi in release:full's dependency chain.
Lesson: If two tasks must always run in a specific order, use depends. "Manual step after X" is not enforcement — it's documentation that gets ignored under time pressure.
---
5. Filesystem-Order Crate Publishing
# ❌ Alphabetical order has no relation to dependency order
for crate_dir in crates/*/; do
cargo publish -p "$(basename "$crate_dir")"
done
# ❌ Hardcoded list drifts when new crates are added
for crate in rangebar-core rangebar-providers; do
cargo publish -p "$crate"
doneFix: Use cargo publish --workspace (Rust 1.90+). It auto-discovers publishable crates, topologically sorts by dependency order, and pre-validates the entire workspace before publishing. See Pattern 3 above.
Real-world failure (rangebar-py #113): rangebar-hurst was added as a workspace dependency of rangebar-core but not added to the hardcoded publish list. cargo publish for rangebar-core failed with "no matching package named rangebar-hurst found" — the crate existed locally but wasn't on crates.io. Three releases went unpublished before discovery. Native cargo publish --workspace would have caught this at pre-validation.
---
Anti-Pattern 5: Implicit Tool Dependencies
The Problem
Some tools require other tools to be installed but don't fail fast when they're missing. Instead, they produce incorrect results or cryptic errors late in the build.
Real-world example: maturin's --zig flag for cross-compilation.
# ❌ zig is installed, but cargo-zigbuild is missing
[tools]
zig = "<version>"
"cargo:maturin" = "latest"
[tasks."release:linux"]
run = """
maturin build --release \
--target x86_64-unknown-linux-gnu \
--zig \
--compatibility manylinux_2_17
"""What happens: The build appears to succeed, produces a wheel file, but the wheel fails manylinux_2_17 compliance check:
💥 maturin failed
Caused by: Error ensuring manylinux_2_17 compliance
Caused by: Your library is not manylinux_2_17 compliant because of
too-recent versioned symbols: GLIBC_2.18, GLIBC_2.25, GLIBC_2.33Why it fails late: maturin's --zig flag internally uses cargo-zigbuild to properly target glibc 2.17. Without cargo-zigbuild, maturin still uses zig as a linker but doesn't set the glibc version target, producing binaries linked against the host's glibc.
The Fix
Declare all implicit dependencies explicitly in [tools]:
# ✅ All tools that work together are declared together
[tools]
zig = "<version>"
"cargo:maturin" = "latest"
"cargo:cargo-zigbuild" = "latest" # Required for maturin --zigCommon Implicit Dependencies
| Primary Tool | Implicit Dependency | Symptom if Missing |
|---|---|---|
maturin --zig | cargo-zigbuild | manylinux compliance failure |
cargo build (PyO3) | python in path | "Python not found" during link |
semantic-release | bun or npm | "Cannot find module" errors |
uv run --with | Network access | Silent fallback to cached stale versions |
gh pr create | GH_TOKEN environment | 401 or prompt for login |
The Lesson
Tools that accept flags for integration features often have undeclared dependencies on other tools. When a flag like --zig implies "use zig for cross-compilation," read the documentation to discover what else must be installed.
Practical rule: If a tool flag name-checks another tool, check if that tool (or a helper for it) needs to be in `[tools]`.
---
Checklist Addition: Tool Dependencies
Add to the release task audit:
- [ ] Cross-compilation tools have their helper binaries declared (e.g.,
cargo-zigbuildformaturin --zig) - [ ] Build tools that interact with language runtimes have those runtimes in
[tools](e.g., Python for PyO3) - [ ] Tasks using external services have their CLI tools and credentials configured
Task Levels Reference
SSoT-OK
Comprehensive guide to mise task features, from basic definitions through advanced execution and watch mode.
Level 1-2: Basic Tasks
Minimal Task
[tasks.hello]
run = "echo 'Hello, World!'"With Description (AI-Agent Context Priming)
CRITICAL: The description field is the single most important field for AI coding agent discoverability. When an AI agent runs mise tasks ls, the description is the ONLY context it has to decide whether and how to use a task. Write descriptions that answer: what does it do, what does it need, what are its side effects, and when should it be run?
# BAD: Too minimal - AI agent has no context
[tasks.test]
description = "Run test suite"
run = "pytest tests/"
# GOOD: Rich context for AI agent decision-making
[tasks.test]
description = "Run pytest test suite against src/ with coverage reporting. Requires virtualenv activated or uv. Depends on build completing first. Exits non-zero on any test failure. Safe to run repeatedly."
run = "pytest tests/"Description checklist:
- What it does (action + scope)
- What it requires (env vars, tools, prerequisites)
- What it produces or modifies (side effects, outputs)
- When to run it (phase context, safety notes)
# File-based task equivalent (in .mise/tasks/release/preflight):
#MISE description="Phase 1 of 4: Validate all release prerequisites before version bump. Checks: clean working directory, GH_TOKEN presence and format, GH_ACCOUNT target, plugin validation, and releasable conventional commits since last tag. Exits non-zero on any failure."With Alias
[tasks.test]
description = "Run pytest test suite with coverage. Requires virtualenv or uv. Exits non-zero on failure."
alias = "t"
run = "pytest tests/"Now mise run t works.
Working Directory
[tasks.frontend]
dir = "packages/frontend"
run = "npm run build"Task-Specific Environment
[tasks.test]
env = { RUST_BACKTRACE = "1", LOG_LEVEL = "debug" }
run = "cargo test"Note: env values are NOT passed to dependency tasks.
GitHub Token Verification Task
For multi-account GitHub setups, add a verification task:
[tasks._verify-gh-auth]
description = "Verify GitHub token matches expected account"
hide = true # Hidden helper task
run = """
expected="${GH_ACCOUNT:-}"
if [ -z "$expected" ]; then
echo "GH_ACCOUNT not set - skipping verification"
exit 0
fi
actual=$(gh api user --jq '.login' 2>/dev/null || echo "")
if [ "$actual" != "$expected" ]; then
echo "ERROR: GH_TOKEN authenticates as '$actual', expected '$expected'"
exit 1
fi
echo "✓ GitHub auth verified: $actual"
"""
[tasks.release]
description = "Create semantic release"
depends = ["_verify-gh-auth"] # Verify before release
run = "npx semantic-release --no-ci"See `mise-configuration` skill for GH_TOKEN setup.
SSH ControlMaster Warning: If using multi-account SSH, ensureControlMaster nois set for GitHub hosts in~/.ssh/config. Cached connections can authenticate with the wrong account.
Multi-Command Tasks
[tasks.setup]
run = [
"npm install",
"npm run build",
"npm run migrate"
]---
Level 3-4: Dependencies & Orchestration
Pre-Execution Dependencies
[tasks.deploy]
depends = ["test", "build"]
run = "kubectl apply -f deployment.yaml"Tasks test and build run BEFORE deploy.
Post-Execution Tasks
[tasks.release]
depends = ["test"]
depends_post = ["notify", "cleanup"]
run = "npm publish"After release succeeds, notify and cleanup run automatically.
Soft Dependencies
[tasks.migrate]
wait_for = ["database"]
run = "./migrate.sh"If database task is already running, wait for it. Otherwise, proceed.
Task Chaining Pattern
[tasks.ci]
description = "Full CI pipeline"
depends = ["lint", "test", "build"]
depends_post = ["coverage-report"]
run = "echo 'CI passed'"Single command: mise run ci executes entire chain.
Parallel Dependencies
Dependencies without inter-dependencies run in parallel:
[tasks.validate]
depends = ["lint", "typecheck", "test"] # These can run in parallel
run = "echo 'All validations passed'"---
Level 5: Hidden Tasks & Organization
Hidden Tasks
[tasks._check-credentials]
description = "Verify credentials are set"
hide = true
run = '''
if [ -z "$API_KEY" ]; then
echo "ERROR: API_KEY not set"
exit 1
fi
'''
[tasks.deploy]
depends = ["_check-credentials"]
run = "deploy.sh"Hidden tasks don't appear in mise tasks output but can be dependencies.
View hidden tasks: mise tasks --hidden
Colon-Prefixed Namespacing
[tasks.test]
run = "pytest"
[tasks."test:unit"]
run = "pytest tests/unit/"
[tasks."test:integration"]
run = "pytest tests/integration/"
[tasks."test:e2e"]
run = "playwright test"Run all test tasks: mise run 'test:*'
Wildcard Patterns
mise run 'test:*' # All tasks starting with test:
mise run 'db:**' # Nested: db:migrate:up, db:seed:test---
Level 6: Task Arguments
Usage Specification (Preferred Method)
[tasks.deploy]
description = "Deploy to environment"
usage = '''
arg "<environment>" help="Target environment" {
choices "dev" "staging" "prod"
}
flag "-f --force" help="Skip confirmation"
flag "--region <region>" default="us-east-1" env="AWS_REGION"
'''
run = '''
echo "Deploying to ${usage_environment}"
[ "$usage_force" = "true" ] && echo "Force mode enabled"
echo "Region: ${usage_region}"
'''Argument Types
Required positional:
usage = 'arg "<file>" help="Input file"'Optional positional:
usage = 'arg "[file]" default="config.toml"'Variadic (multiple values):
usage = 'arg "<files>" var=#true'Flag Types
Boolean flag:
usage = 'flag "-v --verbose"'
# Access: ${usage_verbose:-false}Flag with value:
usage = 'flag "-o --output <file>" default="out.txt"'
# Access: ${usage_output}Environment-backed flag:
usage = 'flag "--port <port>" env="PORT" default="8080"'Accessing Arguments
In run scripts, arguments become usage_<name> environment variables:
/usr/bin/env bash << 'SKILL_SCRIPT_EOF'
${usage_environment} # Required arg value
${usage_verbose:-false} # Boolean flag with default
${usage_output} # Flag with value
SKILL_SCRIPT_EOFDEPRECATION WARNING: The Tera template method ({{arg(name="...")}}) will be removed in mise 2026.11.0. Use usage spec instead.
For complete argument syntax, see: arguments.md
---
Level 7: File Tracking & Caching
Source Files
[tasks.build]
sources = ["Cargo.toml", "src/**/*.rs"]
run = "cargo build"Task re-runs only when source files change.
Output Files
[tasks.build]
sources = ["Cargo.toml", "src/**/*.rs"]
outputs = ["target/release/myapp"]
run = "cargo build --release"If outputs are newer than sources, task is skipped.
Force Execution
mise run build --force # Bypass cachingAuto Output Detection
[tasks.compile]
outputs = { auto = true } # Default behavior
run = "gcc -o app main.c"---
Level 8: Advanced Execution
Confirmation Prompts
[tasks.drop-database]
confirm = "This will DELETE all data. Continue?"
run = "dropdb myapp"Output Control
[tasks.quiet-task]
quiet = true # Suppress mise's output (not task output)
run = "echo 'This still prints'"
[tasks.silent-task]
silent = true # Suppress ALL output
run = "background-job.sh"
[tasks.silent-stderr]
silent = "stderr" # Only suppress stderr
run = "noisy-command"Raw Mode (Interactive)
[tasks.edit-config]
raw = true # Direct stdin/stdout/stderr
run = "vim config.yaml"Warning: raw = true disables parallel execution.
Task-Specific Tools
[tasks.legacy-test]
tools = { python = "3.9", node = "18" }
run = "pytest && npm test"Use specific tool versions for this task only.
Custom Shell
[tasks.powershell-task]
shell = "pwsh -c"
run = "Get-Process | Select-Object -First 5"---
Level 9: Watch Mode
Prefer Runtime-Native Watch
When the runtime has a built-in file watcher, use it instead of mise watch / watchexec -- zero extra memory, zero extra processes.
| Runtime | Command | Notes |
|---|---|---|
| Bun | bun --watch run src/main.ts | 0 MB overhead. Do NOT use bun --hot (stale state). |
| Node.js | node --watch src/main.js | 0 MB overhead. |
| Python | uvicorn app:main --reload | 0 MB overhead. |
External Watch (mise watch)
Use mise watch for runtimes without built-in watchers (Go, Rust, shell) or multi-language orchestration.
mise watch build # Re-run on source changesRequires watchexec: mise use -g watchexec@latest
Watch Options
mise watch build --debounce 500ms # Wait before re-run
mise watch build --restart # Kill and restart on change
mise watch build --clear # Clear screen before runOn-Busy Behavior
mise watch build --on-busy-update=queue # Queue changes
mise watch build --on-busy-update=restart # Restart immediately
mise watch build --on-busy-update=do-nothing # Ignore (default)Related skills
FAQ
What does mise-tasks help configure?
mise-tasks focuses on mise.toml task definitions and mise run workflows so polyglot repositories can execute dev, test, and lint commands through the mise task runner.
Does mise-tasks require mise installed?
mise-tasks assumes the jdx mise CLI is available locally because tasks are declared in mise.toml and executed via mise run commands.