bus-dev — developer workflows: commit, work, spec, e2e, triage (SDD)

bus-dev — developer workflows: commit, work, spec, e2e, triage

Introduction and Overview

Bus Dev is a developer-only companion module that centralizes the workflow logic that module repositories would otherwise duplicate in scripts/: module scaffolding, commit workflows, planning from documentation coverage gaps, agent-runner workflows, AGENTS.md refinement, and end-to-end test scaffolding. It provides a single entry point so behavior and prompts stay consistent and maintainable in one place, and module repositories can rely on bus dev instead of copying and maintaining per-repo scripts. Bus Dev does not implement agentic operations itself: it depends on the bus-agent module through Go library access for all agent runtime execution, prompt-template rendering, runtime detection, and installation references. Bus Dev owns workflow semantics, embedded prompt content, repository and module resolution, and the commit workflow; bus-agent owns the runner abstraction, backends, and execution contract.

The problem it solved was duplication and drift: each BusDK module used to ship its own scripts/commit.sh, scripts/work.sh, scripts/refine-agents.sh, and scripts/e2e.sh (or equivalents), with slightly different behavior and prompts. Bus Dev consolidates that logic into one binary, bus-dev, invoked through the dispatcher as bus dev …, with embedded prompts and deterministic behavior. The triage operation keeps development-state documentation accurate and evidence-based by reconciling test-proven capabilities with planned work and dependency information.

Scope and boundaries are unambiguous. Bus Dev operates on source code repositories and developer workflows only. It does not operate on accounting datasets, workspace CSV, or schemas. It is not part of the end-user bookkeeping command surface. End users running bus accounts, bus journal, or bus validate never need bus dev; it exists for contributors and automation that work inside BusDK module Git repositories.

This module is an explicit exception to the BusDK-wide non-goal NG-001: “BusDK does not execute Git commands or commit changes.” That non-goal applies to the accounting and workspace toolchain. Bus Dev is isolated to developer tooling and enforces strict safety constraints: no remote Git operations (no push, pull, fetch, clone, or submodule update that contacts a remote), no history rewriting (no amend, rebase, or squash), deterministic behavior, and clear diagnostics. The exception is justified by isolating Git use to a single, well-defined developer-only module and by making the constraints normative in this SDD.

The intended users are developers and automation (including AI agents) working inside a BusDK module repository. The document’s purpose is to serve as the single source of truth for implementation and review; the audience includes human reviewers and implementation agents.

Out of scope for this SDD: implementing accounting logic, modifying workspace datasets, providing end-user CLI commands for bookkeeping, and any workflow that pushes to or pulls from a remote.

Goals

G-DEV-001 Centralize developer workflow logic. Replace per-repo scripts with a single bus dev command set so behavior and prompts are consistent and maintainable in one place.

G-DEV-002 Developer-only, repository-scoped operations. All operations run in the context of the current working directory as a Git repository and affect only source code, agent instructions (AGENTS.md), and developer artifacts — never workspace datasets or end-user data.

G-DEV-003 Safety and determinism. No remote Git operations, no history rewriting, and deterministic output and exit codes so scripts and CI can rely on bus dev behavior.

G-DEV-004 Agent-friendly integration via bus-agent. Subcommands that invoke an external agent runtime use embedded prompts (owned by bus-dev) and depend on the bus-agent Go library for execution. The tool exposes consistent defaults for model and timeout and a well-defined contract for stdout/stderr and exit codes by using bus-agent’s runner, templating, and backend support; at least Cursor CLI, Codex, Gemini CLI, and Claude CLI are supported as selectable options through bus-agent.

G-DEV-005 Deterministic module root initialization. A single subcommand (bus dev init [DIR]) MUST initialize a module root in the current working directory by default, or in an explicitly provided target directory when DIR is given, without performing any Git operations. By default it initializes directory structure and baseline files only (including a single AGENTS.md at the project root and a root Makefile from an embedded sample when missing). Workflow operations (plan, spec, work, e2e) run only when the user explicitly appends them in any order or combination (for example bus dev init plan spec work or bus dev init DIR work plan e2e), and they execute in the exact order provided. Init is not required to obtain AGENTS.md: bus dev spec creates it from online SDD and module end-user documentation when the file is missing (FR-DEV-008), so a repository may get AGENTS.md by running spec without having run init.

Non-goals

NG-DEV-001 Bus Dev does not perform remote Git operations. Push, pull, fetch, clone, submodule update/init that contacts a remote, or any operation that could contact a remote is out of scope and must never be implemented.

NG-DEV-002 Bus Dev does not rewrite history. Amend, rebase, squash, or any operation that changes existing commit SHAs is out of scope.

NG-DEV-003 Bus Dev does not operate on workspace accounting datasets. Reading or writing workspace CSV, schemas, or datapackage.json for bookkeeping purposes is out of scope; that remains the domain of other BusDK modules.

NG-DEV-004 Bus Dev does not replace the bus dispatcher or module discovery. It is one module among many; the dispatcher continues to route bus dev … to the bus-dev binary.

NG-DEV-005 Bus Dev must never modify user-global Gemini configuration or memory. Any file under the user’s home Gemini config directory (e.g. ~/.gemini/), including ~/.gemini/GEMINI.md, and any global memory managed via interactive Gemini CLI commands (e.g. /memory add) are strictly off limits. Bus Dev may read global context only insofar as the Gemini CLI loads it by default when it runs; Bus Dev itself does not write, create, or edit anything outside the current repository. Developers who want global Gemini memory or user-level Gemini config must manage it manually outside Bus Dev.

Requirements

FR-DEV-001 CLI integration and naming. The binary MUST be named bus-dev and MUST be invoked through the dispatcher as bus dev <operation> [args]. Acceptance criteria: the dispatcher routes bus dev init, bus dev commit, bus dev stage, bus dev plan, bus dev work, bus dev spec, bus dev e2e, bus dev triage, and bus dev each to the same binary; when multiple operation tokens (base operations or pipeline names) are passed in any order or combination (e.g. bus dev plan spec work, bus dev round, or bus dev plan work stage commit), the binary receives them as positional arguments, expands pipeline tokens to a base-operation sequence, and executes that sequence in the order given.

FR-DEV-001a Multiple workflow operations. The CLI MUST accept one or more consecutive operation tokens in a single invocation; each token is either a base operation name from {plan, spec, work, e2e, triage, stage, commit}, a pipeline name (built-in or user-defined), or a user-defined action name (repository-local prompt action, repository-local script action). The tool MUST expand all tokens first (FR-DEV-015): runnable-step tokens (base operations, user-defined actions) remain as single steps; expandable tokens (pipelines) are expanded in place with no pre-expansion merge, so each occurrence of a pipeline yields its own expanded segment. Only after expansion does the tool normalize the resulting sequence (FR-DEV-015a): merge repeated steps from direct base-operation tokens (e.g. commit commit commit → one commit); steps from pipeline expansion are not merged across pipeline invocations (e.g. iterate iterate iterate runs the iterate sequence three times). When two or more steps result after normalization, the tool MUST execute them in the normalized order, one at a time, in the same process and effective working directory. If a step exits non-zero, the run MUST stop immediately and the tool MUST exit with that step’s exit code; subsequent steps in the list MUST NOT run. With global --check, the same expansion and normalization MUST run, and script-runnability checks MUST run, but no step may be executed. The stage operation invokes the agent to prepare the working tree (see FR-DEV-006a), then stages remaining changes so that commit can be combined with workflow operations in one invocation without a manual staging step. When init is used, only operations from the set {plan, spec, work, e2e} MAY follow init (triage, stage, and commit are not accepted after init); pipeline tokens after init are permitted only if their fully expanded form contains only those base operations (FR-DEV-018). Acceptance criteria: bus dev plan spec work, bus dev round, and bus dev plan work stage commit run the resulting normalized sequence of runnable steps in order; bus dev commit commit commit runs one commit step; bus dev iterate iterate iterate runs the full iterate pipeline sequence three times (no merge across pipeline invocations); bus dev --check stage commit validates and exits 0 without running stage or commit; if one step fails, the run stops with that exit code; bus dev init [DIR] plan spec work e2e runs init first, then the workflow operations in normalized order; stage and commit are combinable with plan, spec, work, e2e, and triage in the same invocation.

FR-DEV-001b Superproject each dispatch. The CLI MUST provide bus dev each [--check] [--skip MODULE[,MODULE...]]... TOKEN... as a built-in operation that is valid only when run from a superproject repository context. The command MUST discover child module directories as a deterministic union: (1) paths declared in .gitmodules at the superproject root, and (2) top-level directories that contain .bus/dev. Discovery MUST deduplicate names when both sources contain the same directory. A discovered child is selected when its local directory exists and contains either a Makefile or .bus/dev. If bus is among selected children it is ordered first, and all remaining selected children are ordered lexicographically by path. --skip excludes one or more discovered child modules by directory name; it MAY be repeated and MUST accept comma-separated names. Before any child execution, each MUST preflight every selected child with the same token sequence and fail immediately if any selected child cannot resolve the sequence or has a non-runnable script action (on Unix, .sh exists but missing +x is non-runnable). For each selected child, each then runs the equivalent of bus dev TOKEN... with that child module as the effective working directory. The run MUST stop on first failure and return that exit code. With --check, the command MUST perform only the preflight and MUST execute no child commands. each MUST fail with exit 2 when used outside superproject context, when no TOKEN is provided (including after --check and --skip options), when any --skip name is not a discovered module, or when no selected child modules remain. Acceptance criteria: bus dev each --skip bus-docs,bus-legacy stage commit in a superproject runs bus dev stage commit in each discovered non-skipped child module in the required order and stops on first non-zero exit code; if one selected child fails preflight, no child command runs; bus dev each --check --skip bus-docs stage commit validates selected children without execution.

FR-DEV-001c Conditional retry workflow. The CLI MUST provide bus dev retry --on-fail|-f <TOKEN>[,<TOKEN>...] [--attempts N] <workflow-token>... to execute one workflow sequence and retry it automatically on failure. The command MUST (1) parse --attempts with default 1, where the value means additional retries after the first workflow attempt; (2) expand and normalize both workflow and fallback token lists using existing token-resolution and normalization rules, including built-ins, pipelines, user-defined actions, and @ forcing built-ins; (3) run the normalized workflow sequence; (4) when workflow execution fails and retries remain, run the fallback sequence, then wait for exponential backoff before retrying; and (5) stop and return the last failing code if a workflow run succeeds or retries are exhausted. --attempts 0 MUST run the workflow once with no retries and no fallback execution. The delay between retries MUST grow exponentially (1s, 2s, 4s, …). Diagnostic output MUST include attempt counters and retry-delay timing. Built-in-only fallback tokens are passed as @name (for example @commit). Acceptance criteria: bus dev retry --on-fail|-f fix-tests,fix-tests-2 --attempts 2 test runs test with fallback list and retries up to three total workflow attempts; bus dev retry --on-fail|-f @commit test_2 executes built-in commit as fallback; bus dev retry -f fix-tests --attempts 0 test runs test once and does not run fix-tests fallback on failure; bus dev retry --on-fail fix-tests,fix-tests-2 --attempts 1 test test_2 runs both workflow tokens in each attempt until success or retries are exhausted; unknown fallback tokens or malformed --attempts values return usage error (exit 2).

FR-DEV-002 Repository discovery. For operations that require a repository context (commit, stage, plan, work, spec, e2e, triage when run standalone), the tool MUST determine the current working repository (or, for triage, the detected project context: docs repo, super-project, or module repo per FR-DEV-013) from the effective working directory (after applying -C/--chdir if present). It MUST NOT require configuration files or environment variables to locate the repo. Acceptance criteria: when run from inside a Git repository root or any subdirectory, the tool uses that repository as the scope for repository-scoped operations; when the effective working directory is not a Git repository, repository-scoped operations exit with a clear diagnostic and a non-zero exit code, while init remains valid because it operates on directory initialization rather than repository discovery.

FR-DEV-003 Module identity for spec and e2e. When a repository-scoped subcommand needs the current module name (e.g. for docs URLs or e2e script naming), the tool MUST derive it deterministically from the repository. The module name is the base name of the repository root directory (the last path component of the absolute path to the repo root). For init, which may run without a repository, the module name is derived from the base name of the resolved target directory. Acceptance criteria: the same repository always yields the same module name for repository-scoped operations; the same init target path always yields the same module name for initialization; no other derivation rule (e.g. canonical file) is used.

FR-DEV-004 Embedded prompts and repository-local prompt actions. All prompts used to drive built-in operations (plan, spec, work, e2e, triage, stage, commit) MUST be embedded as Go string constants or templates inside the bus-dev binary. The tool MUST NOT load prompts from external prompt files for built-in operations. Additionally, bus-dev MAY load repository-local action prompts from disk under a single fixed repository directory: only from <repo-root>/.bus/dev/<NAME>.txt, where <repo-root> is the resolved Git repository root and <NAME> is the action name (e.g. the token used to invoke the action). File discovery MUST be restricted to the resolved repository root; the resolved file path MUST remain inside the repository (symlinks that point outside the repository MUST be refused). Repository-local prompt actions are user-defined and are executed only when explicitly invoked by name in the token list. Acceptance criteria: a fresh clone of the bus-dev repo builds a binary that runs all built-in subcommands without requiring any external prompt files; when a token resolves to a repository-local prompt action, the tool loads only from the corresponding .bus/dev/<NAME>.txt at repo root and refuses symlinks that escape the repo; prompts may still be overridden or extended by flags or environment variables when explicitly documented.

FR-DEV-004a Prompt-template rendering contract. Embedded prompts and repository-local prompt templates (from .bus/dev/<NAME>.txt) that use placeholders MUST use the {{VARIABLE}} syntax. Bus-dev MUST use the bus-agent library’s template renderer (or a contract-compatible implementation) so that rendering is deterministic and satisfies the same strict guarantees as bus-agent’s FR-AGT-006: missing or unresolved placeholders MUST cause the command to fail before any agent invocation, with a clear diagnostic and exit code 2. Rendering a template with no placeholders is a pass-through. This contract applies to both embedded prompts and repository-local prompt action files. This contract is part of the determinism and safety guarantees (NFR-DEV-001, NFR-DEV-005): the tool MUST NOT invoke an agent with partially substituted or ambiguous prompt text. Acceptance criteria: unit tests cover missing variable, unresolved variable, repeated placeholder replacement, and expected placeholder set per prompt template; the agent is never invoked when substitution fails; repository-local .txt templates are subject to the same substitution and failure-before-invocation rules.

FR-DEV-004b Configurable docs base URL. The documentation base URL used to derive links in prompts and diagnostics MUST be configurable via the environment variable BUS_DEV_DOCS_BASE_URL. The default MUST be https://docs.busdk.com. The value MUST be normalized by trimming any trailing slash before use. This value is used to derive documentation links (including the main SDD URL and module-specific SDD and module-docs URLs) that appear in embedded prompts and in diagnostics. The main SDD URL is a first-class derived value: MAIN_SDD_URL = <DOCS_BASE_URL>/sdd. The module SDD URL is <DOCS_BASE_URL>/sdd/<module> and the module docs URL is <DOCS_BASE_URL>/modules/<module>, where <module> is the module name (base name of the repository root directory, per FR-DEV-003). Acceptance criteria: when unset, derived URLs use the default base; when set, they use the normalized value; trailing slash is removed; tests verify override effects on derived URLs.

FR-DEV-005 Dependence on bus-agent for agent execution. The implementation MUST depend on the bus-agent Go library for invoking the configured external agent runtime. Bus-dev MUST use bus-agent’s runner interface, template renderer, runtime detection, and installation references rather than reimplementing them. Model, output format, and timeout remain configurable via flags or environment variables; resolution order for these options MUST follow the same logic as agent selection (see Run-config resolution in Component Design). Defaults are documented and deterministic. Acceptance criteria: bus-dev imports and uses the bus-agent package for all agent invocations; model, output format, and timeout are configurable via flags or environment; the integration is testable in isolation (e.g. by stubbing or mocking the bus-agent dependency or the executable in PATH as supported by bus-agent).

FR-DEV-005a Supported agent runtimes via bus-agent. The implementation MUST use bus-agent’s supported runtimes (Cursor CLI, Codex, Gemini CLI, Claude CLI). Bus-dev MUST NOT implement its own agent backends; it MUST delegate to the bus-agent library for backend selection and execution. The active runtime MUST be selectable via a documented flag or environment variable (e.g. --agent cursor|codex|codex:local|gemini|claude or equivalent) that bus-dev passes through to bus-agent’s selection resolution. codex:local MUST select the Codex backend in local mode (--oss). Acceptance criteria: the user can select any supported runtime token; switching runtime does not change subcommand semantics or embedded prompts; bus-agent’s backends and installation references are the single source of truth; documentation lists all runtimes and selection mechanism and references the bus-agent SDD where appropriate.

FR-DEV-005b Agent selection configuration. The implementation MUST expose configuration so that the user can choose which agent runtime to use, and that choice MUST be easy to change both per command and for the session. Bus-dev MUST invoke the bus-agent library with caller context so that resolution follows the bus-dev order (FR-AGT-005): (1) per-command override (--agent <runtime>), (2) BUS_DEV_AGENT (bus-dev only), (3) BUS_AGENT, (4) per-step selector preference (bus-dev.agent_for.<selector> where selector is token or @token and @token matches built-in steps only), (5) bus-dev persistent preference (bus-dev.agent), (6) bus-agent persistent preference (bus-agent.runtime), (7) first available runtime in the effective order. At any step, if the indicated runtime is disabled by user configuration, bus-agent prints a warning and resolution continues with the next source (FR-AGT-005b). The user sets bus-dev’s persistent default with bus preferences set bus-dev.agent <runtime> and the shared default with bus preferences set bus-agent.runtime <runtime>. Acceptance criteria: the user can set a session default via BUS_DEV_AGENT or BUS_AGENT and run multiple bus dev commands without re-specifying; the user can override for a single command with --agent; the resolution order is documented; invalid runtime names yield a clear usage error (exit 2); when a configured runtime is disabled, a warning is printed and the next source is used.

FR-DEV-005d Agent detection and automatic default. The implementation MUST use bus-agent’s runtime detection and automatic-default selection rather than implementing its own. When no per-command flag, no session-stored preference, and (if supported) no persistent preference specify a runtime, bus-dev MUST call bus-agent’s resolution so that the automatic default is chosen from available runtimes in bus-agent’s documented order (alphabetic by runtime ID when no user order is configured; see bus-agent FR-AGT-005, FR-AGT-005a). Bus-dev MUST pass through to bus-agent any user-configured agent order and enable/disable settings so that the available set and selection order match bus-agent’s contract. If no agent is available, the tool MUST exit with a clear diagnostic and non-zero exit code and MUST direct the user to install or enable at least one supported agent using the installation references provided by the bus-agent library. Acceptance criteria: running bus dev work with no --agent and no BUS_DEV_AGENT results in bus-agent selecting an available agent when at least one is available; when none is available, the tool exits with a clear message and does not proceed; tests can verify behavior by controlling PATH or by using bus-agent’s testability guarantees (stub executables, hermetic tests).

FR-DEV-005e Agent and model disclosure on each step. When the tool executes an agent-invoking step (plan, work, spec, e2e, triage, or stage), it MUST print to stderr, at the start of that step and before streaming or capturing agent output, which internal agent runtime and which model are in use.

FR-DEV-005f Set agent subcommand. The implementation MUST provide a subcommand bus dev set agent <runtime> that writes the bus-dev persistent default agent (e.g. bus-dev.agent) via the bus-preferences Go library. The implementation MUST use the library directly and MUST NOT shell out to the bus preferences CLI. Acceptance criteria: running bus dev set agent gemini persists the preference; a subsequent bus dev work (with no --agent, no BUS_DEV_AGENT, no BUS_AGENT) uses that default when resolution reaches the bus-dev preference step; invalid runtime yields exit 2.

