Imports:
  - Types:
      - BoardRecord
      - collect_topic_board
      - switch_topic
      - create_topic
      - resolve_delete_targets
      - delete_topics
    Usages:
      - topic-board
      - switching
      - todo-entry
      - creating
      - publishing
      - deleting
    From: goga/topics
  - Types:
      - load_project_config
      - TopicsConfig
    Usages:
      - project-configuration
    From: goga/config

Usages:
  convention: .goga/usages/conventions.md
  click: .goga/usages/cooks/click.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

  Use the `click` practice to build the topics command group: the group
  decorator with its own option, the subcommand registration, the
  flags, arguments, and options of each subcommand, echo, exit-code
  propagation, the confirmation of the delete subcommand, and the
  clean error rendering.

  Use the `creating` and `publishing` practices for the creation and
  publication contracts of the domain, the `switching` practice for
  the switching contract, the `todo-entry` practice for the todo entry
  behind the --todo flags, the `topic-board` practice for the board
  contract, and the `deleting` practice for the deletion contract of
  the domain. Use the `project-configuration` practice for the schema
  of the topics section.

  The creation inputs resolve their values at this layer: the base —
  a flag beats the topics section — `TopicsConfig` — of the project
  configuration read via `load_project_config`, which beats the
  current HEAD requested explicitly; no base at all is a clean error
  naming the flag and the configuration line. The commit message
  template — a flag beats the topics section; the built-in default
  lives in the domain. The switch flag passes through to the domain
  like the todo value — no resolution happens here. The configuration
  is read only for values no flag provided.

  This cell is the CLI surface of the topics domain: a thin wrapper
  that resolves inputs, delegates every computation to the domain
  routines, renders the board, and confirms the deletion. No inventory
  walking, no switch resolution, no git access, no editor session live
  here. Domain errors surface as clean CLI errors (stderr, non-zero
  exit, no traceback). Use relative imports.

---

"topics(year: str | None = None)":
  location: topics.py
  annotations: |
    The goga topics command group — a click.Group container for the
    topics subcommands, exported via __all__ and registered in the
    root application group. The group carries the year scope every
    subcommand shares.

    `year`: the --year/-y group option — exactly one year, four digits
            the recognized form; None means the current year; a search
            across years does not exist

    Use the `click` practice for the group decorator, the group option,
    and the subcommand registration.

    Subcommand surfaces:
    - board — a --remote/-r flag, an --info/-i flag
    - create — a NAME positional, a --todo/-t option, a --publish/-p
      flag, a --base-ref option, a --from-current flag, a --commit/-c
      option, a --switch/-s flag
    - switch — an IDENTIFIER positional, a --todo flag
    - delete — IDENTIFIER positionals, a --yes/-y flag

    Apply the `convention` CLI command docstring rule for the --help
    text (rendered verbatim by Click; omit Args/Returns/Raises).
  methods:
    "board(remote: bool = False, info: bool = False) -> exit_code: int": |
      Subcommand goga topics board: print the board — the cross-branch
      topic inventory of the scoped year as a three-column table, or a
      four-column table with the todo column under --info/-i.

      `remote`: the --remote/-r flag — read remote-tracking refs instead of
                local branches
      `info`: the --info/-i flag — add the todo column to the table
      `exit_code`: 0 on success (an empty board included), 1 on error

      Apply the `topic-board` practice for the board contract of the
      domain.
      Apply the `click` practice for the flag, echo, and the color rules.

      Algorithm:
      1. Collect the board via `collect_topic_board` with the scoped year
         and `remote`
      2. Measure the terminal width
      3. Render the table via `render_topic_board` with the records, the
         width, and `info`
      4. An empty board renders nothing — exit 0

      Requirements:
      - Read-only — nothing is created, written, or switched

      Constraints:
      - Do not print the year or the artifacts, and no heading line
        outside the table — the table carries topic, branch, the todo
        column under `info`, and statuses only
    "create(branch_name: str, todo: str | None = None, publish: bool = False, base_ref: str | None = None, from_current: bool = False, commit_message: str | None = None, switch: bool = False) -> exit_code: int": |
      Subcommand goga topics create: create fresh work — a branch off
      the resolved base with the name as entered, its topic of the
      scoped year, and an optional multi-line todo; by default the
      branch is planted at one commit carrying the topic's todo.md and
      the caller stays on their branch; under --switch the branch is
      checked out with its topic directory in the working copy; under
      --publish the work is created off the base and published to
      origin without switching.

      `branch_name`: NAME positional — the branch name as entered
      `todo`: the --todo/-t value — the todo text; only the value form
               exists, an empty value counts as an absent option
      `publish`: the --publish/-p flag — the publication path without
                 the ask
      `base_ref`: the --base-ref value — the base of the branch; valid
                  with and without `publish`
      `from_current`: the --from-current flag — the current HEAD as
                      the base
      `commit_message`: the --commit/-c value — the message template;
                        publication-only
      `switch`: the --switch/-s flag — the checked-out creation; acts
                only without `publish`
      `exit_code`: 0 on success, 1 on error

      Apply the `creating` practice for the creation contract of the
      domain.
      Apply the `publishing` practice for the publication contract of
      the domain.
      Apply the `project-configuration` practice for the topics
      section schema.
      Apply the `click` practice for the options and exit-code
      propagation.

      Algorithm:
      1. `commit_message` without `publish` -> clean error: the option
         is publication-only
      2. `switch` together with `publish` -> clean error: the
         publication never switches
      3. Resolve the base — `base_ref`, otherwise the topics section of
         the configuration loaded via `load_project_config`, otherwise
         the current HEAD under `from_current`; no base at all ->
         clean error naming the flag and the configuration line,
         before anything else
      4. Resolve the template — `commit_message`, otherwise the topics
         section, otherwise None (the built-in default lives in the
         domain)
      5. Delegate to `create_topic` with the name, the base, the todo,
         `publish`, the template, the scoped year, and `switch`
      6. Echo the single result line
      7. Propagate the exit code

      Requirements:
      - The configuration is read only for values no flag provided; a
        missing configuration file counts as an unset value; a present
        but invalid one surfaces its own clean error

      Constraints:
      - Do not validate the name at the CLI layer — the domain and git
        own that
      - Do not open the editor here — the entry belongs to the domain
      - Do not switch branches or render the board here — every
        computation belongs to the domain
    "switch(identifier: str, todo: bool = False) -> exit_code: int": |
      Subcommand goga topics switch: bring the repository onto the
      branch hosting the requested work; under --todo enter the todo
      of the switched topic after the switch.

      `identifier`: IDENTIFIER positional — a branch name, a topic
                    slug, or their prefix
      `todo`: the --todo flag — enter the todo of the switched topic
      `exit_code`: 0 on success, 1 on error

      Apply the `switching` practice for the switching contract of the
      domain.
      Apply the `click` practice for the flag and exit-code
      propagation.

      Algorithm:
      1. Delegate to `switch_topic` with the identifier, the flag, and
         the scoped year
      2. Echo the single result line
      3. Propagate the exit code

      Constraints:
      - Do not launch any pipeline — continuation is a separate
        command
    "delete(identifiers: tuple[str, ...], yes: bool = False) -> exit_code: int": |
      Subcommand goga topics delete: resolve and delete identified
      topics — the local branch, the origin twin, and the topic
      directory — under one confirmation for the whole list.

      `identifiers`: IDENTIFIER positionals — branch names, topic
                     slugs, or their prefixes
      `yes`: the --yes/-y flag — skip the confirmation
      `exit_code`: 0 on success (a declined confirmation included), 1
                   on error

      Apply the `deleting` practice for the deletion contract of the
      domain.
      Apply the `click` practice for the confirmation, echo, and
      exit-code propagation.

      Algorithm:
      1. Resolve the targets via `resolve_delete_targets` with the
         identifiers and the scoped year — a resolution error is clean
         and deletes nothing
      2. Without `yes`: no interactive terminal -> clean error;
         otherwise print the topic-to-branch pairs of the list and ask
         one confirmation for the whole list; a declined answer exits
         0 with nothing deleted
      3. Delegate to `delete_topics` with the targets and the scoped
         year
      4. Echo the single result line
      5. Propagate the exit code

      Requirements:
      - One confirmation for the whole list — never per topic
      - The -y short form collides with the group --year; the
        positions on the command line distinguish them

      Constraints:
      - Do not offer an interactive choice among ambiguous candidates —
        ambiguity is a domain error
      - Do not delete anything before the resolution and the
        confirmation are complete

