Skip to content

Commit 91a3ba7

Browse files
tae2089claude
andauthored
Remove the automatic postprocess failure-policy machine (#18)
Postprocess steps (stored-flow + search-index rebuild) run on every build/update; the escalation layer on top of them — auto fail_closed after repeated failures, per-namespace run-log history, and two inspection tools — was operational overhead the project's purpose does not need. Step failures are already visible: they warn to the log and list in the `failed_steps` response field with `status: "degraded"`. - delete internal/postprocess/policy and its two GORM models - add migration 000007 dropping ccg_postprocess_policy_state and ccg_postprocess_run_logs (sqlite + postgres); RequiredSchemaVersion 6 -> 7 - remove get_postprocess_policy and reset_postprocess_policy MCP tools (26 -> 24) - drop the postprocess_policy request arg and fail_closed path; keep the inline degraded behavior (warn + failed_steps) in build_or_update_graph and run_postprocess - remove the PostprocessPolicy Deps field, runtime wiring, HTTP /status postprocess summary, and the `ccg status` postprocess section (+ --errors/--recent flags) - delete guide/postprocess-failure-policy.md (EN + ko) and sweep doc references Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent aba5d3a commit 91a3ba7

40 files changed

Lines changed: 148 additions & 2199 deletions

CLAUDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
Follow the global prompt rules first. This file adds project-specific skill routing for a project that uses the `agent-team` CLI (github.com/tae2089/agent-team) as its durable work ledger.
66

77
## MCP 서버
8-
`.mcp.json`에 등록된 ccg MCP 서버가 19개 도구를 제공합니다:
8+
`.mcp.json`에 등록된 ccg MCP 서버가 17개 도구를 제공합니다:
99

1010
- `parse_project`, `build_or_update_graph`, `run_postprocess`
11-
- `get_postprocess_policy`, `reset_postprocess_policy`
1211
- `get_node`, `search`, `query_graph`, `list_graph_stats`, `list_namespaces`, `get_minimal_context`
1312
- `get_impact_radius`, `trace_flow`
1413
- `detect_changes`, `get_affected_flows`, `list_flows`
@@ -46,7 +45,7 @@ Graceful shutdown: SIGINT/SIGTERM 시 진행 중인 clone/build에 context cance
4645
상세 문서는 `guide/` 디렉토리를 참조하세요:
4746

4847
- [CLI Reference](guide/cli-reference.md) — 전체 명령어, 플래그, 설정 파일
49-
- [MCP Tools](guide/mcp-tools.md)26개 MCP 도구, Skills, AI-Driven Annotation
48+
- [MCP Tools](guide/mcp-tools.md)17개 MCP 도구, Skills, AI-Driven Annotation
5049
- [Annotations](guide/annotations.md) — 어노테이션 태그, 예시, 검색
5150
- [Webhook](guide/webhook.md) — Webhook sync, 브랜치 필터링, HMAC, graceful shutdown
5251
- [Docker](guide/docker.md) — Docker 빌드, MCP 서버, PostgreSQL 배포

cmd/ccg/health_test.go

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import (
1010
"testing"
1111
"time"
1212

13-
postprocesspolicy "github.com/tae2089/code-context-graph/internal/postprocess/policy"
14-
ccgserver "github.com/tae2089/code-context-graph/internal/server"
1513
ccgdb "github.com/tae2089/code-context-graph/internal/db"
14+
ccgserver "github.com/tae2089/code-context-graph/internal/server"
1615
"github.com/tae2089/code-context-graph/internal/webhook"
1716
)
1817

@@ -113,7 +112,7 @@ func TestStatusHandler_ReportsWebhookQueueFull(t *testing.T) {
113112
req := httptest.NewRequest(http.MethodGet, "/status", nil)
114113
rec := httptest.NewRecorder()
115114

116-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
115+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
117116

118117
if rec.Code != http.StatusServiceUnavailable {
119118
t.Fatalf("expected 503, got %d body=%s", rec.Code, rec.Body.String())
@@ -136,7 +135,7 @@ func TestStatusHandler_ReportsWebhookQueueAges(t *testing.T) {
136135
req := httptest.NewRequest(http.MethodGet, "/status", nil)
137136
rec := httptest.NewRecorder()
138137

139-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
138+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
140139

141140
if rec.Code != http.StatusOK {
142141
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
@@ -207,7 +206,7 @@ func TestStatusHandler_ReportsUnresolvedWebhookFailure(t *testing.T) {
207206
req := httptest.NewRequest(http.MethodGet, "/status", nil)
208207
rec := httptest.NewRecorder()
209208

210-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
209+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
211210

212211
if rec.Code != http.StatusOK {
213212
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
@@ -283,7 +282,7 @@ func TestStatusHandler_DegradedWhenRecentRepoFailureUnresolved(t *testing.T) {
283282

284283
req := httptest.NewRequest(http.MethodGet, "/status", nil)
285284
rec := httptest.NewRecorder()
286-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
285+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
287286

288287
if rec.Code != http.StatusOK {
289288
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
@@ -326,7 +325,7 @@ func TestStatusHandler_DBNotReadyTakesPrecedenceOverWebhookDegraded(t *testing.T
326325

327326
req := httptest.NewRequest(http.MethodGet, "/status", nil)
328327
rec := httptest.NewRecorder()
329-
ccgserver.StatusHandler(func(r *http.Request) error { return errors.New("db down") }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
328+
ccgserver.StatusHandler(func(r *http.Request) error { return errors.New("db down") }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
330329

331330
if rec.Code != http.StatusServiceUnavailable {
332331
t.Fatalf("expected 503, got %d body=%s", rec.Code, rec.Body.String())
@@ -383,7 +382,7 @@ func TestStatusHandler_ReportsPerRepoRecentRepos(t *testing.T) {
383382

384383
req := httptest.NewRequest(http.MethodGet, "/status", nil)
385384
rec := httptest.NewRecorder()
386-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
385+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
387386

388387
if rec.Code != http.StatusOK {
389388
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
@@ -444,7 +443,7 @@ func TestStatusHandler_RecentRepos_FailureHasErrorFields(t *testing.T) {
444443

445444
req := httptest.NewRequest(http.MethodGet, "/status", nil)
446445
rec := httptest.NewRecorder()
447-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
446+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
448447

449448
var body map[string]any
450449
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
@@ -476,7 +475,7 @@ func TestStatusHandler_ExistingAggregateFieldsPreserved(t *testing.T) {
476475

477476
req := httptest.NewRequest(http.MethodGet, "/status", nil)
478477
rec := httptest.NewRecorder()
479-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }, nil).ServeHTTP(rec, req)
478+
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, func() *webhook.SyncQueue { return q }).ServeHTTP(rec, req)
480479

481480
var body map[string]any
482481
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
@@ -493,41 +492,6 @@ func TestStatusHandler_ExistingAggregateFieldsPreserved(t *testing.T) {
493492
}
494493
}
495494

496-
func TestStatusHandler_ReportsPostprocessSummary(t *testing.T) {
497-
req := httptest.NewRequest(http.MethodGet, "/status", nil)
498-
rec := httptest.NewRecorder()
499-
500-
ccgserver.StatusHandler(func(r *http.Request) error { return nil }, time.Minute, nil, func(context.Context) (*postprocesspolicy.StatusSummary, error) {
501-
return &postprocesspolicy.StatusSummary{
502-
Status: postprocesspolicy.StatusDegraded,
503-
FailClosed: []postprocesspolicy.StateSnapshot{{
504-
Namespace: "default",
505-
Tool: postprocesspolicy.ToolRunPostprocess,
506-
Policy: postprocesspolicy.PolicyFailClosed,
507-
ConsecutiveFailures: 3,
508-
}},
509-
}, nil
510-
}).ServeHTTP(rec, req)
511-
512-
if rec.Code != http.StatusOK {
513-
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
514-
}
515-
var body map[string]any
516-
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
517-
t.Fatalf("decode status: %v", err)
518-
}
519-
if got := body["status"]; got != "degraded" {
520-
t.Fatalf("status = %v, want degraded", got)
521-
}
522-
postprocessBody, ok := body["postprocess"].(map[string]any)
523-
if !ok {
524-
t.Fatalf("missing postprocess body: %v", body)
525-
}
526-
if got := postprocessBody["status"]; got != "degraded" {
527-
t.Fatalf("postprocess.status = %v, want degraded", got)
528-
}
529-
}
530-
531495
type recordingPool struct {
532496
maxOpen int
533497
maxIdle int

cmd/ccg/main_test.go

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func TestRunMigrations_AllowsRuntimeSchemaCheck(t *testing.T) {
109109
}
110110
}
111111

112-
func TestRunMigrations_SqliteAppliesPolicyTables(t *testing.T) {
112+
func TestRunMigrations_SqliteDropsPolicyTablesAtHead(t *testing.T) {
113113
requireSQLiteFTS5(t)
114114

115115
dbPath := filepath.Join(t.TempDir(), "ccg.db")
@@ -128,29 +128,11 @@ func TestRunMigrations_SqliteAppliesPolicyTables(t *testing.T) {
128128
t.Fatalf("run migrations: %v", err)
129129
}
130130

131-
if !db.Migrator().HasTable("ccg_postprocess_policy_state") {
132-
t.Fatal("expected ccg_postprocess_policy_state after migrations")
133-
}
134-
if !db.Migrator().HasTable("ccg_postprocess_run_logs") {
135-
t.Fatal("expected ccg_postprocess_run_logs after migrations")
136-
}
137-
138-
var version migration.MigrationSchemaVersion
139-
if err := db.Table("schema_migrations").First(&version).Error; err != nil {
140-
t.Fatalf("load schema version: %v", err)
141-
}
142-
if int(version.Version) != migration.RequiredSchemaVersion {
143-
t.Fatalf("schema version = %d, want %d", version.Version, migration.RequiredSchemaVersion)
144-
}
145-
if version.Dirty {
146-
t.Fatal("schema version is dirty")
147-
}
148-
149-
if !db.Migrator().HasColumn("ccg_postprocess_policy_state", "namespace") {
150-
t.Fatal("expected namespace column on ccg_postprocess_policy_state")
131+
if db.Migrator().HasTable("ccg_postprocess_policy_state") {
132+
t.Fatal("expected ccg_postprocess_policy_state to be dropped at head schema")
151133
}
152-
if !db.Migrator().HasColumn("ccg_postprocess_policy_state", "tool") {
153-
t.Fatal("expected tool column on ccg_postprocess_policy_state")
134+
if db.Migrator().HasTable("ccg_postprocess_run_logs") {
135+
t.Fatal("expected ccg_postprocess_run_logs to be dropped at head schema")
154136
}
155137
}
156138

@@ -268,7 +250,7 @@ func TestRunMigrations_SQLiteDownRestoresNullableColumns(t *testing.T) {
268250
if err != nil {
269251
t.Fatalf("create migrator: %v", err)
270252
}
271-
if err := migrator.Steps(-5); err != nil {
253+
if err := migrator.Steps(-6); err != nil {
272254
t.Fatalf("run down migration: %v", err)
273255
}
274256

@@ -313,7 +295,7 @@ func TestRunMigrations_SQLiteDownFromVersionThreeDropsPolicyTables(t *testing.T)
313295
if err != nil {
314296
t.Fatalf("create migrator: %v", err)
315297
}
316-
if err := migrator.Steps(-4); err != nil {
298+
if err := migrator.Steps(-5); err != nil {
317299
t.Fatalf("run down migration: %v", err)
318300
}
319301

@@ -427,7 +409,7 @@ func TestEnsureSchemaVersion_LogsRuntimeSchemaPassAndFail(t *testing.T) {
427409
t.Fatalf("ensure schema version: %v", err)
428410
}
429411
passLog := logs.String()
430-
for _, want := range []string{"database runtime schema check passed", "driver=sqlite", "required_version=6", "auto_migrated=true"} {
412+
for _, want := range []string{"database runtime schema check passed", "driver=sqlite", "required_version=7", "auto_migrated=true"} {
431413
if !strings.Contains(passLog, want) {
432414
t.Fatalf("expected runtime schema pass log to contain %q, got %q", want, passLog)
433415
}
@@ -441,7 +423,7 @@ func TestEnsureSchemaVersion_LogsRuntimeSchemaPassAndFail(t *testing.T) {
441423
t.Fatal("expected parity failure")
442424
}
443425
failLog := logs.String()
444-
for _, want := range []string{"database runtime schema check failed", "driver=sqlite", "required_version=6", "auto_migrated=false", "error.message="} {
426+
for _, want := range []string{"database runtime schema check failed", "driver=sqlite", "required_version=7", "auto_migrated=false", "error.message="} {
445427
if !strings.Contains(failLog, want) {
446428
t.Fatalf("expected runtime schema failure log to contain %q, got %q", want, failLog)
447429
}

guide/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ explore graph edges.
2626
| [Webhook](webhook.md) | GitHub / Gitea webhook sync, branch filtering, graceful shutdown |
2727
| [Docker](docker.md) | Docker image build, MCP server setup, Wiki UI deployment, PostgreSQL integration |
2828
| [Operations](operations.md) | Deployment profiles, database choice, readiness, webhook operations, troubleshooting |
29-
| [Postprocess Failure Policy](postprocess-failure-policy.md) | Status rules, failure causes, and automatic degraded/fail_closed policy for build and postprocess tools |
3029
| [Runtime Layout](runtime-layout.md) | `ccg`, `ccg-server`, Wiki serving, and shared `ccg-core` ownership boundaries |
3130
| [Architecture](architecture.md) | System architecture, data flow, DB schema |
3231
| [Development](development.md) | Build, test, integration test (Gitea + PostgreSQL) |

guide/cli-reference.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ ccg update ./backend --namespace backend
4141
| `ccg build --fallback-calls` | Enable best-effort fallback call resolution (strict by default) |
4242
| `ccg update [dir]` | Incremental sync |
4343
| `ccg update --fallback-calls` | Enable best-effort fallback call resolution for incremental sync |
44-
| `ccg status` | Graph statistics and postprocess error summary |
45-
| `ccg status --errors` | Include recent postprocess failure details |
46-
| `ccg status --recent <n>` | Number of recent postprocess failures to inspect (default `5`) |
44+
| `ccg status` | Graph statistics |
4745
| `ccg search <query>` | Full-text search |
4846
| `ccg search --path <prefix> <query>` | Scoped search by path prefix |
4947
| `ccg docs [--out dir]` | Generate Markdown documentation, the `wiki-index.json` compatibility snapshot, and the default RAG index (prunes stale generator-managed docs by default) |

guide/ko/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ graph/search 도구로 정확한 위치와 관계를 확인하는 흐름을 권
2929
| [웹훅(Webhook)](webhook.md) | GitHub / Gitea 웹훅 동기화, 브랜치 필터링, Graceful Shutdown |
3030
| [Docker](docker.md) | Docker 이미지 빌드, MCP 서버 설정, Wiki UI 배포, PostgreSQL 연동 |
3131
| [운영(Operations)](operations.md) | 배포 프로필, 데이터베이스 선택, 준비성 체크, 웹훅 운영 및 문제 해결 |
32-
| [사후 처리 실패 정책](postprocess-failure-policy.md) | 상태 규칙, 실패 원인, 빌드 및 사후 처리 도구의 자동 성능 저하(degraded)/폐쇄형 실패(fail_closed) 정책 |
3332
| [런타임 구조](runtime-layout.md) | `ccg`, `ccg-server`, Wiki serving, 공용 `ccg-core` 소유권 경계 |
3433
| [아키텍처](architecture.md) | 시스템 아키텍처, 데이터 흐름, DB 스키마 |
3534
| [개발(Development)](development.md) | 빌드, 테스트, 통합 테스트(Gitea + PostgreSQL) |

guide/ko/cli-reference.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ ccg update ./backend --namespace backend
4343
| `ccg build --fallback-calls` | (기본 off) best-effort fallback 호출 해상도 활성화 |
4444
| `ccg update [dir]` | 증분 동기화(Incremental sync) |
4545
| `ccg update --fallback-calls` | 증분 동기화에서 best-effort fallback 호출 해상도 활성화 |
46-
| `ccg status` | 그래프 통계 및 사후 처리 오류 요약 출력 |
47-
| `ccg status --errors` | 최근 사후 처리 실패 상세 포함 |
48-
| `ccg status --recent <n>` | 확인할 최근 사후 처리 실패 개수 (기본값 `5`) |
46+
| `ccg status` | 그래프 통계 출력 |
4947
| `ccg search <query>` | 전체 텍스트 검색 |
5048
| `ccg search --path <prefix> <query>` | 경로 접두사로 검색 범위 제한 |
5149
| `ccg docs [--out dir]` | 마크다운 문서, `wiki-index.json` 호환 snapshot, 기본 RAG 인덱스 생성 (기본적으로 그래프에 없는 generator-managed 문서를 prune) |

guide/ko/lint.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,3 @@ rules:
267267
- [CLI 레퍼런스](cli-reference.md#lint-categories)
268268
- [커스텀 어노테이션](annotations.md)
269269
- [개발](development.md)
270-
- [사후 처리 실패 정책](postprocess-failure-policy.md)

guide/ko/mcp-tools.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,14 @@ Codex 또는 Claude Code 같은 MCP 지원 코딩 에이전트가 연결할 수
4141
|------|-------------|
4242
| `parse_project` | 소스 파일 파싱 |
4343
| `build_or_update_graph` | 사후 처리를 포함한 전체/증분 빌드 |
44-
| `run_postprocess` | 저장된 흐름(flow), 커뮤니티 및 전체 텍스트 검색 파생 상태 재생성 |
45-
| `get_postprocess_policy` | 자동 사후 처리 정책 상태 및 최근 실패 내역 확인 |
46-
| `reset_postprocess_policy` | 특정 도구에 대한 fail-closed 연속 기록을 지우기 위한 리셋 마커 기록 |
44+
| `run_postprocess` | 저장된 흐름(flow) 및 전체 텍스트 검색 파생 상태 재생성 |
4745
| `get_node` | 정규화된 이름으로 노드 조회 |
4846
| `search` | 전체 텍스트 검색 |
4947
| `query_graph` | 정의된 그래프 쿼리(callers, callees, imports 등) |
5048
| `list_graph_stats` | 노드/엣지/파일 수 확인 |
51-
| `get_minimal_context` | AI 에이전트 진입점을 위한 경량 요약(~100 토큰) — 그래프 통계, 리스크, 주요 커뮤니티/흐름, 도구 추천 포함 |
49+
| `get_minimal_context` | AI 에이전트 진입점을 위한 경량 요약(~100 토큰) — 그래프 통계, 리스크, 주요 흐름, 도구 추천 포함 |
5250

53-
`build_or_update_graph``run_postprocess`는 모두 자동 사후 처리 실패 정책(postprocess failure policy)을 지원합니다. 명시적인 `postprocess_policy`가 제공되지 않으면 CCG는 기본적으로 `degraded` 모드를 사용하며, 동일한 `(namespace, tool)` 쌍에 대해 3회 연속 `degraded` 실행 시 자동으로 `fail_closed`로 격상합니다.
54-
55-
상태 표, 실패 원인, 건너뛰기 동작 및 `build_or_update_graph``run_postprocess`에 대한 정책 격상 규칙 등 자세한 내용은 [사후 처리 실패 정책](postprocess-failure-policy.md)을 참조하십시오.
56-
57-
CCG는 아직 Prometheus `/metrics` 엔드포인트를 제공하지 않습니다. 사후 처리 작업에 대해서는 현재 기계가 읽을 수 있는 운영 인터페이스인 `get_postprocess_policy`와 HTTP `/status` 요약을 사용하십시오.
51+
`build_or_update_graph``run_postprocess`는 그래프 기록 후 파생 상태 단계(저장된 흐름, 전체 텍스트 검색)를 실행합니다. 단계가 실패하면 도구는 여전히 `status: "degraded"`와 실패한 단계를 담은 `failed_steps`가 포함된 구조화된 결과를 반환하며, 실패는 로그에도 남습니다. 이 응답(또는 HTTP `/status` 요약)으로 성능 저하된 사후 처리를 감지하십시오.
5852

5953
### 분석 (Analysis)
6054

0 commit comments

Comments
 (0)