FR-DEV-005g Set run-config subcommands (bus-dev namespace only). The implementation MUST provide subcommands bus dev set model <value>, bus dev set model-reasoning-effort <minimal|low|medium|high|xhigh>, bus dev set model-verbosity <low|medium|high>, bus dev set output-format <ndjson|text>, and bus dev set timeout <duration> that write only the bus-dev preference keys bus-dev.model, bus-dev.model_reasoning_effort, bus-dev.model_verbosity, bus-dev.output_format, and bus-dev.timeout respectively via the bus-preferences Go library. The implementation MUST also provide bus dev set agent-for <token|@token> <runtime>, bus dev set model-for <token|@token> <value>, bus dev set model-reasoning-effort-for <token|@token> <value>, bus dev set model-verbosity-for <token|@token> <value>, bus dev set model-reasoning-effort-for-model <model> <value>, bus dev set model-verbosity-for-model <model> <value>, bus dev set env-for <token|@token> <ENV_NAME> <value>, bus dev set allow-dirs-for <token|@token> <dir...>, and bus dev set allow-dirs-for-module <module> <token|@token> <dir...> writing only bus-dev.agent_for.<selector>, bus-dev.model_for.<selector>, bus-dev.model_reasoning_effort_for.<selector>, bus-dev.model_verbosity_for.<selector>, bus-dev.model_reasoning_effort_for_model.<model>, bus-dev.model_verbosity_for_model.<model>, bus-dev.env_for.<selector>.<env>, bus-dev.allowed_dirs_for.<selector>, and bus-dev.allowed_dirs_for_module.<module>.<selector>. The implementation MUST use the library directly and MUST NOT shell out to the bus preferences or bus agent CLI. Bus-dev MUST NOT write to any other namespace (e.g. bus-agent.*). Valid values for output-format are ndjson and text; valid runtime values are cursor, codex, codex:local, gemini, claude. ENV_NAME MUST match [A-Z_][A-Z0-9_]*. dir... MUST contain at least one non-empty value. Invalid selector, module name, env name, dir list, or value for any subcommand MUST yield exit 2. Selector semantics: token affects both built-in and user-defined steps named token; @token affects built-in steps named token only. Allowed-directory values are runtime-agnostic preference data and MUST be passed only to runtimes that implement explicit directory allow-list support (initially Codex). Acceptance criteria: each subcommand persists only the corresponding bus-dev key; a subsequent agent-invoking command uses the new value when resolution reaches the selector/preference step; invalid value yields exit 2; no bus-agent namespace key is ever written by bus-dev.

FR-DEV-005c Gemini integration is repository-local only. When the selected agent runtime is Gemini CLI, Bus Dev MUST create, edit, and rely only on repository-scoped files: a repo-root GEMINI.md, optional directory-scoped GEMINI.md files, .gemini/settings.json (for context discovery, ignore behavior, tool policy, and MCP configuration), and .geminiignore (for excluding files from context ingestion). Bus Dev MUST NOT modify any user-global Gemini configuration or memory (see NG-DEV-005). Acceptance criteria: no code path writes to ~/.gemini/ or any path outside the current repository; documentation describes the repo-local Gemini files and their roles; tests verify that the tool never invokes Gemini CLI in a way that would add or change global memory (e.g. no /memory add or equivalent).

FR-DEV-006 Commit subcommand behavior. The bus dev commit subcommand MUST implement behavior equivalent to the normative commit workflow specified in the Command Surface section below, including: require Git to be installed and executable in PATH (missing Git => clear diagnostic and non-zero exit); do nothing and exit success when there is nothing staged (or working tree clean in the staged-set sense); operate strictly on the Git index; no file modification, no staging, no amend/rebase, no remote operations; depth-first submodule commits when submodules have staged changes; clear report and stop when a submodule commit causes an unstaged gitlink in the superproject; and enforce the commit message quality rules, atomicity guidance, and failure/hook handling rules as normative requirements. Acceptance criteria: the SDD defines these rules; the implementation enforces them; tests verify behavior with a fixture repo (including submodules when applicable).

FR-DEV-006a Stage subcommand behavior. The bus dev stage subcommand MUST prepare the working tree so that only intended files are staged. If there are no unstaged tracked changes and no untracked files to stage, the command MUST exit 0 immediately without invoking the agent and without running git add. Otherwise, the tool MUST invoke the configured agent runtime with an embedded prompt that instructs the agent to: (1) review the working tree and identify files that should not be tracked by Git (for example temporary files, e2e test leftovers, build artifacts, or other files that do not belong in version control); (2) for each such file or pattern, either add it (or a matching pattern) to .gitignore so it is not staged, or remove the file from the project if it is purely temporary and should not exist in the working tree; (3) leave the working tree in a state where only intended, trackable changes remain. After the agent has completed successfully, the tool MUST stage all remaining changes at the repository root (e.g. the equivalent of git add . from the resolved repository root). The tool MUST NOT commit, amend, rebase, or perform any remote Git operation. Only the working tree of the current repository is in scope; the tool MUST NOT recursively stage inside submodules. Acceptance criteria: the stage subcommand skips agent execution when nothing is stageable; otherwise invokes the embedded stage prompt; the agent may update .gitignore and may remove temporary files; after the agent exits successfully, the tool runs local git add to stage remaining changes; no remote operations; tests verify behavior with a fixture repo (including stubbed agent when hermetic tests are required).

FR-DEV-007 Work subcommand behavior. The bus dev work subcommand MUST implement the canonical “do the work in this repo now” agent-runner workflow equivalent to the provided work script: operate only inside the current module repository, follow the module’s AGENTS.md and design docs, implement concrete code changes, add tests, run the Makefile checks, and update README.md before finishing. The agent MUST be allowed to read the repository’s own docs and AGENTS.md as the authoritative specs for that repo. When PLAN.md exists at repository root, the embedded work prompt MUST instruct the agent to read it first and prioritize unchecked items in PLAN.md before proposing or executing additional work. When work completes an item from PLAN.md, the workflow MUST check off that item in PLAN.md (for example as - [x]). bus dev work MUST NOT remove already checked items from PLAN.md; pruning completed checked items is owned by bus dev plan (FR-DEV-011). When the selected runtime is Gemini CLI, the agent MUST also be able to use repository-local Gemini context (e.g. repo-root and directory-scoped GEMINI.md, .gemini/settings.json, .geminiignore) as documented in the Gemini CLI integration subsection; Bus Dev supports AGENTS.md and Gemini context in parallel without changing core workflow contracts. In headless or script mode, the workflow MUST remain non-interactive and MUST NOT perform prohibited actions (especially network operations); see Headless and script mode below. Acceptance criteria: the workflow is driven by an embedded prompt template; no external prompt file is required; when PLAN.md exists the prompt consumes it before other task discovery; completed plan items are checked off by work; checked plan items are never removed by work; behavior is documented and testable via a stubbed agent; headless invocation does not trigger interactive or network operations.

FR-DEV-008 Spec subcommand behavior. The bus dev spec subcommand MUST refine only the current repository’s AGENTS.md and nothing else. The tool MUST use the path AGENTS.md at the repository root (no module-derived path). The file is the single, open-format agent-instruction file per the AGENTS.md convention. If AGENTS.md does not exist, the tool MUST create it before refining: the embedded spec prompt MUST instruct the agent to produce an initial AGENTS.md from the online BusDK SDD (main SDD, module SDD, module end-user documentation) and related documents, so that the resulting file is a compact but detailed local spec describing how to implement this tool. If AGENTS.md already exists, the tool MUST run an embedded refinement prompt that instructs the agent to update only that file and to align it with the latest BusDK specs and the AGENTS.md format. In both cases the agent MUST use the documentation URLs derived from the docs base URL and prompt variables (e.g. MAIN_SDD_URL, MODULE_SDD_URL, MODULE_DOCS_URL). The written AGENTS.md MUST be a compact but detailed local spec for implementing the current module — grounded in the module’s SDD and end-user docs, not a generic template. Acceptance criteria: no refinement of source code, tests, or README; the path is always repository-root AGENTS.md; when AGENTS.md is missing it is created from online docs then refined in the same run; when it exists it is refined in place; created or refined content is module-specific implementation guidance. FR-DEV-008a Cursor rules refactor. When running bus dev spec, if a Cursor rule file exists at .cursor/rules/<MODULE_NAME>.mdc (where <MODULE_NAME> is the module name per FR-DEV-003, e.g. bus-accounts), the tool MUST include that file’s content in the refinement context. The embedded spec prompt MUST instruct the agent to refactor the substantive content from that Cursor rule into AGENTS.md so that agent instructions are consolidated in AGENTS.md. After the agent has written the updated AGENTS.md, the tool MUST remove the file .cursor/rules/<MODULE_NAME>.mdc. This is the only permitted case where bus-dev (or the shared instruction contract used by bus-agent) may replace or remove existing repo content for instruction standardization; all other repo-local file changes MUST be additive only per NFR-DEV-008. If .cursor/rules/<MODULE_NAME>.mdc does not exist, no Cursor rule file is read or deleted. Acceptance criteria: when the Cursor rule file exists, its content is merged into AGENTS.md and the file is deleted; when it does not exist, spec behavior is unchanged; no other files under .cursor/rules/ are modified; the MDC exception is documented and testable.

FR-DEV-009 E2E subcommand behavior. The bus dev e2e subcommand MUST provide a guided workflow to detect missing end-to-end tests for the current module repository and scaffold them in a hermetic way, consistent with BusDK testing conventions and the module’s SDD and end-user documentation. E2E tests are Bash shell scripts that run the module’s compiled binary; they MUST live under the tests/ directory and MUST be named e2e_bus_<name>.sh where <name> is the module name with the bus- prefix stripped (e.g. module bus-accountstests/e2e.sh). Each bus module MUST also implement complete unit tests; e2e and unit-test conventions are described in agent instructions (e.g. AGENTS.md) and agent-specific configuration. The tool MUST use the module’s SDD and end-user documentation to determine which tests are needed and MUST produce scaffolds that cover the behavior described there; language- and module-specific details are defined in agent rules. When PLAN.md exists at repository root, the embedded e2e prompt MUST instruct the agent to read it first, verify that each checked item is fully covered by e2e tests, and prioritize unchecked test-related items from the plan before proposing additional test scope. Independently of PLAN.md, the command MUST continue discovering other untested behavior required by the module SDD and end-user docs. bus dev e2e MUST NOT remove already checked items from PLAN.md; pruning completed checked items is owned by bus dev plan (FR-DEV-011). Acceptance criteria: the command detects missing e2e tests by consulting the module SDD and end-user docs; when PLAN.md exists the prompt consumes it before additional discovery; checked plan items are validated against e2e coverage; other untested documented behavior is still discovered; checked plan items are never removed by e2e; scaffold output uses the tests/e2e_bus_<name>.sh layout and naming; scaffold content is deterministic and aligned with those sources and with agent rules; the command has defined preconditions and safety constraints.

FR-DEV-010 Init subcommand behavior. The bus dev init [DIR] [--lang go] [plan|spec|work|e2e|pipeline ...] subcommand MUST initialize module-root files without performing any Git operations, and MUST run workflow operations only when they are explicitly appended after init. Behavior MUST be equivalent to: (1) resolve the target directory as current working directory when DIR is omitted, or as DIR when provided; (2) if DIR is provided and does not exist, create it; if the resolved target path exists but is not a directory, exit with code 2 and a clear error; (3) when init runs, ensure a single AGENTS.md exists at the target directory root (project root), with default content that MAY depend on --lang per the AGENTS.md format; (4) ensure a root Makefile exists, creating it from an embedded sample Makefile when missing; (5) if one or more workflow operation or pipeline tokens are appended after init, the tool MUST expand pipelines first; if the expanded sequence contains any of triage, stage, or commit, the tool MUST fail with invalid usage (exit 2) before running any operation (FR-DEV-018); otherwise set the effective working directory to the target directory and execute the expanded sequence exactly in order. The --lang flag MUST default to go and MUST control which kind of default AGENTS.md content the tool installs for future multi-language support. Init is not a prerequisite for AGENTS.md: repositories may obtain AGENTS.md by running bus dev spec when the file is missing (FR-DEV-008), without ever running init. Acceptance criteria: no Git commands are executed; running bus dev init initializes only the current directory and does not invoke plan/spec/work/e2e; running bus dev init DIR performs the same initialization in DIR; running bus dev init [DIR] plan spec work e2e (or any order/combination of plan, spec, work, e2e, or pipelines that expand only to those) runs all listed operations in that exact order and stops on first failure; init followed by a pipeline that expands to stage or commit fails with exit 2 before any operation; initialization creates missing scaffold files and does not overwrite an existing Makefile by default; tests verify both default init-only behavior and ordered optional workflow execution.

FR-DEV-011 Plan subcommand behavior. The bus dev plan subcommand MUST review online documentation and current repository content to produce a compact task plan in PLAN.md at repository root. The required documentation inputs are: the main SDD URL (MAIN_SDD_URL), the current module SDD URL (MODULE_SDD_URL), the current module end-user docs URL (MODULE_DOCS_URL), and any project SDD pages directly relevant to requirements found in those pages. The command MUST detect unimplemented features and other undone work by comparing documented requirements and expected behavior to the current repository. If PLAN.md already exists, the command MUST re-validate existing items against current repository state and current documentation, remove items that are already done (including previously checked items), retain items that are still undone, and then add any newly detected missing work. The command MUST write only PLAN.md and MUST NOT modify source files, tests, or other docs. PLAN.md content written by bus dev plan MUST be a compact unchecked Markdown task list, with one unchecked item per still-undone or newly detected unimplemented feature or undone work item, ordered from highest to lowest priority. Each item MUST be short and action-oriented, with enough context for an agent to choose the next task, and MUST avoid implementation details that belong in the project SDD and related docs. Acceptance criteria: bus dev plan creates or overwrites PLAN.md deterministically for the same repository and docs inputs; when PLAN.md existed before the run, completed items (including checked ones) are removed and still-valid undone items are preserved or rephrased compactly; newly detected missing work is appended into the same prioritized list; every listed item is unchecked (- [ ]); list ordering reflects priority; no implementation-level steps are included; no files other than PLAN.md are changed.

FR-DEV-012 Working-directory lock. At most one bus dev invocation that operates on a given directory MAY run at a time. For subcommands that read or modify the effective operation directory (init, commit, stage, plan, work, spec, e2e, triage, retry), the tool MUST acquire an exclusive lock on that directory before performing any subcommand work and MUST release the lock when the process exits, whether the exit is normal or abnormal. A second invocation that targets the same effective operation directory MUST block until the first invocation releases the lock, then acquire the lock and proceed. Subcommands that only write preferences (e.g. bus dev set agent, bus dev set model, bus dev set output-format, bus dev set timeout, bus dev set agent-for, bus dev set model-for) do not require the lock because they do not touch the working directory. The lock scope is the effective operation directory: for bus dev init [DIR] it is the resolved init target directory (effective working directory when DIR is omitted, or DIR when provided); for commit, stage, plan, work, spec, e2e, triage, and retry it is the repository root (or, for triage, the docs repo root, super-project root, or module repo root per the detected project context in FR-DEV-013). Acceptance criteria: two concurrent bus dev work (or plan, spec, e2e, triage, stage, commit, retry, or init targeting the same directory) run one after the other; the second blocks until the first exits; the lock is released on success, on failure, and on interrupt; bus dev set … does not participate in locking; tests can verify serialization and lock release.

FR-DEV-014 Pipeline token resolution and @ semantics. A pipeline is a named sequence of tokens that expands into runnable steps (base operations and user-defined actions). Operation tokens in the command surface MAY be base operation names, pipeline names (built-in or user-defined), or user-defined action names (repository-local prompt action, repository-local script action). Resolution and expansion MUST occur before any handler runs. Token resolution MUST follow this model. A token of the form @name MUST resolve only to a built-in symbol: if name is a built-in operation, @name means that operation; if name is a built-in pipeline, @name means that built-in pipeline; otherwise it is invalid usage (exit 2). A token without @ follows the deterministic precedence in FR-DEV-021 (repository-local prompt, repository-local script, repository-local pipeline, preference pipeline, built-in operation, built-in pipeline). Built-in operation names are reserved and MUST NOT be definable as repository-local or preference items; so @plan (and similarly @spec, @work, etc.) is a stable “force built-in operation” escape hatch. User-defined pipelines MAY override built-in pipeline names; when a user-defined pipeline overrides a built-in pipeline name, @round (or @<built-in-pipeline-name>) MUST force the built-in pipeline. Acceptance criteria: resolution order and @ semantics are deterministic and testable; unknown token and unknown @name yield exit 2 with clear stderr diagnostic; reserved names are never shadowed; ambiguity rules in FR-DEV-021 yield exit 2 before any operation.

FR-DEV-015 Pipeline expansion before execution. Operation tokens fall into two types. Runnable-step tokens are already single steps: base operation names (plan, spec, work, e2e, triage, stage, commit) and user-defined action names (repository-local prompt or script); they do not expand. Expandable tokens are pipeline names (built-in or user-defined); each resolves to a sequence of tokens and expands recursively until only runnable steps remain. The tool MUST never merge or collapse repeated tokens before expansion: every token in the command list is expanded in place, so e.g. iterate iterate iterate yields three separate expansions of the iterate pipeline, not one. Expansion MUST be a normative part of argument parsing and handler dispatch. All tokens MUST be fully expanded into a sequence of runnable steps (each step is either a base operation or a user-defined action) before any normalization or execution. The final flattened stream MUST contain only base operations and user-defined actions; pipelines are fully expanded away. Only after expansion completes does normalization (FR-DEV-015a) apply; execution then proceeds per FR-DEV-001a. Pipelines do not introduce any new Git behaviors and do not relax non-goals about remote Git operations or history rewriting. Acceptance criteria: expansion is deterministic and testable in isolation; repeated pipeline tokens each expand separately (no pre-expansion merge); no agent invocation or git write occurs until expansion completes successfully.

FR-DEV-015a Final-sequence normalization and duplicate-step merge. Merging is applied only after expansion (FR-DEV-015) has produced the full runnable sequence; the tool MUST never merge or deduplicate tokens before expanding them. Given the expanded sequence (with runnable-step tokens and expandable tokens already flattened to steps, and with each pipeline occurrence represented by its own expanded segment), bus-dev MUST normalize as follows. (1) Segments from direct base-operation tokens: When the user supplied a base operation name (plan, spec, work, e2e, triage, stage, commit) directly as a token one or more times, those steps are merged so that step name appears once in first-appearance order (e.g. commit commit commit → one commit). (2) Segments from pipeline expansion: Each pipeline token has already contributed its full expanded sequence (no pre-expansion merge); steps from different pipeline invocations are NOT merged with each other (so iterate iterate iterate runs the iterate sequence three times). Within the expansion of a single pipeline token, repeated step names (e.g. commit commit from cycle then refresh) MUST be merged so each step appears once in first-appearance order within that segment. Thus each of the three iterate runs has at most one commit per run. This normalization affects execution and preview output equally: both MUST use the same normalized final sequence. Acceptance criteria: merging is never applied before expansion; bus dev commit commit commit normalizes to one commit; bus dev iterate iterate iterate results in the iterate pipeline’s expanded sequence (with internal duplicate-step merge applied per run) executed three times; repeated base-operation steps within a single pipeline expansion (e.g. commit commit) are reduced to a single step for that run; normalization is deterministic and testable.

FR-DEV-015b Pipeline preview without execution. The implementation MUST provide bus dev pipeline preview TOKEN... as a management command that resolves tokens with the same rules as runnable invocations, performs full expansion and normalization (FR-DEV-015 and FR-DEV-015a), prints the normalized final sequence to stdout in deterministic form (one step name per line), and exits without running any agent, script action, or Git operation. Acceptance criteria: preview output exactly matches the sequence bus dev TOKEN... would execute; preview never performs runnable-step side effects.

