Plan-tune cathedral T10. Reads auq-other free-text events from this
project's question-log.jsonl, calls Claude via the Anthropic SDK to extract
structured proposals (preference candidates, declared-profile nudges, memory
nuggets), writes them to distillation-proposals.json for the user to review
via /plan-tune (never autonomous — every apply requires explicit Y).
Subcommands:
gstack-distill-free-text # sync distill
gstack-distill-free-text --background # detach + return PID
gstack-distill-free-text --dry-run # emit prompt + events, no API call
gstack-distill-free-text --status # run history + cost-to-date
D7 rate cap: 3 distills per slug per day. Reads ~/.gstack/distill-cost.jsonl
for the count, exits with RATE_CAPPED when limit hit. Cost log lines tagged
by slug so sibling projects don't share the cap. Yesterday runs don't count.
D6 API auth: Anthropic SDK direct, fail-loud on missing ANTHROPIC_API_KEY
with explicit message that distill is a separate billing surface from the
interactive Claude Code session. Uses claude-haiku-4-5 for cost (~$0.001/
1k input, $0.005/1k output) — sufficient for structured extraction.
D14 execution context: --background spawns detached (nohup) so auto-trigger
during /ship doesn't add 30s of pause; results surface on next /plan-tune.
Source events get distilled_at:<ts> stamped on them after the run so they
don't re-propose on the next distill. Match by ts + question_id.
Cost-log line per run includes: slug, proposals_count, rejected_low_confidence,
input_tokens, output_tokens, cost_usd_est. /plan-tune stats reads this to
show "$X estimated, N runs this month" per Layer 4 surface.
10 unit tests cover --status, rate cap (3/day, yesterday-not-counted,
other-slug-not-counted), no-log/no-free-text paths, --dry-run, missing
API key, --background spawn. The actual SDK call is exercised by the T16
E2E test (uses real key, ~$0.001 per run).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>