OpenCode must remain a first-class Ballast target with parity across CLI target selection, saved configuration, generated artifacts, cleanup flows, documentation, and CI validation. Regressions in target-specific paths can leave stale .opencode/ rules or make OpenCode behave differently from Cursor, Claude Code, Codex, and Gemini.
- Ballast must accept
opencodeanywhere targets are listed or validated, including install commands, saved.rulesrc.jsonconfiguration, add/remove target flows, help text, and error messaging. ballast install --target opencodemust generate OpenCode rules and skills in canonical.opencode/locations using the same sourceagents/andskills/content as other supported targets.- OpenCode cleanup and refresh flows must remove only Ballast-managed
.opencode/rules and skills for removed selections while preserving unrelated targets. - Automated tests and smoke checks must cover OpenCode install, config persistence, idempotent reinstall behavior, remove-target cleanup, and cross-language target parity.
- Documentation and examples must list OpenCode consistently as a supported target.
opencodeis included in wrapper, TypeScript, Python, and Go target allowlists and target-related help text.- OpenCode installs write rules under
.opencode/and skills under.opencode/skills/. - Removing
opencodefrom an existing install deletes managed.opencode/language rules and skills while leaving other target artifacts intact. - CI smoke coverage includes OpenCode in examples, config persistence, monorepo target parity, and cross-language validation paths.
- README and reference docs list OpenCode as a supported first-class target and document its output paths.
Agents repeatedly need to create pull requests, request GitHub Copilot review, triage Copilot comments, decide whether comments require human input, fix actionable feedback, push updates, and repeat the review loop. Without a dedicated skill, agents often request Copilot incorrectly or silently ignore comments, leaving PRs in an unclear review state.
- Ballast must distribute a
github-pr-copilot-cyclecommon skill for PR creation and bounded Copilot review loops. - The skill must use the documented GitHub CLI reviewer syntax for Copilot:
gh pr create --reviewer "@copilot"andgh pr edit <pr-number> --add-reviewer "@copilot". - The skill must explicitly prohibit common incorrect Copilot request paths such as assigning Copilot, using Copilot bot logins as reviewers, or using requested-reviewer API calls as substitutes for the documented CLI flow.
- The skill must gather unresolved Copilot review threads and score each comment for no action, direct fix, fix with validation, or human input required.
- The skill must require a reply on every Copilot comment, including comments that are ignored or require human input.
- The skill must cap Copilot review iterations at three cycles unless all unresolved Copilot comments are handled earlier.
- The skill must be installable through Codex and Claude target outputs and through all language package skill registries.
github-pr-copilot-cycleis listed in the saved skill config and all package skill registries.- Generated Codex and Claude skill outputs exist for
github-pr-copilot-cycle. - The skill text includes the documented
@copilotreviewer commands and forbids assignee/bot/API substitutes. - The skill text requires replying to every Copilot comment before resolving or stopping.
- Tests cover skill registration and skill content loading.
- Smoke checks that validate
--all-skillsincludegithub-pr-copilot-cycle.
Ballast supports TypeScript, Python, Go, Ansible, and Terraform language profiles, but Flutter mobile applications use Dart and are not detected or given Dart/Flutter-specific linting, logging, testing, and hook guidance. Flutter app repositories such as sujiko expose stable markers through pubspec.yaml, .metadata, analysis_options.yaml, lib/, test/, and integration_test/, and agents need generated rules that reflect current Flutter mobile practices instead of generic TypeScript or Go guidance.
- Ballast must support
dartas a configured language profile for Flutter mobile app repositories. - Project-root detection must recognize Flutter/Dart projects using stable Flutter markers such as
pubspec.yaml,.metadata, andanalysis_options.yaml. - Monorepo profile detection must identify Flutter/Dart app directories and persist
languagesandpathsfordart. - Wrapper installs for Dart profiles must dispatch to an existing backend capable of rendering Dart rule content.
- Generated Dart linting guidance must prefer
analysis_options.yaml,flutter_lints,flutter analyze, anddart format --set-exit-if-changed. - Generated Dart logging guidance must cover Flutter-appropriate logging through
dart:developer,package:logging, and production-safe forwarding to crash or observability tooling without leaking secrets. - Generated Dart testing guidance must cover unit, widget, and integration tests with
flutter_test,test, andintegration_test, including mobile emulator/device placement for slower checks. - Generated Dart git-hook guidance must use
pre-commitand run fast format/analyze checks before commit, with Flutter tests on pre-push or CI.
- Given a Flutter app containing
pubspec.yaml,analysis_options.yaml, and.metadata, Ballast resolves the project root to that app directory. - Given a monorepo with a Flutter app under a subdirectory, wrapper profile detection returns a
dartprofile with the app path. - Given
ballast --language dart install --target codex --agent linting --yes, generated Dart linting rules are written under Dart-prefixed rule names. - Generated Dart rules mention
flutter_lints,flutter analyze,dart format --set-exit-if-changed,dart:developer,package:logging,flutter_test, andintegration_test. - Wrapper install-cli/backend routing supports Dart without requiring a separate Dart backend package.
Ballast common rules can be installed in Go, Python, Terraform, Ansible, or mixed-language repositories, but some generated common guidance still frames CI/CD, local development, and observability as TypeScript/JavaScript-only. Publishing rules can also be generated as a broad pack even when .rulesrc.json sets deploymentModel: "none", which makes deploy-on-main web/API templates look mandatory. The tasks install flow asks operators for a task system including none, but the config model and generated task-system rule must make that no-tracker mode explicit.
- Common CI/CD, local-dev, and observability rule sources must use language-neutral framing.
- Language-specific examples in common rules must be labeled as examples or ecosystem-specific guidance.
- Generated web/API publishing rules must distinguish active deployment models from
deploymentModel: none. - With
deploymentModel: none, generated web/API publishing content must not require deploy-on-main workflows or deployment-state updates. - Ballast must support
taskSystem: noneas a valid configured value for repositories that do not use a durable issue tracker. - With
taskSystem: none, generated task-system rules must clearly state that no external task system is configured and that issue/ticket creation guidance is conditional.
- Generated common CI/CD, local-dev, and observability rules no longer describe themselves as TypeScript/JavaScript-only.
- Generated publishing web/API rules with
deploymentModel: nonecontain explicit inactive-deployment guidance. - Generated publishing web/API rules with
deploymentModel: nonedo not say every merge tomaindeploys as a mandatory rule. TASK_SYSTEMSacceptsnone, config load/save preserves it, and invalid values remain ignored or rejected.- Generated task-system content with
taskSystem: nonecontains no{{taskSystem}}placeholder and does not require MCP setup or issue creation. - Tests cover the language-neutral common wording, inactive deployment guidance, and
taskSystem: nonerendering.
Ballast's primary pull-request validation is split across separate lint, test, and language-pack workflows. Contributors need one canonical CI entry point that shows the full merge validation contract while still running independent language lanes in parallel.
- The repository must use
.github/workflows/ci.ymlas the primary CI workflow for pull requests and pushes tomain. - The CI workflow must include a workflow-level
concurrencyblock that cancels superseded runs for the same workflow and ref. - The CI workflow must preserve TypeScript lint, format, test, and coverage validation, including Node 22 and Node 24 test runs for supported runtime compatibility.
- The CI workflow must preserve Python lint, format, test, coverage, and package import validation.
- The CI workflow must preserve Go package and wrapper CLI lint, vet, tidy, test, coverage, and build validation.
- Independent language and package lanes must run in parallel wherever practical.
- Superseded primary CI workflow files must be removed or reduced to non-overlapping helpers so
ci.ymlis the clear contributor-facing CI contract. - README badges must point at the canonical CI workflow instead of retired lint or test workflows.
.github/workflows/ci.ymlexists and triggers onpull_requestand pushes tomain.ci.ymlincludesconcurrency.group: ${{ github.workflow }}-${{ github.ref }}andcancel-in-progress: true.ci.ymlcontains separate parallel jobs for TypeScript, Python, Go package, and wrapper CLI validation.- TypeScript tests run against Node 22 and Node 24, and TypeScript coverage is uploaded to Codecov.
- Python and Go coverage outputs are uploaded to Codecov.
- Python package import checks, Go language-pack builds, and wrapper CLI builds remain represented in CI.
.github/workflows/lint.yaml,.github/workflows/test.yml, and.github/workflows/language-packs.ymlare removed because their coverage is represented inci.yml.- The README CI badge references
.github/workflows/ci.yml.
Ballast's Terraform linting and testing rules still describe an older validation baseline centered on tfsec and Terratest as optional add-ons. Terraform 1.6+ ships native tests, TFLint uses explicit provider/plugin configuration, tfsec has moved under Trivy, and many teams now need OpenTofu-compatible command guidance. Agents need generated Terraform rules that reflect those current practices without losing the conservative validation path for infrastructure changes.
- Terraform linting guidance must keep
terraform fmt -check -recursive,terraform init -backend=false,terraform validate, and recursivetflintas the baseline local and CI validation path. - Terraform version guidance must keep
.terraform-version/tfenvas the default Ballast path while allowing teams already standardized onasdformiseto use those managers consistently. - TFLint guidance must require
.tflint.hclplugin blocks for the active providers andtflint --initbefore recursive linting. - Security scanning guidance must prefer
trivy configfor new work and describetfsecas legacy-compatible because tfsec is now part of Trivy. - Terraform testing guidance must document native
terraform testfor Terraform 1.6+ module assertions and Terratest for Go-backed or live integration tests. - CI guidance for Terraform validation must include GitHub Actions
concurrency, PR-time validation, plan/apply separation, and optional orchestration through Atlantis, Terraform Cloud, HCP Terraform, or OpenTofu-compatible platforms. - OpenTofu guidance must acknowledge
tofuas a compatible alternative when the repository standardizes on it, includingtofu fmt,tofu init -backend=false,tofu validate, andtofu testequivalents. - Terraform repo-layout guidance must prefer readable root files and independently testable modules without forcing a one-size-fits-all file split.
- Generated Terraform linting rules mention
trivy configas the preferred new security scanner andtfsecas legacy-compatible. - Generated Terraform linting rules require
.tflint.hclplugin blocks andtflint --initbefore recursive linting. - Generated Terraform testing rules document native
terraform testfor Terraform 1.6+ and Terratest for Go-backed/live integration coverage. - Generated Terraform testing rules include a GitHub Actions
concurrencyblock and state that PR validation is separate from merge-gated apply workflows. - Generated Terraform rules acknowledge OpenTofu command equivalents where relevant.
- Backend git-hook guidance no longer presents
tfsecas the only Terraform security scanner.
Ballast stores project-local backend CLIs under .ballast/, but the product contract does not clearly say whether that directory is required source state, generated state, or disposable cache state. Operators and AI agents need deterministic behavior when .ballast/ is absent or incomplete so they can inspect, initialize, and repair a Ballast-managed repository without guessing.
.ballast/must be treated as generated, repository-local tool state that is safe to recreate and should remain ignored by git.ballast installmust not require.ballast/to exist before installing agent rules or skills.- Wrapper backend dispatch must recreate missing local tool directories when it needs to install a backend CLI.
ballast install-climust recreate.ballast/binand.ballast/toolsbefore installing backend CLIs.ballast doctormust report whether.ballast/,.ballast/bin, and.ballast/toolsexist.ballast doctormust provide actionable remediation when.ballast/state is missing or incomplete.ballast doctor --fixmust recreate missing.ballast/directories through the same local CLI install path.- When
ballast install-cliruns without an explicit--version, it must install backend CLIs matching the saved.rulesrc.jsonballastVersionwhen present, instead of defaulting to the running wrapper version. - When the saved
.rulesrc.jsonballastVersionis newer than the running wrapper version,ballast install-climust exit without installing backend CLIs and tell the operator to update Ballast. - Agent guidance must explain how to check and repair Ballast local state.
- Ballast must ship a dedicated common skill for AI agents that covers Ballast-managed repository status, bootstrap, and repair workflows.
- Documentation must describe the dedicated Ballast skill and how to install it.
- Given a repository without
.ballast/,ballast doctorreports.ballast: missingand recommendsballast install-cliorballast doctor --fix. - Given a repository with
.ballast/but missing.ballast/binor.ballast/tools,ballast doctorreports the incomplete state and recommends repair. - Given a repository without
.ballast/,ballast install-cli --language go --version <version>creates.ballast/binand.ballast/toolsbefore running the install command. - Given a repository without
.ballast/,ballast doctor --fixcreates.ballast/binand.ballast/toolsbefore running backend install commands. - Given
.rulesrc.jsoncontainsballastVersion: 5.12.0and the running wrapper is newer,ballast install-cliinstalls backend CLIs at5.12.0. - Given
.rulesrc.jsoncontains aballastVersionnewer than the running wrapper,ballast install-cliexits non-zero, runs no backend installer, and tells the operator to update Ballast. - Generated local-dev guidance tells agents to use
ballast doctorto inspect Ballast local state andballast doctor --fixorballast install-clito repair it. - The new Ballast skill is available through
--skill ballast-project-maintenanceand--all-skills. - README and installation docs document
.ballast/as generated local state and list the Ballast project maintenance skill.
AI agents need a deterministic first command that prepares a repository for local development before they inspect, edit, or test code. Without a canonical bootstrap path, agents may miss prerequisite setup such as enabling Corepack for a declared Node package manager, which can leave commands like pnpm unavailable even when the repository clearly declares them.
- The Ballast wrapper must expose a canonical
setup-devcommand for agent startup. setup-devmust resolve the project root using the same root-detection behavior as other wrapper commands.- For Node repositories with a declared
packageManager,setup-devmust enable Corepack before running package-manager installs when the declared manager is managed through Corepack. setup-devmust install or verify dependencies using the detected repository package manager.- Missing prerequisite and command failure output must name the failed command and provide actionable remediation.
- Repositories without recognized dependency manifests must be skipped with clear output instead of failing.
- Agent local-development guidance must tell agents to run
ballast setup-devas their first startup step when Ballast is available. - Ballast must persist opinionated default developer tools per configured language in
.rulesrc.jsonwhile allowing repositories to override each language's tool list. ballast doctorand generated local-development guidance must make the configured language tools visible so agents and contributors prefer the repository policy over ad hoc tool choices.
- Given a repository with
package.jsondeclaringpackageManager: "pnpm@...",ballast setup-devrunscorepack enablebeforepnpm install. - Given a repository with npm lockfile/package-manager signals,
ballast setup-devrunsnpm installwithout requiring Corepack. - Given a repository without recognized dependency manifests,
ballast setup-devexits successfully and prints that no setup steps were detected. - Given a setup command failure,
ballast setup-devexits non-zero and prints the command that failed plus manual remediation guidance. - Wrapper tests cover Corepack/package-manager behavior and the no-op path.
- Generated local-dev rule output references
ballast setup-devas the first agent startup step. - Given a saved
.rulesrc.jsonwithouttools, Ballast defaults Python touv, pyenvand TypeScript topnpm, corepack, with documented defaults for Go, Terraform, Ansible, and Dart. - Given
.rulesrc.jsoncontainstools.python, Ballast preserves that override instead of replacing it with defaults. - Given
ballast doctorruns with configured tools, the report prints atoolsline grouped by language.
Ballast-generated rule files for persistent agent context have accumulated large generic playbooks and repeated examples. This creates avoidable context bloat for installed targets such as Codex, especially in the local-dev, linting, logging, and testing rule families. The source templates need explicit size and density constraints so future generated rules stay concise without editing checked-in installed rule snapshots by hand.
- Canonical rule templates under repo-root
agents/must prefer concise persistent guidance over long example-heavy walkthroughs. - Deep reference material should live in documentation or skills, while persistent rules keep only the minimum instructions needed to route the agent correctly.
- Source template updates must flow through the existing package-content sync path so TypeScript, Python, and Go package payloads can be refreshed from the same canonical sources.
- The change must not require manual edits to installed repository rule snapshots such as
.codex/rules/*. - Automated tests must enforce size budgets for the worst persistent Codex rule offenders.
- Package-content sync must delete stale mirrored files when source files are removed or renamed.
- Root
.rulesrc.jsontarget policy must include every checked-in Ballast-managed target surface that the repo expects to keep refreshed. - Repository guidance must require PRs that change Ballast generator inputs or target policy to include regenerated local Ballast-managed
.claudeand.codexoutputs.
- The generated Codex
local-dev-envrule built from source templates is smaller than 6 KB while still mentioning.nvmrc,docker-compose.local.yaml,Makefile, andmake up-local. - The generated Codex TypeScript
loggingrule built from source templates is smaller than 6 KB while still mentioningpino-browserand/api/logs. - The generated Codex TypeScript
testingrule built from source templates is smaller than 6 KB. - The generated Codex TypeScript
lintingrule built from source templates is smaller than 5 KB. - The content sync workflow can refresh package template mirrors from the repo-root
agents/andskills/sources without editing installed target rule directories directly. - Content sync deletes stale mirrored files when a source file is removed or renamed.
- Root
.rulesrc.jsonincludesclaudeandcodexso tracked generated artifacts for both targets can be refreshed by config-driven installs. AGENTS.mddocuments that PRs touching Ballast generator inputs or target policy must include regenerated local.claudeand.codexartifacts.
Ballast has task-system and branch TODO guidance, but it does not distribute the Plan -> ADR lifecycle for non-trivial features. Agents in Ballast-managed repositories need installable guidance for when to create a plan, how to maintain it during implementation, and how to graduate completed plans into durable ADRs without duplicating tasks/todo.md behavior.
- Ballast must distribute
plan-lifecycleas a language-agnostic common agent rule alongside the existing task rules. - The plan-lifecycle rule must define when to create a plan, when to skip one, the expected
plans/,tasks/, andadr/directory structure, and the required plan file naming convention. - The rule must include a plan template with status, branch, created date, related ADRs, problem, approach, files affected, phases, verification, rejected alternatives, open questions, and change log.
- The rule must describe maintaining the plan during implementation, including checking off phases, updating the approach when it changes, committing plan updates with related code, reading the plan at session start, and routing out-of-scope work to the TODO rule.
- The rule must define the graduation trigger and steps for turning a plan into an ADR, including checking
tasks/todo.md, creating generic task-system work items for incomplete TODOs, assigning the next ADR number, creating the ADR, updating indexes, removing the plan, and committing the graduation. - The rule must include an ADR template and ADR management rules for status values, sequential numbering, one decision per ADR, and never deleting ADR history.
- The rule must defer to the existing
tasks-todorule fortasks/todo.mdbehavior instead of restating that rule in detail. - The generated rule and installed support-file entries must use generic task-system terminology, not Jira-specific wording.
- Installing the
plan-lifecycleagent produces plan-lifecycle rule files for supported targets. - Generated
.claude/rules/common/plan-lifecycle.mdand.codex/rules/common/plan-lifecycle.mdexist in this repository when the checked-in Ballast-managed outputs are refreshed. CLAUDE.mdandAGENTS.mdinstalled rules lists include the newplan-lifecyclerule under common task rules.- Generated plan-lifecycle content includes the create/skip criteria, directory structure, plan template, maintenance rules, graduation steps, ADR template, ADR management rules, and quick reference.
- Generated plan-lifecycle content explicitly defers
tasks/todo.mdbehavior to the branch-local TODO tracking rule. - Automated tests cover rule suffix discovery, generated content, and support-file listing for the new rule.
TypeScript-only repositories use Husky instead of pre-commit, but the generated Husky guidance does not explicitly require YAML formatting checks for both .yaml and .yml files or a tracked push-time test hook. YAML-heavy configuration such as GitHub Actions, Dependabot, Docker Compose, and Helm can drift outside TypeScript checks, and developers can push changes before the repo's canonical tests run.
- TypeScript-only
git-hooksguidance must use Husky andlint-stagedor the repository's equivalent fast formatter/linter path for commit-time checks. - TypeScript-only Husky pre-commit guidance must explicitly include both
.yamland.ymlformatting checks. - TypeScript-only Husky guidance must prefer the repository's existing formatter or linter command when one is already established.
- TypeScript-only Husky guidance must configure
.husky/pre-pushto run the detected or canonical package-manager test command. - TypeScript-only Husky guidance must run the repository's required build or typecheck command before tests when that is the repo convention.
- Pre-commit must remain fast; heavier build, typecheck, and unit test work belongs in
pre-push. - Multi-language and non-TypeScript git-hook guidance must continue using
pre-commitwithout inheriting Husky-specific instructions. - Documentation must explain the TypeScript-only split between Husky pre-commit formatting and Husky pre-push tests.
- Generated TypeScript-only Husky
git-hookscontent mentions.yamland.ymlexplicitly. - Generated TypeScript-only Husky
git-hookscontent mentionslint-stagedor the repo formatter/linter as the fast pre-commit path. - Generated TypeScript-only Husky
git-hookscontent mentions.husky/pre-push, the package-manager test command, and build/typecheck before tests when the repo convention requires it. - Multi-language TypeScript output continues to mention
.pre-commit-config.yamlandpre-commit install --hook-type pre-push, and does not mention Husky orlint-staged. - Unit and E2E coverage assert the Husky YAML/YML and pre-push guidance.
Ballast does not apply the same overwrite decision matrix to installed skill files that it applies to agent rule files, and --force can silently replace support files such as AGENTS.md, CLAUDE.md, and GEMINI.md. Operators need a safe way to merge upstream skill updates without discarding local edits, and destructive support-file overwrites must require explicit confirmation.
- Existing installed skill files must follow the same force/patch/skip decision matrix as agent rule files across the TypeScript, Python, Go, and wrapper install paths.
--patchmust merge canonical skill content into an existing skill file using the existing patch logic for the selected target.--forcemust overwrite an existing skill file without patching.--forcemust prompt before replacing an existing support file (AGENTS.md,CLAUDE.md, orGEMINI.md) that would lose user customizations.- In non-interactive mode (
--yesor CI environment variables), an attempted--forceoverwrite of an existing support file must fail with a clear error telling the operator to rerun interactively without--yes. - Creating a missing support file with
--forcemust continue without prompting. - Existing support files must be patched by default when
--forceis not set, updating only Ballast-managed installed-rule and installed-skill sections while preserving user-managed sections. - README and installation documentation must describe the updated
--patchand--forcebehavior. - If an existing Claude
.skillarchive is unreadable during--patch, install must recover by overwriting it with canonical packaged skill content instead of failing the run.
- Given an existing skill file and
force=false, patch=false, install skips the file and leaves the existing content unchanged. - Given a missing skill file and
force=false, patch=true, install creates the file with canonical content. - Given an existing skill file and
force=false, patch=true, install merges canonical content into the existing file and preserves user-managed sections supported by the patcher. - Given an existing skill file and
force=true, install overwrites the file with canonical content. - Given an existing support file and interactive
--force, answering no skips the support file and prints a clear notice. - Given an existing support file and interactive
--force, answering yes overwrites the support file with canonical content. - Given an existing support file and non-interactive
--force, install exits with an error and does not overwrite the file. - Automated tests cover the skill-file decision matrix and support-file confirmation behavior in the TypeScript, Python, and Go backends.
- README and
docs/installation.mddescribe when support files are patched by default and when to use--patchversus--force, including the support-file confirmation behavior. - Given an existing unreadable Claude
.skillarchive andforce=false, patch=true, install replaces it with canonical content and completes without an install error.
Some agents require repo-level option values before rule content is generated. Wrapper-driven installs resolved publishing deployment model differently from the tasks task system, so first-run installs with --all could prompt for publishing while silently defaulting tasks when no .rulesrc.json existed.
- Wrapper-driven installs must resolve required options through one shared code path.
- When
tasksis selected and.rulesrc.jsonhas notaskSystem, interactive installs must prompt for the task system and non-interactive installs must use the default. - When
publishingis selected and.rulesrc.jsonhas nodeploymentModel, interactive installs must prompt for the deployment model and non-interactive installs must use the default. - Explicit CLI flags must override saved config and prompted/default values.
- Resolved values must be saved to
.rulesrc.jsonand forwarded to backend invocations.
- Given a first-run multi-language install with
--all, Ballast prompts for both task system and deployment model. - Given saved values in
.rulesrc.json, Ballast does not prompt and reuses the saved values. - Given
--yesor CI, Ballast uses defaults for missing selected-agent options. - Tests cover first-run prompt resolution and backend argument forwarding.
Ballast-installed skill files are generated managed artifacts, but the current refresh behavior treats existing skill files like user-owned agent rules. As a result, ballast upgrade replays saved .rulesrc.json skill selections without updating stale skill file content unless the operator also passes --force.
- Config-refresh flows (
install --refresh-config,upgrade, anddoctor --fix) must rewrite selected managed skill files when they already exist. - Ordinary backend install behavior outside those refresh flows must keep the existing skill decision matrix: skip on existing files unless
--patchor--forceis selected. ballast upgradeanddoctor --fixmust refresh saved skill selections through their existinginstall --refresh-configpath without requiring--force.- Existing agent rule overwrite, patch, and force semantics must remain unchanged.
- The behavior must stay consistent across the TypeScript, Python, Go, and wrapper CLIs.
- Support files such as
AGENTS.mdandCLAUDE.mdmust continue reflecting the saved skill list after refresh.
- Given an existing installed skill file with stale content, running backend install with the same skill and
force=false, patch=falseleaves the file unchanged outside config-refresh flows. - Given an existing installed skill file with stale content and refresh mode enabled through the wrapper config-refresh path, backend install rewrites the file to current packaged skill content without
--force. - Given an existing installed agent rule and
force=false, backend install still skips the rule unless patch mode or force mode is selected. - Given a repository with
.rulesrc.jsonthat declares a skill, running wrapperupgradewithout--forceinvokes the refresh path that updates the existing managed skill file. - Automated unit coverage demonstrates the backend skill refresh behavior for TypeScript, Python, and Go.
- Smoke coverage demonstrates the wrapper upgrade path refreshes stale managed skill content.
Ballast refreshes saved installs from .rulesrc.json, but removing managed agents or skills from saved config can leave stale managed files on disk for targets that remain installed. Operators need refresh behavior that reconciles the managed surface to the current saved config instead of only adding newly selected content.
- Wrapper
install --refresh-config,upgrade, anddoctor --fixmust remove stale managed agent-rule files for agents no longer present in saved.rulesrc.jsonwhile preserving remaining configured agents. - Wrapper
install --refresh-config,upgrade, anddoctor --fixmust remove stale managed skill files for skills no longer present in saved.rulesrc.jsonwhile preserving remaining configured skills. - Reconciliation must apply only to Ballast-managed files for targets that remain installed; it must not remove unrelated user files.
- Support-file managed sections such as
AGENTS.mdandCLAUDE.mdmust be refreshed so removed agents and skills are no longer referenced. - Target removal behavior must remain unchanged and continue deleting the full Ballast-managed surface for removed targets.
- Given an existing install with configured agents
linting, docs, editing.rulesrc.jsonto keep onlylintingand running wrapperinstall --refresh-configdeletes the manageddocsrule files while leavinglintingfiles intact. - Given an existing install with configured skills
owasp-security-scan, github-health-check, editing.rulesrc.jsonto keep onlyowasp-security-scanand running wrapperinstall --refresh-configdeletes the managedgithub-health-checkfiles while leavingowasp-security-scanintact. - After either reconciliation flow, support files no longer list removed agent or skill references.
- Refresh does not delete unmanaged user-authored files outside the Ballast-managed paths for the retained targets.
Ballast currently installs Codex skills as managed rule files under .codex/rules/<skill>.md and lists them in AGENTS.md. Modern Codex exposes installed skills through the native skills registry, which expects each skill to be a directory containing SKILL.md under .codex/skills/<skill>/. As a result, Ballast-configured Codex skills appear in ballast doctor but do not show up in Codex /skills.
- Codex skill installs must write native skill directories at
.codex/skills/<skill>/SKILL.md. - Codex native skill content must preserve the source
SKILL.mdfrontmatter and copy referenced resource files, such asreferences/andscripts/, into the skill directory. AGENTS.mdmust list Codex skills using.codex/skills/<skill>/SKILL.mdpaths.install --refresh-config,upgrade, anddoctor --fixmust remove legacy Ballast-managed.codex/rules/<skill>.mdfiles for configured or stale skills when migrating to native Codex skills.- Stale skill reconciliation must remove both legacy Codex rule-format skill files and native Codex skill directories for skills no longer present in saved config.
- Target removal must remove native Codex skill directories along with the existing managed Codex target surface.
- Migration cleanup must remove only Ballast-managed files or directories and must preserve unrelated user-authored
.codex/rules/and.codex/skills/content. - The behavior must stay consistent across the TypeScript, Python, Go, and wrapper CLIs.
- Given a Codex skill install for
owasp-security-scan, Ballast creates.codex/skills/owasp-security-scan/SKILL.mdand does not create.codex/rules/owasp-security-scan.md. - Given a repository with a legacy managed
.codex/rules/owasp-security-scan.md, runningballast upgraderemoves the legacy rule-format skill file and writes the native skill directory. - Given a repository where
github-health-checkwas removed from.rulesrc.json, refresh reconciliation deletes both.codex/rules/github-health-check.mdand.codex/skills/github-health-check/when they are Ballast-managed. - Given unmanaged user content under
.codex/skills/custom-skill/, refresh and target removal preserve it unless the whole Codex target is explicitly removed and the file is Ballast-managed. - Generated
AGENTS.mdreferences.codex/skills/<skill>/SKILL.mdfor Codex skills. - Automated unit coverage demonstrates the migration in TypeScript, Python, Go, and wrapper paths.
Operators use ballast doctor to inspect the effective Ballast state for a repository, but the current report omits the saved languages and paths from .rulesrc.json. This makes it hard to confirm which language profiles Ballast considers installed in monorepos or mixed-language repos.
Operators also need to know when saved language paths have drifted from the repository, such as when a configured target directory was deleted, renamed, or when a new language profile was added after Ballast was installed.
ballast doctormust display configuredlanguageswhen.rulesrc.jsoncontains them.ballast doctormust display configuredpathswhen.rulesrc.jsoncontains them.ballast doctormust display configuredtaskSystemwhen.rulesrc.jsoncontains it.- The change must apply consistently across the TypeScript, Python, Go, and wrapper CLIs.
- Existing
doctoroutput for targets, agents, skills, installed CLIs, and recommendations must remain intact. - The wrapper
ballast doctorreport must identify configured language paths that are missing from disk or no longer match detected language profiles. - The wrapper
ballast doctorreport must identify detected language profiles that are not saved in.rulesrc.json, including newly added directories and newly added languages. ballast doctor --fixmust refresh savedlanguagesandpathsfrom current repository detection before reapplying the saved install configuration when current detection can produce a supported profile set.- When
.rulesrc.jsoncontains configuredlanguages, wrapperballast doctormust check only the backend CLIs required by those languages; Ansible and Terraform configurations use the Go backend. - A configured TypeScript path containing a JavaScript package without
tsconfig.jsonmust be reported as misconfigured, with guidance to remove the TypeScript language or add the missing configuration. This issue alone must not recommenddoctor --fix.
- Given a
.rulesrc.jsonwithlanguages,ballast doctorprints a- languages: ...line in theConfig:section. - Given a
.rulesrc.jsonwithpaths,ballast doctorprints a- paths: ...line in theConfig:section. - Given a
.rulesrc.jsonwithtaskSystem,ballast doctorprints a- taskSystem: ...line in theConfig:section. - Given a
.rulesrc.jsonwithoutlanguages,paths, ortaskSystem,ballast doctordoes not print empty placeholder lines for those fields. - Automated tests cover the new output in each CLI implementation that renders
doctoroutput. - Given a configured path that no longer exists, wrapper
ballast doctorprints a config drift line that names the missing language path and recommendsballast doctor --fix. - Given a repo where current detection finds a profile path not saved in
.rulesrc.json, wrapperballast doctorprints a config drift line that names the untracked detected profile. - Given a repo where current detection finds a language not saved in
.rulesrc.json, wrapperballast doctorprints a config drift line that names the untracked detected language. - Given stale saved TypeScript paths and a current detected TypeScript path, wrapper
ballast doctor --fixrewrites.rulesrc.jsonto the detected path before refreshing generated outputs. - Given a
.rulesrc.jsonwith onlygoconfigured and a globally installedballast-typescriptonPATH, wrapperballast doctordoes not report the TypeScript backend. - Given a configured TypeScript path with JavaScript package metadata and no
tsconfig.json, doctor reports a misconfigured profile instead of a stale profile and gives explicit remediation. Other drift still recommendsdoctor --fix. - The diagnostic recognizes minimal packages with
type: moduleortype: commonjs. The recommended removal command must clean up the saved language, paths, tools, and managed language rules even when TypeScript is the only saved language, preserving user-authored rules. - Cleanup-only language removal must subtract from saved profiles without adopting newly detected languages. Removing an unrelated language must preserve the saved language and path selections, including empty path entries.
Ballast-generated rule files can diverge from current source templates when users edit generated files, merge conflicts leave noise behind, or saved .rulesrc.json targets and agents change while old managed files remain on disk. Without a machine-readable ownership marker, ballast doctor cannot reliably distinguish current managed files from stale managed files or user-authored files.
- Every generated Ballast-managed rule file must include a machine-readable marker with
id,version, andchecksum. - The marker checksum must be computed from generated rule content with the marker excluded so the checksum is stable and non-self-referential.
ballast doctormust enumerate generated rule destinations for configured targets and known rule directories, parse rule markers, and categorize files asok,drifted,stale, orunowned.ballast doctormust compare marked rule files against the canonical content Ballast would generate for the marked rule ID.ballast doctormust add recommendations for drifted and stale managed rule files while making unowned files explicitly skipped.ballast doctor --fixmust delete stale managed rule files and print each deleted path.ballast doctor --fixmust never delete unowned files and must never overwrite drifted files.- The behavior must be covered across the TypeScript backend and mirrored rule-generation surfaces in Python and Go where applicable.
- Given a Ballast-generated rule file, the file contains
<!-- ballast:rule id="..." version="..." checksum="..." -->. - Given a marked file whose body still matches current generated content and remains in
.rulesrc.json,ballast doctorreports it asok. - Given a marked active file whose content differs from its marker checksum or current generated content,
ballast doctorreports it asdriftedand recommendsballast install --refresh-config. - Given a marked file that is no longer part of the current saved config,
ballast doctorreports it asstaleand recommendsballast doctor --fix. - Given a file without a Ballast rule marker,
ballast doctorreports it asunownedand skips remediation. - Given stale, drifted, and unowned rule files,
ballast doctor --fixremoves only the stale managed files, prints removed paths, leaves drifted and unowned files untouched, and keeps recommending refresh for drifted files. - Automated tests cover marker parsing, checksum comparison, report formatting, stale cleanup, and safety behavior.
Some repositories contain browser or Node.js components that are still JavaScript-first and therefore do not produce a reliable TypeScript profile for Ballast. This can hide real application components from Ballast's language/profile reporting, especially in mixed-language repos.
- The
ballastwrapper must warn when it detects a realpackage.json-based JavaScript component or app without atsconfig.json. - The warning must apply in both single-language detection and monorepo planning paths.
- The warning must tell the operator to convert the component to TypeScript or add
tsconfig.jsonso Ballast can track it as a TypeScript profile. - The warning must not trigger for placeholder
package.jsonfiles that do not look like an app or component.
- Given a repo with
package.jsoncontaining app/component signals and notsconfig.json, calling the wrapper detection path emits a warning on stderr. - Given a mixed-language repo where monorepo planning detects non-TypeScript profiles but the root still contains a JavaScript app/component, the wrapper emits the same warning on stderr.
- Given a repo with
tsconfig.json, the warning is not emitted. - Smoke coverage must exercise at least one single-language JavaScript package case and one mixed-language non-TypeScript monorepo case that emits the warning.
Ballast testing and publishing rules mention smoke tests, but they do not consistently define the expected product-level coverage for web applications and installable CLIs. Agents need concise generated guidance that distinguishes fast local checks, pre-push confidence checks, and CI release gates without requiring broad integration-framework detection work.
- Generated testing guidance must define a baseline web smoke test for runnable web applications that starts the real app and verifies a live route or health endpoint.
- Generated testing guidance must define a narrow web end-to-end baseline for one critical user workflow when the repo has a browser application surface.
- Browser end-to-end guidance must prefer Playwright when the repo already uses Playwright or the app shape calls for browser automation, while leaving broader framework detection to the integration-framework detection workstream.
- Generated guidance must explain local, pre-push, and CI placement: fast unit and targeted smoke checks locally, deterministic smoke and required build/typecheck checks before push, and full smoke/E2E gates in CI.
- Generated CLI publishing/testing guidance must require packaged-command smoke tests that install or execute the built artifact, verify
--helpand--version, and run at least one representative command. - Documentation must describe the same web smoke/E2E and CLI packaged-command smoke expectations.
- Automated generated-content tests must cover the smoke/E2E placement guidance and CLI packaged-command smoke guidance.
- Generated web testing rules include smoke and end-to-end expectations for runnable web apps.
- Generated testing rules prefer Playwright for browser E2E when appropriate without implementing the broader #145 framework-detection scope.
- Generated guidance explains which checks belong locally, in pre-push, and in CI.
- Generated CLI publishing/testing rules require packaged-command smoke tests for install/startup, help output, version output, and a representative command.
docs/agents/testing.mdanddocs/agents/publishing.mdinclude the same operator-facing guidance.- Tests or snapshots fail if the generated guidance drops the required smoke/E2E or CLI packaged-command expectations.
Ballast testing rules now define smoke and E2E expectations, but agents still need explicit framework-detection discipline before adding or changing integration tests. Without language-aware detection markers, agents can replace established E2E stacks, add Playwright to library-only repositories, or miss browser app surfaces that should use Playwright when no browser E2E framework exists.
- Generated TypeScript testing guidance must tell agents to detect existing unit, integration, and browser E2E frameworks before introducing new test tooling.
- Generated Python and Go testing guidance must tell agents to detect existing unit, integration, API, service, and browser E2E frameworks before introducing new test tooling.
- Browser E2E guidance must preserve an existing browser E2E framework such as Cypress, WebdriverIO, Selenium, Puppeteer, Robot Framework, pytest-playwright, Playwright Test, or Go browser harnesses when one is already present.
- Browser E2E guidance must prefer Playwright only when Playwright markers already exist or when the repository has a real browser application surface and no existing browser E2E framework.
- Generated guidance must warn agents not to add browser E2E tooling to library-only, CLI-only, infrastructure-only, or backend-only repositories without a user-facing browser surface.
- Documentation must describe the same framework-detection and Playwright-selection expectations.
- Automated generated-content tests must cover the framework markers, existing-framework preservation, Playwright preference, and non-browser guardrail.
- TypeScript generated testing rules mention package/config markers for Jest, Vitest, Cypress, Playwright, WebdriverIO, Selenium, Puppeteer, and Testing Library.
- Python generated testing rules mention markers for pytest, unittest, tox/nox, Robot Framework, Selenium, Playwright or pytest-playwright, and API/service test clients.
- Go generated testing rules mention
go test, integration build tags or naming, API/service tests, Selenium/chromedp/rod/agouti, and Playwright-driven browser harnesses. - Generated testing rules for TypeScript, Python, and Go preserve existing browser E2E frameworks before adding Playwright.
- Generated testing rules for TypeScript, Python, and Go prefer Playwright only for existing Playwright repos or browser apps with no existing browser E2E framework.
docs/agents/testing.mdincludes operator-facing framework-detection guidance aligned with generated rules.- Tests fail if generated guidance loses required detection markers, existing-framework preservation, Playwright preference, or non-browser guardrails.
Ballast publishing guidance currently bakes in one Kubernetes deployment shape. Repositories use different app deployment models, and agents need a durable repository-level answer so generated publishing rules do not assume Kubernetes, serverless platforms, hosted platforms, or self-managed servers incorrectly. The same setup flow that captures the durable task system should capture deployment model when publishing rules are installed.
- Ballast must support a repository-level
deploymentModelconfig value with valid valuesnone,kubernetes,serverless,server, andhosted. - Interactive install must prompt for
deploymentModelonly when thepublishingagent is selected and no prior deployment model or explicit flag exists. - Non-interactive install must default
deploymentModeltononewhen thepublishingagent is selected without an explicit value or existing config. - CLI installs must accept
--deployment-model <model>and reject invalid values with a clear list of valid options. - Wrapper monorepo installs must persist, validate, and forward
deploymentModelto backend invocations that install thepublishingagent. .rulesrc.jsonmust preserve an existing deployment model across installs that do not explicitly change it.ballast doctormust display configureddeploymentModelwhen present.- Generated publishing guidance must render model-specific deployment guidance:
kubernetes: application repo ownscharts/<app>/; a separate GitOps repo owns ArgoCDApplicationorApplicationSetconfiguration; CI publishes image tags or digests and updates the GitOps repo when image references are environment-specific there.serverless: guidance covers managed function/container platforms, environment configuration, least-privilege deploy credentials, and preview/stage/prod promotion.server: guidance covers self-managed VM or bare-metal deploys, service managers, artifact transfer, rollback, health checks, and secrets outside the repo.hosted: guidance covers hosted app platforms such as Vercel, Netlify, Render, Railway, or Fly.io, including platform config ownership, environment variables, previews, and production promotion.none: guidance avoids app deployment assumptions and keeps library/CLI publishing guidance intact.
- Given an interactive install selecting
publishingwith no prior config, Ballast prompts fordeploymentModel. - Given
--deployment-model kubernetes,.rulesrc.jsonstores"deploymentModel": "kubernetes"and generated publishing guidance uses the local Helm chart plus external ArgoCD GitOps model. - Given an invalid deployment model, install exits non-zero and prints the valid values.
- Given a non-interactive install selecting
publishingwithout a deployment model,.rulesrc.jsonstores"deploymentModel": "none". - Given an existing
.rulesrc.jsonwithdeploymentModel, later installs preserve it unless--deployment-modelis provided. ballast doctorprints- deploymentModel: <value>when configured.- Automated tests cover config parsing/persistence, CLI parsing, install prompt/default behavior, wrapper forwarding, doctor output, and generated publishing guidance.
Running ballast install from a new nested project directory that does not contain Ballast project markers can incorrectly install files into a parent directory when the parent does contain recognized markers. Root resolution currently walks upward looking for project markers and must not escape the active git repository or silently inherit a non-git ancestor for an unmarked current directory.
- Project-root resolution must check the current directory for recognized Ballast project markers before considering parent directories.
- Upward traversal must stop at the first git repository boundary when the current directory does not contain project markers.
- The change must apply consistently across the TypeScript, Python, and Go Ballast backends, and the
ballastwrapper must treat Ballast config files as project-root markers. - Root resolution for nested directories inside a marked repository must continue to work when the traversal has not yet crossed a git boundary.
- If no git boundary connects the current directory to an ancestor project marker, an unmarked current directory must remain the resolved project root.
- End-to-end smoke coverage must exercise the case where an unmarked child directory has no project markers and its parent does.
- Given a child directory that is its own git repository and contains no recognized project markers, Ballast resolves the project root to that child directory rather than a marked parent directory.
- Given a nested directory inside a repository whose root contains recognized project markers, Ballast resolves the project root to the marked repository root.
- Given an unmarked child directory with no git repository boundary and a marked parent directory, Ballast resolves the project root to the child directory and does not write managed files to the parent.
- TypeScript, Python, and Go automated tests cover the git-boundary stop condition and the unmarked nested project condition.
- Wrapper tests cover root resolution when a repo is anchored by
.rulesrc.jsonand legacy Ballast config files. - The examples smoke workflow runs an end-to-end unmarked nested project scenario that fails if install output is written to the parent directory.
Ballast scaffolds AGENTS.md and CLAUDE.md with a Repository Facts section, but currently leaves placeholder values. Agents then re-derive stable repository metadata repeatedly instead of using durable facts captured during install.
- The
ballastwrapper must discover repository facts once per invocation and pass them to backend installers through a temporary JSON file path provided in environment (BALLAST_REPOSITORY_FACTS_FILE) and optional backend CLI flag support (--repository-facts-file). - TypeScript, Python, and Go backends must consume the wrapper-provided facts section when present and valid.
- On first-time support-file creation and on
--forceregeneration, generatedAGENTS.md/CLAUDE.mdcontent must include discovered values for detectable fields. - Monorepo support-file generation in the wrapper must render the same discovered repository facts content.
- When a fact cannot be detected, the generated value must remain an explicit placeholder marker.
- Discovery and rendering must remain non-destructive and read-only outside writing Ballast-managed outputs.
- Given a repository with detectable git origin, default branch, and package-manager signals, running install produces
AGENTS.mdwith detected values instead of<OWNER/REPO>,<main>, and package-manager placeholders. - Given wrapper-driven monorepo install flows, generated support files include the same discovered facts.
- Given missing signals, generated support files retain placeholder markers for undetected fields.
- TypeScript, Python, and Go backends accept
--repository-facts-fileand honorBALLAST_REPOSITORY_FACTS_FILEwhen present. - E2E coverage verifies populated repository facts in generated support files.
Ballast-generated local-development rules treat PR hygiene as part of the agent workflow, but they do not explicitly require agents to keep checking Copilot review feedback after PR creation and subsequent pushes. Agents can miss follow-up Copilot comments or mark work complete without replying directly on addressed review threads, leaving unresolved PR feedback for operators to clean up manually.
- Generated local-development PR workflow guidance must instruct agents to poll for Copilot review comments after PR creation.
- Generated local-development PR workflow guidance must instruct agents to poll again after each push that updates an open PR.
- Agents must summarize actionable Copilot review asks before making code changes.
- Agents must reply directly on every Copilot review thread or comment they address, and must resolve addressed review threads when the review system supports thread resolution.
- The same review loop must remain compatible with human reviewer comments; Copilot-specific guidance must not cause agents to ignore human review feedback.
- The stop condition for PR readiness must be explicit: required checks are green and there are no unresolved actionable Copilot or human review comments.
- Generated guidance must include concrete command examples using
ghor GitHub MCP tools where available.
- Generated local-development rule output tells agents to check Copilot comments repeatedly during PR readiness work.
- Generated local-development rule output requires direct per-thread replies and supported review-thread resolution for addressed Copilot comments.
- Generated local-development rule output defines the stop condition for the review loop.
- Generated local-development rule output says the workflow also applies to human review comments.
- Automated tests cover the generated PR workflow rule text.
Ballast-generated task guidance still treats tasks/todo.md as a simple branch scratchpad, while the global execution framework uses lowercase tasks/todo.md as a structured planning and evidence artifact. Ballast testing rules also focus on runner setup and coverage plumbing without telling agents to drive behavioral changes through TDD.
- Ballast task guidance must standardize on lowercase
tasks/todo.md. - Ballast task guidance must align
tasks/todo.mdwith the global execution template sections for context, scope, acceptance criteria, execution checklist, test strategy, rollback, and outcome. - Lightweight branch notes may omit sections that do not apply, but they must remain a subset of the canonical structured template rather than a separate checklist format.
- Ballast task guidance must include canonical templates for
tasks/todo.md,tasks/lessons.md, and strict issue output. - Task-system guidance must continue to distinguish branch-local notes from durable external issue tracking.
- TypeScript, Python, and Go testing rules must include explicit TDD process discipline in addition to runner and coverage setup.
- TDD guidance must require acceptance criteria, a failing test first, minimum implementation, green/refactor, proof, failure-path coverage, and traceability to requirements or issues.
- Generated task TODO guidance mentions only lowercase
tasks/todo.mdand contains the structured task template. - Generated task TODO guidance contains canonical
tasks/lessons.mdand issue output templates. - Generated task TODO guidance allows lightweight optional sections only as a subset of the structured template.
- Generated TypeScript, Python, and Go testing guidance includes the required TDD cycle and distinguishes process discipline from tooling setup.
- Tests cover the task templates and TDD guidance across supported generated testing rules.