spec(p9-fb-01..03): ingest progress events + cancellation in §2.4a / §10 #51

Merged
altair823 merged 2 commits from spec/p9-fb-01-progress-events into main 2026-05-02 19:19:09 +00:00
Owner

Summary

도그푸딩 후 추가된 long-running 작업 진행 표시 + cancel 정책을 frozen design 에 명시. p9-fb-01/02/03 (ingest progress callback / CLI display / TUI background) 의 spec PR — impl PR 들이 이어진다. p9-dogfooding-feedback.md 의 "spec PR vs impl PR" 정책에 따라 impl PR 들과 분리.

변경

신규 wire schema: docs/wire-schema/v1/ingest_progress.schema.json

kebab ingest --json 의 line-delimited streaming event schema. discriminated by kind:

  • scan_startedscan_completed(asset_started < asset_finished)*(completed | aborted).
  • embed batch (embed_batch_started / embed_batch_finished) 는 asset 이벤트 사이 임의 위치.
  • 마지막 줄은 기존 ingest_report.v1 그대로 — 외부 wrapper backward-compat.

Frozen design §2.4a (신규)

IngestProgressEvent 절. 이벤트 ordering / aborted 의 idempotency / CLI 의 stderr (사람-친화) vs stdout (--json line-delimited) 분리 / TUI · desktop 의 in-memory mpsc 소비.

Frozen design §10 (long-running 작업 절 추가)

두 invariant:

  1. 진행 표시는 surface 별로 분리되되 source 는 단일. facade 가 mpsc::Sender<IngestEvent> 로 흘려보내고 CLI / TUI / desktop 이 각자 방식으로 소비.
  2. cancel 은 cooperative + step boundary 에서 즉시 응답. Option<Arc<AtomicBool>> cancel token, true 면 in-flight asset 마무리 후 Aborted event + Ok(IngestReport) 정상 반환. 부분 commit 된 doc/chunk 는 idempotent 재실행으로 이어짐.

kebab-core trait (§7.2) 시그니처는 무영향 — kebab-app facade 의 hidden parameter 로 추가.

Out of scope (impl PR 들에서 처리)

  • kebab-appIngestEvent enum + ingest_with_config_progress impl → p9-fb-01 PR
  • CLI 의 indicatif spinner + line-delimited JSON dump → p9-fb-02 PR
  • TUI 의 background worker + status bar → p9-fb-03 PR
  • cancel token wiring (CLI Ctrl-C + TUI Esc) → p9-fb-04 PR

Test plan

코드 변경 없음. doc + JSON Schema 만.

  • JSON Schema 7 valid (수동 검토)
  • 기존 ingest_report.v1 와 backward-compat (마지막 줄 형식 유지)
  • frozen design §11 동결 범위 준수 — wire schema / trait 추가만, 기존 schema 변경 0
