| name | rack-advanced |
| description | Advanced rack beyond day-to-day lifecycle: authoring a new custom backlog, and cross-project work. Use when defining a backlog beyond the built-in tasks/HYP/PROP, or registering and searching backlogs across several projects. |
| license | MIT |
Rack Advanced
Advanced rack operations beyond day-to-day lifecycle:
When to Use
Reach here when the task is to define a new backlog (a distinct catalog with
its own id prefix, state machine, fields, and link kinds) — e.g. a project
decisions, experiments, or incidents backlog alongside the built-in
tasks / hypotheses / proposals.
Also reach here for cross-project work — registering projects with rack root
(Registry) or listing / reading one backlog across several projects
(Search across projects).
Not this skill when you are:
- filing a card (
rack create, or the backlog-create shim) — that adds a
card to an existing backlog, it does not define one;
- driving a card's lifecycle / ops (transition, log, link, hyp/proposal
verbs) — that is hatrack.
The moment you are editing backlog.yaml itself (not a task.yaml), you are here.
Authoring a new backlog
There is no rack init / register verb — a backlog is drop-a-file. Author
the definition, place it under tracker/, and it mounts on the next rack call.
-
Pick identity. A prefix (cards become <prefix>-<n>) unique within the
root, and a cli_alias (the short rack <group> name) unique across mounted
backlogs. Do not reuse HATS or an existing alias (DuplicatePrefixError /
DuplicateGroupNameError at mount).
-
Make the catalog dir under <ai_hats_dir>/tracker/ — convention
tracker/backlog/<name>/, a sibling of tasks/ (where the shipped HYP/PROP
live). Anywhere under tracker/ works except inside the tasks catalog.
-
Author backlog.yaml at that dir's root. Full key grammar +
fail-closed invariants: references/backlog-yaml-grammar.md. Fastest
start — print a shipped example (HYP / PROP) from the installed package and
adapt (works in any project, no repo checkout needed):
python -c "from ai_hats_rack.definition import packaged_definition_source as s; print(s('hypotheses'))"
(If a bare python can't import ai_hats_rack, use the interpreter that
backs rack.) Or start from the minimum skeleton:
name: decisions
prefix: DEC
cli_alias: decision
fsm:
initial: open
states: [{ name: open }, { name: accepted }, { name: superseded }]
edges:
- { from: open, to: accepted, name: }
{ , , }
{ , , }
{ , , }
Before you save — invariant checklist (each is a fail-closed loader error):
- Every top/section key is in the grammar — a stray key (e.g.
sections:) is
rejected (UnsupportedBacklogKeyError).
links.kinds is non-empty (LinksRegistryError otherwise) — a backlog
with no cross-links still needs one kind; related (symmetric) is the minimum.
- No edge
name equals a state name (EdgeNameStateCollisionError).
- Any stored inverse pair (
inverse: on both, both stored) carries
handlers: [mirror-link] (MissingMirrorReactionError). Symmetric
(related) and derived (children) kinds are exempt.
- Complex fields are
type: any plus a validator: name.
- Every handler / validator / extension name resolves in the stock code-side
registry — you cannot introduce one in YAML alone.
Plan sections are separate. If (and only if) the backlog uses the plan gate,
its plan-section catalog lives in a sibling plan-sections.yaml, not a
backlog.yaml key (HATS-635 never-drift). Most custom backlogs need neither.
What the backlog gets for free
Once mounted, the definition alone yields (no per-backlog code):
rack <alias> create … — schema-driven from fields (required/choices enforced).
rack <alias> update <ID> --<field> … — scalar (str/int) field edits.
- Extension verbs any declared
extensions contribute (e.g. append-verdict).
- Prefix-routed reads/moves:
rack context <ID>, rack ls <ID>, rack transition <ID> <state|edge-name>, rack ls --backlog <alias>.
Registry
To sweep a backlog across projects (below), the projects must be known. Note the
contrast with Authoring a new backlog: a backlog
needs no registration (drop a backlog.yaml and it auto-mounts), but a project
is registered explicitly — a persistent list at ~/.ai-hats/roots.yaml (override
the path with RACK_ROOTS_FILE):
rack root add <path>
rack root ls
rack root rm <path>
root_id is the project's folder name (no alias). add validates the path is
a project, stores it resolved, and de-dupes (a re-add is a no-op); rm takes the
same path. Registering is optional — --root (below) sweeps ad-hoc roots without
touching the file.
Search across projects
List or read one backlog across several projects — "hypotheses of all
projects" in one command (HATS-1081) — over the registered roots (Registry)
and/or ad-hoc --root paths.
List on the no-id scan (rows gain a project marker column / project json
key; the current project is always included, roots dedup by real path):
rack ls --projects all
rack ls --projects projA,projB
rack ls --root ../other
rack ls --backlog hyp --projects all
Read — a bare id is ambiguous when two projects share a prefix, so qualify it
(or mount an unregistered project by path):
rack context projB:HATS-9
rack context projB:HATS-9 --root ../projB
Boundaries.
- Read-only. The sweep and qualified
context are reads; cross-project
writes (qualified transition, cross-project create, cross-project links)
are out of scope — a sweep never mutates another project.
- Read-tolerant registry, fail-fast
--root. A registered root that has
vanished is skipped with a non-silent footer (the sweep survives); an explicit
--root to a non-project is a hard error.
- Folder-name collision (two
ai-hats clones): the <root_id>:<id> qualifier
is ambiguous — disambiguate with the full path via --root.
Completion
Authoring a backlog — done when: the backlog.yaml sits under tracker/,
rack --help shows its group, and rack <alias> create + rack context <PREFIX>-001 both work — with no loader error on any rack call.
Cross-project sweep — done when: rack root add <path> then rack root ls
shows it, and rack ls --projects all lists cards from the registered project(s)
alongside the current one, each row carrying its project marker.
Validation scenario (RED → GREEN). RED — asked to add a project decisions
backlog, an agent without this skill hunts for a rack init-backlog/register
verb that does not exist, reuses the HATS prefix (DuplicatePrefixError), puts
a sections: key in the file (rejected — unknown key), or declares a
supersedes/superseded_by pair with no mirror-link
(MissingMirrorReactionError) — a multi-error discovery loop. GREEN — with the
skill it writes tracker/backlog/decisions/backlog.yaml (unique prefix: DEC +
cli_alias: decision, valid fsm/fields/links, no sections:, mirror-link on
any stored inverse), drops it in, and rack decision create "…" works first try.
Anti-Patterns
- Searching for a create/register/init command — mounting is drop-a-file;
Workspace.discover does it.
- Reusing
HATS (or another backlog's prefix / alias) — collisions fail closed
at mount.
- Putting a
sections: (or any undeclared) key in backlog.yaml — plan sections
are a sibling plan-sections.yaml; unknown keys are rejected.
- Declaring a stored inverse link pair without
mirror-link — the reverse edge
would drift, so the loader refuses it.
- Inventing a handler / validator name in YAML with no code-side factory — the
name must resolve in the stock registry or composition fails closed.
- Expecting
--projects / --root to route a write — the sweep is read-only;
cross-project mutation is out of scope.
- Leaning on a
<root_id>:<id> qualifier when two roots share a folder name — the
id is ambiguous; use the full path via --root.
- Hand-editing
~/.ai-hats/roots.yaml for a bad path — rack root add validates
and resolves; a non-project path is rejected there.