#!/usr/bin/env python3
"""Stamp `Task:` trailers for in-progress TASKs onto a commit message (GHI #731).

`.gzkit/rules/tests.md` § TASK-Driven Workflow makes a `Task:` trailer mandatory
on `src/**` and `tests/**` commits, and the OBPI pipeline mints one TASK per REQ
— then relies on an author to recall every one of them. Measured 2026-07-29 that
convention had 15% adherence: 87 post-epoch OBPIs minted 467 TASKs appearing in
no trailer at all, leaving Signature (c)'s commit-trailer channel empty for 96 of
102 OBPIs and therefore skipped, so total under-declaration read as "nothing to
compare" rather than maximum drift.

This stamps the attribution the runtime already holds instead of asking a human
to remember it — the producer-side fix, the same move GHI #653 needed twice.

Deliberately conservative:

* An authored `Task:` trailer of ANY form wins. Direct-fix slugs
  (`Task: TASK-fix-thing`) and ceremony trailers (`Task: TASK-gz-git-sync`) are
  never displaced by a formal id the author did not choose.
* Nothing is stamped outside `src/**` / `tests/**`, where the rule does not
  require attribution — inventing one there would pollute the very channel this
  exists to make meaningful.
* Every failure path is a silent no-op. This runs on every commit; a hook that
  raises blocks all work, and a missing trailer is caught downstream by
  `gz validate --commit-trailers` anyway.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path


def _staged_paths(repo: Path) -> list[str]:
    result = subprocess.run(
        ["git", "diff", "--cached", "--name-only"],
        cwd=repo,
        capture_output=True,
        text=True,
        encoding="utf-8",
        errors="replace",
        check=False,
        timeout=30,
    )
    if result.returncode != 0:
        return []
    return [line.strip() for line in result.stdout.splitlines() if line.strip()]


def main(argv: list[str]) -> int:
    """Append missing `Task:` trailers; never fail the commit."""
    if not argv:
        return 0
    message_path = Path(argv[0])

    try:
        sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))
        from gzkit.tasks import active_task_trailers, has_task_trailer

        message = message_path.read_text(encoding="utf-8")
        if has_task_trailer(message):
            return 0  # the author declared attribution; do not second-guess it

        repo = Path(__file__).resolve().parents[2]
        trailers = active_task_trailers(repo / ".gzkit" / "ledger.jsonl", _staged_paths(repo))
        if not trailers:
            return 0

        separator = "" if message.endswith("\n\n") else ("\n" if message.endswith("\n") else "\n\n")
        message_path.write_text(message + separator + "\n".join(trailers) + "\n", encoding="utf-8")
    except Exception:  # noqa: BLE001 — a commit hook must never block work
        return 0
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