## Summary 도그푸딩 후 추가된 long-running 작업 진행 표시 + cancel 정책을 frozen design 에 명시. p9-fb-01/02/03 (ingest progress callback / CLI display / TUI background) 의 spec PR — impl PR 들이 이어진다. p9-dogfooding-feedback.md 의 \"spec PR vs impl PR\" 정책에 따라 impl PR 들과 분리. ## 변경 ### 신규 wire schema: `docs/wire-schema/v1/ingest_progress.schema.json` `kebab ingest --json` 의 line-delimited streaming event schema. discriminated by `kind`: - `scan_started` → `scan_completed` → `(asset_started < asset_finished)*` → `(completed | aborted)`. - embed batch (`embed_batch_started` / `embed_batch_finished`) 는 asset 이벤트 사이 임의 위치. - 마지막 줄은 기존 `ingest_report.v1` 그대로 — 외부 wrapper backward-compat. ### Frozen design §2.4a (신규) `IngestProgressEvent` 절. 이벤트 ordering / `aborted` 의 idempotency / CLI 의 stderr (사람-친화) vs stdout (`--json` line-delimited) 분리 / TUI · desktop 의 in-memory mpsc 소비. ### Frozen design §10 (long-running 작업 절 추가) 두 invariant: 1. **진행 표시는 surface 별로 분리되되 source 는 단일.** facade 가 `mpsc::Sender<IngestEvent>` 로 흘려보내고 CLI / TUI / desktop 이 각자 방식으로 소비. 2. **cancel 은 cooperative + step boundary 에서 즉시 응답.** `Option<Arc<AtomicBool>>` cancel token, true 면 in-flight asset 마무리 후 `Aborted` event + `Ok(IngestReport)` 정상 반환. 부분 commit 된 doc/chunk 는 idempotent 재실행으로 이어짐. `kebab-core` trait (§7.2) 시그니처는 무영향 — `kebab-app` facade 의 hidden parameter 로 추가. ## Out of scope (impl PR 들에서 처리) - `kebab-app` 의 `IngestEvent` enum + `ingest_with_config_progress` impl → p9-fb-01 PR - CLI 의 indicatif spinner + line-delimited JSON dump → p9-fb-02 PR - TUI 의 background worker + status bar → p9-fb-03 PR - cancel token wiring (CLI Ctrl-C + TUI Esc) → p9-fb-04 PR ## Test plan 코드 변경 없음. doc + JSON Schema 만. - [x] JSON Schema 7 valid (수동 검토) - [x] 기존 `ingest_report.v1` 와 backward-compat (마지막 줄 형식 유지) - [x] frozen design §11 동결 범위 준수 — wire schema / trait 추가만, 기존 schema 변경 0
altair823 added 1 commit 2026-05-02 19:15:03 +00:00
도그푸딩 후 추가된 long-running 작업 진행 표시 + cancel 정책을 frozen
design 에 명시. p9-fb-01/02/03 (ingest progress callback / CLI display
/ TUI background) 의 spec PR — impl PR 들이 이어진다.

변경:
- docs/wire-schema/v1/ingest_progress.schema.json (신규):
  line-delimited streaming event schema. discriminated by `kind`
  (scan_started → scan_completed → asset_started → asset_finished* →
  embed_batch_* → completed | aborted). 마지막 줄은 기존
  ingest_report.v1 그대로 (외부 wrapper backward-compat).
- 2026-04-27-kebab-final-form-design.md §2.4a (신규):
  IngestProgressEvent 절. 이벤트 ordering / aborted 의 idempotency /
  CLI 의 stderr vs stdout 분리 / TUI · desktop 의 in-memory 소비.
- 2026-04-27-kebab-final-form-design.md §10:
  long-running 작업 (ingest, future eval run, RAG streaming, embed
  batch) 의 두 invariant — progress 의 단일 source / cooperative
  cancel + step boundary. trait (§7.2) 시그니처는 무영향 — facade
  hidden parameter 로 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-02 19:17:35 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — nit 3건 + 칭찬 2건. spec PR (코드 무영향) 이라 표면 작음.

Actionable:

  1. §10 long-running 절 끝 빈 줄 3 → 1 (가독성).
  2. wire schema 의 kind_result 필드명 모호 → result 권장. 예제 JSON 줄도 동시 fix.
  3. wire schema 의 tsformat: "date-time" 추가 — RFC 3339 자동 검증.

frozen design §11 동결 범위 (wire schema 추가만, 기존 변경 X) 정확히 준수. p9-fb-04 cancel invariant (AbortedErr 아닌 Ok 반환 + idempotent 재실행) 까지 spec 으로 끌어올린 게 좋음 — 후속 impl PR 의 review 부담 ↓.