FR-DEV-016 Pipeline recursion and expansion limits. The tool MUST detect pipeline cycles deterministically (including indirect cycles) and MUST exit with code 2 and a clear stderr diagnostic that includes the cycle path. The tool MUST enforce a maximum expansion depth or maximum expanded-token limit to prevent pathological configs; exceeding the limit MUST be a usage error (exit 2). All pipeline expansion errors (unknown token, unknown @name, invalid pipeline definition format, invalid pipeline name, recursion detected, expansion limit exceeded) MUST be usage errors (exit 2) and MUST occur before any agent invocation and before any git write operations. Acceptance criteria: cycle detection is deterministic and includes indirect cycles; diagnostic includes the cycle path; exceeding depth or token limit yields exit 2; no agent or git side effects when expansion fails.

FR-DEV-017 User-defined pipeline storage and validation. User-defined pipelines MUST be stored in the bus-preferences namespace via keys of the form bus-dev.pipeline.<name>, where the value is a JSON array of strings representing tokens (each token is either a base operation name, a pipeline name, or an @-prefixed built-in reference). The <name> MUST conform to a deterministic, shell-friendly grammar: lowercase ASCII letters, digits, and hyphen; MUST start with a letter. Names that match any built-in operation (plan, spec, work, e2e, triage, stage, commit) are forbidden for user-defined pipelines. Bus-dev MUST read pipeline definitions via the bus-preferences Go library and MUST NOT shell out. Invalid JSON, non-array JSON, empty name, invalid name grammar, or invalid token types in a pipeline definition MUST be usage errors (exit 2) and MUST fail before any agent invocation or git side effects. Acceptance criteria: pipeline definitions are read via bus-preferences; invalid definition or name yields exit 2 before any operation; name grammar is enforced and documented.

FR-DEV-018 Init and pipeline expansion. When init is used, only operations from the set {plan, spec, work, e2e} MAY follow init. When init is present, any subsequent pipeline token is permitted only if its fully expanded form contains only base operations from {plan, spec, work, e2e}. If expansion would introduce stage, commit, triage, or any user-defined action after init, the tool MUST treat this as invalid usage and MUST fail with exit 2 before running any subsequent operation. The expanded sequence after init MUST contain only base operations from {plan, spec, work, e2e}. Acceptance criteria: bus dev init round (when round expands to work e2e stage commit) fails with usage error 2; pipelines that expand only to plan, spec, work, e2e are accepted after init; expansion validation runs before any handler.

FR-DEV-019 Repository-local pipeline definitions. A pipeline MAY be defined by the presence of <repo-root>/.bus/dev/<NAME>.yml, where <repo-root> is the resolved Git repository root and <NAME> is the pipeline name (the token used to invoke it). The file MUST parse as a strict minimal YAML format: a YAML sequence of strings only (a list of scalar strings). Each element is a token that is resolved using the same token-resolution rules as command-line tokens. Any other YAML node types or structures (e.g. mappings, non-string scalars, nested sequences) MUST be rejected as invalid usage with exit code 2. Pipelines MAY reference other pipelines and actions; expansion MUST be fully resolved before executing any operation. Expansion MUST include deterministic cycle detection (with a diagnostic showing the cycle path) and a fixed maximum expansion depth or maximum expanded-token limit; exceeding that limit MUST be a usage error (exit 2). All expansion and parse errors MUST occur before any agent invocation and before any git write operations. Acceptance criteria: only YAML sequences of string scalars are accepted; invalid YAML structure yields exit 2; pipeline references expand recursively with same resolution rules; cycles and limit exceeded yield exit 2 with clear diagnostics; no agent or git side effects when expansion or parse fails.

FR-DEV-020 Repository-local script actions. If <repo-root>/.bus/dev/<NAME>.sh exists then it is a script action available on non-Windows platforms; if <repo-root>/.bus/dev/<NAME>.bat or <repo-root>/.bus/dev/<NAME>.ps1 exists then it is a script action available on Windows platforms. When both .bat and .ps1 exist for the same <NAME>, the tool uses .ps1. All of .sh, .bat, and .ps1 MAY exist for the same <NAME>; that is not an ambiguity: on Windows the .ps1 variant is used if present, otherwise .bat, and on non-Windows the .sh variant is used. A script action is enabled only if it is executable (or, for .ps1, when the file exists and is readable). On non-Windows platforms, the .sh file MUST have at least one execute bit set in filesystem permissions; otherwise it is treated as disabled and ignored for resolution purposes. On Windows, the enable rule is deterministic: for .bat, the file MUST be tracked by Git with executable mode 100755, otherwise the script action is treated as disabled and ignored; for .ps1, the file MUST exist and be readable. If the selected platform variant (.sh, .bat, or .ps1) is disabled, the script action does not exist for resolution purposes and invoking the token MUST yield a usage error (exit 2). Script actions run with effective working directory at repository root, inherit the current environment, and additionally receive the full prompt-variable catalog as environment variables (each variable name is the environment variable key, each value is the derived value; e.g. DOCS_BASE_URL, MAIN_SDD_URL, MODULE_NAME, MODULE_SUFFIX, MODULE_SDD_URL, MODULE_DOCS_URL, E2E_SCRIPT, E2E_SCRIPT_PATH, E2E_WORKSPACE_DIR, AGENTS_FILE_PATH, PLAN_FILE, PLAN_FILE_PATH). Injected variables MUST override any existing variables of the same name for the child process to ensure determinism. Execution: .sh runs via exec of the file path (shebang respected) or via a fixed shell (the implementation MUST document which); .bat runs via cmd.exe /C on Windows; .ps1 runs via PowerShell (e.g. powershell.exe -NoProfile -ExecutionPolicy Bypass -File <path> or the platform’s PowerShell executable; exact invocation is implementation-defined and MUST be documented). Bus-dev MUST NOT capture stdin interactively; script execution remains non-interactive and inherits stdin, stdout, and stderr by default unless the SDD already defines a different logging contract. Acceptance criteria: script discovery is limited to .bus/dev/<NAME>.sh, .bus/dev/<NAME>.bat, and .bus/dev/<NAME>.ps1; executable requirement is enforced per platform; on Windows .ps1 is preferred over .bat when both exist; disabled script invoke yields exit 2; scripts receive prompt-variable catalog as env vars with override semantics; execution method is documented; no interactive stdin capture.

FR-DEV-021 Token resolution and ambiguity. Tokens beginning with @ MUST resolve only to built-in symbols (built-in operations and built-in pipelines) and MUST NOT resolve to repository-local or preference-defined items. Built-in operation names (plan, spec, work, e2e, triage, stage, commit) are reserved and MUST NOT be definable as repository-local actions, repository-local pipelines, or preference pipelines; if such files or preferences exist for a reserved name, they are ignored for resolution and MAY be reported as diagnostics by a listing command. Non-@ tokens resolve with a deterministic precedence order: (1) repository-local action prompt (.txt) first, (2) repository-local script action (platform-selected: non-Windows uses .sh; Windows uses .ps1 if present, else .bat) second, (3) repository-local pipeline (.yml) third, (4) preference pipeline (bus-dev.pipeline.<NAME>) fourth, (5) built-in operation fifth, (6) built-in pipeline sixth. Ambiguity rule: it is a hard usage error (exit 2) if NAME is defined in more than one of the following classes for the same name: repository-local action prompt (.txt), repository-local pipeline (.yml), preference pipeline, and repository-local script action (considering the platform-selected script variant). It is also a hard usage error if both .txt and .yml exist for the same NAME, or if a repository-local definition exists and a preference pipeline with the same NAME exists; these errors MUST occur before any agent invocation or git side effects. The special case of having .sh, .bat, and/or .ps1 for the same NAME is allowed because they are platform-specific variants of the same script action and do not create ambiguity; on Windows .ps1 is preferred over .bat when both exist. User-defined pipeline name collisions with built-in pipelines are allowed only if @NAME continues to force the built-in pipeline; built-in operations are never shadowable. Acceptance criteria: @ tokens resolve only to built-ins; reserved names are never shadowed; precedence order is deterministic and testable; ambiguity between .txt, .yml, preference, and script (per platform) yields exit 2 before any operation; .sh and .bat for same NAME are allowed; listing may report reserved-name conflicts.

FR-DEV-022 Context subcommand. The implementation MUST provide a subcommand (e.g. bus dev context) that prints the full prompt-variable catalog and the current resolved values in deterministic order. The output MUST be script-friendly and stable: a concrete format of one KEY=VALUE line per variable, sorted by KEY ascending, with values printed exactly as used for prompt rendering and environment injection. When not in a Git repository, the command MUST fail with a clear diagnostic and exit code 2. Acceptance criteria: bus dev context prints all catalog variables in KEY=VALUE form, sorted by KEY; values match those used for prompts and script env injection; outside a repo the command exits 2 with clear message.

FR-DEV-023 Management operations and lock. The top-level operations pipeline, action, script, context, and list are management operations: they do not participate in workflow chaining and MUST NOT be expanded as runnable steps when listed with plan, spec, work, e2e, triage, stage, or commit. They MUST be treated like set in that they do not run agent workflows unless explicitly requested (e.g. action generate, script generate) and they do not implicitly stage or commit. pipeline preview is also a management operation: it performs resolution, expansion, and normalization only, then exits without execution. Repository-writing management operations (pipeline set repo, pipeline unset repo, action set, action unset, action generate, script set, script unset, script generate) MUST take the same per-directory lock as other repo-scoped operations (init, commit, stage, plan, work, spec, e2e, triage) to avoid concurrent edits to .bus/dev. Preference-only operations (pipeline set prefs, pipeline unset prefs) and read-only or listing operations (list, pipeline list, pipeline preview, action list, script list, context) MAY omit the lock consistent with set and context. Acceptance criteria: pipeline/action/script/context/list are never expanded as workflow steps; preview performs no runnable-step execution; repo-writing management commands serialize per directory; preference, listing, and preview commands do not block on the lock.

FR-DEV-024 User-defined name grammar. Pipeline names, action names, and script names MUST obey a single deterministic grammar suitable for both filenames (e.g. .bus/dev/NAME.yml, .bus/dev/NAME.txt, .bus/dev/NAME.sh, .bus/dev/NAME.bat, .bus/dev/NAME.ps1) and preference keys (e.g. bus-dev.pipeline.NAME): lowercase ASCII letters a–z, digits 0–9, hyphen, and underscore; MUST start with a letter. Names that match any reserved built-in operation name (plan, spec, work, e2e, triage, stage, commit) are forbidden. Invalid names MUST be rejected as usage error (exit code 2). Acceptance criteria: name validation is shared and testable; invalid or reserved names yield exit 2 with clear diagnostic before any file or preference write.

FR-DEV-025 Pipeline management commands. The implementation MUST provide bus dev pipeline set repo NAME TOKEN… (write or overwrite .bus/dev/NAME.yml as a strict YAML sequence of scalar strings), bus dev pipeline unset repo NAME (remove .bus/dev/NAME.yml if present; exit 0 if absent), bus dev pipeline set prefs NAME TOKEN… (write preference key bus-dev.pipeline.NAME as a JSON array of strings via the bus-preferences Go library, not via shell-out), and bus dev pipeline unset prefs NAME (remove the preference key if present; exit 0 if absent). **bus dev pipeline list [all repo prefs builtin]** MUST print a deterministic line-oriented listing of available pipelines and their source; format MUST be stable (e.g. one line per pipeline name with a source tag and the fully expanded token list in canonical form). bus dev pipeline preview TOKEN… MUST print the deterministic normalized final sequence (one step per line) that runnable invocation would execute, without execution (FR-DEV-015b). List output ordering MUST be lexicographic by pipeline name and then by source precedence so it is stable for scripts and tests. Acceptance criteria: repo pipeline writes YAML sequence only; prefs pipeline uses bus-preferences library; list output is deterministic and parseable; preview output is deterministic and side-effect-free.

FR-DEV-026 Ambiguity rule at creation. When defining a repo or prefs pipeline, an action, or a script with a given NAME, the tool MUST refuse the write with exit code 2 if a conflicting definition exists in any other user-defined class for that same NAME: an existing .bus/dev/NAME.txt, an existing enabled .bus/dev/NAME.sh (non-Windows) or .bus/dev/NAME.bat or .bus/dev/NAME.ps1 (Windows), an existing .bus/dev/NAME.yml (repo pipeline), or an existing preference pipeline bus-dev.pipeline.NAME. A disabled script (e.g. .sh without execute bit) still reserves the name: creating a pipeline or action with the same NAME MUST fail with exit 2. The diagnostic MUST clearly identify the conflicting artifact path(s) or preference key(s). The tool MUST also refuse if both .bus/dev/NAME.txt and .bus/dev/NAME.yml exist or would exist for the same name as a result of the command. The only allowed multi-definition cases for scripts are .sh plus .bat, .sh plus .ps1, .bat plus .ps1, or all three for the same NAME (platform variants of one script action); that is treated as one script definition for conflict detection. Acceptance criteria: set/generate commands check conflicts before any write; conflict diagnostic lists paths/keys; disabled script reserves name; .sh+.bat for same NAME is not a conflict.

FR-DEV-027 Action management commands. bus dev action set NAME MUST read the full content from stdin until EOF and write it to .bus/dev/NAME.txt; the tool MUST create .bus/dev if missing. Stdin MUST be non-empty; if stdin is empty, treat as usage error (exit code 2) to avoid accidental empty overwrites. bus dev action unset NAME MUST remove .bus/dev/NAME.txt if present and exit 0 if absent. bus dev action list MUST print available repo actions deterministically by name. bus dev action set MUST fail with exit code 2 if a pipeline definition (repo or prefs) or an enabled script for NAME exists for the current platform (same ambiguity rule as FR-DEV-026). Acceptance criteria: action set requires non-empty stdin; unset is idempotent; list is deterministic; ambiguity yields exit 2 before write.

FR-DEV-028 Script management commands. bus dev script set NAME MUST support selecting platform variants via a flag (e.g. --platform=unix, --platform=windows, --platform=windows-ps1, --platform=both), defaulting to the current OS. When writing the Unix variant, write .bus/dev/NAME.sh from stdin and set the executable bit; if setting the bit fails, exit 1. When writing the Windows batch variant, write .bus/dev/NAME.bat from stdin. When writing the Windows PowerShell variant, write .bus/dev/NAME.ps1 from stdin (e.g. --platform=windows-ps1). bus dev script unset NAME MUST remove the selected variant(s) per the same --platform flag and exit 0 even if absent. bus dev script list MUST print available repo scripts deterministically by name and indicate which variants exist (.sh, .bat, .ps1) and whether the Unix variant is executable (enabled vs disabled). A Unix script without the execute bit is disabled and MUST NOT be listed as runnable; the file still exists and MUST be reported by list as disabled. Disabled scripts reserve the name per FR-DEV-026. Acceptance criteria: script set supports –platform including windows-ps1; .sh gets executable bit and failure to chmod yields exit 1; list shows variants and enabled/disabled; unset is idempotent.

FR-DEV-029 Agent-assisted action and script generation. bus dev action generate NAME INSTRUCTION… MUST invoke an embedded bus-dev prompt template that instructs the agent to produce a complete .bus/dev/NAME.txt prompt template, using the same placeholder and context-parameter conventions as internal bus-dev prompts. Bus dev MUST render the generator prompt with the current context parameters, run the agent via the bus-agent library, then write the returned content to .bus/dev/NAME.txt. **bus dev script generate NAME –platform=unix windows windows-ps1 both INSTRUCTION…** MUST use an embedded generator prompt to produce script content for the selected variants, then write the content to the correct file(s) and set the executable bit on .sh. The agent MUST NOT be given permission to directly mutate the repository; bus dev MUST be the component that writes files. Both generator commands MUST enforce the same ambiguity checks as manual set commands and MUST fail before any agent invocation if a conflict exists (exit code 2). Acceptance criteria: generate uses embedded prompt and bus-agent; bus dev performs all file writes; ambiguity check runs before agent call; tests can stub agent and assert file writes only after successful agent output.

FR-DEV-030 Context parameters for repo actions and scripts. Repository-local prompt actions (.bus/dev/NAME.txt) MUST support the same `` placeholder syntax and the same placeholder variable catalog as internal bus-dev prompts. Repository-local scripts MUST receive the same catalog as environment variables; bus dev MUST inject them into the child process environment, overriding any same-named variables for determinism. Environment variable names and values MUST match the catalog (e.g. DOCS_BASE_URL, MAIN_SDD_URL, MODULE_NAME, MODULE_SDD_URL, MODULE_DOCS_URL, E2E_SCRIPT, E2E_SCRIPT_PATH, PLAN_FILE, PLAN_FILE_PATH). bus dev context MUST print the full catalog and current resolved values in a stable, script-friendly format (e.g. one KEY=VALUE line per variable, sorted by key). When not in a Git repository, bus dev context MUST fail with exit code 2 and a clear diagnostic (no alternate behavior such as printing a subset). Acceptance criteria: .txt and scripts use same catalog; context output format and out-of-repo behavior are specified and consistent in SDD and CLI docs.

FR-DEV-031 Help and built-in disclosure. When the user requests help (e.g. bus dev --help or bus dev -h), the tool MUST print to stdout, without running any agent, script action, or Git operation: (1) a concise usage line and global flags; (2) all built-in workflow operations (plan, spec, work, e2e, triage, stage, commit) each with a one-line description of what the operation does; (3) all built-in pipelines (snapshot, refresh, round, cycle, iterate) each with a one-line description and the normalized expanded step sequence that the pipeline executes; (4) a statement that user-defined pipelines, prompt actions, and script actions may exist in the repository or in preferences, and MUST direct the user to discover them via bus dev list (unified catalog when in a repo), bus dev pipeline list, bus dev action list, and bus dev script list. Help MUST be producible without repository resolution so that users can see built-in operations and pipelines from any working directory. Acceptance criteria: bus dev --help lists every built-in operation and pipeline with description and pipeline expansions; no execution; outside a repo help still shows full built-in set; help text directs to list commands for user-defined items.

FR-DEV-032 Unified listing without execution. The implementation MUST provide a command bus dev list that prints a deterministic, parseable listing of every runnable token available in the current context, with no execution of any agent, script action, or Git write. When run from outside a Git repository, bus dev list MUST list only built-in operations and built-in pipelines: for each built-in operation, a one-line description and the fact that it executes as a single step; for each built-in pipeline, a one-line description and the normalized expanded step sequence. When run from inside a Git repository, bus dev list MUST list, in addition: repository-local pipelines (source path and normalized expanded step sequence), repository-local prompt actions (source path .bus/dev/NAME.txt), repository-local script actions (source path and platform variants), and preference pipelines (source key and normalized expanded step sequence). Each entry MUST include token name, type (operation pipeline action prompt action script), a short description or source identifier, and for pipelines the normalized expanded step sequence. Output format MUST be stable, line-oriented or otherwise parseable, and documented. list is a management operation (FR-DEV-023): it does not participate in workflow chaining and does not acquire the working-directory lock. Acceptance criteria: bus dev list outside a repo shows only built-ins with descriptions and pipeline expansions; inside a repo it shows built-ins plus all user-defined pipelines, actions, and scripts with source and expansion where applicable; no agent, script, or git execution; format is deterministic and documented.

NFR-DEV-001 Determinism. Output and exit codes MUST be deterministic for the same inputs and repository state. Acceptance criteria: repeated runs with the same staged set and same repo state yield the same exit code and consistent diagnostics.

NFR-DEV-002 No remote operations. The implementation MUST NOT perform any Git operation that contacts a remote. Acceptance criteria: no code path may call push, pull, fetch, clone, or submodule update/init in a way that touches a remote; tests and code review can verify this.

NFR-DEV-003 Hermetic tests. Tests MUST be hermetic: no network, no real external services, no reliance on a real agent runtime (e.g. Cursor CLI, Codex, Gemini CLI, or Claude CLI) or remote. Acceptance criteria: unit tests and fixture-based tests run in CI without network; agent invocation is tested by stubbing the agent binary in PATH and feeding deterministic NDJSON or output to exercise parsing, filtering, and error handling.

NFR-DEV-004 Cross-platform. Behavior and tests MUST be defined so they can run on Linux and macOS in line with the project’s CI. Acceptance criteria: no OS-specific assumptions are left unspecified; where behavior differs by platform, it is documented.

