Imports:
  - Types:
      - HookRegistry
      - emit_hook_event
      - declared_actions
    Usages:
      - registering-hooks
      - declaring-actions
    From: goga/hooks

Usages:
  convention: .goga/usages/conventions.md

Annotations: |
  The `convention` practice is used for:
  - Working with the codebase
  - Organizing the REPL development cycle
  - Debugging and testing
  - Organizing the test infrastructure
  - Understanding the general principles and rules of development and testing in the project

  This cell owns the topic status scale: the built-in artifact axis, the
  semantics of tool-status registration — what a registration may carry,
  the anchoring and the add-only rule over the immutable built-in axis —
  and the computation of a topic's maximal present statuses. Tool packages
  reach the scale through the status action: the cell emits the action and
  each subscribed tool registers through the registry delivered to its
  hook. Pure scale logic — no package enumeration, no facade imports, no
  callback invocation, no filesystem probing of topic directories, no git
  access, no CLI, no output rendering. Use the `registering-hooks`
  practice for the tool-facing registration contract behind the action.
  Use the `declaring-actions` practice for the emission contract of the
  action. Use relative imports.

---

"StatusScale(stages: list[Stage])":
  location: scale.py
  annotations: |
    The assembled partially ordered scale of topic statuses — the single
    source of scale order and maximal-status computation.

    `stages`: the scale content in scale order

    Apply the `convention` practice for the data-model rules and
    intra-package imports.

    Requirements:
    - The built-in axis is ordered empty, todo, defined, discovered,
      backlog, designed, specified, planned, done by the artifacts
      todo.md, prd.md, adr.md, task.md, arch.md, design.md, plan.md,
      completed/plan.md
    - The built-in empty entry carries the empty artifact path — one name
      more than the artifact list: it is never markable and surfaces as
      the single status of a topic with no artifact present
    - A tool status never reorders or replaces a built-in one
  properties:
    "stages -> list[Stage]": |
      The scale content in scale order.
  methods:
    "maximal_present(paths: list[str]) -> statuses: list[str]": |
      Compute the maximal present statuses of one topic.

      `paths`: the artifact paths present in a topic directory, relative to
               it
      `statuses`: the qualified names of the maximal present statuses, in
                  scale order

      Apply the `convention` practice for docstring style and intra-package
      imports.

      Algorithm:
      1. Mark every scale entry whose artifact path is present
      2. Drop every marked entry strictly below another marked entry
      3. Return the names of the surviving entries in scale order

      Requirements:
      - Every maximal entry is returned — a present entry outranked by no
        other present entry stays visible
      - A topic with no artifact present yields the single built-in name
        empty

      Constraints:
      - Do not probe the filesystem — presence is decided by the caller's
        `paths` input alone
    "resolve_status(name: str) -> stage: Stage": |
      Resolve a qualified status name for filter validation.

      `name`: a status name as entered by a consumer — a built-in name or a
              qualified tool name
      `stage`: the scale entry carrying the name

      Apply the `convention` practice for docstring style and intra-package
      imports.

      Algorithm:
      1. Find the scale entry whose qualified name equals `name` exactly
      2. An unknown name raises a clean error

      Constraints:
      - Do not fuzzy-match or fall back — the exact qualified name is the
        contract

"Stage(name: str, filepath: str, before: str | None = None, after: str | None = None)":
  location: scale.py
  annotations: |
    One entry of the scale — a named position anchored to the artifact that
    marks it.

    `name`: the qualified name — bare for built-in entries, <tool>.<name>
            for tool entries
    `filepath`: the artifact path relative to the topic directory; nested
                paths allowed; empty only for the built-in empty entry —
                such an entry is never marked present
    `before`: the qualified name of the entry this one precedes; None for
              built-in entries
    `after`: the qualified name of the entry this one follows; None for
             built-in entries

    Apply the `convention` practice for the data-model rules and
    intra-package imports.

    Requirements:
    - A tool entry carries at least one anchor; a built-in entry carries
      none — its position is the axis order
    - A filepath of the empty string belongs to the built-in empty entry
      alone — no tool entry may register the unmarked floor
  properties:
    "name -> str": |
      The qualified status name.
    "filepath -> str": |
      The artifact path relative to the topic directory.
    "before -> str | None": |
      The qualified name of the entry this one precedes, or None.
    "after -> str | None": |
      The qualified name of the entry this one follows, or None.