회차 1 — nit 3건 + 칭찬 2건. spec PR (코드 무영향) 이라 표면 작음. Actionable: 1. §10 long-running 절 끝 빈 줄 3 → 1 (가독성). 2. wire schema 의 `kind_result` 필드명 모호 → `result` 권장. 예제 JSON 줄도 동시 fix. 3. wire schema 의 `ts` 에 `format: "date-time"` 추가 — RFC 3339 자동 검증. frozen design §11 동결 범위 (wire schema 추가만, 기존 변경 X) 정확히 준수. p9-fb-04 cancel invariant (`Aborted` 가 `Err` 아닌 `Ok` 반환 + idempotent 재실행) 까지 spec 으로 끌어올린 게 좋음 — 후속 impl PR 의 review 부담 ↓.
@@ -277,6 +277,27 @@ variant 별 해당 키만 채움. `path` 와 `uri` 는 항상 채움 (`uri` 는
`--summary-only``items: null`.
### 2.4a IngestProgressEvent

(칭찬) §2.4a 의 신설 위치 — IngestReport (§2.4) 다음, DocSummary (§2.5) 앞 — 가 wire schema 흐름과 일관 (스캔 결과 → 진행 → 문서 목록). number suffix a 로 기존 §2.5+ 번호 유지 → frozen design §11 동결 범위 ("신규 wire schema 추가 OK") 와 일치.

(칭찬) §2.4a 의 신설 위치 — IngestReport (§2.4) 다음, DocSummary (§2.5) 앞 — 가 wire schema 흐름과 일관 (스캔 결과 → 진행 → 문서 목록). number suffix `a` 로 기존 §2.5+ 번호 유지 → frozen design §11 동결 범위 ("신규 wire schema 추가 OK") 와 일치.
@@ -1302,0 +1325,4 @@
초 단위 이상 걸리는 모든 명령 (`kebab ingest`, future `kebab eval run`, RAG streaming, embed 배치) 은 다음 두 invariant 를 지킨다:
1. **진행 표시는 surface 별로 분리되되 source 는 단일.** facade (`kebab-app`) 가 progress event 를 `mpsc::Sender<IngestEvent>` (또는 그에 준하는 channel) 로 흘려보내고, CLI / TUI / desktop 이 각자 방식으로 소비. CLI 의 `--json` 모드는 §2.4a 의 line-delimited dump, 사람-친화 모드는 stderr spinner + 단계 라인. TUI 는 status bar 1 줄. desktop (P9-5) 는 progress widget.
2. **cancel 은 cooperative + step boundary 에서 즉시 응답.** facade 가 `Option<Arc<AtomicBool>>` cancel token 받음. asset loop iteration / embed batch / vector upsert 같은 step boundary 마다 check, true 면 in-flight asset 마무리 후 `Aborted` event 발신 + `Ok(IngestReport)` 정상 반환 (Err 아님 — 정상 종료의 한 형태). 부분 commit 된 doc/chunk 는 SQLite 에 살아있어 재실행이 idempotent. CLI 는 SIGINT, TUI 는 `Esc` / `Ctrl-C` 가 cancel 신호.

(칭찬) cancel 정책 명시: AbortedErr 가 아닌 Ok(IngestReport) 정상 반환 + 부분 commit 된 doc/chunk 는 idempotent 재실행으로 이어진다는 contract 를 spec 옆에서 잡음. p9-fb-04 (cancellation) impl PR 의 핵심 invariant 를 spec 이 미리 frozen — review 시 "왜 Err 안 던지나" 의문 즉시 차단.

(칭찬) cancel 정책 명시: `Aborted` 가 `Err` 가 아닌 `Ok(IngestReport)` 정상 반환 + 부분 commit 된 doc/chunk 는 idempotent 재실행으로 이어진다는 contract 를 spec 옆에서 잡음. p9-fb-04 (cancellation) impl PR 의 핵심 invariant 를 spec 이 미리 frozen — review 시 "왜 Err 안 던지나" 의문 즉시 차단.
@@ -1302,0 +1329,4 @@
`kebab-core` trait (§7.2) 시그니처는 무영향 — progress / cancel 은 `kebab-app` facade 의 hidden parameter 로 추가 (`ingest_with_config_progress(..., progress: Option<Sender<IngestEvent>>, cancel: Option<Arc<AtomicBool>>)`).

(nit / 가독성) §10 의 long-running 절 끝 + kebab doctor 출력 (사람): 시작 사이에 빈 줄 3 줄 (file line 1331 / 1332 / 1333) — 한 줄이면 충분. 다른 절 사이는 모두 1 줄.

Why: markdown 자체 렌더링은 영향 없지만 raw spec 의 일관성 ↓.

How to apply: 빈 줄 2 줄 제거 (3 → 1).

(nit / 가독성) §10 의 long-running 절 끝 + `kebab doctor 출력 (사람):` 시작 사이에 빈 줄 3 줄 (file line 1331 / 1332 / 1333) — 한 줄이면 충분. 다른 절 사이는 모두 1 줄. Why: markdown 자체 렌더링은 영향 없지만 raw spec 의 일관성 ↓. How to apply: 빈 줄 2 줄 제거 (3 → 1).
@@ -0,0 +19,4 @@
"completed",
"aborted"
]
},

