update roadmap and storage migrations
This commit is contained in:
parent
c9952509e9
commit
2a7c3ae9bd
6 changed files with 78 additions and 20 deletions
|
|
@ -11,7 +11,7 @@ NomadCode가 `develop` 같은 source-of-truth branch 변경을 즉시 감지할
|
|||
|
||||
## 상태
|
||||
|
||||
[진행중]
|
||||
[완료]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ Forgejo push event를 Gito 내부 revision event 입력으로 변환한다.
|
|||
|
||||
- [x] [watch-bootstrap] `POST /api/listeners/branches` in-memory branch watch bootstrap을 추가한다. 검증: `services/core/internal/controlplane` 테스트가 `nomadcode/develop` watch 등록 후 Forgejo push matching을 확인한다.
|
||||
- [x] [event-record] watched branch만 in-memory event record로 남기고 unwatched branch는 `matched=false`로 처리한다. 검증: `services/core/internal/controlplane` 테스트가 unwatched branch push를 무시하는지 확인한다.
|
||||
- [ ] [watch-store] branch watch와 revision cursor를 Postgres store로 옮긴다. 검증: restart 이후에도 watch/cursor가 유지되고 duplicate push가 idempotent하게 처리된다.
|
||||
- [x] [watch-store] branch watch와 revision cursor를 Postgres store로 옮긴다. 검증: restart 이후에도 watch/cursor가 유지되고 duplicate push가 idempotent하게 처리된다.
|
||||
|
||||
### Epic: [nomadcode-consume] NomadCode event consumption contract
|
||||
|
||||
|
|
@ -56,17 +56,21 @@ NomadCode가 Gito branch event를 내부 runtime event로 받을 수 있게 계
|
|||
- [x] [protosocket-event] `/proto-socket` WebSocket upgrade와 `event.subscribe`, `event.list` MVP action을 추가한다. 검증: `services/core/internal/controlplane` registry test가 event channel action 상태를 확인한다.
|
||||
- [x] [contract] `gito.forgejo-branch-events.v1` 제공 계약을 만든다. 검증: `agent-contract/index.md`가 계약 id와 원문 경로를 가리킨다.
|
||||
- [x] [connection-filter] proto-socket subscription payload 기준으로 connection별 event filtering을 추가한다. 검증: subscription filter unit test와 `event.subscribe` handler test가 repo/branch/action matching을 확인한다.
|
||||
- [ ] [consumer-smoke] `../nomadcode`의 proto-socket client가 `branch.updated`를 수신하는 smoke 또는 fixture를 추가한다. 검증: NomadCode client/core 테스트가 `branch.updated` envelope를 wakeup signal로 해석한다.
|
||||
- [x] [consumer-smoke] `../nomadcode`의 proto-socket client가 `branch.updated`를 수신하는 smoke 또는 fixture를 추가한다. 검증: NomadCode client/core 테스트가 `branch.updated` envelope를 wakeup signal로 해석한다.
|
||||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: 없음
|
||||
- 상태: 승인됨
|
||||
- 요청일: 2026-06-13
|
||||
- 완료 근거:
|
||||
- `consumer-smoke`: `agent-task/archive/2026/06/m-forgejo-branch-event-mvp/01_consumer_smoke/complete.log`의 Roadmap Completion PASS.
|
||||
- `watch-store`: `agent-task/archive/2026/06/m-forgejo-branch-event-mvp/02+01_watch_store/complete.log`의 Roadmap Completion PASS.
|
||||
- 모든 기능 Task가 evidence와 함께 `[x]` 상태라 Milestone을 `[검토중]` 완료 후보로 전환했다.
|
||||
- 종료 전 코드 레벨 검토에서 기존 dev/runtime DB에 남은 legacy `revision_cursors_repo_id_fkey` 흡수 이슈를 발견해 작은 migration 보정과 회귀 테스트를 추가했고, 전체 검증을 재통과했다.
|
||||
- 리뷰 필요:
|
||||
- [ ] 사용자가 완료 결과를 확인했다
|
||||
- [ ] archive 이동을 승인했다
|
||||
- 리뷰 코멘트: 없음
|
||||
- [x] 사용자가 완료 결과를 확인했다
|
||||
- [x] archive 이동을 승인했다
|
||||
- 리뷰 코멘트: 코드 레벨 종료 검토 완료. 큰 이슈 없음. 작은 legacy FK 호환 보정 반영 후 archive 승인됨.
|
||||
|
||||
## 범위 제외
|
||||
|
||||
|
|
@ -84,7 +88,7 @@ NomadCode가 Gito branch event를 내부 runtime event로 받을 수 있게 계
|
|||
- 선행 작업: Runtime Scaffold and Contract Baseline
|
||||
- 후속 작업: Operation Event Outbox, Provider Adapter Foundation
|
||||
- 확인 필요: 없음
|
||||
- 현재 작업현황(2026-06-13): Gito 쪽 1차 in-memory slice는 구현되어 있다. Forgejo push callback, signature 검증, push payload normalization, branch watch bootstrap, in-memory event record, proto-socket `event.subscribe`/`event.list`, connection별 subscription filtering, `gito.forgejo-branch-events.v1` 제공 계약까지 반영했다. active task는 `agent-task/m-forgejo-branch-event-mvp/01_consumer_smoke/`와 `agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/`가 생성되어 있으며, 둘 다 `complete.log`가 없어 Milestone 기능 Task는 미완료 상태로 유지한다.
|
||||
- 검증 현황(2026-06-13): `[connection-filter]`까지 반영한 뒤 `bin/test`, `bin/lint`, `bin/build`, `cd services/core && go test ./...`, `cd services/core && go vet ./...`가 PASS했다. `bin/test`에서 `apps/client`는 Flutter toolchain/test scaffold 없음으로 skip됐다. `consumer-smoke`와 `watch-store` active review stub에는 아직 구현 검증 출력과 PASS 판정이 없다.
|
||||
- 남은 MVP 항목(우선순위): 1) `[consumer-smoke]` 구현/검증/코드리뷰 PASS, 2) `[watch-store]` Postgres persistence와 duplicate push idempotency hardening. `watch-store` plan은 `consumer-smoke`의 `complete.log`를 선행 조건으로 둔다.
|
||||
- 현재 작업 포커스: `../nomadcode`가 직접 소비할 수 있는 1차 MVP를 먼저 닫기 위해 `agent-task/m-forgejo-branch-event-mvp/01_consumer_smoke/` 구현과 리뷰 완료를 다음 실행 1순위로 둔다. `agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/`는 consumer wakeup fixture가 PASS된 뒤 진행한다.
|
||||
- 현재 작업현황(2026-06-13): Gito 쪽 Forgejo push callback, signature 검증, push payload normalization, branch watch routing, proto-socket event subscribe/list/filtering, 제공 계약, NomadCode consumer smoke, Postgres watch/cursor/delivery idempotency 검증까지 모두 완료했다. active task는 없고 완료 산출물은 `agent-task/archive/2026/06/m-forgejo-branch-event-mvp/01_consumer_smoke/`와 `agent-task/archive/2026/06/m-forgejo-branch-event-mvp/02+01_watch_store/`에 있다.
|
||||
- 검증 현황(2026-06-13): `consumer-smoke`는 NomadCode proto-socket branch event service focused/full integration tests와 analyze, diff check가 PASS했다. `watch-store`는 code-server 동반 Postgres로 `GITO_TEST_DATABASE_URL="$DATABASE_URL" go test -v -count=1 ./internal/storage`를 skip 없이 PASS했고, legacy revision cursor FK migration 회귀 테스트도 PASS했다. `services/core` focused/full tests, `go vet`, `./bin/test`, `./bin/lint`, `./bin/build`, `git diff --check`가 PASS했다. NomadCode `flutter analyze --no-fatal-infos`는 exit 0이며 기존 Mattermost `avoid_print` info 13건만 남았다.
|
||||
- 남은 MVP 항목(우선순위): 없음.
|
||||
- 현재 작업 포커스: 완료 및 archive 이동 완료 후 다음 Milestone `Repo Registry and Workspace Lease`로 전환한다.
|
||||
|
|
@ -14,15 +14,15 @@ Gito의 초기 control plane을 build 가능한 modular monolith로 구성한다
|
|||
완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다.
|
||||
스케치 Milestone은 아직 구현 가능한 계획이 아니므로 계획 Milestone보다 아래에 둔다.
|
||||
|
||||
- [완료] Forgejo Branch Event MVP
|
||||
- 경로: `agent-roadmap/archive/phase/control-plane-foundation/milestones/forgejo-branch-event-mvp.md`
|
||||
- 요약: Forgejo push webhook을 watched branch `branch.updated` event로 정규화하고 NomadCode가 proto-socket으로 받을 수 있는 MVP slice를 만든다.
|
||||
|
||||
- [검토중] Runtime Scaffold and Contract Baseline
|
||||
- 경로: `agent-roadmap/phase/control-plane-foundation/milestones/runtime-scaffold-and-contract-baseline.md`
|
||||
- 요약: Go command, Flutter control surface placeholder, contracts, agent-ops, README, architecture 문서를 실행 가능한 기준선으로 정리한다.
|
||||
|
||||
- [진행중] Forgejo Branch Event MVP
|
||||
- 경로: `agent-roadmap/phase/control-plane-foundation/milestones/forgejo-branch-event-mvp.md`
|
||||
- 요약: Forgejo push webhook을 watched branch `branch.updated` event로 정규화하고 NomadCode가 proto-socket으로 받을 수 있는 MVP slice를 만든다.
|
||||
|
||||
- [계획] Repo Registry and Workspace Lease
|
||||
- [진행중] Repo Registry and Workspace Lease
|
||||
- 경로: `agent-roadmap/phase/control-plane-foundation/milestones/repo-registry-and-workspace-lease.md`
|
||||
- 요약: 관리 대상 repo와 workspace slot/lease를 Postgres source of truth로 저장하고 안전하게 할당한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -72,4 +72,3 @@ Gito가 관리할 repository와 기본 branch/workspace root/credential ref를
|
|||
- 선행 작업: Runtime Scaffold and Contract Baseline
|
||||
- 후속 작업: Platformless Git Operation Engine
|
||||
- 확인 필요: 없음
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,58 @@ func TestPostgresBranchEventStorePersistsWatchCursorAndDedupesDelivery(t *testin
|
|||
}
|
||||
}
|
||||
|
||||
func TestPostgresMigrationDropsLegacyRevisionCursorRepoFK(t *testing.T) {
|
||||
dsn := os.Getenv("GITO_TEST_DATABASE_URL")
|
||||
if dsn == "" {
|
||||
t.Skip("GITO_TEST_DATABASE_URL not set; skipping Postgres integration test")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
dsn = isolatedPostgresDSN(t, ctx, dsn)
|
||||
|
||||
legacyPool, err := pgxpool.New(ctx, dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("open legacy pool: %v", err)
|
||||
}
|
||||
defer legacyPool.Close()
|
||||
|
||||
_, err = legacyPool.Exec(ctx, `
|
||||
CREATE TABLE repos (
|
||||
id TEXT PRIMARY KEY
|
||||
);
|
||||
CREATE TABLE revision_cursors (
|
||||
repo_id TEXT NOT NULL REFERENCES repos(id),
|
||||
branch TEXT NOT NULL,
|
||||
revision TEXT NOT NULL,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (repo_id, branch)
|
||||
);
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("create legacy schema: %v", err)
|
||||
}
|
||||
|
||||
migrationData, err := os.ReadFile("../../migrations/00001_initial.sql")
|
||||
if err != nil {
|
||||
t.Fatalf("read migration: %v", err)
|
||||
}
|
||||
store, err := storage.NewPgStore(ctx, dsn, string(migrationData))
|
||||
if err != nil {
|
||||
t.Fatalf("open store: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
cursor := core.RevisionCursor{
|
||||
RepoID: "provider-repo-without-repos-row",
|
||||
Branch: "develop",
|
||||
Revision: "abc123",
|
||||
ObservedAt: time.Now().UTC(),
|
||||
}
|
||||
if err := store.RevisionCursors().UpsertRevisionCursor(ctx, cursor); err != nil {
|
||||
t.Fatalf("upsert cursor after legacy FK migration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func isolatedPostgresDSN(t *testing.T, ctx context.Context, dsn string) string {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ func TestInitialMigrationSupportsStoreBoundary(t *testing.T) {
|
|||
// required column/constraint snippets
|
||||
columnSnippets := []string{
|
||||
"operation_id",
|
||||
"ALTER TABLE revision_cursors DROP CONSTRAINT IF EXISTS revision_cursors_repo_id_fkey",
|
||||
"UNIQUE (provider, repo_id, branch)",
|
||||
"UNIQUE (provider, dedupe_key)",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ CREATE TABLE IF NOT EXISTS revision_cursors (
|
|||
PRIMARY KEY (repo_id, branch)
|
||||
);
|
||||
|
||||
ALTER TABLE revision_cursors DROP CONSTRAINT IF EXISTS revision_cursors_repo_id_fkey;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS branch_watches (
|
||||
id TEXT PRIMARY KEY,
|
||||
provider TEXT NOT NULL,
|
||||
|
|
|
|||
Loading…
Reference in a new issue