bug: capture relay has no SummaryWriter wired — summary silently fails (ai-sessions path) #66

Closed
opened 2026-06-23 08:36:50 +00:00 by mathias · 3 comments
Owner

Symptom

First live dogfood of brain:capture from claude.ai (2026-06-23 closeout) returned a partial success: insights + ticket landed, but the summary failed with:

"summary": {"ok": false}
"errors": [{"item": "summary", "error": "no summary writer configured"}]

The SummaryWriter port has no concrete implementation wired in the deployed relay — so summary.{title,body,repos_touched} is accepted, validated, and then silently no-ops with an error in the receipt. Insights (BrainStore) and tickets (IssueTracker) work; the ai-sessions summary path does not.

What worked (so this is a wiring gap, not a design fault)

  • 4 insights written to brain (wiki/hyperguild/{decisions,failures}/...)
  • ticket filed (this issue's sibling, #65)
  • effective_classification correctly resolved to internal; I1 gate passed
  • partial-failure semantics worked as specced (§4): no rollback, honest per-item receipt naming exactly the failed component. That's the best-effort design validated under a real failure.

Likely cause

The SummaryWriter impl (writes to mathias/ai-sessions at summaries/<harness>/<YYYY-MM>/...) is either not constructed or not injected into the deployed CaptureService wiring. Capture sub-issue #54/#49d-e built the port + adapter; the deploy (infra #152) may not have provided the ai-sessions Gitea target/token to the relay, or the constructor leaves SummaryWriter nil and the service treats nil as "not configured."

Acceptance

  • SummaryWriter concretely wired in the deployed relay (ai-sessions repo + token/credential present)
  • A capture with a summary block writes the file to ai-sessions and returns summary.ok=true with the path
  • If SummaryWriter is intentionally optional, the receipt should distinguish "not configured" (operator gap) from a write error — and a capture that supplies a summary to a relay without a writer should surface as a louder warning, not a quiet per-item error
  • Verify the deploy manifest (infra k3s/apps/**) provides the ai-sessions target + credential
  • Re-dogfood from claude.ai: full capture (insights + ticket + summary) all ok=true

Repro

Run any brain:capture from the claude.ai connector with a populated summary block. Insights/tickets succeed; summary returns ok:false, "no summary writer configured".

Related

  • #49 (capture epic), #54 (audit/adapter), infra #152 (relay deploy)
  • The 2026-06-23 closeout that surfaced this; insights captured at wiki/hyperguild/decisions/the-capture-capability-shipped-v0-11-0-hyperguild-49.md et al.
## Symptom First live dogfood of `brain:capture` from claude.ai (2026-06-23 closeout) returned a **partial success**: insights + ticket landed, but the summary failed with: ``` "summary": {"ok": false} "errors": [{"item": "summary", "error": "no summary writer configured"}] ``` The `SummaryWriter` port has no concrete implementation wired in the **deployed relay** — so `summary.{title,body,repos_touched}` is accepted, validated, and then silently no-ops with an error in the receipt. Insights (BrainStore) and tickets (IssueTracker) work; the ai-sessions summary path does not. ## What worked (so this is a wiring gap, not a design fault) - 4 insights written to brain (`wiki/hyperguild/{decisions,failures}/...`) - ticket filed (this issue's sibling, #65) - effective_classification correctly resolved to `internal`; I1 gate passed - **partial-failure semantics worked as specced** (§4): no rollback, honest per-item receipt naming exactly the failed component. That's the best-effort design validated under a real failure. ## Likely cause The `SummaryWriter` impl (writes to `mathias/ai-sessions` at `summaries/<harness>/<YYYY-MM>/...`) is either not constructed or not injected into the deployed `CaptureService` wiring. Capture sub-issue #54/#49d-e built the port + adapter; the deploy (infra #152) may not have provided the ai-sessions Gitea target/token to the relay, or the constructor leaves `SummaryWriter` nil and the service treats nil as "not configured." ## Acceptance - [ ] `SummaryWriter` concretely wired in the deployed relay (ai-sessions repo + token/credential present) - [ ] A capture with a `summary` block writes the file to `ai-sessions` and returns `summary.ok=true` with the path - [ ] If `SummaryWriter` is *intentionally* optional, the receipt should distinguish "not configured" (operator gap) from a write error — and a capture that supplies a summary to a relay without a writer should surface as a louder warning, not a quiet per-item error - [ ] Verify the deploy manifest (infra `k3s/apps/**`) provides the ai-sessions target + credential - [ ] Re-dogfood from claude.ai: full capture (insights + ticket + summary) all `ok=true` ## Repro Run any `brain:capture` from the claude.ai connector with a populated `summary` block. Insights/tickets succeed; summary returns `ok:false, "no summary writer configured"`. ## Related - #49 (capture epic), #54 (audit/adapter), infra #152 (relay deploy) - The 2026-06-23 closeout that surfaced this; insights captured at `wiki/hyperguild/decisions/the-capture-capability-shipped-v0-11-0-hyperguild-49.md` et al.
Author
Owner

Koala dispatch — "make capture fully work from claude.ai" (pairs #66 + #67)

Goal: after this session, a brain:capture call from the claude.ai connector with insights + tickets + summary touching the normal homelab repos succeeds fully (all ok=true, no I1 refusal). Two sibling fixes get there; do both, in this order.

Order matters

  1. #67 first (classification.yaml). Until homelab repos are tagged internal, any capture touching ai-sessions fail-safes to confidential and the I1 gate refuses it — which would make a #66 fix untestable from claude.ai. So tag first, then you can actually verify the summary path end-to-end.
  2. #66 second (SummaryWriter wiring). Wire the ai-sessions SummaryWriter into the deployed relay so the summary block stops returning no summary writer configured.

Autonomy boundaries — STOP and ask Mathias, do not invent:

  • #66 credential: wiring the SummaryWriter needs the relay to reach mathias/ai-sessions with a token. If no ai-sessions-capable credential is already in the relay's env/secret, stop — do not mint or hardcode one. Secret provisioning (1Password vault + external-secrets) is Mathias's call, and any new secret + manifest change is a GitOps/I3 change in infra/k3s/apps/**, not a code-only fix.
  • #67 repo→level map: the classification map is a sovereignty decision (which repos are safe through us-nexus), not a mechanical fill. Draft the internal list, but have Mathias confirm it before merge. When unsure, leave a repo untagged (fail-safe confidential) rather than tag it permissively.

Verify before claiming done

  • Re-run a capture from a test principal (or have Mathias re-dogfood from claude.ai) with insights + ticket + summary, all touching only tagged-internal repos → expect effective_classification: internal, every item ok=true, a real summary.path.
  • Regression: an untagged/unknown repo still resolves confidential (don't break the fail-safe).
  • task check green; secret-handling respected (no token in argv/logs).

Context koala should read first

  • specs/capture-bdd-spec.md (the binding design — esp. §4.1 classification model C, §4.4 audit posture)
  • #56 diff (ingestion/internal/classification/) for the classification.yaml schema + defaultFor() behavior
  • brain wiki/hyperguild/failures/first-live-dogfood-of-braincapture-surfaced-two-real-operat.md (why both gaps exist)

Both are bounded and testable. The only non-autonomous parts are the two STOP points above — flag them and wait, don't work around them.

## Koala dispatch — "make capture fully work from claude.ai" (pairs #66 + #67) **Goal:** after this session, a `brain:capture` call from the claude.ai connector with insights + tickets + **summary** touching the normal homelab repos succeeds fully (all `ok=true`, no I1 refusal). Two sibling fixes get there; do both, in this order. ### Order matters 1. **#67 first (classification.yaml).** Until homelab repos are tagged `internal`, any capture touching `ai-sessions` fail-safes to confidential and the I1 gate refuses it — which would make a #66 fix untestable from claude.ai. So tag first, then you can actually verify the summary path end-to-end. 2. **#66 second (SummaryWriter wiring).** Wire the ai-sessions `SummaryWriter` into the deployed relay so the summary block stops returning `no summary writer configured`. ### Autonomy boundaries — STOP and ask Mathias, do not invent: - **#66 credential:** wiring the SummaryWriter needs the relay to reach `mathias/ai-sessions` with a token. If no ai-sessions-capable credential is already in the relay's env/secret, **stop** — do not mint or hardcode one. Secret provisioning (1Password vault + external-secrets) is Mathias's call, and any new secret + manifest change is a GitOps/I3 change in `infra/k3s/apps/**`, not a code-only fix. - **#67 repo→level map:** the classification map is a **sovereignty decision** (which repos are safe through us-nexus), not a mechanical fill. Draft the `internal` list, but have Mathias confirm it before merge. When unsure, leave a repo untagged (fail-safe confidential) rather than tag it permissively. ### Verify before claiming done - Re-run a capture from a test principal (or have Mathias re-dogfood from claude.ai) with insights + ticket + **summary**, all touching only tagged-internal repos → expect `effective_classification: internal`, every item `ok=true`, a real `summary.path`. - Regression: an untagged/unknown repo still resolves confidential (don't break the fail-safe). - `task check` green; secret-handling respected (no token in argv/logs). ### Context koala should read first - `specs/capture-bdd-spec.md` (the binding design — esp. §4.1 classification model C, §4.4 audit posture) - #56 diff (`ingestion/internal/classification/`) for the `classification.yaml` schema + `defaultFor()` behavior - brain `wiki/hyperguild/failures/first-live-dogfood-of-braincapture-surfaced-two-real-operat.md` (why both gaps exist) Both are bounded and testable. The only non-autonomous parts are the two STOP points above — flag them and wait, don't work around them.
Author
Owner

Fixed — PR #68 (code-only, no manifest change)

gitea.Client.WriteFile (contents API, upsert) → the client now implements capture.SummaryWriter; injected into the deployed CaptureService. Summaries write to mathias/ai-sessions at summaries/<harness>/<YYYY-MM>/….

STOP point handled, not worked around: the relay already carries BRAIN_GITEA_TOKEN (owner mathias, used for tracker issue writes). I reused it — did not mint/hardcode a credential, so no new secret + no infra change. The issue's "likely cause" (deploy didn't provide an ai-sessions target/token) turned out not to need one.

⚠️ One thing for you to confirm: the token must have contents/write:repository scope on mathias/ai-sessions, not issue-only. If it's issue-scoped, the live write 422s → widen the token in 1P (your secret call, not a code fix). Tests cover create / update-with-sha / error-no-token-leak.

Re-dogfood is gated on #67 — until ai-sessions is tagged internal, the I1 gate refuses a claude.ai capture before the summary path runs. Merge order: brain #12 (#67) → restart → #68 → re-dogfood. task check green.

## Fixed — PR #68 (code-only, no manifest change) `gitea.Client.WriteFile` (contents API, upsert) → the client now implements `capture.SummaryWriter`; injected into the deployed `CaptureService`. Summaries write to `mathias/ai-sessions` at `summaries/<harness>/<YYYY-MM>/…`. **STOP point handled, not worked around:** the relay already carries `BRAIN_GITEA_TOKEN` (owner mathias, used for tracker issue writes). I **reused it** — did not mint/hardcode a credential, so no new secret + no infra change. The issue's "likely cause" (deploy didn't provide an ai-sessions target/token) turned out not to need one. ⚠️ **One thing for you to confirm:** the token must have **contents/`write:repository` scope** on `mathias/ai-sessions`, not issue-only. If it's issue-scoped, the live write 422s → widen the token in 1P (your secret call, not a code fix). Tests cover create / update-with-sha / error-no-token-leak. **Re-dogfood is gated on #67** — until ai-sessions is tagged `internal`, the I1 gate refuses a claude.ai capture before the summary path runs. Merge order: brain #12 (#67) → restart → #68 → re-dogfood. `task check` green.
Author
Owner

Live re-dogfood passed (2026-07-07) — loop closed

Full brain:capture from the claude.ai connector, dry_run: false, all items ok=true, errors: []:

  • insight → wiki/hyperguild/facts/capture-re-dogfood-verification-2026-07-07-confirming-the.md
  • summary → summaries/claudeai-chat/2026-07/2026-07-07-capture-re-dogfood-...-8360f6dd.md ← the path type that returned no summary writer configured on 2026-06-23, now writing.
  • effective_classification: internal, I1 gate passed.

This confirms the two things a dry-run couldn't:

  1. Token scopeBRAIN_GITEA_TOKEN has contents/write on ai-sessions (the live write didn't 422; the issue-scope caveat from PR #68 is moot).
  2. POST-create/PUT-update fix works against the real Gitea API, not just the httptest mock (the mock-validates-the-bug trap that hid the original 422 is genuinely closed).

Capture epic #49 is now verified end-to-end from claude.ai. No open capture items remain.

## ✅ Live re-dogfood passed (2026-07-07) — loop closed Full `brain:capture` from the claude.ai connector, `dry_run: false`, all items `ok=true`, `errors: []`: - insight → `wiki/hyperguild/facts/capture-re-dogfood-verification-2026-07-07-confirming-the.md` - **summary → `summaries/claudeai-chat/2026-07/2026-07-07-capture-re-dogfood-...-8360f6dd.md`** ← the path type that returned `no summary writer configured` on 2026-06-23, now writing. - `effective_classification: internal`, I1 gate passed. This confirms the two things a dry-run couldn't: 1. **Token scope** — `BRAIN_GITEA_TOKEN` has contents/write on `ai-sessions` (the live write didn't 422; the issue-scope caveat from PR #68 is moot). 2. **POST-create/PUT-update** fix works against the **real** Gitea API, not just the httptest mock (the mock-validates-the-bug trap that hid the original 422 is genuinely closed). Capture epic #49 is now verified end-to-end from claude.ai. No open capture items remain.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/hyperguild#66