(보강 권장) ts 가 RFC 3339 timestamp 라고 description 에 명시했는데 JSON Schema 의 format: "date-time" 추가하면 자동 검증 가능. 외부 wrapper (Claude Code skill / MCP) 가 schema validator 통과 시 timestamp parse 실패 즉시 잡힘.

Why: RFC 3339 vs ISO 8601 vs 임의 string 이 free-text description 으로만 구분되면 wrapper 가 다른 format 을 emit 할 위험.

How to apply: "ts": { "type": "string", "description": ... }"format": "date-time" 한 줄 추가.

(보강 권장) `ts` 가 RFC 3339 timestamp 라고 description 에 명시했는데 JSON Schema 의 `format: "date-time"` 추가하면 자동 검증 가능. 외부 wrapper (Claude Code skill / MCP) 가 schema validator 통과 시 timestamp parse 실패 즉시 잡힘. Why: RFC 3339 vs ISO 8601 vs 임의 string 이 free-text description 으로만 구분되면 wrapper 가 다른 format 을 emit 할 위험. How to apply: `"ts": { "type": "string", "description": ... }` 에 `"format": "date-time"` 한 줄 추가.
@@ -0,0 +26,4 @@
"idx": { "type": "integer", "minimum": 1, "description": "asset_started / asset_finished: 1-based index of the current asset within the scan." },
"path": { "type": "string", "description": "asset_started: workspace-relative path of the asset being processed." },
"media": { "type": "string", "description": "asset_started: media kind label (e.g. `markdown`, `pdf`, `image`)." },
"kind_result": {

(naming / 모호성) kind_result 필드명이 모호 — top-level kind 가 이미 event discriminator 라 kind_result 가 "무슨 kind?" 의문 유발. result 또는 outcome 이 명확.

Why: asset_finished event 의 per-asset 결과 (new / updated / skipped / error) 인데 kind_result 는 "kind 의 result" 와 "result 가 kind" 둘 다로 읽힘. ingest_report.v1.items[].kind 와 짝을 맞추는 의도였다면 result 가 더 명료 (또는 description 그대로 사용한 outcome).

How to apply: schema 의 필드명 kind_resultresult 로 변경 + 같은 변경을 §2.4a 의 예제 JSON 줄 (file line 290) 에도 적용. 두 곳 동시 fix 필요.

(naming / 모호성) `kind_result` 필드명이 모호 — top-level `kind` 가 이미 event discriminator 라 `kind_result` 가 "무슨 kind?" 의문 유발. `result` 또는 `outcome` 이 명확. Why: `asset_finished` event 의 per-asset 결과 (new / updated / skipped / error) 인데 `kind_result` 는 "kind 의 result" 와 "result 가 kind" 둘 다로 읽힘. `ingest_report.v1.items[].kind` 와 짝을 맞추는 의도였다면 `result` 가 더 명료 (또는 description 그대로 사용한 `outcome`). How to apply: schema 의 필드명 `kind_result` → `result` 로 변경 + 같은 변경을 §2.4a 의 예제 JSON 줄 (file line 290) 에도 적용. 두 곳 동시 fix 필요.
altair823 added 1 commit 2026-05-02 19:18:40 +00:00
- §10 long-running 절 끝 빈 줄 3 → 1 (다른 절 사이 일관)
- wire schema + §2.4a 예제 JSON: kind_result → result (top-level
  kind 와의 모호성 제거; ingest_report.v1.items[].kind 와 짝)
- wire schema 의 ts 필드: format: \"date-time\" 추가 (RFC 3339
  자동 검증, wrapper 가 다른 format emit 시 즉시 잡힘)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 approved these changes 2026-05-02 19:18:57 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — 회차 1 nit 3건 모두 반영 (빈 줄 3→1, kind_result→result, ts format date-time). 추가 actionable 0. APPROVE.

회차 2 — 회차 1 nit 3건 모두 반영 (빈 줄 3→1, kind_result→result, ts format date-time). 추가 actionable 0. APPROVE.
altair823 merged commit 85c535d220 into main 2026-05-02 19:19:09 +00:00
altair823 deleted branch spec/p9-fb-01-progress-events 2026-05-02 19:19:10 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#51