Skip to content

Writing a new Rotkeeper command

Command development requirements, the help page contract, plain-language style, sidecar schema, and validation.

Browse Docs pages

Every Rotkeeper command is a Bash script invoked through the dispatcher. This page defines the script requirements and the command reference contract. Use rc-preflight.sh as a minimal script example and the generated assets reference as the help-page model.

Where new behaviors belong

Required shape (in order)

  1. Shebang and strict mode:
    #!/usr/bin/env bash
    set -euo pipefail
    IFS=$'\n\t'
    
  2. Header block identifying the script, purpose, version, and update date. Keep the Project / Script / Purpose / Version / Updated layout. Include Env assumptions, CWD assumptions, and Input/Output contracts. Name actual inputs, outputs, dependencies, and dry-run exceptions.
  3. Help block between # @HELP and # @END-HELP, containing a title, Usage, Description, Options, realistic Examples, and Exit codes. Use the literal {VERSION} token. The shared emitter rk_show_help substitutes the loaded version for --help/-h; rc-autopsy.sh extracts the same text. Define a custom show_help only for runtime content, such as rc-new template completion. Support --version/-v, --dry-run, and --verbose where applicable. Show dispatcher commands in examples.
  4. Bootstrap (always, in this order):
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    source "$SCRIPT_DIR/rc-utils.sh" || { echo "FATAL: cannot source rc-utils.sh" >&2; exit 1; }
    rk_init_script "rc-<name>" "$@"
    
    rk_init_script parses common flags, installs traps, loads the canonical environment through rk_load_env strict, and opens the log. Do not source rc-env.sh directly. ROT_SKIP_ENV=true is reserved for help extraction by rc-autopsy.sh.
  5. Version: read via the shared rk_load_version (sourced by rc-utils.sh). Never hard-code version strings.
  6. Sidecar: provide a reviewed file sidecar under bones/meta using the sidecar contract. Keep explanatory details there, rather than editing a generated reference page.

Registration

Command reference contract

The contract identifier is rotkeeper.command-reference.v1. DIP owns command reference pages; authors own task guides. Change the script annotations, help block, sidecar, or CHANGELOG when reference information is missing or wrong. Do not repair a generated page by hand.

Frontmatter

Use this key order and shape. Values shown here are placeholders, not additional copies of command metadata:

---
reference_contract: "rotkeeper.command-reference.v1"
title: "rc-name.sh"
slug: "rc-name"
target_file: "bones/scripts/rc-name.sh"
template: "rotkeeper-doc.html"
status: "active"
version: "<loaded Rotkeeper version>"
author: "Rotkeeper DIP"
project: "Rotkeeper"
description: "<script Purpose line>"
---

target_file is repository-relative and determines ownership. version comes from the shared version loader, not the script's historical header stamp. Omit generation timestamps so an unchanged source produces unchanged bytes. status: active identifies the page type; it is not evidence of completeness.

Section order and sources

Use exactly one H1, the script filename. Every section below is an H2 and appears once, in this order:

Section Authoritative source Content
Overview Header Purpose What the command does
Usage @HELP Usage Dispatcher syntax in a Bash code block
Options @HELP Options Flags, arguments, defaults, and relevant modes
Examples @HELP Examples Runnable dispatcher examples in a Bash code block
Exit codes @HELP Exit codes Success, expected failures, and propagated statuses
Reads and writes Header Env assumptions, CWD assumptions, Input/Output contracts Actual variables, dependencies, input/output paths, and dry-run behavior
Side effects SIDE EFFECT (...) annotations Writes, moves, deletions, and relevant guards
Notes Reviewed file sidecar body Design, Limits, and Cautions
History CHANGELOG Whole matching bullets with continuation lines, grouped by release

Use H3 for Notes subheadings and release headings in History. Deeper headings are unnecessary. Keep usage, options, and exit-code text literal; do not interpret flags or paths as Markdown markup.

