Fixes#66 — the deployed relay had a nil SummaryWriter, so a capture carrying a summary block returned summary.ok=false, "no summary writer configured" (the 2026-06-23 claude.ai dogfood partial-success).
Fix (code-only, no new secret)
gitea.Client.WriteFile (contents API) — the Gitea client now also implements capture.SummaryWriter. Upserts: a GET resolves the current blob sha so a re-captured session updates its summary (the richer-fidelity-supersedes rule) instead of 422'ing on a duplicate path.
main: inject the Gitea client as the SummaryWriter (type-asserted from the tracker — same *gitea.Client). Summaries write to mathias/ai-sessions at summaries/<harness>/<YYYY-MM>/<date>-<slug>-<ref8>.md.
The relay already hasBRAIN_GITEA_TOKEN (owner mathias, used by the tracker for mathias/* issue writes). I reused it — did not mint or hardcode a new credential. So no infra/manifest change.
⚠️One assumption for you to confirm: this needs the token to carry write:repository/contents scope on mathias/ai-sessions, not just issue scope. It already does issue writes; if it's issue-scoped only, the live summary write will 422 and you'll need to widen the token scope in 1Password (a secret call — yours, not a code fix). Everything else lands regardless (partial-receipt semantics).
Acceptance
SummaryWriter concretely wired in the relay (reuses present credential)
A capture with a summary writes to ai-sessions and returns summary.ok=true (service test with writer + gitea WriteFile httptest tests: create, update-with-sha, error-no-token-leak)
No manifest change needed — credential already present (corrects the issue's "likely cause")
Re-dogfood from claude.ai (insights + ticket + summary all ok=true) — yours, and gated on #67 (until ai-sessions is tagged internal, the I1 gate refuses the capture from us-nexus)
task check green; secret-handling respected
Verification order
Per the dispatch, end-to-end claude.ai verification needs #67 first (tag homelab repos internal) — otherwise a capture touching ai-sessions fail-safes to confidential and is refused before the summary path is reached. #67 PR follows (held for your sovereignty review of the repo→level map).
Fixes #66 — the deployed relay had a nil `SummaryWriter`, so a capture carrying a `summary` block returned `summary.ok=false, "no summary writer configured"` (the 2026-06-23 claude.ai dogfood partial-success).
## Fix (code-only, no new secret)
- **`gitea.Client.WriteFile`** (contents API) — the Gitea client now also implements `capture.SummaryWriter`. Upserts: a GET resolves the current blob sha so a re-captured session **updates** its summary (the richer-fidelity-supersedes rule) instead of 422'ing on a duplicate path.
- **main:** inject the Gitea client as the `SummaryWriter` (type-asserted from the tracker — same `*gitea.Client`). Summaries write to `mathias/ai-sessions` at `summaries/<harness>/<YYYY-MM>/<date>-<slug>-<ref8>.md`.
## On the #66 STOP point (ai-sessions credential)
The relay **already has** `BRAIN_GITEA_TOKEN` (owner `mathias`, used by the tracker for `mathias/*` issue writes). I **reused it** — did **not** mint or hardcode a new credential. So no infra/manifest change.
⚠️ **One assumption for you to confirm:** this needs the token to carry **`write:repository`/contents scope** on `mathias/ai-sessions`, not just issue scope. It already does issue writes; if it's issue-scoped only, the live summary write will **422** and you'll need to widen the token scope in 1Password (a secret call — yours, not a code fix). Everything else lands regardless (partial-receipt semantics).
## Acceptance
- [x] `SummaryWriter` concretely wired in the relay (reuses present credential)
- [x] A capture with a `summary` writes to ai-sessions and returns `summary.ok=true` (service test with writer + gitea WriteFile httptest tests: create, update-with-sha, error-no-token-leak)
- [x] No manifest change needed — credential already present (corrects the issue's "likely cause")
- [ ] **Re-dogfood from claude.ai** (insights + ticket + summary all `ok=true`) — yours, and **gated on #67** (until ai-sessions is tagged `internal`, the I1 gate refuses the capture from us-nexus)
- [x] `task check` green; secret-handling respected
## Verification order
Per the dispatch, end-to-end claude.ai verification needs **#67 first** (tag homelab repos `internal`) — otherwise a capture touching `ai-sessions` fail-safes to confidential and is refused before the summary path is reached. #67 PR follows (held for your sovereignty review of the repo→level map).
Closes #66
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds Client.WriteFile (Gitea contents API) so the Gitea client also
implements capture.SummaryWriter. Upserts: a GET resolves the current
blob sha so an existing file is updated (the richer-fidelity-supersedes
rule for re-captured sessions) rather than 422'd. Owner stays the fixed
const; token only in the Authorization header (no leak — regression
tested). Refactors the HTTP path into a shared request() helper so the
contents flow can branch on 404 without it being an error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deployed CaptureService was constructed with a nil SummaryWriter, so
a capture carrying a summary block returned the partial-failure
"no summary writer configured" (surfaced in the 2026-06-23 claude.ai
dogfood). The Gitea client already reaches mathias/* over BRAIN_GITEA_TOKEN
and now implements SummaryWriter, so inject it (type-asserted from the
tracker) — no new credential, no manifest change. Session summaries now
write to mathias/ai-sessions at summaries/<harness>/<YYYY-MM>/...
Reuses the existing token deliberately; assumes it carries contents:write
scope on ai-sessions (it already does issue writes for the tracker). If
the token is issue-scoped only, the live write 422s — a token-scope widen,
not a code fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A live token-scope probe against mathias/ai-sessions revealed gitea's
contents API uses POST to create and PUT (sha required) to update — the
first impl always PUT'd, so creating a new summary 422'd "[SHA]: Required".
The httptest mock had the same wrong assumption. Pick the method by
whether the file exists (GET sha). Token confirmed contents:write
(push:true) by the probe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mathias
merged commit d39a18dd69 into main2026-06-23 15:22:42 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #66 — the deployed relay had a nil
SummaryWriter, so a capture carrying asummaryblock returnedsummary.ok=false, "no summary writer configured"(the 2026-06-23 claude.ai dogfood partial-success).Fix (code-only, no new secret)
gitea.Client.WriteFile(contents API) — the Gitea client now also implementscapture.SummaryWriter. Upserts: a GET resolves the current blob sha so a re-captured session updates its summary (the richer-fidelity-supersedes rule) instead of 422'ing on a duplicate path.SummaryWriter(type-asserted from the tracker — same*gitea.Client). Summaries write tomathias/ai-sessionsatsummaries/<harness>/<YYYY-MM>/<date>-<slug>-<ref8>.md.On the #66 STOP point (ai-sessions credential)
The relay already has
BRAIN_GITEA_TOKEN(ownermathias, used by the tracker formathias/*issue writes). I reused it — did not mint or hardcode a new credential. So no infra/manifest change.⚠️ One assumption for you to confirm: this needs the token to carry
write:repository/contents scope onmathias/ai-sessions, not just issue scope. It already does issue writes; if it's issue-scoped only, the live summary write will 422 and you'll need to widen the token scope in 1Password (a secret call — yours, not a code fix). Everything else lands regardless (partial-receipt semantics).Acceptance
SummaryWriterconcretely wired in the relay (reuses present credential)summarywrites to ai-sessions and returnssummary.ok=true(service test with writer + gitea WriteFile httptest tests: create, update-with-sha, error-no-token-leak)ok=true) — yours, and gated on #67 (until ai-sessions is taggedinternal, the I1 gate refuses the capture from us-nexus)task checkgreen; secret-handling respectedVerification order
Per the dispatch, end-to-end claude.ai verification needs #67 first (tag homelab repos
internal) — otherwise a capture touchingai-sessionsfail-safes to confidential and is refused before the summary path is reached. #67 PR follows (held for your sovereignty review of the repo→level map).Closes #66
🤖 Generated with Claude Code