From 0644fc9b3bce32062aaff0d3c92278cadd45f835 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 8 May 2026 13:08:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20startup=20reaper=20for=20MCP=20sys?= =?UTF-8?q?tem=20tokens=20(spec=20=C2=A76.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Runs after ACP startup reaper marks stuck sessions as crashed - mcpRepo.ReapStaleSystemTokens revokes tokens bound to crashed/exited sessions - Single audit entry per batch: mcp.token.expire_on_restart with count + token_ids - Errors logged but don't block boot Co-Authored-By: Claude Opus 4.6 --- internal/app/app.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/internal/app/app.go b/internal/app/app.go index ed33db9..b73e70e 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -329,6 +329,30 @@ func New(ctx context.Context, cfg *config.Config, dist embed.FS) (*App, error) { } } + // Phase 1.5 (post-ACP-reaper): revoke MCP system tokens still bound to crashed/exited + // sessions. spec §6.4 — must run AFTER ACP reaper so sessions are already marked crashed. + revokedTokenIDs, rerr := mcpRepo.ReapStaleSystemTokens(ctx) + if rerr != nil { + log.Warn("mcp.startup_reaper.failed", "err", rerr.Error()) + } else if len(revokedTokenIDs) > 0 { + log.Info("mcp.startup_reaper.revoked", "count", len(revokedTokenIDs)) + _ = auditRec.Record(ctx, audit.Entry{ + Action: "mcp.token.expire_on_restart", + TargetType: "mcp_token", + TargetID: "(batch)", + Metadata: map[string]any{ + "count": len(revokedTokenIDs), + "token_ids": func() []string { + out := make([]string, 0, len(revokedTokenIDs)) + for _, id := range revokedTokenIDs { + out = append(out, id.String()) + } + return out + }(), + }, + }) + } + // Phase 2: build supervisor + session service. acpProjAccess := acpProjectAccessAdapter{wsRepo: wsRepo, projectRepo: projectRepo} acpSup := acp.NewSupervisor(