2026-07-13 13:18:45 +00:00
#!/usr/bin/env python3
""" session_start.py — deterministic SessionStart injection note for os-backlog.
Modeled on os - adr ' s session_start.py (the wording-source-of-record pattern).
Injects the backlog process rules — WHEN - > THEN mechanical triggers only .
Deliberately contains ZERO board state and no briefs ( notification policy v2 :
pull beats push — board state is fetched on demand via / os - backlog : list , never
pushed into a session ) .
2026-07-13 15:00:26 +00:00
Sibling hook triage_check . rb ( card 1818330518584820864 ) is a narrow , sanctioned
exception to that policy : it makes one bounded Planka read to decide whether to
push a single mechanical dispatch directive — not a brief , not board contents —
when the current project ' s own Backlog has unlabeled cards. The only board-
derived fact it ever surfaces is the unlabeled count that justifies dispatching
card - triage ; it never lists card titles or otherwise renders board state . Kept
as a separate script precisely so this file can stay a pure , network - free
function of filesystem state .
2026-07-13 13:18:45 +00:00
git project - > additionalContext note ( rules only , near - zero tokens )
not a git repo - > silent ( process rules only apply inside real projects )
Autonomy - label semantics in NOTE are the ADR - 029 contract ( which supersedes
the wording in issue #16's body): hitl never picked up autonomously; semi
stops at Review for human sign - off ; afk - ready goes straight to Done after
verification , skipping Review .
Pure function of the project ' s filesystem state; no model, no network, no
Ruby , no Planka call . Always exits 0 — the hook must never block a session .
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import Optional
NOTE = """ \
[ os - backlog ] Backlog process rules ( rules only — this note never carries board state ; board state is pull - only via / os - backlog : list when the user asks ) .
- CAPTURE : WHEN a concrete follow - up task , bug , or piece of deferred work surfaces mid - session that will NOT be done this session - > capture it as a Backlog card via / os - backlog : capture . Never a TODO comment in code , never only a mention in chat .
2026-07-13 14:48:19 +00:00
- ROUTING : WHEN process / backlog / spec - tracking work surfaces - > read the tracker key from . cc - os / config ( ` tracker = planka : < board > ` | ` forgejo : < owner > / < repo > ` | ` github : < owner > / < repo > ` | ` repo : < path > ` ) and send work where it says . WHEN this is a git project and no tracker key is configured - > suggest / os - backlog : route once ; do not nag . A subdirectory ' s own .cc-os/config overrides the repo root ' s for any session started there — umbrella repos route each subproject to its own board / tracker this way ; do not assume one repo means one board .
2026-07-13 13:18:45 +00:00
- PROMOTION : WHEN a Planka - tracked effort accretes significant code or design decisions in card comments - > flag promotion to the user ( never silently ) : carve out a repo , move the durable spec into git issues via / to - issues , and keep the Planka card as a pointer ( title + link ) . Never duplicate spec text into a card description .
- COLUMN OWNERSHIP ( CLI - enforced per ADR - 029 ; a violating card - move fails with the rule named ) : create cards at Backlog only . Never move a card into or out of Next — Next is human - curated in both directions . Never move a card labeled hitl ; never move a card out of Done . Working a card MEANS moving it via card - move : Doing when work starts , Waiting + a blocker comment when blocked , and on shipping : Review ( semi ) or Done ( afk - ready , only after verification ) .
2026-07-13 14:48:19 +00:00
- LABELING / TRIAGE : priority ( P0 - P3 ) and autonomy ( hitl / semi / afk - ready ) labeling is the RESPONSIBLE project ' s job (it knows its own priorities) — a parent umbrella may label its own and its child boards, but never label cards on a board that belongs to a different project you ' re just filing onto . Triage is initiated by this project ' s own SessionStart check (a separate hook), not by capture — if it reports unlabeled Backlog cards, dispatch the card-triage agent (haiku, background) to clear them.
- CROSS - PROJECT : WHEN a needed change belongs to a DIFFERENT project THEN do not edit it — query ` os - backlog projects ` , file an issue / card with that project ' s tracker using the Discoverer template (see os-backlog route references/cross-project-filing.md).
2026-07-13 13:18:45 +00:00
- AUTONOMY LABELS ( ADR - 029 ) : hitl - > human - owned ; never picked up autonomously . semi - > run the mechanical parts , stop at named decision gates ; shipped work goes to Review for human sign - off . afk - ready - > shipped + verified goes straight to Done , skipping Review .
"""
def find_project_root ( cwd : Path ) - > Optional [ Path ] :
""" Nearest ancestor (including cwd) containing .git, else None. """
for candidate in [ cwd , * cwd . parents ] :
if ( candidate / " .git " ) . exists ( ) :
return candidate
return None
def run ( root : Optional [ Path ] , out ) - > str :
""" Pure decision: ( ' context ' | ' silent ' ). Emits hook JSON when in a project. """
if root is None :
return " silent "
out . write ( json . dumps ( {
" hookSpecificOutput " : {
" hookEventName " : " SessionStart " ,
" additionalContext " : NOTE ,
}
} ) )
return " context "
def main ( ) - > int :
try :
run ( find_project_root ( Path . cwd ( ) ) , sys . stdout )
except Exception :
pass # never block a session
return 0
if __name__ == " __main__ " :
sys . exit ( main ( ) )