NFR-DEV-005 Security. The tool MUST NOT execute arbitrary code from repository content except as defined for repository-local script actions (FR-DEV-020). The only permitted external execution is: the configured agent runtime (with embedded prompts or repository-local prompt templates from .bus/dev/<NAME>.txt per FR-DEV-004), local git for the commit and stage workflows (NFR-DEV-002 covers the no-remote constraint), and repository-local scripts under .bus/dev/<NAME>.sh, .bus/dev/<NAME>.bat, or .bus/dev/<NAME>.ps1 when the file meets the executable and path constraints in FR-DEV-020 (resolved path inside repository, no symlink escape). Acceptance criteria: no execution of repository-provided scripts or binaries other than .bus/dev/*.sh, .bus/dev/*.bat, and .bus/dev/*.ps1 under the normative constraints; agent, Git, and repo-local scripts under .bus/dev are the only defined execution boundaries.

NFR-DEV-006 Maintainability. The bus-agent integration and subcommand handlers MUST be testable in isolation with stubbed dependencies (e.g. stub agent in PATH as supported by bus-agent, fixture repositories). Prompts and derivation rules MUST be documented in this SDD so that behavior can be verified without reading source. Acceptance criteria: unit tests cover repo resolution, flag parsing, and subcommand flow with bus-agent stubbed or stub executable in PATH; design docs are the single source of truth for derivation and command semantics.

NFR-DEV-007 No user configuration outside project. Bus-dev MUST NOT edit any user configuration outside the project working directory (the repository or init target directory). Bus-dev MAY set environment variables and command-line options for the child agent process; it MUST NOT create, modify, or delete files outside the project working directory for configuration or instruction discovery. This aligns with the same constraint in bus-agent (NFR-AGT-005). Acceptance criteria: no code path writes to paths outside the effective project workdir for config or instruction setup; tests verify workdir-scoped behavior.

NFR-DEV-008 Additive-only repo file changes. When bus-dev creates or modifies files inside the repository to enable instruction discovery or runtime adapter support (e.g. Gemini repo-local files, Cursor rules), changes MUST be additive only: existing user content MUST NOT be removed or rewritten. The only permitted exception is the legacy Cursor rule file at .cursor/rules/<MODULE_NAME>.mdc (module name per FR-DEV-003), which MAY be replaced or migrated as part of standardizing on AGENTS.md (see FR-DEV-008a). Any other repo-local file creation or merge MUST use append-only Bus-owned blocks with explicit markers. This aligns with bus-agent NFR-AGT-006 and FR-AGT-014. Acceptance criteria: repo-local file creation or merge uses append-only Bus-owned blocks with clear markers; existing user content is never deleted or overwritten except for the documented .cursor/rules/.mdc exception; behavior is testable.

FR-DEV-013 Triage subcommand behavior. The bus dev triage subcommand MUST keep development-state documentation accurate, compact, and evidence-based by reconciling what users can actually do (as proven by tests) with what is planned next (PLAN.md) and what depends on what. Triage is a generic project-management style action usable in principle for any software project; this SDD defines BusDK’s concrete initial behavior and project-type detection so the implementation is unambiguous. The command MUST update documentation files only — never source code or test files. It MUST prioritize verified capabilities (those directly covered by at least one unit or e2e test) over code presence; implemented-but-untested behavior MUST be treated as unverified and MUST NOT increase readiness or completeness percentages. Output produced by triage MUST be end-user-value oriented and “journey readiness” oriented: readiness text MUST be compact and specifically about the relevant user story or journey, not a generic status essay.

The overall development-status page MUST be grouped by documented use cases. It MUST include a short use-case list with internal links, a per-use-case table of the modules that use case needs, and a list of orphan modules (modules that do not map to any documented use case) unless a use case can be inferred from the module’s value promise. If inference suggests a missing use case document, triage MAY add the missing use case documentation page in the docs repo in the appropriate location only if it can do so safely and compactly without inventing requirements. Development-status tables MUST use compact module labels without the “bus-“ prefix. Any column that mentions a Bus module MUST include an inline link to that module’s end-user docs page (not plain text).

BusDK documentation targets. Triage is responsible for updating: (1) the overall development-status page at ./docs/implementation/development-status.md (path relative to the docs content root); (2) each module end-user docs page at ./docs/modules/bus-<NAME>.md for every bus-<NAME>.md in the docs repo. The per-module “Development state” section that triage writes MUST follow this structure: a one-sentence value promise; a single completeness percent from the discrete scale 0, 10, 20, …, 95, 100 with a short rationale based on verified coverage and usefulness for published workflows; a compact “Use cases” line that links to the relevant use case sections or pages and avoids duplication; then Current (limited strictly to test-proven behavior, with inline references to the proving test file names), Planned next, Blockers, Depends on, and Used by. “Verified” means directly covered by at least one unit or e2e test; implemented-but-untested behavior MUST NOT increase readiness or completeness.

Project context and detection. Triage MUST support three BusDK project contexts with deterministic auto-detection. Bus-dev embeds one of three triage prompt variants and selects it based on the detected context (prompts are embedded per FR-DEV-004; bus-dev does not load them from disk). Docs project: detected when the effective working directory (or a known docs root) contains both ./docs/modules and ./docs/sdd. Scope: triage updates the overall development-status page plus all module pages under ./docs/modules. Super-project: detected when the repository root contains .gitmodules and a docs submodule directory (the docs submodule). Scope: triage updates the docs submodule’s files (same targets as docs project) and MAY read module repos via the super-project’s submodule working tree. Module repo: detected when the repository root’s base name matches bus-* and a sibling directory ../docs exists and contains the docs structure (e.g. docs/modules, docs/sdd or equivalent). Scope: triage updates only that module’s page (./docs/modules/bus-<NAME>.md in the sibling docs repo) plus the overall development-status page in that sibling docs repo; it MUST NOT rewrite other modules’ pages in this mode. When detection is ambiguous (e.g. both docs layout and super-project layout present, or neither matches), the tool MUST emit a clear diagnostic to stderr, exit with code 2, and include guidance on where to run the command (e.g. run from docs repo root, super-project root, or module repo root). Acceptance criteria: triage updates only the allowed documentation files per mode; in docs mode many module pages and the overall page are updated; in module mode only one module page and the overall page are updated; triage never touches code or test files; project-type detection, path resolution, and prompt selection are deterministic and testable; ambiguous detection yields exit 2 and clear guidance.

Bus-dev uses the same instruction model as bus-agent for its developer workflows (commit, work, spec, e2e, triage): AGENTS.md at the repository root is the canonical, vendor-neutral instruction source; per-runtime adapters (Codex, Cursor, Gemini CLI, Claude Code) follow the contract defined in the bus-agent SDD (“Project instructions (AGENTS.md) and per-runtime adapters”), so the agent instruction contract is shared across modules and can be implemented and tested consistently.

System Architecture

Bus Dev is a thin CLI that delegates to testable packages. The main entrypoint is Run(args, workdir, stdout, stderr) int; the main package calls it and exits with its return value. No os.Exit is used outside main, so behavior is testable without process exit.

High-level components:

  • CLI layer. Parses global flags (including BusDK-standard -C, -o, -v, -q, --help, --version) and the bus dev operation token(s). When --help is requested, the CLI prints built-in operations and pipelines with descriptions and expansions and directs to list commands (FR-DEV-031) without running any handler. When the operation is list, the CLI prints the unified catalog of runnable tokens (built-in plus repo/prefs when in a repo) without execution (FR-DEV-032). Operation tokens (base operations, pipeline names, or user-defined action names) are resolved per FR-DEV-021 and expanded to a flattened sequence of runnable steps (base operations and user-defined actions) before any handler runs (FR-DEV-015); expansion is part of argument parsing and handler dispatch. After expansion, the CLI normalizes the final sequence by merging repeated step names in first-appearance order (FR-DEV-015a). The CLI resolves the effective working directory and resolves the agent runtime selection: per-command flag overrides session-stored preference; when neither is set, the implementation delegates to bus-agent for the automatic default from available runtimes in bus-agent’s order (alphabetic when no user order is configured; user can configure order and enable/disable per bus-agent FR-AGT-005a) (FR-DEV-005b, FR-DEV-005d). For subcommands that operate on a directory (init, commit, stage, plan, work, spec, e2e, triage), the CLI acquires an exclusive working-directory lock on the effective operation directory before running any handler and releases it when the process exits; a concurrent invocation for the same directory blocks until the lock is available (FR-DEV-012). When an agent step runs, the tool prints to stderr which agent and model are in use (FR-DEV-005e). After normalization, when multiple steps result, the CLI runs them in order, delegating to each handler in sequence and stopping on first failure (FR-DEV-001a); otherwise it delegates to the single step’s handler.

  • Repository and module resolution. A small package (or internal function set) that, given a workdir, detects whether it is inside a Git repository, finds the repository root, and derives the module name when needed (e.g. for bus dev plan, bus dev spec, and bus dev e2e). The module name is the base name of the repository root directory (FR-DEV-003). This layer does not perform any Git write operations; it only reads repository metadata.

  • bus-agent library dependency. Bus Dev does not implement an agent runner itself. It depends on the bus-agent Go library for runtime selection, prompt-template rendering, command construction, timeout enforcement, and output capture or streaming. Subcommand handlers (plan, work, spec, e2e) pass workflow-specific prompt content and variables to bus-agent, which invokes the configured external agent (Cursor CLI, Codex, Gemini CLI, or Claude CLI) in the given workdir. Bus Dev supports both AGENTS.md and Gemini context in parallel: workflow contracts do not change based on which backend is selected; the agent reads the repo’s Cursor rules and, when Gemini CLI is selected, the repo’s repository-local Gemini context as documented in the Gemini CLI integration subsection. In headless or script mode, bus-dev invokes bus-agent in a way that keeps workflows non-interactive and prevents prohibited actions (especially network operations); bus-agent’s script-safe contract supports this.

  • Subcommand handlers. One logical component per operation: init, commit, stage, plan, work, spec, e2e, triage. Each handler receives parsed flags, the resolved workdir (and repo root and module name when relevant), and stdout/stderr writers, and returns an exit code. Pipeline and action tokens are expanded to a sequence of runnable steps before any handler runs (FR-DEV-015), then normalized (FR-DEV-015a); the CLI invokes handlers in the normalized order and stops on first non-zero exit (FR-DEV-001a). Triage is one of the accepted pipeline-chained actions. User-defined actions (repository-local prompt from .bus/dev/<NAME>.txt, repository-local script from .bus/dev/<NAME>.sh, .bus/dev/<NAME>.bat, or .bus/dev/<NAME>.ps1) are dispatched by dedicated handling that loads the template or runs the script with the prompt-variable catalog as environment (FR-DEV-004, FR-DEV-020). Init resolves the target directory (current directory by default), creates missing scaffold files (repository-root AGENTS.md and root Makefile from an embedded sample if missing), and by default does not invoke agent workflows; it invokes plan/spec/work/e2e only when those operations (or pipelines that expand only to them) are explicitly present after init; if expansion would introduce stage, commit, or triage after init, the tool fails with usage error before running any subsequent operation (FR-DEV-018). The plan handler derives documentation URLs, reads existing PLAN.md when present, drives the embedded planning prompt via bus-agent to re-validate prior items and discover new missing work, prunes completed checked items, and enforces that only repository-root PLAN.md is written. The spec handler creates repository-root AGENTS.md from online SDD and user docs when missing, then refines it (or refines it in place when it exists); when .cursor/rules/<MODULE_NAME>.mdc exists, the handler includes its content in the refinement context and removes that file after the agent has merged it into AGENTS.md (FR-DEV-008, FR-DEV-008a). The work handler checks off completed plan items but does not delete checked items. The e2e handler validates checked plan items against e2e coverage and does not delete checked items. The stage handler invokes the agent with the embedded stage prompt (FR-DEV-006a); after the agent completes successfully, the handler stages all remaining changes at the repository root (e.g. git add .). Commit may use Git only for read operations plus git commit with already-staged content; it must not stage, amend, or touch remotes. The triage handler detects project context (docs repo, super-project, or module repo per FR-DEV-013), selects the corresponding embedded triage prompt variant, resolves triage-specific path variables (DOCS_REPO_ROOT, DEV_STATUS_PATH, etc.), and invokes the agent to update only the allowed documentation files for that context.

  • Embedded prompts. Prompts for commit, plan, work, spec, stage, triage, and (when applicable) e2e are compiled into the binary as string constants or templates. Templates use {{VARIABLE}} placeholders; variables are derived at runtime from the repository resolution layer and the docs base URL (see Prompt variable catalog and FR-DEV-004a). Bus-dev uses the bus-agent library for template rendering (or a contract-compatible implementation) so that rendering is deterministic and fails fast on missing or unresolved placeholders before any agent invocation. Built-in operations use only embedded prompts; repository-local prompt actions are loaded from .bus/dev/<NAME>.txt per FR-DEV-004 when a token resolves to them.

  • Log formatting (optional). The NDJSON-to-text style formatter (equivalent to the provided format-cursor-log / ndjson-to-text behavior) may be provided by the bus-agent library or implemented locally when bus-dev streams agent output to stdout. The SDD treats this as the desired direction; whether it is a separate subcommand or only internal is left as an implementation detail, with the initial scope kept minimal and deterministic.

Data flow: user invokes bus dev <subcommand>; dispatcher runs bus-dev <subcommand> [args]; CLI parses args and workdir; repo resolution checks Git repo and optionally module name; subcommand handler runs (Git for commit; agent then Git for stage; agent for plan/work/spec/e2e, or scaffold/detect for e2e). When an agent is used, the handler supplies embedded prompt template and variables to the bus-agent library, which renders the prompt and invokes the selected external agent; agent output is captured or streamed to stdout so it can be piped. Repository-local script action stdout is passed through to process stdout. All action output (built-in agent steps, repository-local prompt actions, repository-local script actions) goes to stdout; diagnostics, progress, and runtime/model disclosure go to stderr; management commands write deterministic results to stdout.

Key Decisions

KD-DEV-001 Git exception scoped to bus-dev. BusDK’s NG-001 (no Git execution) is relaxed only for the bus-dev module, and only for local, non-remote, non-rewriting operations, to support developer workflows without embedding Git logic in every other module.

KD-DEV-002 Prompts embedded in binary. Prompts are not loaded from the repository or from external files so that behavior is versioned with the binary and consistent across all module repos that use the same bus-dev version.

KD-DEV-003 Agent execution via bus-agent. The tool does not implement its own agent runner; it depends on the bus-agent Go library to invoke external agent runtimes. This keeps bus-dev focused on workflow semantics and prompts while bus-agent owns the runner abstraction, backends, and execution contract.

KD-DEV-004 Modular agent backends from bus-agent. Cursor CLI, Codex, Gemini CLI, and Claude CLI are supported as selectable backends through the bus-agent library. Bus-dev passes the user’s selection (or automatic default) to bus-agent, which provides the backend abstraction and preference order (FR-DEV-005d). Adding or changing runtimes is done in bus-agent; bus-dev’s subcommand semantics and embedded prompts do not change.

KD-DEV-005 Thin CLI, testable core. The CLI parses arguments and delegates to packages that take workdir and I/O writers; Run(...) int allows full unit and integration tests without spawning processes or calling os.Exit.

KD-DEV-006 E2E test convention. Bus module e2e tests are Bash scripts under tests/ named e2e_bus_<name>.sh, running the compiled binary. Detection of missing tests and scaffold content are driven by the module’s SDD and end-user documentation; agent instructions (e.g. AGENTS.md) define language and module-specific testing expectations. Modules also implement full unit tests in addition to e2e.

KD-DEV-007 Single-instance per directory via file lock. To prevent two bus dev invocations from running concurrently on the same working directory (which would risk conflicting edits to PLAN.md, AGENTS.md, staging, and agent context), the tool acquires an exclusive lock on the effective operation directory and blocks a second invocation until the first exits. Locking is implemented with a lock file and exclusive file lock (e.g. flock) so behavior is deterministic and script-friendly: the next command waits rather than failing. The set and context subcommands and read-only pipeline commands (pipeline list, pipeline preview) are excluded because they write preferences only or read and print deterministic results without touching runnable workflow state.

Component Design and Interfaces

Interface IF-DEV-001 (dispatcher). The bus dispatcher invokes the bus-dev binary with the remaining arguments after dev: global flags and one or more operation tokens. Each operation token MAY be a base operation name (plan, spec, work, e2e, triage, stage, commit), a pipeline name (built-in or user-defined), or a user-defined action name (repository-local prompt or script). The binary MUST expand all pipeline tokens into a flattened sequence of runnable steps (base operations and user-defined actions) before running any handler (FR-DEV-015); after expansion, it MUST normalize the final sequence by merging repeated step names in first-appearance order (FR-DEV-015a), then execute that normalized sequence with stop-on-first-failure as in FR-DEV-001a. For bus dev init [DIR], the first positional after flags is init; the next positional is optional and, when present and not one of plan|spec|work|e2e and not a known pipeline or operation name, is treated as the target directory; any following operation tokens (base ops or pipelines) MUST expand to base operations from {plan, spec, work, e2e} only (FR-DEV-018). For a single operation, e.g. bus dev plan, bus dev work, bus dev round, the binary expands and normalizes tokens then runs the resulting sequence. For multiple operations — pipeline chaining — (e.g. bus dev plan spec work, bus dev round refresh, or bus dev plan work stage commit), the binary receives those tokens in the order given, expands and normalizes them to a single runnable sequence, and executes that sequence (FR-DEV-001a). The accepted steps for pipeline chaining are base operations (plan, spec, work, e2e, triage, stage, commit) and user-defined actions (repository-local prompt actions, repository-local script actions). Standard global flags (-C, -o, -v, -q, --help, --version, etc.) follow BusDK CLI conventions. An agent selection flag (e.g. --agent <runtime>) and the session-stored preference (e.g. BUS_DEV_AGENT) are resolved as per FR-DEV-005b; see traceability links.

Interface IF-DEV-002 (Run entrypoint). The program exposes a single entrypoint Run(args []string, workdir string, stdout, stderr io.Writer) int. main passes os.Args[1:], the effective working directory (from -C or current process), os.Stdout, and os.Stderr, and exits with the returned code. No other package calls os.Exit.

Interface IF-DEV-003 (bus-agent integration). Bus-dev invokes the bus-agent library’s runner interface (see bus-agent IF-AGT-001) with: selected runtime (or leave resolution to bus-agent), prompt text (after rendering the embedded template with the prompt variable catalog), workdir, timeout, and output mode (capture/stream). Bus-agent returns an exit code and streams or captures output per its contract. Bus-dev does not implement this interface; it is a caller of the bus-agent package. Tests may stub the agent executable in PATH (as supported by bus-agent) or mock the bus-agent dependency to verify bus-dev’s workflow and prompt wiring without running a real agent.

Interface IF-DEV-004 (repo resolution). Given a directory path, the resolver returns: whether the path is inside a Git repository, the repository root path, and (when requested) the module name. For repository-scoped operations, module name MUST be the base name of the repository root directory (see FR-DEV-003). If not inside a repo, the resolver returns an error suitable for a clear user-facing message. init target naming is deterministic but does not require repo resolution; it uses the base name of the resolved target directory.

Interface IF-DEV-005 (working-directory lock). For subcommands that operate on a directory (init, commit, stage, plan, work, spec, e2e, triage), the implementation MUST acquire an exclusive lock keyed by the effective operation directory before performing any subcommand work. The lock MUST be implemented so that only one process holds it at a time per directory; a second process attempting to acquire the lock for the same directory MUST block until the first releases it. The lock MUST be released when the process exits (normal exit, non-zero exit, or signal). Lock scope: for bus dev init [DIR], the resolved init target directory; for commit, stage, plan, work, spec, e2e, and triage, the repository root (or, for triage, the docs repo root, super-project root, or module repo root per FR-DEV-013). The set and context subcommands and read-only pipeline commands (pipeline list, pipeline preview) do not acquire the lock. If the lock cannot be acquired (e.g. the directory is not writable or the lock file cannot be created), the tool MUST exit with a clear diagnostic and a non-zero exit code. See FR-DEV-012 and Data Design (lock file).

Pipelines and expansion. Built-in pipelines are defined as part of the bus-dev binary (not loaded from disk). They are stable, deterministic, and versioned with bus-dev. Users MAY override built-in pipeline names via preferences (keys bus-dev.pipeline.<name>); built-in operation names (plan, spec, work, e2e, triage, stage, commit) are reserved and MUST NOT be overridable — user-defined pipelines MUST NOT use those names. To force the built-in pipeline when a user-defined pipeline overrides that name, use the @ prefix (e.g. @round). The table below shows raw expansion before final normalization:

Built-in pipeline Default expansion
snapshot stage commit
refresh spec plan stage commit
round work e2e stage commit
cycle spec plan work e2e stage commit
iterate cycle refresh → spec plan work e2e stage commit spec plan stage commit (after full expansion)

Token resolution and @ semantics are defined in FR-DEV-014 and FR-DEV-021. Expansion MUST complete before any handler runs; the result MUST be a flat list of runnable steps (base operations and user-defined actions). After expansion, final-sequence normalization (FR-DEV-015a) merges repeated step names so each appears once in first-appearance order; this normalized sequence is what runnable invocations execute and what pipeline preview prints (FR-DEV-015b). Recursion detection and expansion limits are in FR-DEV-016 and FR-DEV-019. User-defined pipeline storage (preference and repository-local) and name grammar are in FR-DEV-017 and FR-DEV-019.

Repository-local extension. Repository-local definitions live under <repo-root>/.bus/dev/. Discovery is restricted to the resolved repository root; the resolved path for any loaded file MUST remain inside the repository (symlinks that point outside the repository are refused). Repository-local prompt actions are loaded only from .bus/dev/<NAME>.txt (FR-DEV-004, FR-DEV-004a). Repository-local pipelines are defined by .bus/dev/<NAME>.yml with strict YAML sequence-of-strings format (FR-DEV-019). Repository-local script actions are .bus/dev/<NAME>.sh (non-Windows) and .bus/dev/<NAME>.bat or .bus/dev/<NAME>.ps1 (Windows; when both .bat and .ps1 exist, .ps1 is used), with executable and platform rules in FR-DEV-020. The prompt variable catalog (table above) is used for both prompt template substitution and for environment variable injection into script actions; script actions receive each catalog variable as an environment variable (same key and derived value), with injected values overriding any existing same-named env vars (FR-DEV-020). Token resolution precedence and ambiguity rules are normative in FR-DEV-021; ambiguity and parse errors MUST fail before any agent invocation or git write. The context subcommand (e.g. bus dev context, FR-DEV-022) prints the full catalog and resolved values in KEY=VALUE form, sorted by KEY, for use by prompt and script authors; it requires a Git repository and exits 2 when not in a repo.

Prompt variable catalog. The following variables are available for substitution in embedded prompt templates (FR-DEV-004, FR-DEV-004a). All are derived at runtime from repository resolution, the configured docs base URL (FR-DEV-004b), or the operation context. Each prompt template documents which of these it uses; substitution MUST be complete before agent invocation (FR-DEV-004a).

Variable Description Source / derivation
DOCS_BASE_URL Base URL for documentation; used to build all doc links. BUS_DEV_DOCS_BASE_URL env (default https://docs.busdk.com), trailing slash trimmed.
MAIN_SDD_URL URL of the main BusDK SDD index. <DOCS_BASE_URL>/sdd
MODULE_NAME Current module name (e.g. bus-accounts). Base name of repository root directory (FR-DEV-003).
MODULE_SUFFIX Module name with bus- prefix stripped (e.g. accounts). Derived from MODULE_NAME.
MODULE_SDD_URL URL of this module’s SDD page. <DOCS_BASE_URL>/sdd/<MODULE_NAME>
MODULE_DOCS_URL URL of this module’s end-user docs page. <DOCS_BASE_URL>/modules/<MODULE_NAME>
E2E_SCRIPT E2E script filename (e.g. e2e.sh). e2e_bus_<MODULE_SUFFIX>.sh
E2E_SCRIPT_PATH Path to the E2E script under the repo (e.g. tests/e2e.sh). tests/<E2E_SCRIPT>
E2E_WORKSPACE_DIR Absolute path to the repository root used as e2e workspace. Resolved repo root.
AGENTS_FILE_PATH Path to the repository-root AGENTS.md (e.g. <repo-root>/AGENTS.md). Repository root + AGENTS.md. Spec-subcommand prompts only.
PLAN_FILE Repository-local planning file used by plan/work/e2e (PLAN.md). Literal value PLAN.md at repository root.
PLAN_FILE_PATH Absolute path to PLAN.md in the current repository. <repo-root>/PLAN.md
DOCS_REPO_ROOT Absolute path to the root of the docs repository (where triage writes). Resolved from detected project context (docs repo root, docs submodule root, or sibling ../docs in module mode). Triage prompts only.
DOCS_CONTENT_ROOT Root path under which docs content lives (e.g. docs/ or . in docs repo). Derived from DOCS_REPO_ROOT and repo layout. Triage prompts only.
DOCS_MODULES_DIR Path to the module end-user docs directory (e.g. docs/modules or modules). <DOCS_CONTENT_ROOT>/modules or equivalent. Triage prompts only.
DOCS_SDD_DIR Path to the SDD directory in the docs repo (e.g. docs/sdd or sdd). Used for context detection and triage prompts. Triage prompts only.
DEV_STATUS_PATH Path to the overall development-status page. <DOCS_CONTENT_ROOT>/implementation/development-status.md. Triage prompts only.
DOCS_REPO_SIBLING_PATH In module mode, absolute path to the sibling docs repo. Resolved as ../docs from the module repo root when context is module repo. Required only for the triage module-mode prompt variant.

AGENTS_FILE_PATH is used only in the spec subcommand’s prompt context. PLAN_FILE and PLAN_FILE_PATH are used by plan, and are also available to work and e2e so those prompts can read PLAN.md when present. Triage uses the triage-specific variables above: the docs-project triage prompt requires DOCS_REPO_ROOT, DOCS_CONTENT_ROOT, DOCS_MODULES_DIR, DOCS_SDD_DIR, and DEV_STATUS_PATH; the super-project triage prompt requires the same set (with paths resolved into the docs submodule). The module-mode triage prompt requires those variables plus DOCS_REPO_SIBLING_PATH (and typically MODULE_NAME). The implementation MUST supply the expected placeholder set per template so that missing or unresolved placeholders are detected before agent invocation (FR-DEV-004a).

Supported agent runtimes and installation references. The set of supported runtimes (Cursor CLI, Codex, Gemini CLI, Claude CLI), their executable detection, and the canonical installation URLs for diagnostics are defined in the bus-agent SDD (Component Design: Runtime installation references; FR-AGT-003, FR-AGT-010). Bus-dev MUST use the bus-agent library for execution and therefore uses the same runtimes and installation references; bus-dev does not duplicate this table or implement its own backends. When the selected agent is not installed or not in PATH, bus-dev surfaces the failure and installation reference as provided by bus-agent.

Agent selection configuration. Resolution order when bus-dev invokes the bus-agent library is (FR-AGT-005, FR-DEV-005b): (1) Per-command override: --agent cursor|codex|codex:local|gemini|claude for that invocation only. (2) BUS_DEV_AGENT: bus-dev-only session default; when set, used for every bus dev command in that session until unset or overridden by --agent. (3) BUS_AGENT: shared session default; when set and BUS_DEV_AGENT is not, used for bus-dev and for bus agent CLI. (4) step selector preference: bus-dev.agent_for.<selector> where selector is token or @token; built-in step selection checks @token first, then token; user-defined step selection checks token. (5) bus-dev persistent preference: e.g. bus-dev.agent read via bus-preferences; affects only bus-dev’s agent use (e.g. plan, work, spec, e2e, triage). (6) bus-agent persistent preference: bus-agent.runtime read via bus-preferences. (7) First available runtime in the effective order (user-configured order or alphabetical by runtime ID; see bus-agent FR-AGT-005a). At any step, if the indicated runtime is disabled by user configuration, bus-agent prints a warning to stderr and resolution continues with the next source (FR-AGT-005b). Bus-dev MUST invoke bus-agent with caller context so bus-agent consults BUS_DEV_AGENT and bus-dev preferences in the correct place. Bus-dev MUST pass through to bus-agent any user-configured agent order and enable/disable settings. Invalid runtime names (e.g. --agent unknown) MUST produce a clear usage error and exit 2. If no agent is available when resolution reaches step (7), the tool MUST exit with a clear diagnostic and direct the user to install or enable at least one supported agent (see Error Handling). The executable name(s) used to detect each runtime are defined in the bus-agent SDD (IF-AGT-002). At the start of each agent step (plan, work, spec, e2e, triage), the tool MUST print to stderr which internal agent and model are in use (FR-DEV-005e).

Run-config resolution (model, model reasoning effort, model verbosity, timeout, output-format). For these run-config values, bus-dev MUST use the same resolution logic and order as for agent, so that each option has a consistent precedence and bus-dev never writes outside the bus-dev namespace. When bus-dev invokes the bus-agent library, it MUST resolve run-config in this order: (1) per-command override (if implemented as flags); (2) bus-dev session environment (BUS_DEV_MODEL, BUS_DEV_MODEL_REASONING_EFFORT, BUS_DEV_MODEL_VERBOSITY, BUS_DEV_TIMEOUT, BUS_DEV_OUTPUT_FORMAT); (3) selector-specific model preferences (bus-dev.model_for.<selector>, with built-in steps using @token then token and user-defined steps using token); (4) model-scoped preferences (bus-dev.model_reasoning_effort_for_model.<model>, bus-dev.model_verbosity_for_model.<model>, using the resolved model value); (5) selector-specific effort/verbosity (bus-dev.model_reasoning_effort_for.<selector>, bus-dev.model_verbosity_for.<selector> with the same selector semantics); (6) bus-dev persistent defaults (bus-dev.model, bus-dev.model_reasoning_effort, bus-dev.model_verbosity, bus-dev.timeout, bus-dev.output_format); (7) bus-agent persistent preferences as fallback (bus-agent.model, bus-agent.model_reasoning_effort, bus-agent.model_verbosity, bus-agent.timeout, bus-agent.output_format); (8) bus-agent documented defaults. Step environment overrides resolve from bus-dev.env_for.<selector>.<env> with selector precedence @token then token for built-in steps and token only for user-defined steps. Step allowed-directory overrides resolve from bus-dev.allowed_dirs_for.<selector> plus module-scoped bus-dev.allowed_dirs_for_module.<module>.<selector> layered after global values when repository module name equals <module>, with the same selector precedence rules. The implementation MUST canonicalize resolved directories relative to repository root before invoking a backend. The implementation MUST pass directory overrides only to backends that support explicit allowed directories; for unsupported runtimes, the step continues and the tool reports that allowed dirs are ignored for that runtime. The implementation MUST read preferences via the bus-preferences library only; when the user runs bus dev set model, bus dev set model-reasoning-effort, bus dev set model-verbosity, bus dev set output-format, bus dev set timeout, bus dev set agent-for, bus dev set model-for, bus dev set model-reasoning-effort-for, bus dev set model-verbosity-for, bus dev set model-reasoning-effort-for-model, bus dev set model-verbosity-for-model, bus dev set env-for, bus dev set allow-dirs-for, or bus dev set allow-dirs-for-module, the implementation MUST write only the corresponding bus-dev.* key and MUST NOT write to any key under another namespace (e.g. bus-agent.*). This ensures that configuration set through bus-dev affects only bus-dev’s defaults and never changes the bus-agent namespace.

Gemini CLI integration. When the user selects Gemini CLI as the agent runtime, Bus Dev supports the same workflow as for Cursor (plan, work, spec, e2e) without changing core workflow contracts. In Gemini terms, project-level agent-instruction configuration means the use of project-level context and settings files that play a role analogous to Cursor’s .cursor/rules/*.mdc and project settings: the Gemini CLI loads context from markdown files (by default GEMINI.md) and respects a project .gemini/settings.json and .geminiignore. Instruction loading for Gemini follows the bus-agent per-runtime adapter: AGENTS.md is the canonical instruction source; repo-local .gemini/settings.json (e.g. context.fileName to prefer or include AGENTS.md) and .geminiignore are used as documented there, with additive-only merges and Bus-owned markers when bus-dev or bus-agent touches those files. Bus Dev documents and, where applicable, creates or updates only the repository-local layer so that both AGENTS.md and Gemini context can be used in parallel for the same repo.

Hard boundary: Bus Dev MUST NOT modify any user-global Gemini configuration or memory. That includes ~/.gemini/GEMINI.md, any other file under the user’s home Gemini config directory, and any global memory managed via interactive Gemini CLI commands such as /memory add. Bus Dev may read global context only if the Gemini CLI loads it by default when the agent is invoked; Bus Dev itself does not write, create, or edit anything outside the current repository (NG-DEV-005, FR-DEV-005c). Developers who want global Gemini memory or user-level Gemini config must manage it manually outside Bus Dev.

Repository-local files that Bus Dev may create, edit, or rely on are: (1) a repo-root GEMINI.md that supplies project-level instructions and context for the agent; (2) optional directory-scoped GEMINI.md files for subdirectory-specific context; (3) .gemini/settings.json for context discovery, ignore behavior, tool policy, and MCP configuration; and (4) .geminiignore for excluding files from context ingestion. The intended precedence and scoping model when the Gemini CLI runs is: global user context (loaded by the CLI from ~/.gemini/ and any user-managed memory) is applied first; then repository context from the repo-root GEMINI.md and .gemini/settings.json; then directory context from any GEMINI.md in the current or ancestor directories. Bus Dev’s responsibility is limited to documenting and managing the repository-local layer only; it does not configure or alter the global layer.

Example narrative of repo-local files and responsibilities: The repo-root GEMINI.md typically holds project-wide instructions, conventions, and links to design docs so that when bus dev work or bus dev spec invokes the Gemini CLI, the agent receives consistent project context. Directory-level GEMINI.md files (e.g. under internal/ or docs/) can narrow scope for that subtree. The .gemini/settings.json file controls how the Gemini CLI discovers context files, which paths to ignore, tool usage policy, and MCP server configuration for that repo. The .geminiignore file lists patterns of files or directories to exclude from context ingestion, analogous to .cursorignore for Cursor. Bus Dev may scaffold or update these files during bus dev init or when documenting the layout; it never touches ~/.gemini/ or global memory.

Command Surface

Invocation. The CLI is invoked as bus dev [global-flags] <operation> [operation ...]. Operations are: init, commit, stage, plan, spec, work, e2e, triage, each, retry, set, context, list, pipeline, action, and script. The management operations pipeline, action, script, context, and list do not participate in workflow chaining (FR-DEV-023). Global --help (e.g. bus dev --help) MUST show every built-in operation and pipeline with descriptions and pipeline expansions and direct the user to bus dev list, bus dev pipeline list, bus dev action list, and bus dev script list for user-defined items (FR-DEV-031). For workflow chaining, each token MAY be a base operation name, a pipeline name (built-in or repository-local or preference), or a user-defined action name (repository-local prompt or script); tokens are resolved per FR-DEV-021 and pipelines expand into a sequence of runnable steps before execution (see Pipelines and expansion and Repository-local extension in Component Design). After expansion, bus-dev normalizes the final sequence by merging repeated step names in first-appearance order (FR-DEV-015a); runnable invocation semantics always use this normalized sequence. Only one invocation that operates on a given directory (init, commit, stage, plan, work, spec, e2e, triage, retry) runs at a time for that directory; a second invocation for the same directory waits until the first exits (see FR-DEV-012). Pipeline chaining is supported: the base operations plan, spec, work, e2e, triage, stage, and commit — or pipeline names that expand to them — MAY be combined in one invocation in any order and any combination (e.g. bus dev plan work stage commit, bus dev round, or bus dev round refresh); when multiple tokens are given, they are expanded, normalized, and run in order, one at a time, with the run stopping on first failure (see FR-DEV-001a, FR-DEV-015, FR-DEV-015a). stage invokes the agent to prepare the working tree (identify and ignore or remove unintended files), then stages remaining changes so that commit can be used in the same invocation without a manual staging step. init accepts an optional target directory and may be followed by zero or more workflow tokens that expand only to {plan, spec, work, e2e}: bus dev init [DIR] [plan|spec|work|e2e|pipeline ...] (FR-DEV-018). each is a superproject-only dispatcher operation (FR-DEV-001b). retry is a standalone workflow operation; it takes --on-fail and optional --attempts, runs the target workflow and fallback sequence in a bounded retry loop, and is not mixed with other workflow tokens at the top level. set is a preference-setting operation (see below) and does not participate in the working-directory lock. Global flags apply as per BusDK CLI conventions. The agent runtime is selected by the resolution order in Agent selection configuration above: --agent, then BUS_DEV_AGENT, then BUS_AGENT, then bus-dev and bus-agent preferences, then first available enabled agent.

bus dev set agent <runtime>

Intent: Set the bus-dev persistent default agent (e.g. bus-dev.agent) via the bus-preferences Go library so that every bus dev command in future sessions uses that runtime when no --agent, BUS_DEV_AGENT, or BUS_AGENT is set. The implementation MUST use the bus-preferences library to write the preference; it MUST NOT shell out to bus preferences. <runtime> MUST be one of cursor, codex, codex:local, gemini, or claude. Invalid key or value yields exit 2. Success MAY be confirmed by a short message to stderr or by exiting 0 with no required stdout output.

bus dev set model <value> — Set the bus-dev persistent default model (bus-dev.model) via the bus-preferences library. Only the bus-dev namespace is written; no other namespace (e.g. bus-agent) is modified. Invalid value yields exit 2.

bus dev set model-reasoning-effort <minimal|low|medium|high|xhigh> — Set the bus-dev persistent default model reasoning effort (bus-dev.model_reasoning_effort) via the bus-preferences library. Only the bus-dev namespace is written. Invalid value yields exit 2.

bus dev set model-verbosity <low|medium|high> — Set the bus-dev persistent default model verbosity (bus-dev.model_verbosity) via the bus-preferences library. Only the bus-dev namespace is written. Invalid value yields exit 2.

bus dev set output-format <ndjson|text> — Set the bus-dev persistent default output format (bus-dev.output_format) via the bus-preferences library. Valid values: ndjson, text. Only the bus-dev namespace is written. Invalid value yields exit 2.

bus dev set timeout <duration> — Set the bus-dev persistent default timeout (bus-dev.timeout) via the bus-preferences library (e.g. 60m). Only the bus-dev namespace is written. Invalid value yields exit 2.

bus dev set agent-for <token|@token> <runtime> — Set a per-step runtime preference in bus-dev.agent_for.<selector> via bus-preferences. token selector applies to both built-in and user-defined steps named token; @token applies to built-in steps named token only. Valid runtime values are cursor, codex, codex:local, gemini, claude. Invalid selector or runtime yields exit 2.

bus dev set model-for <token|@token> <value> — Set a per-step model preference in bus-dev.model_for.<selector> via bus-preferences, with the same selector semantics as agent-for. Invalid selector yields exit 2.

bus dev set model-reasoning-effort-for <token|@token> <value> — Set a per-step model reasoning effort preference in bus-dev.model_reasoning_effort_for.<selector> via bus-preferences, with the same selector semantics as agent-for. Invalid selector or value yields exit 2.

bus dev set model-verbosity-for <token|@token> <value> — Set a per-step model verbosity preference in bus-dev.model_verbosity_for.<selector> via bus-preferences, with the same selector semantics as agent-for. Invalid selector or value yields exit 2.

bus dev set model-reasoning-effort-for-model <model> <value> — Set a per-model model reasoning effort preference in bus-dev.model_reasoning_effort_for_model.<model> via bus-preferences. Invalid model or value yields exit 2.

bus dev set model-verbosity-for-model <model> <value> — Set a per-model model verbosity preference in bus-dev.model_verbosity_for_model.<model> via bus-preferences. Invalid model or value yields exit 2.

bus dev set env-for <token|@token> <ENV_NAME> <value> — Set a per-step environment override in bus-dev.env_for.<selector>.<env>. Selector semantics are the same as agent-for. ENV_NAME must match [A-Z_][A-Z0-9_]*. Invalid selector or env name yields exit 2.

bus dev set allow-dirs-for <token|@token> <dir...> — Set a per-step allowed-directory override in bus-dev.allowed_dirs_for.<selector>. Selector semantics are the same as agent-for. Values are stored as a JSON array of directory strings. Invalid selector or empty dir list yields exit 2.

bus dev set allow-dirs-for-module <module> <token|@token> <dir...> — Set a per-module per-step allowed-directory override in bus-dev.allowed_dirs_for_module.<module>.<selector>. The override applies only when the resolved repository module name equals <module> and is layered after matching global per-step entries in bus-dev.allowed_dirs_for.*. module must use lowercase letters, digits, _, -, starting with a letter. Invalid module, selector, or empty dir list yields exit 2.

bus dev context

Intent: Print the full prompt-variable catalog and the current resolved values in deterministic, script-friendly form for use by prompt and script authors. The output MUST be one KEY=VALUE line per variable, sorted by KEY ascending, with values exactly as used for prompt rendering and environment injection into repository-local script actions (FR-DEV-022). When the effective working directory is not inside a Git repository, the command MUST fail with a clear diagnostic and exit code 2. The command does not acquire the working-directory lock; it only requires repository resolution to derive the catalog. Success: exit 0 and the KEY=VALUE lines to stdout.

bus dev each [--check] [--skip MODULE[,MODULE...]]... TOKEN...

Intent: Run the remaining bus dev command in every selected child module of a superproject. The command is valid only in superproject context. Child discovery is deterministic and limited to top-level directories at the superproject root: include bus first if present and containing a Makefile, then include bus-* directories that contain a Makefile, sorted lexicographically. --skip excludes one or more modules by directory name; it can be repeated and accepts comma-separated names. For each selected child module, run the equivalent of bus dev TOKEN... with that child as the effective working directory. The dispatch loop stops on first non-zero exit and returns that code.

Preconditions: Effective working directory resolves to a superproject context (FR-DEV-013). At least one token is provided after each options. Any --skip names must exist in the discovered module set. At least one selected buildable child module remains after skip filtering.

Must never do: Run outside superproject context, recurse into arbitrary nested directories, or include directories without a top-level Makefile.

bus dev retry --on-fail|-f <TOKEN>[,<TOKEN>...] [--attempts N] <workflow-token>...

Intent: Execute one normalized workflow sequence, and on failure execute fallback token(s) before each retry, up to the configured number of additional attempts. Workflow execution includes all <workflow-token>... entries for each attempt.

Preconditions: --on-fail is required and must be a non-empty comma-separated token list. Workflow tokens are required after flags. --attempts defaults to 1 when omitted and must be >= 0. --attempts 0 is valid and runs the workflow once without retrying or running fallback. Expanded workflow and fallback tokens are normalized; after expansion, each must still include at least one runnable step.

Behavior: The retry loop runs the workflow sequence on each attempt. If an attempt fails and retries remain, bus-dev runs the expanded fallback sequence. If fallback fails, the command stops immediately and returns that fallback code. If fallback succeeds, the command waits for exponential delay (1s, 2s, 4s, …) and retries the workflow. A successful workflow attempt exits 0 immediately. If no attempt succeeds, the command exits with the last failing workflow code and emits the total attempt count.

Examples: bus dev retry --on-fail|-f fix-tests,fix-tests-2 --attempts 2 test, bus dev retry -f @commit test_2, bus dev retry -f fix-tests,fix-tests-2 --attempts 0 test.

bus dev list

Intent: Show every possible runnable token (operations, pipelines, prompt actions, script actions) and what they execute, without running any agent, script, or Git operation (FR-DEV-032). Outside a Git repository, output lists only built-in operations and built-in pipelines, each with a one-line description and (for pipelines) the normalized expanded step sequence. Inside a Git repository, output additionally lists repository-local pipelines, repository-local prompt actions (.bus/dev/NAME.txt), repository-local script actions (with platform variants), and preference pipelines, each with token name, type, source or description, and for pipelines the normalized expanded step sequence. Output format is stable, parseable, and documented. The command does not acquire the working-directory lock. Success: exit 0 and the listing to stdout.

bus dev pipeline — Management operations for user-defined pipelines (FR-DEV-025). Do not participate in workflow chaining. pipeline set repo NAME TOKEN… writes or overwrites .bus/dev/NAME.yml with a strict YAML sequence of scalar strings; requires repo and takes the per-directory lock. pipeline unset repo NAME removes .bus/dev/NAME.yml if present; exit 0 if absent. pipeline set prefs NAME TOKEN… writes bus-dev.pipeline.NAME as a JSON array of strings via the bus-preferences Go library (no shell-out); does not take the lock. pipeline unset prefs NAME removes the preference key if present; exit 0 if absent. **pipeline list [all repo prefs builtin]** prints a deterministic line-oriented listing (e.g. one line per pipeline with source tag and expanded token list); output lexicographic by name then source. pipeline preview TOKEN… resolves, expands, and normalizes tokens with the same logic as runnable invocations, prints one normalized step name per line to stdout, and exits without executing any runnable step (FR-DEV-015b). Invalid NAME or ambiguity (FR-DEV-026) yields exit 2 with diagnostic identifying conflicting path(s) or key(s).

bus dev action — Management operations for repository-local prompt actions (FR-DEV-027). action set NAME reads content from stdin until EOF and writes .bus/dev/NAME.txt; creates .bus/dev if missing; stdin must be non-empty (empty stdin → exit 2). action unset NAME removes .bus/dev/NAME.txt if present; exit 0 if absent. action list prints available repo actions deterministically by name. action set and action generate fail with exit 2 if a pipeline or enabled script exists for NAME (ambiguity rule). action generate NAME INSTRUCTION… (FR-DEV-029) uses an embedded generator prompt, runs the agent via bus-agent, and writes the result to .bus/dev/NAME.txt; ambiguity check before any agent invocation.

bus dev script — Management operations for repository-local script actions (FR-DEV-028). **script set NAME [–platform=unix windows windows-ps1 both]** writes .bus/dev/NAME.sh (unix), .bus/dev/NAME.bat (windows), and/or .bus/dev/NAME.ps1 (windows-ps1) from stdin; default platform is current OS; sets executable bit on .sh (chmod failure → exit 1). script unset NAME [–platform=…] removes the selected variant(s); exit 0 if absent. script list prints available repo scripts by name with variant (.sh, .bat, .ps1) and enabled/disabled status. **script generate NAME –platform=unix windows windows-ps1 both INSTRUCTION…** (FR-DEV-029) uses an embedded generator prompt, runs the agent, and writes result to the correct file(s) and sets executable bit on .sh. Ambiguity check before agent invocation; disabled script reserves name (FR-DEV-026).

Headless and script mode. When bus dev invokes an agent (plan, work, spec, e2e, triage, stage) from a non-interactive environment — for example from a script, CI, or a headless runner — the workflow MUST remain non-interactive and MUST NOT perform prohibited actions. Prohibited actions include network operations (e.g. the agent must not be instructed or allowed to push, pull, fetch, or call external APIs as part of the workflow), modifying user-global configuration or memory (see NG-DEV-005 and Gemini CLI integration), and any operation that would block waiting for user input. The implementation MUST invoke the selected agent with flags or environment that enforce non-interactive, script-safe behavior so that bus dev plan, bus dev work, bus dev spec, bus dev e2e, bus dev triage, and bus dev stage can be used in automation without requiring a TTY or user interaction. Exit codes and stderr output remain deterministic as specified elsewhere in this SDD.

bus dev init [DIR] [--lang go] [plan|spec|work|e2e ...]

Intent: Initialize a BusDK module root in place, in the current working directory by default or in an explicitly provided target directory, without performing any Git operations. By default, init performs initialization only; it does not run spec, work, or e2e unless those operations are explicitly appended after init.

Preconditions: The effective working directory need not be a Git repository. If DIR is omitted, the target directory is the effective current working directory and must exist. If DIR is provided and does not exist, init creates it. If the target path exists but is not a directory, the command exits with code 2 and a clear message. Agent runtime availability is required only when appended operations (plan/spec/work/e2e) are requested.

Reads: Existing repository files in the target directory (to detect whether scaffold files are already present), embedded init templates (including the sample Makefile), and BusDK documentation and embedded prompts only when appended operations are run. No workspace datasets.

Writes: Ensures a single AGENTS.md exists at the target directory root (project root), with default content controlled by --lang (default go) per the AGENTS.md format. Ensures a root Makefile exists by writing an embedded sample Makefile when missing. If appended workflow operations are present, sets effective working directory to the target directory and runs them in the exact order provided by the command line.

Allowed mutations: Creating the target directory when needed, creating missing initialization files, and optionally writing changes produced by explicitly requested workflow operations under the target directory. No mutations outside the target directory. No Git operations.

Must never do: Run any Git command (no git init, no add, no commit). Run plan/spec/work/e2e unless explicitly requested after init. Overwrite an existing Makefile by default during initialization. Create or modify anything outside the resolved target directory (except stderr and any documented stdout).

Language flag: --lang defaults to go. It controls which default or language-specific AGENTS.md content init installs at the project root, so init can scaffold modules for different programming languages. The set of supported values and their exact effect on installed content is implementation-defined and MUST be documented in the CLI reference.

bus dev commit

Intent: Create one or more commits from the currently staged changes only, with high-quality messages and strict safety rules, without touching remotes or history.

Preconditions: Effective working directory is the root of a Git repository (or a subdirectory of one). git is installed and executable in PATH. Optional: repository may contain submodules.

Reads: Git index and repository metadata (e.g. git status, git diff --cached). Does not read workspace datasets or accounting files for bookkeeping.

Writes: Only new commits created from the existing staging area (via git commit). Does not write to working tree files, does not create or modify files outside Git’s normal commit operation.

Allowed mutations: Creating new commit(s) from already-staged content. Committing in submodules first (depth-first), then the superproject only if it has staged changes. No other mutations.

Must never do: Modify files; stage anything (git add); amend, rebase, or rewrite history; push, pull, fetch, clone, or any remote operation; run hooks that are not the standard Git commit hooks (the tool may run git commit, which may run commit-msg/pre-commit etc.; the tool must not bypass or suppress hooks).

Behavior when nothing to commit: If there is nothing staged (and no staged changes in submodules that need committing), the command does nothing and exits with code 0. No commit is created, no error.

Submodules: If the repository has submodules and a submodule has staged changes, commit inside that submodule first (recursively depth-first). After all submodules with staged changes are committed, if the superproject has staged changes (including gitlink updates), commit the superproject. If a submodule commit resulted in an unstaged gitlink change in the superproject, the tool MUST report that clearly (e.g. to stderr) and STOP without staging that gitlink; the user must stage it manually if desired.

Commit message quality (normative): For every commit, the message MUST have a concise, action-oriented subject line in the imperative mood. A body MAY follow, separated by a blank line. The message SHOULD explain what changed and why, mention user-visible impact or risk when relevant, and include traceability (issue IDs or URLs) when helpful. Vague summaries are not acceptable. Conventional prefixes (feat, fix, docs, refactor, test, chore) MAY be used when they improve clarity but MUST NOT replace a precise subject.

Atomicity: Before creating each commit, the implementation (or the agent it invokes) MUST review the staged set at a high level. If the staged set contains multiple logical changes, the implementation/agent MUST propose an atomic commit split plan. By default, the tool commits exactly what is currently staged and MUST NOT alter the staging area to perform a split unless explicitly instructed.

Failures and hooks: If a commit is rejected by hooks (e.g. pre-commit, commit-msg), the tool MUST report the exact failure reason and output and suggest the minimal correction. The tool MUST NOT retry the commit automatically unless explicitly instructed.

bus dev stage

Intent: Prepare the working tree for commit so that only intended files are staged. The agent identifies temporary files, e2e test leftovers, build artifacts, and other files that should not be tracked; adds them (or patterns) to .gitignore or removes them from the project; then the tool stages all remaining changes. Enables combined invocations such as bus dev plan work stage commit without manually cleaning and staging.

Preconditions: Effective working directory is the root of a Git repository (or a subdirectory of one). The tool MUST resolve the repository root from the effective working directory. The agent runtime MUST be available (same resolution as for plan, work, spec, e2e).

Reads: Repository metadata to resolve the repository root. The agent reads the working tree, existing .gitignore, and (when present) AGENTS.md or project conventions to decide which files should not be tracked. Does not read workspace accounting datasets.

Writes: Determined by the agent under the embedded prompt: updates to .gitignore (add patterns or paths for files that should not be tracked), and optional removal of temporary files that should not exist in the working tree. After the agent completes, the tool updates the Git index (staging area) by staging all remaining changes at the repository root.

Allowed mutations: The agent may add entries to .gitignore and may remove files from the working tree that are purely temporary. The tool may then run local git add to stage all remaining changes at the repository root. No recursive staging inside submodules. No commit, amend, rebase, or remote operations.

Must never do: Commit; amend, rebase, or rewrite history; push, pull, fetch, clone, or any remote operation; stage inside submodules (only the current repository’s working tree is in scope). The agent must not stage files that the prompt defines as unintended (temporary, e2e leftovers, etc.); it must ignore or remove them first.

bus dev work

Intent: Run the canonical “do the work in this repo now” agent workflow: implement code changes, add/update tests, run Makefile checks, and update README in the current module repository, following the module’s AGENTS.md and design docs.

Preconditions: Effective working directory is inside a BusDK module Git repository. The repository contains AGENTS.md and design docs that the agent will read. When the selected runtime is Gemini CLI, repository-local Gemini context (e.g. repo-root GEMINI.md, .gemini/settings.json, .geminiignore) may also be present; Bus Dev supports AGENTS.md and Gemini context in parallel without changing this workflow.

Reads: Repository source code, repository-root AGENTS.md, design docs (e.g. SDD, CLI reference) as referenced by AGENTS.md, and PLAN.md when it exists. When PLAN.md exists, the prompt requires the agent to review it first and prioritize its unchecked items while retaining checked items as completed history until plan pruning. When Gemini CLI is selected, the agent also reads repo-root and directory-scoped GEMINI.md, .gemini/settings.json, and respects .geminiignore for context ingestion. The agent is allowed to read these as the authoritative specs for that repo. Bus Dev does not read or modify user-global Gemini config or memory; developers who want global Gemini memory must manage it manually outside Bus Dev.

Writes: Determined by the agent under the constraints of the embedded prompt (code, tests, README, and PLAN.md item check-offs when applicable). The tool itself does not write repository files directly; it only invokes the agent with the embedded prompt.

Allowed mutations: Whatever the embedded prompt permits (code, tests, README, and checking off completed PLAN.md items), under the rule that the agent operates only in the current module and follows the module’s rules.

Must never do: Invoke remote Git operations; operate outside the current module repository; change workspace accounting datasets; remove already checked items from PLAN.md; modify user-global Gemini configuration or memory (see Headless and script mode and Gemini CLI integration). In headless or script mode, the agent MUST NOT perform network operations or other prohibited actions.

Implementation note: The workflow is executed via an embedded prompt template shipped inside bus-dev. Documentation links in the prompt (e.g. main SDD, module SDD, module end-user docs) are derived from the configurable docs base URL (FR-DEV-004b) and the prompt variable catalog (MAIN_SDD_URL, MODULE_SDD_URL, MODULE_DOCS_URL). The agent runtime reads the repository’s own docs, AGENTS.md, and (when Gemini is selected) repository-local Gemini context as part of doing the work; those are the authoritative specs for that repo. For a concrete picture of repo-local files and what each is responsible for, see the Gemini CLI integration subsection.

bus dev plan

Intent: Build a compact prioritized implementation plan for the current module repository by comparing documented requirements to repository reality, then write that plan to PLAN.md.

Preconditions: Effective working directory is inside a BusDK module Git repository. The agent runtime is available. Documentation URLs derived from the docs base URL are reachable to the selected runtime.

Reads: Repository files, current tests, current docs in the repository, existing PLAN.md when present, the main SDD URL (MAIN_SDD_URL), the current module SDD URL (MODULE_SDD_URL), the current module end-user docs URL (MODULE_DOCS_URL), and other project SDD pages relevant to requirement coverage. The prompt uses these sources to re-validate existing plan items and to detect unimplemented features and other undone work.

Writes: PLAN.md at repository root only. The file is a compact Markdown checklist with unchecked items ordered by priority.

Allowed mutations: Creating or overwriting repository-root PLAN.md.

Must never do: Modify source code, tests, README, AGENTS.md, or any file other than PLAN.md. Include implementation-level instructions that duplicate or override project SDD details.

Plan format contract: PLAN.md contains one unchecked item (- [ ]) per still-undone or newly detected unimplemented feature or undone work item that the command determines from documented sources and repository state. If PLAN.md already exists, its prior items are treated as candidates: items now completed are removed, and items still undone are retained and reprioritized with newly discovered items in one combined list. Items are concise and action-oriented, with enough context for the next execution step, and avoid implementation details because implementation must come from project SDD and related documentation. If no undone work is detected after re-validation, the file still exists and states that no open items were found.

bus dev spec

Intent: Ensure the current repository has a compact but detailed local spec in AGENTS.md that reflects the latest BusDK specifications and describes how to implement this tool; create AGENTS.md from online SDD and user documentation when missing; no changes to source code, tests, or README.

Preconditions: Effective working directory is inside a BusDK module Git repository. When the selected runtime is Gemini CLI, Bus Dev uses repository-local Gemini context in parallel with AGENTS.md; headless and script mode constraints apply as in Headless and script mode.

Reads: The current AGENTS.md when it exists (subject of refinement); when it does not exist, the agent produces it from online docs. If .cursor/rules/<MODULE_NAME>.mdc exists (module name per FR-DEV-003), the tool reads it and includes its content in the refinement context so the agent can merge it into AGENTS.md. BusDK documentation is referenced via the embedded prompt; links are derived from the docs base URL and prompt variables such as MAIN_SDD_URL, MODULE_SDD_URL, MODULE_DOCS_URL, and AGENTS_FILE_PATH per the prompt variable catalog. When Gemini CLI is selected, the agent may also use repo-local GEMINI.md and .gemini/settings.json. The agent may read the spec pages and the AGENTS.md format to align the file.

Writes: The single file AGENTS.md at the repository root (created when missing, otherwise updated). After a successful refinement that incorporated Cursor rule content, the tool MUST remove .cursor/rules/<MODULE_NAME>.mdc if it existed. Bus Dev does not write to any user-global Gemini config or memory.

Allowed mutations: Creating or updating AGENTS.md; deleting .cursor/rules/<MODULE_NAME>.mdc only after its content has been refactored into AGENTS.md (FR-DEV-008a).

Must never do: Modify source code, tests, README, or any file other than repository-root AGENTS.md and the single Cursor rule file specified above; perform Git operations other than those implied by the user saving the file (if the agent writes the file, the user may then commit).

AGENTS.md path: The tool MUST use the path AGENTS.md at the repository root (no module-derived path). When AGENTS.md does not exist, the tool MUST create it in the same run by having the agent produce it from the online SDD and module end-user documentation, so the result is a compact but detailed local implementation spec for the current module.

bus dev e2e

Intent: Guided workflow to detect missing end-to-end tests for the current module and scaffold them in a hermetic way, consistent with BusDK testing conventions and the module’s SDD and end-user documentation.

Preconditions: Effective working directory is inside a BusDK module Git repository. The module has (or the tool can resolve) an SDD and end-user documentation so that required test coverage can be determined.

Reads: Repository layout, existing tests under tests/, the module’s SDD document, end-user documentation (e.g. CLI reference) for the current module, and PLAN.md when it exists. When PLAN.md exists, the prompt requires the agent to review it first, validate that each checked item is fully covered by e2e tests, and prioritize unchecked test-related items. Documentation links in the prompt are derived from the docs base URL (FR-DEV-004b) and the prompt variable catalog (MAIN_SDD_URL, MODULE_SDD_URL, MODULE_DOCS_URL, E2E_SCRIPT, E2E_SCRIPT_PATH, E2E_WORKSPACE_DIR, PLAN_FILE, PLAN_FILE_PATH). The tool uses these inputs both to verify checked-item coverage and to discover other untested behavior from the SDD and end-user docs. Agent-specific rules (e.g. AGENTS.md) define language and module-specific testing expectations and are used when generating or refining scaffolds.

Writes: New or updated test files under tests/ only. E2E scripts MUST be named e2e_bus_<name>.sh where <name> is the module name with the bus- prefix stripped (e.g. bus-accountstests/e2e.sh). Scaffold content MUST align with the behavior described in the SDD and end-user docs and with agent rules. No modification of production code.

Allowed mutations: Adding or updating files under tests/ (e.g. e2e_bus_<name>.sh and any agreed boilerplate) as defined by the subcommand’s acceptance criteria. If PLAN.md is touched by this workflow, checked items MUST be preserved and must not be removed.

Must never do: Remote Git operations; history rewriting; modifying workspace accounting datasets; removing already checked items from PLAN.md; non-hermetic or network-dependent test scaffolding.

Detection and scaffold: The tool detects missing e2e tests by comparing required coverage (derived from the module’s SDD and end-user documentation, with URLs supplied via the prompt variable catalog) to existing tests under tests/. When PLAN.md exists, checked items are treated as completed feature commitments and MUST be verified as fully covered by existing or newly scaffolded e2e tests; coverage gaps for checked items are reported and scaffolded. The command also continues discovering other untested documented behavior that may not appear in PLAN.md. The exact scaffold (file names, boilerplate, and suggested cases) is deterministic: it follows the tests/e2e_bus_<name>.sh naming and directory layout, and the content is generated to cover the behavior and CLI surface described in the SDD and end-user docs. Language- and module-specific details (e.g. how to build or invoke the binary, Go test layout) are defined in agent instructions (e.g. AGENTS.md) and are used when the agent runs or when the tool produces guidance. When bus dev e2e invokes an agent, Headless and script mode and repository-local-only Gemini rules apply; the agent must not perform network operations or modify user-global Gemini config or memory.

bus dev triage

Intent: Keep development-state documentation accurate, compact, and evidence-based by reconciling test-proven capabilities with planned work (PLAN.md) and dependencies. Update only documentation files (development-status page and module end-user docs); never source code or tests. Output is end-user-value and journey-readiness oriented, with compact readiness text per user story or journey.

Preconditions: Effective working directory is the root of a Git repository that can be classified as one of: docs project (contains ./docs/modules and ./docs/sdd), BusDK super-project (contains .gitmodules and a docs submodule), or individual bus module repo (repo root base name matches bus-* with sibling ../docs containing docs structure). The agent runtime is available. If project context is ambiguous, the command exits with code 2 and clear guidance (see FR-DEV-013).

Reads: Repository layout and, depending on context, the docs content root, module pages under ./docs/modules, the overall development-status page path, existing “Development state” sections, module SDDs and end-user docs, test files (to determine verified behavior), and PLAN.md in relevant module repos. In super-project mode, may read module repos via submodule working tree.

Writes: Only documentation files. In docs-project mode: ./docs/implementation/development-status.md and each ./docs/modules/bus-<NAME>.md. In super-project mode: the same paths under the docs submodule. In module mode: only ./docs/modules/bus-<NAME>.md for the current module (in the sibling docs repo) and the overall development-status page in that sibling docs repo.

Allowed mutations: Creating or updating the development-status page and the per-module “Development state” sections per the structure defined in FR-DEV-013 (value promise, completeness percent with rationale, Use cases line, Current / Planned next / Blockers / Depends on / Used by). Optionally adding a missing use case documentation page only when inference suggests it and it can be done safely and compactly without inventing requirements. No other mutations.

Must never do: Modify source code, test files, or any file outside the allowed documentation targets. In module mode, must not rewrite other modules’ pages. Perform remote Git operations or history rewrite. Touch workspace datasets. Implemented-but-untested behavior must not be counted as verified or increase completeness.

Determinism and safety: The same detected context and same repository state must yield the same set of files to update and the same prompt variant. Template rendering must satisfy FR-DEV-004a: missing or unresolved triage prompt placeholders cause failure before agent invocation (exit 2). Triage respects the same headless/script-mode and agent-disclosure rules as other agent-invoking subcommands.

I/O Conventions

Standard output is reserved for (1) deterministic, machine-readable command results from management and non-action subcommands (list, context, pipeline list, pipeline preview, action list, script list), and (2) all runnable-step output so that piping works. Repository-local script action stdout, repository-local prompt action (agent) output, and built-in operation output (plan, work, spec, e2e, triage, stage when they invoke the agent) MUST be written to stdout. This allows invocations such as bus dev open-tasks | do-something to produce pipeable output from the action itself.

Standard error: All diagnostics, progress, and the single line at the start of each agent-invoking step that identifies the internal agent runtime and model (FR-DEV-005e) MUST go to stderr. The tool MUST NOT write action result content to stderr so that stdout remains the sole stream for action output and can be piped reliably.

When an external agent is invoked, the agent’s raw output (e.g. NDJSON) may be piped through an internal formatter that writes readable text to stdout. The tool MUST NOT inject color or control sequences into stdout when the output is intended to be machine-readable.

Determinism: Given the same repository state, same staged set, and same flags, the tool MUST produce the same exit code and consistent stderr output (up to timing or non-deterministic agent output when an agent is used; the tool’s own messages and exit code must still be deterministic for the “no agent” and “stub agent” cases).

Exit Codes

  • 0: Success. For bus dev commit, “nothing to commit” is success (0).
  • 1: Execution failure (e.g. Git command failed, hook failed, agent failed or timed out, selected agent runtime not found or not executable, or no agent enabled when the automatic default would apply).
  • 2: Invalid usage (e.g. unknown subcommand or unknown operation/pipeline token, unknown @name, invalid flag including invalid --agent or set agent runtime name, missing required argument, precondition not met — e.g. not in a Git repository for repo-scoped commands or for bus dev context, or init target path is not a directory — ambiguous triage project context per FR-DEV-013, invalid pipeline definition format or invalid pipeline name, pipeline recursion detected, expansion limit exceeded, pipeline after init that expands to stage/commit/triage per FR-DEV-018, repository-local ambiguity or reserved-name conflict per FR-DEV-021, disabled script action invoked per FR-DEV-020, invalid repository-local YAML or symlink escape, bus dev context run outside a Git repository per FR-DEV-022, or bus dev each used outside superproject context, without tokens, with unknown --skip module names, or with no selected buildable child modules).

The distinction between invalid usage (2) and execution failure (1) follows BusDK CLI conventions: usage errors are 2; failures during an otherwise valid invocation are 1. All pipeline expansion errors (unknown token, unknown @name, invalid pipeline definition, invalid name, recursion, expansion limit, init-following violation) are usage errors and MUST exit 2 before any agent invocation or git write.

Error Handling and Resilience

  • Configured agent runtime not installed or not in PATH: When the user has selected an agent (via flag or session preference) and that agent’s CLI is not found or not executable, the tool MUST surface the failure from the bus-agent library and MUST exit with a clear diagnostic to stderr and a non-zero exit code (1). The message MUST indicate that the selected agent runtime could not be found or executed and MUST direct the user to the canonical installation URL for that runtime; bus-agent provides the installation reference (FR-AGT-010) and bus-dev uses it in the diagnostic.
  • No agent available (automatic default): When no runtime is selected via flag or session preference and the automatic default would apply, but no agent is available (no enabled CLI in PATH, or all are disabled, or enable list excludes all), the tool MUST exit with a clear diagnostic to stderr and a non-zero exit code (1 or 2). The message MUST state that at least one supported agent runtime must be installed or enabled and MUST direct the user to the canonical installation URLs; bus-dev uses the installation references provided by the bus-agent library to build this message.
  • git not installed or not in PATH: When a subcommand needs Git, the tool MUST detect failure to run git and MUST exit with a clear diagnostic and non-zero exit code (1).
  • Working directory not a Git repo: For subcommands that require a Git repository (commit, stage, plan, work, spec, e2e when run standalone), exit with code 2 and a clear message. For bus dev init, the effective working directory need not be a Git repository; init targets the current directory by default or the provided DIR path and initializes missing files there.
  • No staged changes (commit): Exit 0 and do nothing; optionally print a short message to stderr that there was nothing to commit.
  • Hooks fail (commit): Exit 1 and report the hook failure output; do not retry.
  • Timeouts (agent subcommands): When the agent run (via the bus-agent library) exceeds the configured timeout, the tool MUST exit with a non-zero code (1) and MUST report that the run timed out.

  • Ambiguous triage project context: When bus dev triage cannot deterministically classify the effective working directory as docs project, super-project, or module repo (e.g. both docs layout and super-project layout present, or neither matches), the tool MUST emit a clear diagnostic to stderr, exit with code 2, and include guidance on where to run the command (e.g. run from docs repo root, super-project root, or module repo root). See FR-DEV-013.

  • Lock acquisition failure: When a subcommand requires the working-directory lock (init, commit, stage, plan, work, spec, e2e, triage) and the lock cannot be acquired because the directory is not writable, the lock file cannot be created, or the platform does not support the required locking primitive, the tool MUST exit with a clear diagnostic to stderr and a non-zero exit code (1). The message MUST indicate that the working-directory lock could not be acquired and SHOULD suggest checking directory permissions or concurrent bus dev usage.

  • Pipeline expansion errors: Unknown operation or pipeline token, unknown @name, invalid pipeline definition (bad JSON, non-array, empty name, invalid name grammar, invalid token type; or repository-local YAML that is not a sequence of strings per FR-DEV-019), invalid pipeline name (e.g. user-defined name matching a built-in operation), recursion detected (cycle in pipeline definitions), and expansion limit exceeded MUST all yield exit code 2 with a clear stderr diagnostic. Pipeline expansion that would introduce stage, commit, or triage after init (FR-DEV-018) MUST also yield exit 2 with a clear diagnostic. Repository-local ambiguity (same NAME in more than one of .txt, .yml, preference pipeline, or platform-selected script per FR-DEV-021), disabled script action invoked (FR-DEV-020), repository-local file path outside repo or symlink escape (FR-DEV-004), and bus dev context when not in a Git repository (FR-DEV-022) MUST yield exit 2 with a clear diagnostic. The same expansion-validation behavior applies to pipeline preview: it must fail before any runnable-step execution. These errors MUST occur before any agent invocation and before any git write operations. Failures during an otherwise valid operation (e.g. agent timeout during work) remain execution failures (exit 1) as specified above.

All error messages MUST be written to stderr. The tool MUST NOT crash or exit with an ambiguous code when the above conditions occur; behavior MUST be as specified.

Data Design

Bus Dev does not own or read workspace datasets (CSV, schemas, datapackage.json) for bookkeeping. It operates on:

  • Working-directory lock file. To enforce single-instance execution per directory (FR-DEV-012, IF-DEV-005), the tool uses an exclusive lock keyed by the effective operation directory. The lock MUST be implemented using a lock file at a deterministic path within that directory (e.g. <effective-operation-dir>/.bus-dev.lock). The implementation MUST use an exclusive file lock (e.g. flock or equivalent) so that only one process holds the lock at a time; blocking until the lock is acquired when another process holds it. The lock file MAY be created if missing; it MUST be released when the process exits. For bus dev init when the target directory does not exist, the directory is created first per FR-DEV-010, then the lock is acquired in that directory. The lock file is an implementation detail and may be left on disk after a normal run; it does not contain user data and is used only for mutual exclusion.

  • The Git repository (metadata and index) for the current working directory.
  • The repository’s working tree (source files, repository-root AGENTS.md) when the agent is run, so the agent can read and optionally write files under the repo. When Gemini CLI is selected, Bus Dev only creates, edits, or relies on repository-scoped Gemini files: repo-root and optional directory-scoped GEMINI.md, .gemini/settings.json, and .geminiignore. Bus Dev never writes to the user’s home Gemini config directory (e.g. ~/.gemini/) or to global memory (FR-DEV-005c, NG-DEV-005).
  • Repository-root PLAN.md for planning continuity. bus dev plan creates or refreshes it as a compact unchecked priority list by re-validating existing items, removing completed checked items, and adding newly detected missing work. bus dev work reads it when present, prioritizes unchecked items, and checks off completed items without deleting checked items. bus dev e2e reads it when present, validates checked items against e2e coverage, prioritizes unchecked test-related items, and does not delete checked items.
  • Triage documentation targets. bus dev triage updates only documentation files: the overall development-status page at ./docs/implementation/development-status.md and module end-user docs at ./docs/modules/bus-<NAME>.md (paths relative to the docs content root). It never writes to source code or test files. Scope depends on detected project context (docs repo, super-project, or module repo) per FR-DEV-013.
  • No persistent data store beyond what Git and the filesystem provide for bus-dev’s own state. Agent selection follows the order in FR-DEV-005b and FR-AGT-005: per-command --agent, then BUS_DEV_AGENT, then BUS_AGENT, then bus-dev persistent preference (bus-dev.agent), then bus-agent preference (bus-agent.runtime), then first available runtime. Run-config (model, timeout, output-format) follows the same logic: per-command override if present, then BUS_DEV_MODEL, BUS_DEV_TIMEOUT, BUS_DEV_OUTPUT_FORMAT, then bus-dev persistent preferences (bus-dev.model, bus-dev.timeout, bus-dev.output_format), then bus-agent preferences as fallback, then bus-agent defaults. Bus-dev writes only to the bus-dev namespace when the user runs bus dev set (agent, model, output-format, timeout); it never writes to bus-agent.* or any other namespace via the bus-preferences library. Preferences are read via the bus-preferences library; the user sets bus-dev defaults with bus dev set agent|model|output-format|timeout or with bus preferences set bus-dev.<key> <value> (e.g. bus preferences set bus-dev.agent gemini). When a configured runtime is disabled, bus-agent prints a warning and resolution continues with the next source (FR-AGT-005b). User-configured agent order and enable/disable are passed through to bus-agent. No bus-dev-specific config file is required; configuration is via flags, environment, and bus-preferences.

  • User-defined pipeline storage. User-defined pipelines may be stored in two scopes. (1) Repository scope: <repo-root>/.bus/dev/<NAME>.yml — a strict YAML sequence of scalar strings (same format as FR-DEV-019). (2) Preferences scope: bus-preferences keys bus-dev.pipeline.<name>, where <name> conforms to the name grammar in FR-DEV-024 (lowercase a–z, digits, hyphen, underscore; must start with a letter; must not match a reserved built-in operation name). The value MUST be a JSON array of strings; each string is a token (base operation name, pipeline name, or @-prefixed built-in reference). Bus-dev MUST read and write these keys via the bus-preferences Go library (no shell-out). Pipeline management commands (FR-DEV-025) allow defining and undefining both repo and prefs pipelines. Pipelines do not introduce any new git behaviors and do not relax the non-goals about remote git operations or history rewriting.

  • Repository-local extension directory (.bus/dev/). Repository-local actions and pipelines are discovered under <repo-root>/.bus/dev/. Only the following file patterns are considered: <NAME>.txt (prompt action), <NAME>.yml (pipeline), <NAME>.sh (script action, non-Windows), <NAME>.bat (script action, Windows), <NAME>.ps1 (script action, Windows; preferred over .bat when both exist). File discovery MUST be restricted to the resolved repository root. The resolved path for any loaded file MUST remain inside the repository: symlinks that point outside the repository MUST be refused. Names are derived from the filename stem (e.g. mytask from mytask.txt). UTF-8 encoding is required for .txt and .yml files. YAML files MUST parse as a sequence of string scalars only (FR-DEV-019). Script executable and platform rules are in FR-DEV-020. Ambiguity (same NAME in more than one of .txt, .yml, preference pipeline, or platform-selected script) and reserved-name conflicts are defined in FR-DEV-021 and MUST fail before any agent invocation or git write.

Assumptions and Dependencies

AD-DEV-001 Git is available. The commit workflow and repository resolution depend on git being in PATH and the repository being a valid Git repo. If Git is missing or the directory is not a repo, the tool fails with a clear error.

AD-DEV-002 Agent runtime availability. Subcommands that invoke an agent (plan, work, spec, e2e, triage, and stage) require the chosen agent runtime (Cursor CLI, Codex, Gemini CLI, or Claude CLI) to be installed and in PATH when those subcommands are run. The chosen runtime is either explicitly selected (flag or session preference) or the automatic default (first available agent in bus-agent’s effective order, alphabetic when no user order is set). If the explicitly selected agent is missing, the tool reports that and exits non-zero; it does not silently fall back. If no agent is selected and none are available, the tool exits with a clear message directing the user to install or enable at least one supported agent (FR-DEV-005d).

AD-DEV-003 Repository layout. For bus dev spec, the agent-instruction file is AGENTS.md at the repository root (per the AGENTS.md convention). If the project adopts a different convention, this assumption will be updated in the SDD.

AD-DEV-004 Operating environment. Same as BusDK: Linux and macOS. Tests and behavior are defined for these environments; Windows is out of scope unless otherwise stated.

AD-DEV-005 bus-agent dependency. Bus Dev depends on the bus-agent Go module for agent execution. The bus-agent library provides the runner interface, supported runtimes (Cursor, Codex, Gemini, Claude), runtime detection, selection resolution, prompt-template rendering contract, timeout enforcement, output capture/streaming, and installation references for diagnostics. If bus-agent is unavailable or its interface changes in an incompatible way, bus-dev’s agent-invoking subcommands (plan, work, spec, e2e, triage, and stage) are affected; bus-dev does not implement a fallback agent runner.

Testing Strategy

  • Unit tests. All library-style code (repo resolution, argument parsing including multiple operations (FR-DEV-001a), pipeline parsing and expansion (FR-DEV-014–FR-DEV-022), init target resolution and scaffold creation, prompt template expansion, and agent invocation via bus-agent with stub) MUST have unit tests. Tests MUST be hermetic: no network, no real Git remotes, no real agent. Parsing and execution of multi-op invocations (e.g. spec, work, e2e, triage in one call, and init with appended operations) MUST be covered; when a stub agent returns non-zero for the second operation, the third MUST not run and the exit code MUST be that of the failed operation. Retry-specific tests MUST cover --attempts defaults, edge cases (--attempts 0 and malformed input), fallback token expansion/normalization, fallback execution failure short-circuit, exponential backoff scheduling (deterministic through test seams), and final failure diagnostics after retries are exhausted. Tests MUST verify that plain bus dev init does not invoke any agent operation and that a missing root Makefile is created from the embedded sample while an existing Makefile is preserved. Plan-specific tests MUST verify that bus dev plan writes only PLAN.md, produces only unchecked checklist items, orders them by priority, and keeps item text compact and non-implementation-specific. Plan-specific tests MUST also verify rerun behavior when PLAN.md already exists: completed checked items are removed, still-undone items remain, and newly detected missing work is added. Work-specific tests MUST verify that completed plan items are checked off and that checked items are never removed by work. E2E-specific tests MUST verify checked-item coverage validation and additional untested-feature discovery from SDD/docs while preserving checked plan items. Spec-specific tests MUST verify that when AGENTS.md is missing, the command creates it (via stubbed agent) from online SDD and user docs so the result is a compact local implementation spec, and that when .cursor/rules/<MODULE_NAME>.mdc exists, its content is merged into AGENTS.md and the file is then removed (FR-DEV-008, FR-DEV-008a). Triage-specific unit tests MUST cover: (1) project-type detection — the correct context (docs project, super-project, or module repo) is chosen from directory layout and repo structure; (2) path resolution — docs repo location, DEV_STATUS_PATH, DOCS_MODULES_DIR, and (in module mode) DOCS_REPO_SIBLING_PATH are resolved correctly for each context; (3) prompt selection — the correct embedded triage prompt variant is chosen for the detected context; (4) placeholder completeness — all required variables for the selected triage prompt are supplied and missing or unresolved placeholders cause failure before agent invocation (exit 2) per FR-DEV-004a.

  • Pipeline expansion tests. Hermetic unit tests MUST cover pipeline parsing, expansion, normalization, and preview. Tests MUST verify: (1) pipeline overrides of built-in pipeline names — when a user-defined pipeline exists for a built-in name (e.g. round), the unqualified token uses the user-defined pipeline and @round uses the built-in; (2) @ forcing built-ins — @plan, @round, etc. resolve only to built-in operation or built-in pipeline; (3) nested pipelines — pipelines that reference other pipelines expand to the correct flat sequence; (4) repeated tokens and repeated steps — after expansion, normalization merges repeated step names so each appears once in first-appearance order; (5) recursion detection — direct and indirect cycles are detected, exit 2, and the diagnostic includes the cycle path; (6) expansion limit — exceeding the maximum expansion depth or expanded-token limit yields exit 2; (7) validation errors — bad JSON, non-array JSON, empty name, invalid name grammar, invalid token type in a pipeline definition yield exit 2 before any operation; (8) init-following validation — when init is present, a pipeline token that expands to include stage, commit, or triage fails with usage error (exit 2) before any agent invocation or git write; (9) preview parity — bus dev pipeline preview TOKEN... prints exactly the normalized sequence that bus dev TOKEN... would execute, and performs no runnable-step side effects. Tests MUST confirm that expansion/preview errors stop before any agent invocation and before any git write action, consistent with the SDD’s determinism and safety goals.

  • Management command tests. Unit tests MUST cover: name validation (FR-DEV-024) and path resolution; YAML write format determinism for repo pipelines; JSON preference write/read via the bus-preferences library for prefs pipelines; ambiguity detection across repo and prefs scopes (FR-DEV-026); script executable enable/disable behavior and that disabled scripts reserve the name (FR-DEV-028); deterministic ordering for pipeline list, action list, script list, and context output; and deterministic one-step-per-line output for pipeline preview. Agent-assisted action generate and script generate MUST be tested with a stubbed bus-agent runner so no external runtime is required; tests MUST assert that file writes occur only after successful agent output and that ambiguity or other errors before invocation prevent any agent call (FR-DEV-029).

  • Repository-local extension tests. Unit tests MUST cover repository-local discovery, token resolution precedence, ambiguity rules, and @ semantics. Tests MUST verify: (1) repository-local discovery — only files under <repo-root>/.bus/dev/ with names <NAME>.txt, <NAME>.yml, <NAME>.sh, <NAME>.bat, <NAME>.ps1 are considered; resolved path must stay inside repo and symlinks that escape are refused; (2) precedence order — non-@ token resolves in order: repository-local .txt, then repository-local script (platform variant: non-Windows .sh; Windows .ps1 if present else .bat), then repository-local .yml, then preference pipeline, then built-in operation, then built-in pipeline; (3) ambiguity — same NAME in more than one of .txt, .yml, preference pipeline, or platform-selected script yields exit 2 before any agent or git write; (4) allowed .sh, .bat, and/or .ps1 for same NAME (platform variants) — no ambiguity; on Windows .ps1 preferred over .bat when both exist; (5) @ tokens resolve only to built-ins, never to repository-local or preference; (6) reserved names (built-in operations) — repository-local or preference definitions with those names are ignored (and may be reported by a listing command); invoking reserved name without @ uses built-in; (7) YAML strictness — repository-local .yml must be a sequence of string scalars only; any other structure yields exit 2; (8) script enable/disable — on non-Windows, .sh without execute bit is disabled and invoke yields exit 2; on Windows, script not tracked with mode 100755 is disabled and invoke yields exit 2; (9) script actions receive the full prompt-variable catalog as environment variables and values match the derived catalog; injected vars override existing same-named env vars; (10) disabled script invoke yields exit 2; (11) full expansion before execution and cycle detection with diagnostic for repository-local pipelines; expansion bounds (depth or token limit) exceeded yield exit 2; (12) ambiguity and parse errors occur before any agent invocation and before any git write operations. Tests for bus dev context: (13) inside a repo, output is KEY=VALUE lines sorted by KEY and values match catalog derivations; (14) outside a repo, command exits 2 with clear diagnostic.

  • Prompt rendering tests. Unit tests MUST cover the prompt-template rendering contract (FR-DEV-004a): (1) missing variable — when a required variable is not supplied, the command MUST fail with a clear diagnostic and exit code 2 before any agent invocation; (2) unresolved variable — when any {{...}} token remains after substitution, the command MUST fail with a clear diagnostic and exit code 2 before agent invocation; (3) repeated placeholder replacement — the same variable MAY appear multiple times in a template and each occurrence MUST be replaced consistently; (4) expected placeholder set per prompt template — each template’s required variables MUST be documented and tests MUST verify that supplying that set yields a fully resolved prompt; (5) docs base override — when BUS_DEV_DOCS_BASE_URL is set (and when unset), derived URLs (MAIN_SDD_URL, module SDD URL, module docs URL) MUST match the specified formulas and normalization (trailing slash trimmed). Tests for work and e2e prompts MUST verify that PLAN.md is included in prompt context when present and omitted cleanly when absent, and that prompt instructions enforce the checked-item lifecycle rules.

  • Fixture repository. At least one end-to-end style test MUST use a fixture Git repository in a temporary directory (e.g. a repo with a few commits and optionally a submodule) to verify commit behavior: nothing to commit exits 0, staged change leads to commit with message, submodule ordering, unstaged gitlink handling, etc. This test MUST not push or pull; it may run git commit only.

  • Agent runner tests. When the agent is involved, the test strategy MUST stub the agent binary in PATH (or inject a fake path). The stub MUST feed deterministic NDJSON (or plain output) to stdout and exit with a chosen code so that parsing, filtering, and error handling are exercised without calling a real agent or network. Agent detection and automatic default selection (FR-DEV-005d) MUST be testable by controlling PATH so that zero, one, or multiple agent CLIs appear to exist; tests MUST verify that the chosen default is deterministic and that “no agent enabled” yields a clear exit and diagnostic.

  • Locking tests. Tests MUST verify working-directory lock behavior (FR-DEV-012): (1) two concurrent invocations that target the same effective operation directory run one after the other — the second blocks until the first exits; (2) the lock is released on normal exit, on non-zero exit, and on process termination so that a subsequent invocation can acquire it; (3) bus dev set … and bus dev context do not acquire the lock and do not block on a running init/plan/work/spec/e2e/triage/stage/commit in the same directory. Tests MAY use a helper process that holds the lock while a second process blocks, or use a fixture that asserts serialized execution; hermetic tests MUST NOT rely on real wall-clock timing for correctness.

  • Triage e2e/fixture tests. Fixture-based tests MUST verify that bus dev triage only mutates the allowed documentation files for each detected mode: in docs-project mode, the overall development-status page and all module pages under the docs modules directory may be updated; in module mode, only the current module’s page and the overall development-status page in the sibling docs repo are updated; other modules’ pages must not be rewritten in module mode. Tests MUST verify that triage never touches source code or test files. These expectations align with FR-DEV-013 and the command’s non-goals (no remote git ops, no history rewrite, no workspace dataset operations); agent delegation remains as for other agent-invoking subcommands.

  • Definition of Done. For any implementation of this SDD, the following are required: unit tests for the new or touched code paths, at least one end-to-end style test using a fixture repo as above, locking tests as above, and README updates that document the subcommands and link to this SDD and the canonical BusDK design spec where relevant.

Traceability to BusDK Spec

This module intentionally deviates from the following BusDK design spec elements, and is constrained by the following:

  • NG-001 (BusDK does not execute Git commands). Bus Dev is the defined exception: it may run local Git commands (status, diff, commit) under the strict constraints in this SDD (no remote, no history rewrite, no staging). Rationale: developer workflow centralization without putting Git into every module.

  • CLI conventions. Bus Dev follows the same global CLI conventions as other modules (e.g. Error handling, dry-run, and diagnostics, Command structure and discoverability) for flags, stdout/stderr, and exit codes. Where it invokes an external agent, it still obeys the convention that machine-readable output (if any) is on stdout and diagnostics on stderr.

  • Module structure. Bus Dev is implemented as a library-first, thin-CLI module consistent with Module repository structure and dependency rules and Independent modules. It depends on the bus-agent Go library for agent execution and does not implement its own agent runner; it does not invoke other bus-* binaries as subprocesses.

  • Testing. The testing strategy aligns with the BusDK Testing strategy: hermetic, no network, deterministic, and with command-level or fixture-based tests where appropriate.

The most relevant BusDK spec pages for implementors are: the BusDK Software Design Document (SDD) (goals and non-goals, especially NG-001), CLI tooling and workflow, Error handling, dry-run, and diagnostics, Module repository structure and dependency rules, and Testing strategy.

Glossary and Terminology

  • Built-in operation: One of the base workflow operations implemented by bus-dev: plan, spec, work, e2e, triage, stage, commit. These names are reserved and cannot be used as user-defined pipeline names; @plan (and similarly for each) forces the built-in operation.

  • Built-in pipeline: A named sequence of tokens (operations or other pipelines) defined inside the bus-dev binary and versioned with it. Built-in pipelines are snapshot, refresh, round, cycle, and iterate (see Pipelines and expansion in Component Design). Users may override a built-in pipeline name via preferences; @round (or @<name>) forces the built-in pipeline when overridden.

  • User-defined pipeline: A named sequence of tokens stored in bus-preferences under bus-dev.pipeline.<name>. The value is a JSON array of tokens (base operation names, pipeline names, or @-prefixed built-in references). User-defined pipeline names MUST NOT match any built-in operation name.

  • Token: A single positional argument in the operation list after bus dev: either a base operation name (plan, spec, work, e2e, triage, stage, commit), a pipeline name (built-in, repository-local, or preference), a user-defined action name (repository-local prompt or script), or an @-prefixed built-in reference (e.g. @plan, @round). Tokens are resolved and expanded before execution per FR-DEV-021.

  • Expanded operation list: The flattened sequence of runnable steps produced after resolving and expanding all pipeline and action tokens. Each step is either a base operation (plan, spec, work, e2e, triage, stage, commit) or a user-defined action (repository-local prompt action or repository-local script action).

  • Normalized final sequence: The runnable sequence after applying duplicate-step merge to the expanded operation list: repeated step names are merged so each appears once in first-appearance order. Execution and pipeline preview both use this sequence (FR-DEV-015a, FR-DEV-015b).

  • Repository-local prompt action: A user-defined action whose prompt template is loaded from <repo-root>/.bus/dev/<NAME>.txt. Rendered with the same {{VARIABLE}} contract as embedded prompts (FR-DEV-004, FR-DEV-004a). Executed only when invoked by name in the token list.

  • Repository-local script action: A user-defined action implemented by a script at <repo-root>/.bus/dev/<NAME>.sh (non-Windows) or <repo-root>/.bus/dev/<NAME>.bat or <repo-root>/.bus/dev/<NAME>.ps1 (Windows; .ps1 preferred when both .bat and .ps1 exist). Enabled only when the file meets the executable requirement for the platform (FR-DEV-020). Receives the prompt-variable catalog as environment variables.

  • Repository-local pipeline: A pipeline defined by a YAML file at <repo-root>/.bus/dev/<NAME>.yml. Must be a YAML sequence of string scalars; each element is a token resolved with the same rules as command-line tokens (FR-DEV-019).

  • Module name: The identifier used for the current repository when a subcommand needs it (e.g. for docs URLs or e2e script naming). It is derived deterministically as the base name of the repository root directory (the last path component of the absolute path to the repo root). See FR-DEV-003 and IF-DEV-004.

  • Staging area / Git index: The set of changes that have been git added and will be included in the next git commit. Bus Dev commit operates only on this set; it does not add or remove from it.
  • Gitlink: The Git submodule pointer (commit SHA) stored in the superproject tree for a submodule. When you commit inside a submodule, the superproject’s view of that submodule becomes an unstaged gitlink change until the user stages it.
  • AGENTS.md: The single repository-root file that provides agent instructions per the AGENTS.md open format. It MUST be a compact but detailed local spec for implementing the current module, grounded in the module’s SDD and end-user documentation. Bus Dev spec creates AGENTS.md from online SDD and user docs when missing, then refines it; when it exists, spec refines it in place. Init ensures AGENTS.md exists at the project root, creating default content when missing; that content may depend on --lang.
  • Cursor rule file (spec refactor): For bus dev spec, the optional file at .cursor/rules/<MODULE_NAME>.mdc (where <MODULE_NAME> is the module name, e.g. bus-accounts). When present, the tool includes its content in the refinement context, the agent refactors that content into AGENTS.md, and the tool then removes the file so agent instructions live only in AGENTS.md. This is the only permitted exception where bus-dev may remove or replace existing repo content for instruction standardization (FR-DEV-008a, NFR-DEV-008). See FR-DEV-008a.
  • Bus-owned block: A contiguous block of content in a repo-local file (e.g. CLAUDE.md, .gemini/settings.json) added by BusDK with an explicit marker so it can be identified and updated without touching existing user content. All such changes are append-only; user content is never removed or rewritten except the legacy .cursor/rules/.mdc exception. See NFR-DEV-008 and the [bus-agent](./bus-agent) SDD.
  • Agent runner: The abstraction that invokes the configured external agent runtime (Cursor CLI, Codex, Gemini CLI, or Claude CLI) with a prompt, timeout, and output formatting. In bus-dev this is provided by the bus-agent Go library; bus-dev does not implement the runner itself. The active runtime is selectable via a flag or environment variable; supported runtimes and backends are defined in the bus-agent SDD.

  • Cursor CLI: One of the supported agent runtimes. The Cursor CLI backend invokes the cursor-agent (or equivalent) executable. Used when the user selects the Cursor CLI option.

  • Codex: One of the supported agent runtimes. The Codex backend invokes the Codex CLI (or equivalent) executable. Used when the user selects the Codex option. Integration is modular so that prompt, workdir, and I/O contract are the same across all backends.

  • Gemini CLI: One of the supported agent runtimes. The Gemini CLI backend invokes the Gemini CLI (or equivalent) executable. Used when the user selects the Gemini CLI option. Integration is modular so that prompt, workdir, and I/O contract are the same as for other backends. Bus Dev supports repository-local Gemini context (e.g. GEMINI.md, .gemini/settings.json, .geminiignore) in parallel with AGENTS.md and never modifies user-global Gemini configuration or memory (see Gemini CLI integration and NG-DEV-005).

  • MDC-style configuration (Gemini): In Gemini CLI terms, the use of project-level context and settings files that play a role analogous to Cursor’s .cursor/rules/*.mdc and project settings: the Gemini CLI loads context from markdown files (by default GEMINI.md) and respects a project .gemini/settings.json and .geminiignore. Bus Dev documents and manages only the repository-local layer of this configuration.

  • PLAN.md: Repository-root compact priority task list generated by bus dev plan. Each task written by bus dev plan is an unchecked item for one still-undone or newly detected unimplemented feature or other undone work item from documentation and repository state. bus dev work checks off items it completes and preserves previously checked items. bus dev e2e uses checked items as completed-feature coverage obligations and preserves checked items. On rerun, bus dev plan is the only command that prunes completed checked items and merges newly detected missing work into the prioritized unchecked list.

  • Repository-local (Gemini): The set of Gemini-related files that Bus Dev may create, edit, or rely on, all under the current repository: repo-root GEMINI.md, optional directory-scoped GEMINI.md, .gemini/settings.json, and .geminiignore. Contrast with user-global Gemini config (e.g. ~/.gemini/GEMINI.md) and global memory, which Bus Dev must never modify.

  • Headless and script mode: Invocation of bus dev (and thus of the agent) from a non-interactive environment (script, CI, headless runner). In this mode workflows MUST remain non-interactive and MUST NOT perform prohibited actions such as network operations or modifying user-global Gemini config or memory.

  • Claude CLI: One of the supported agent runtimes. The Claude CLI backend invokes the Claude CLI (or equivalent) executable. Used when the user selects the Claude CLI option. Integration is modular so that prompt, workdir, and I/O contract are the same as for other backends.

  • Enabled agent: An agent runtime whose CLI command exists at resolution time (e.g. the corresponding executable is found in PATH and is executable). The available set for automatic default is determined by bus-agent and may be restricted by user-configured enable/disable (FR-AGT-005a). When the user does not specify a runtime, bus-dev delegates to bus-agent for the automatic default from available runtimes in bus-agent’s order (alphabetic when no user order is set) (FR-DEV-005d).

  • Session-stored preference: The user’s default choice of agent runtime for the current session. Stored in an environment variable (e.g. BUS_DEV_AGENT) so that every bus dev command in that shell session uses that runtime unless overridden by the --agent flag. Easy to change by re-exporting the variable or by using the flag for a single command.

  • Working-directory lock: An exclusive lock keyed by the effective operation directory so that only one bus dev invocation (init, commit, stage, plan, work, spec, e2e, or triage) runs at a time per directory. A second invocation for the same directory blocks until the first exits and releases the lock. Implemented via a lock file (e.g. .bus-dev.lock) and an exclusive file lock. See FR-DEV-012 and IF-DEV-005.

  • Embedded prompt: A prompt string or template compiled into the bus-dev binary, not loaded from the filesystem.

  • DOCS_BASE_URL: The base URL for BusDK documentation used to build all documentation links in prompts and diagnostics. Configurable via BUS_DEV_DOCS_BASE_URL (default https://docs.busdk.com); normalized by trimming a trailing slash. See FR-DEV-004b and the prompt variable catalog.

  • MAIN_SDD_URL: The URL of the main BusDK SDD index page. Derived as <DOCS_BASE_URL>/sdd. Used in prompts and diagnostics when referring to the design spec. See FR-DEV-004b and the prompt variable catalog.

  • Prompt-template variable rendering contract: The normative behavior for substituting {{VARIABLE}} placeholders in embedded prompts: rendering MUST be deterministic; missing or unresolved placeholders MUST cause the command to fail before agent invocation (exit code 2). Ensures the agent never receives partially substituted text. See FR-DEV-004a, NFR-DEV-001, NFR-DEV-005.

  • E2E test script (Bus module): A Bash script that runs the module’s compiled binary to exercise end-to-end behavior. It MUST live under tests/ and MUST be named e2e_bus_<name>.sh where <name> is the module name with the bus- prefix stripped (e.g. bus-accountstests/e2e.sh). See KD-DEV-006 and FR-DEV-009.

  • Verified (triage): For triage, a capability or behavior is verified only if it is directly covered by at least one unit or e2e test. Implemented-but-untested behavior MUST be treated as unverified and MUST NOT increase readiness or completeness percentages in development-state documentation. See FR-DEV-013.

  • Development-state documentation: The documentation files that describe current implementation status, completeness, and planned work — in BusDK, the overall page at ./docs/implementation/development-status.md and the “Development state” sections in each module end-user docs page at ./docs/modules/bus-<NAME>.md. Triage updates these only; it never modifies source code or test files. See FR-DEV-013.

  • Triage project context: The classification of the effective working directory for bus dev triage as one of: docs project (contains ./docs/modules and ./docs/sdd), super-project (contains .gitmodules and a docs submodule), or module repo (repo root base name matches bus-* with sibling ../docs containing docs structure). Detection is deterministic; ambiguous context yields exit 2 and guidance. See FR-DEV-013.

  • Pipeline chaining: The feature that allows multiple bus dev operation tokens (base operations, pipeline names, or user-defined action names) to be combined in a single invocation. Tokens are resolved per FR-DEV-021, expanded to a flattened sequence of runnable steps (base operations and user-defined actions), then normalized by duplicate-step merge (FR-DEV-015a), and executed in order. The accepted steps are plan, spec, work, e2e, triage, stage, commit, and user-defined actions (repository-local prompt or script). Named pipelines (built-in, repository-local, or preference) are shorthand for such sequences. Init may be followed only by tokens that expand to plan, spec, work, and e2e (not triage, stage, commit, or user-defined actions). See FR-DEV-001a, FR-DEV-015, FR-DEV-015a, FR-DEV-021, and IF-DEV-001.