If a source is missing, retain the section and state what is not documented. Do not invent an overview, imply that an empty section is complete, or replace per-script contracts with a generic environment list. Description text that carries facts beyond Purpose must be incorporated into Purpose, the contracts, or the sidecar. The full engine work in #327 must include command-specific help sections, such as Modes, under Options.

Plain-language style

The selected pillar names are Notes and History, replacing Necromancer's Notes and Ritual History. Keep the existing DIP-SOUL-EXTRACTED and DIP-HISTORY-EXTRACTED marker identifiers. The engine migrates marker-owned headings alongside reference generation, including boundary recognition, stub templates, and TODO counting. It also recognizes old heading names while reading older sidecar tails.

Sidecar contract

File sidecars use this frontmatter:

---
target_file: "bones/scripts/rc-name.sh"
reviewed: "YYYY-MM-DD"
reviewed_against: "<Rotkeeper version>"
---

Use exactly these H3 body headings: Design, Limits, and Cautions. Describe implementation choices, restrictions, and operational risks without duplicating help text. Use an explicit statement when a category has no additional information. Do not add a page H1, a Notes wrapper, DIP markers, or a self-stitched copy of the body.

For directory sidecars, use title, description, and reviewed, with H3 body headings Purpose and Contents and conventions. Directory metadata is merged into generated index frontmatter by rc-glue, so include only keys intended for that page. Do not add file-only ownership/version keys.

File sidecars for DIP core targets mirror repository-relative paths under bones/meta, with the final extension removed before .soul.md. Their target_file is repository-relative. Directory sidecars mirror paths relative to the active content root, keeping the directory name. Glue uses directory frontmatter, not the sidecar body, when creating indexes. It does not merge a sidecar with nonempty target_file into an index.

bash rotkeeper.sh new <file> --soul uses a content-relative lookup path under bones/meta, but still records a repository-relative target_file. It emits the three file headings and TODO text with reviewed: null and reviewed_against: null. These null values identify an unreviewed scaffold, not a completed review. Existing sidecars are preserved; dry-run publishes neither the source nor the sidecar.

Before publishing, review each claim against the target, static help, and shared helpers. Replace the TODO text, then set the file review date and version together using bones/config/version. Directory sidecars record only the review date; do not add a version key that would enter the index. The assets sidecar reference is a reviewed example.

Check that a consumer can reach the sidecar: DIP discovers non-content core files, render looks up content-file sidecars, and glue looks up directories under the content root. DIP skips Markdown cores and .github, and glue does not walk the asset tree. For unreachable notes, fold verified content into an existing reader-facing page and delete the sidecar. When a target is removed, retire its sidecar and obsolete reference page, remove retired whitelist entries, and repair links. Do not add whitelist entries to conceal missing ownership or coverage.

Remove generated tails at the source. Regenerate references with bash rotkeeper.sh book --fsbook followed by bash rotkeeper.sh dip. Review the affected pages and keep unrelated generated changes out of the commit.

Pilot findings and follow-up tasks

The assets pilot is generated by DIP from rc-assets.sh, its sidecar, and CHANGELOG. Its reference_contract field identifies the contract. The engine applies the same source-based rebuilding to all owned script references, whether or not that field already exists. Internal libraries and standalone tools without static help retain every section and explicitly state what is not documented; DIP never executes them to discover their behavior.

Comparison with the previous hand-written page found these source gaps, now corrected for the pilot:

Follow-up tasks for #327:

Follow-up tasks for #329:

Behavior rules

Validation before merge

  1. bash -n on the new script.
  2. shellcheck with the repository .shellcheckrc.
  3. bash rotkeeper.sh test (the full harness, including every layout).
  4. bash rotkeeper.sh status.
  5. The relevant --dry-runs.
  6. Regenerate bash rotkeeper.sh book --fsbook and bash rotkeeper.sh dip. Check the reference against its sources and run DIP again to confirm unchanged page bytes.

On macOS, report a realpath -m portability failure rather than weakening the harness.

Back to: Documentation overview ยท Dispatcher reference