Imports:
  - Types:
      - load_project_config
      - ProjectConfig
      - DepConfig
    From: goga/config

Usages:
  convention: .goga/usages/conventions.md
  git: |
    External git binary invoked via subprocess.run (check=True, capture_output=True).
    Set GIT_TERMINAL_PROMPT=0 in the env to suppress interactive prompts. Mock the
    subprocess call in tests per `convention`.

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

  The domain is config-driven: `sync` loads configuration itself via
  `load_project_config` and reads `ProjectConfig`.usages ({group: {dep: DepConfig}} or
  None). All git operations flow through `clone_repository` via the `git` practice;
  mock subprocess calls in tests per `convention`. Use relative imports.

---

"sync(force: bool = False, group: str | None = None, dep: str | None = None) -> exit_code: int":
  location: sync.py
  annotations: |
    Config-driven synchronization of cell-level usages from declared git dependencies
    into .goga/usages/<group>/<dep>/.

    `force`: True — clean .goga/usages/ (except cooks and root *.md) then re-sync all
             declared deps; False (default) — incremental, skip deps whose target dir exists.
    `group`: optional filter — limit the sync to one group name; None (default) → all groups.
    `dep`: optional filter — limit the sync to one dep name; None (default) → all deps. `dep`
           without `group` applies across every group.
    `exit_code`: 0 on success (incl. "nothing to sync"), 1 if any dep failed.

    Algorithm:
    1. Load configuration via `load_project_config` → ProjectConfig
    2. If ProjectConfig.usages is None → nothing to sync, return 0
    3. usages_root = .goga/usages (relative to CWD)
    4. If `force` → `clean_usages_dir`(usages_root)
    5. For each group, dep, depcfg (`DepConfig`) in ProjectConfig.usages (insertion order),
       **applying the `group`/`dep` filters (a non-matching name is skipped, not an error)**:
       5.1. target = usages_root / group / dep
       5.2. If (not force) and target.exists() → skip (incremental)
       5.3. Else: repo = `clone_repository`(depcfg.git, depcfg.ref); try:
            `deploy_usages`(repo, target, depcfg.root); finally: remove the repo temp directory
       5.4. On per-dep error → log ERROR, set exit_code = 1, continue (best-effort)
    6. Return exit_code

    Requirements:
    - "synchronized" marker = existence of target dir; dep git/ref changes without force do NOT re-sync
    - Clone into a temp dir removed in a finally block
    - deploy receives the dep's optional root verbatim from `DepConfig` (None = walk from
      repo root); `sync` does not resolve or validate root — `deploy_usages` does, against
      the clone
    - A non-matching `group`/`dep` filter value is skipped, never treated as an error; only
      real sync errors on a matching dep set exit_code = 1.

    Constraints:
    - Never touch .goga/usages/cooks or root *.md (`clean_usages_dir` owns that)
    - Do not re-sync an existing dep unless force
    - Sources are git only (no local-path mode)

"clean_usages_dir(usages_root: Path) -> removed: int":
  location: clean.py
  annotations: |
    Destructive cleanup of .goga/usages/ for force: remove every subdirectory except
    cooks; keep all files directly in usages_root.

    `usages_root`: path to .goga/usages/ (relative to CWD)
    `removed`: number of subdirectories removed

    Algorithm:
    0. If `usages_root` does not exist → create it (ensure .goga/usages/ exists) and
       return 0 (nothing to clean)
    1. For each entry in usages_root: skip if name equals cooks; if directory → remove it
       and count; if file → leave untouched
    2. Return removed count

    Requirements:
    - When `usages_root` does not exist, create it and return 0 (no-op clean) — keeps the
      routine safe to call on a fresh project and preserves idempotence
    - Preserve the cooks directory verbatim; preserve every file in usages_root root

    Constraints:
    - Do NOT remove cooks even with force
    - Do NOT remove files in usages_root root (only subdirs, except cooks)
    - Idempotent

