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(