"StatusRegistry(builtin_stages: list[Stage], tool_prefix: str)":
  location: registry.py
  annotations: |
    The controlled registration surface handed to a tool package — the only
    way a tool status enters the scale. One registry instance is the context
    view delivered to the hook of one subscribed tool; the hook registers
    through it and nothing else.

    `builtin_stages`: the immutable built-in axis the registry extends
    `tool_prefix`: the qualifier applied to every name registered through
                   this registry — the tool identity of the receiving hook

    Apply the `convention` practice for the data-model rules and
    intra-package imports.

    Requirements:
    - Registration is add-only — a built-in entry is never modified,
      removed, or re-anchored
  properties:
    "stages -> list[Stage]": |
      The built-in axis plus every accepted tool entry.
  methods:
    "register(name: str, filepath: str, before: str | None = None, after: str | None = None)": |
      Register one tool status.

      `name`: the status name as the tool defines it — stored qualified as
              <tool_prefix>.<name>
      `filepath`: the artifact path relative to the topic directory
      `before`: optional anchor — the qualified name of an entry this one
                precedes
      `after`: optional anchor — the qualified name of an entry this one
               follows

      Apply the `convention` practice for docstring style and intra-package
      imports.

      Algorithm:
      1. Qualify `name` with the registry's tool prefix
      2. Validate the content: a non-empty name, a non-empty `filepath`,
         and at least one anchor present
      3. Reject a name already registered in this registry — the qualified
         name is the identity, one entry per name
      4. Append the entry to the registry content

      Requirements:
      - A structural violation raises a clean registration error naming the
        entry
      - Both anchors given define a placement range; anchor resolution and
        range validity are decided at scale assembly

      Constraints:
      - Do not resolve anchors here — a tool may anchor to an entry
        registered by another tool
      - Do not modify built-in entries

"assemble_status_scale() -> scale: StatusScale":
  location: assembly.py
  annotations: |
    Assemble the full status scale — the built-in axis extended by every
    tool subscribed to the status action.

    `scale`: the assembled scale

    Apply the `convention` practice for docstring style and intra-package
    imports.
    Use the `declaring-actions` practice for the emission contract.
    Use the `registering-hooks` practice for the registration contract
    behind the action.

    Algorithm:
    1. Build the built-in axis of nine entries
    2. Create the run registry via `HookRegistry`
    3. Emit the status action — the address resolved against
       `declared_actions` — via `emit_hook_event`: the context view of one
       receiving tool is a `StatusRegistry` over the axis, qualified by
       the tool identity — at most one `StatusRegistry` per tool identity,
       all hooks of the tool share it; a hook registers its statuses
       through the delivered registry
    4. Collect the registries of the subscribed tools in enumeration order
    5. Resolve the anchors of each surviving entry against the list
       assembled by the moment the entry is processed — the built-in axis
       plus the entries of the earlier tools and the earlier entries of
       the current one; an anchor naming anything else, or an invalid
       placement range, skips the registration with a warning in the log
    6. Assemble and return the scale

    Requirements:
    - The emission assembles the registry on first use — the single build
      of the run
    - The scale assembles on the caller's demand at most once per run —
      every command that renders statuses calls it before any output and
      before any state change
    - A failing or rejected registration never cancels the others
    - Entries sharing an anchor form one continuous block in registration
      order: an after-anchored entry lands at the end of its anchor's
      block, a before-anchored entry right in front of its anchor, and
      both anchors given define a range the entry must fit into

    Constraints:
    - Do not enumerate the installed tool packages and do not import their
      facades — the platform carries the tool packages
    - Do not cache the scale across command runs

---

Author: Goga
CreatedAt: 29/08/26
Description: |
  Owner of the topic status scale — the built-in artifact axis, tool-status
  registration, and maximal-status computation.
