ai-sessions#13: capture's `summary` field wrote a frontmatter shape (title/harness/fidelity/captured_at/repos_touched) that ai-sessions' own extract/audit pipeline can't parse -- silently invisible to that repo's own audit tooling, and bypassing its redaction + Stage2 completeness gates by construction. Only 5 files ever landed this way over 3 weeks; the summary capability's whole value (speed) fights ai-sessions' whole value (redacted, audited, complete), so kill it rather than build a second parse branch. capture now persists insights -> brain and action items -> Gitea tickets only. Removes Summary/SummaryResult/SummaryWriter and all wiring (service, REST body, MCP tool schema); close-session updated to stop assembling a summary payload. specs/capture-*.md kept as historical record with a superseded note -- the feature shipped and is documented, just no longer current behaviour. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WPdSHbp9Utb2hPFm9wDG59
9.3 KiB
name, description
| name | description |
|---|---|
| close-session | Disciplined end-of-session closeout for a Claude.ai chat before archiving it. Harvests the session's decisions, artifacts, and open threads and durably persists them to the brain MCP and the right Gitea repo so nothing is lost when context resets. Use this whenever the user signals they are wrapping up — phrases like "close this out", "let's wrap up", "before I archive", "session retro", "capture this before I go", "did we lose anything", or any end-of-session/handoff cue — even if they don't say the word "close". Also use when the user explicitly asks to retro, archive, or hand off a working session. |
close-session
Capture a finishing Claude.ai work session into durable storage before the chat is archived and its context is lost. The goal is simple and load-bearing: after this runs, a fresh session (or another agent) can reconstruct what was decided, what was shipped, and what is still open — without the original chat.
This skill is batch: one session in, findings out, done. Run the phases in order. Stop at any confirmation gate that says STOP.
Operating constraints (read first)
- Gitea owner is always
mathias. Never guess another owner. - Ground-truth at HEAD before acting. Issue bodies and doc references rot — stale hostnames, retired services, moved endpoints. Before closing/commenting on any issue,
gitea:issue_getit fresh. Before asserting an infra fact, verify it; do not copy it from memory or from a stale issue body. - Current infra truths (verify rather than trust, but these are the known-good baseline): Gitea is
git.d-ma.be(notgitea.d-ma.be). LiteLLM ishttp://koala:30401/v1/(publichttps://llm-api.d-ma.be); piguard runs NGINX Proxy Manager only — never referencepiguard:4000orkoala:4000. Identity provider is Authentik (Dex migration complete). - Side-effects need a confirmation gate. Closing issues and capturing to brain/Gitea/ai-sessions are real writes. Surface exactly what will happen and get a clear yes before doing it. Reads are free; writes are gated.
- Never fabricate. If the session didn't produce a decision worth persisting, say so and skip that write. An empty-but-honest closeout beats an invented one.
Phase 1 — Harvest
Reconstruct what actually happened this session from the conversation itself. Produce, in working memory:
- Decisions taken — what was decided and the reasoning, not just the outcome.
- Artifacts produced — issues filed/closed, PRs opened/merged, files committed, brain notes written, ADRs. Capture identifiers (issue numbers, PR numbers, paths, commit SHAs) as you go.
- Open threads — what was deferred, what's blocked, what the next session should pick up.
- Generalizable learnings — reusable patterns or footguns that would bite anyone again (these are brain-worthy; project status is not).
Be honest about fidelity: a long session compresses harder at the start than the end. Flag anything you're reconstructing rather than certain of.
Phase 2 — Ground-truth Gitea state
For every repo touched this session, get its true current state before proposing any change. gitea:repo_status (owner mathias) gives branches + open PRs + protection in one call. For each issue you intend to close, comment on, or reference: gitea:issue_get it fresh and compare to what the session assumed. Note any drift (closed-already, body rotted, renamed) — you'll surface it in Phase 3.
Do not write anything in this phase. This is the read pass.
Phase 3 — Plan the issue changes
Decide the issue actions: which to close (with closing comment), which to file (discovered-but-deferred work — token-budget gaps, recorded limitations, v2 follow-ups), which to comment on. Include the exact title/body for any new issue and the closing rationale for any close.
These actions are carried into the Phase 4 capture call as tickets[] rather than executed here with direct gitea:issue_* calls — routing them through capture puts each one into the I5 audit record. (Closing an issue that needs a separate explanatory comment first is the one case to do directly; otherwise prefer the capture path.)
Phase 4 — Capture (one uniform call)
Persist the session via a single capture call (the brain:capture MCP tool, live on the Claude.ai connector). Capture owns the writes server-side — insights → brain, action items → Gitea tickets — plus the I1 sovereignty gate, the I5 audit record, and the supersession/read-after-write discipline. The skill's job is to assemble the payload, not to write each store itself. Do NOT fall back to separate gitea:file_write_branch + brain_write steps unless capture is unreachable (see fallback below).
Note: capture no longer writes a session summary anywhere (the summary → ai-sessions write path was removed — it produced a frontmatter shape ai-sessions' own pipeline couldn't parse, silently invisible to that repo's own audit tooling; see ai-sessions#13). If the session's decisions/artifacts are worth a durable narrative beyond the insights[] this skill writes to the brain, that's a separate, explicit call — not something this skill does implicitly.
Assemble one payload:
insights[]— the generalizable learnings from Phase 1 (decisions/failures worth re-reading). Each:{text, wing, hall}; addsupersede_slugto revise a prior note in place instead of creating a duplicate.hall∈ facts/decisions/failures/hypotheses/sources.tickets[]— the issue actions from Phase 3:{repo, action, ...}where action ∈ create/close/comment. Owner is alwaysmathias(server-forced).context—{harness: "claudeai-chat", session_ref: <chatid8-or-slug>, fidelity: "live-capture", actor: "mathias", classification: <see gate below>}.
THE CLASSIFICATION GATE (read before calling — this is where capture refuses).
Capture computes an effective classification = the strictest across EVERY target it touches (each insight's wing, each ticket's repo), then refuses if that effective level is confidential and the origin is us-nexus (claude.ai is us-nexus). Levels come from classification.yaml at the brain root (source of truth, #67), with the code defaults as the floor: hyperguild/homelab → internal; client-* → confidential; anything untagged → confidential (fail-safe).
- Tagged
internaltoday (safe through claude.ai): wingshyperguild,homelab; reposbrain,ai-sessions,infra,hyperguild,homelab,tapir,agentsquad,jepa-fx-risk,swedsl. Treatclassification.yamlas authoritative — this list is a hint, not gospel. - Declare
context.classification: "internal"for normal homelab work. - Insight
wings and ticketrepos are classification INPUTS, not free-form metadata — every target must resolveinternalor the whole capture escalates toconfidentialand the gate refuses via claude.ai. Listing the central homelab repos (incl.brain/ai-sessions) is now fine; they're tagged. - If a session genuinely touched
client-*or otherwise-untagged material, it cannot be captured through claude.ai — note that in the verdict rather than trying to force it.
GATE — dry-run first, then execute.
- Call
capturewithdry_run: true. It validates the whole payload and returns the would-be receipt +effective_classification, writing nothing. - STOP. Show the dry-run receipt (effective classification, the insights/tickets that would land) and get explicit confirmation.
- On confirmation, call
captureagain withdry_run: false. Read the returned receipt: it is partial-aware (errors[], per-itemok). Report exactly what landed.
If capture is unreachable (tool not on the connector — e.g. a session that started before a deploy; a tool-list refresh usually fixes it): say so. Only then fall back to the legacy inline path (brain_write/brain_update + brain_get confirm), and note in the verdict that the I5 audit record was NOT produced.
Phase 5 — Verdict
Deliver a final "safe to archive" verdict in the chat. Either:
- SAFE TO ARCHIVE — list what landed from the capture receipt (issues closed/filed with numbers, brain note ids/paths) so the trail is auditable. Then list anything still in the user's queue (e.g. a PR awaiting their merge, a decision owed next session).
- NOT YET — name the specific gate that wasn't passed, the capture refusal reason, or the per-item error from the receipt, and what to do about it.
Never claim safe-to-archive if the capture refused, any receipt item errored, or a gated confirmation was declined. The verdict is the skill's contract: if it says safe, the session can be lost without losing the work.
Why the gate and the single-call shape matter
The whole point is durability across a context reset. The capture call is the one place a wrong payload would silently corrupt the record (close the wrong issue, escalate to a refusal, commit a half-truth), which is why it is dry-run-then-confirm. Routing everything through one capture keeps the supersession discipline, the read-after-write confirmation, and the I5 audit trail server-side — the skill never has to carry those rules itself, and every closeout is uniformly audited. Get the payload and the classification right and the skill does what it promises — nothing important is lost when the chat goes away.