"clone_repository(git: str, ref: str | None) -> repo_path: Path":
  location: clone.py
  annotations: |
    Clone a git repository into a fresh temp dir and return its path. Caller owns cleanup.

    `git`: git repository URL (non-empty)
    `ref`: optional git ref — branch, tag, or commit; None → default branch
    `repo_path`: path to the cloned repo (temp dir; caller must remove it)

    Algorithm:
    1. Create a fresh temporary directory
    2. Suppress interactive prompts per the `git` practice
    3. Clone `git` into the temporary directory via the `git` practice
    4. If `ref` is not None → check out `ref` inside the temporary directory (uniform for branch/tag/commit)
    5. Return the temporary directory path

    Requirements:
    - Use the `git` practice for subprocess invocation

    Constraints:
    - On success, do NOT remove the temp dir here — the caller removes it in a
      finally block (caller owns the returned path only on the success path)
    - On failure (clone/checkout error), DO remove the temp dir here before
      re-raising, so a failed clone never leaks a temp directory
    - Authentication via stock git; no token injection

"deploy_usages(source_repo: Path, target_dir: Path, root: str | None = None) -> count: int":
  location: deploy.py
  annotations: |
    Deploy cell-level usages from a cloned repo into a target dir: discover .usages folders
    under the optional root, lift their contents (drop the .usages name), and copy each to its
    path relative to the root. Placement is deterministic (no flattening).

    `source_repo`: path to the cloned repository root
    `target_dir`: destination .goga/usages/<group>/<dep>/ (created if missing)
    `root`: optional subpath inside `source_repo` from which to walk .usages and against which
            destination paths are computed; None (default) → walk from `source_repo` root
    `count`: number of .usages folders deployed

    Algorithm:
    1. Compute the walk origin: root is None → origin = `source_repo`; otherwise →
       origin = `source_repo` / `root`. Verify origin is an existing directory; if it is
       missing or not a directory → raise an explicit error (walking a missing path or a file
       yields no .usages — do not silently return 0)
    2. Recursively discover every directory named .usages under origin, skipping
       .git/.hg/.svn; record each with its parent's relative path from origin
    3. Create `target_dir` if missing
    4. For each discovered .usages whose parent rel-path from origin is <rel>: copy its
       contents (files + subdirs) into `target_dir`/<rel>/ (create intermediates; preserve
       non-cell directories); drop the .usages segment from every destination path; <rel>
       empty (.usages directly in origin) → copy into `target_dir` root
    5. Return count of deployed .usages folders

    Requirements:
    - Destination path = the .usages parent path relative to the walk origin (root), with the
      .usages segment dropped — verbatim copy, hierarchy preserved including non-cell
      intermediates (root=folder: folder/cell_1/cell_2 → cell_1/cell_2;
      folder/subfolder/cell_1/cell_2 → subfolder/cell_1/cell_2;
      folder/subfolder/cell_1/another_folder/cell_2 → subfolder/cell_1/another_folder/cell_2)
    - root default None → walk from `source_repo` root (the repo-wide default origin)
    - Drop the .usages segment from every destination path
    - Skip .git/.hg/.svn during discovery and copying; copy directories via the standard
      library per `convention`
    - Copy symlinks verbatim — do NOT dereference them. The source is a freshly cloned
      third-party (untrusted) repository; dereferencing its symlinks would copy the contents
      of arbitrary local files/dirs the links point at into the synced output (local-file
      disclosure / aggregation vector). Copying the links themselves never reads those targets.

    Constraints:
    - Placement is deterministic: a single .usages in the repo lands at its origin-relative
      path — at the target root only when it sits directly in the walk origin (no flattening
      applied)
    - Do NOT keep the .usages name in destination
    - Do NOT delete `target_dir` before deploying (sync owns skip; clean owns removal)
    - Do NOT silently succeed when `root` resolves to a missing path or a file — raise

---

Author: Goga
CreatedAt: 27/07/26
Description: |
  Config-driven synchronization of cell-level usages from declared git dependencies
  into .goga/usages/<group>/<dep>/.