"render_topic_board(records: list[BoardRecord], width: int, info: bool = False)":
  location: render.py
  annotations: |
    Render the board as a table: topic, branch, and statuses — under
    `info` the todo column sits between branch and statuses.

    `records`: the collected board records — already sorted by the domain
    `width`: the measured terminal width in columns
    `info`: True adds the todo column and switches to the four-column
            width rule

    Apply the `click` practice for echo.

    Algorithm:
    1. Compute the column widths from `width` alone per the width rule of
       the requirements — the three-column rule without `info`, the
       four-column rule with it; the grid is fixed and independent of the
       record content
    2. Print one header row and one separator row with column and row
       dividers — the column order is topic, branch, todo, statuses
       under `info`
    3. Print each record: every text column truncated with an ellipsis when
       it exceeds its column, the statuses wrapped onto continuation lines
       without affecting the column widths
    4. Print one row divider after every record — the same dash run as the
       header separator — the last record included; the wrapped
       continuation lines of one record stay undivided
    5. Mark the record hosting the current branch with an asterisk; keep
       the remote prefix of a remote host visible in the branch column
    6. An empty `records` prints nothing

    Requirements:
    - Three-column widths: topic and branch get an equal share first,
      statuses take the remainder — each of topic and branch is capped at
      one third of `width` minus the dividers, statuses receives what is
      left, and every column keeps a minimum of 8 columns before
      truncation applies
    - Four-column widths under `info`: topic, branch, and todo get an
      equal share — each capped at one quarter of `width` minus the
      dividers, statuses receives the non-negative remainder, and every
      column keeps a minimum of 8 columns before truncation applies
    - The todo column header is the word todo
    - A todo of None or an empty string renders an empty cell
    - The truncation marker is a single ellipsis character
    - An overlong status is truncated like the other columns
    - A row divider — identical to the header separator row — closes every
      record: it prints after the wrapped statuses of each record, the
      last record included, and never between the continuation lines of
      one record
    - The table never exceeds `width`, with one documented exception: when
      the minimum columns no longer fit — below the narrow threshold of the
      active column rule — every column keeps its minimum of 8 and the
      table may exceed `width`; minimum readability wins over the width cap
      on ultra-narrow terminals

    Constraints:
    - Read-only on `records` — do not mutate, do not re-sort, do not filter
    - Do not print the year or the artifacts

---

Author: Goga
CreatedAt: 29/08/26
Description: |
  The goga topics command group — board, create, switch, and delete —
  over the topics domain.
