Repository navigation
fix(scaffold): generate and validate Compose structurally - #4
Merged
Merged
Conversation
BullModule.forRootAsync was assembled in AppModule while the queue, adapter and processor were assembled in QueueModule, so one broker's composition root was split across two files and swapping BullMQ for another broker meant editing both. Move the root connection into QueueModule so AppModule only selects a module. This also narrows when the Bull root is registered: it used to be created whenever BULLMQ_ENABLED was set, and now follows the existing `bullmqEnabled && redisEnabled` gate, so BULLMQ_ENABLED=true with REDIS_ENABLED=false no longer opens a Redis connection. Verified: e2e passes with BULLMQ_ENABLED=true and REDIS_ENABLED=false (2 files / 20 tests). No queue generator profile is introduced. BullMQ is the only supported broker today, and broker replacement changes delivery, retry and DLQ policy rather than just a connection, so the profile axis waits for a second broker and its delivery contract. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qkwp4T95EwEXeoDozJ6Y4m
The generator located the RDB service block with a regex whose end was "the redis service comes next", an undocumented ordering invariant of docker-compose.yml. Any service inserted between the database and redis was swallowed by the lazy match and deleted, with no error. Reproduced by calling materializeTypeOrmCompose directly: an added pgadmin service did not survive materialization to mysql. check:scaffold could not catch it. Its Compose check only asserted that the selected database image was present; it never asserted that an unselected one was absent, while every other axis in the validator (drivers, ORM dependencies, module markers) is bidirectional. A compose carrying both postgres and mysql reported "Scaffold selection is consistent". Leftover profile files were unchecked the same way. Rewrite both on top of a YAML document: - materializeTypeOrmCompose edits service nodes instead of substituting text, and requires exactly one primary RDB service. Other services, top-level sections and comments are preserved. - scaffoldConsistencyIssues reads both Compose files structurally and checks the selected image, the app service's depends_on and DB_* env, ports, tmpfs, leftover RDB services and leftover ORM profile files. Also isolate the e2e database. README, CLAUDE.md and AGENTS.md all documented `DB_PORT=15432 pnpm test:e2e`, but 15432 is the CI service container port and nothing in docker-compose.yml published it, so the documented command could not connect on a clean clone. Dropping the override sent an e2e suite that runs `synchronize: true` and truncates in beforeEach at the development database on 5432. docker-compose.test.yml now runs the selected RDB in its own Compose project on tmpfs and publishes 15432, and the e2e default port points at it. The e2e datasource derives its driver from DB_TYPE instead of hardcoding postgres, so the engine has one materialized source rather than three. Running the e2e against a real MySQL for the first time surfaced an engine-specific defect the PostgreSQL-only runs had hidden: QueryFailedError: BLOB/TEXT column 'name' used in key specification without a key length The shared fixture's `text` unique column only worked on PostgreSQL. It is now varchar(255), and `schema` is passed for PostgreSQL only. Verified: check:scaffold, check:observability, lint, typecheck and build pass; 35 files / 317 tests; both Compose files produce no config warnings; the documented e2e path passes on PostgreSQL (2 files / 20 tests) and on a generated TypeORM/MySQL project (2 files / 20 tests); MikroORM/PostgreSQL and TypeORM/MySQL materialize and typecheck. The original C1 and C2 reproductions now preserve the added service and exit 1 respectively. Not verified: MikroORM and Prisma on MySQL/MariaDB, MariaDB e2e, and Docker e2e for the alternative ORMs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qkwp4T95EwEXeoDozJ6Y4m
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
두 레인(Claude / Codex)이 각각 리뷰·검증하고 브리프로 토의한 결과를 구현했습니다.
무엇이 잘못돼 있었나
생성기가 compose 서비스를 조용히 삭제했습니다.
materializeTypeOrmCompose의 정규식이 RDB 서비스 블록의 끝을 "redis:가 바로 뒤에 온다"로 정의했습니다. 문서화되지 않은 파일 순서 불변식이라, DB와 redis 사이에 서비스를 하나 끼우면 lazy 매칭이 그것까지 삼켜서 지웠습니다. 에러도 경고도 없었습니다.check:scaffold가 그걸 잡지 못했습니다. compose 검사가 선택된 DB image 존재만 보고 선택되지 않은 것의 잔존은 보지 않았습니다. 드라이버·ORM 의존성·모듈 마커는 전부 양방향인데 compose만 단방향이었습니다. postgres와 mysql이 함께 있는 compose가Scaffold selection is consistent로 통과했습니다. 잔존 profile 파일도 같았습니다.문서의 로컬 E2E 명령이 성립하지 않았습니다.
DB_PORT=15432 pnpm test:e2e의 15432는 CI service container 포트고 compose엔 없었습니다. override를 떼면synchronize: true+beforeEachtruncate가 개발 DB(5432)로 갔습니다.무엇을 했나
app의 depends_on과 DB_* 환경변수, port, tmpfs, 잔존 RDB 서비스, 잔존 profile 파일을 검사합니다.docker-compose.test.yml이 선택된 RDB를 자기 Compose project에서 tmpfs로 띄우고 15432를 publish합니다. E2E 기본 포트가 여기를 가리킵니다.DB_TYPE에서 driver를 파생합니다. 엔진의 진실 원천이 셋에서 하나로 줄었습니다.QueueModule로 옮겨 broker의 composition root를 한 파일에 모읍니다.MySQL에서 처음 드러난 결함
실제 MySQL E2E를 처음 돌리자 PostgreSQL 전용 실행이 가려온 것이 나왔습니다.
공용 픽스처의
textunique 컬럼이 PostgreSQL에서만 성립했습니다.varchar(255)로 바꾸고schema는 PostgreSQL에서만 전달합니다.검증
check:scaffold/check:observability/lint/typecheck/build통과config --quietstderr 없음BULLMQ_ENABLED=true,REDIS_ENABLED=false조합 E2E 통과미검증
MikroORM·Prisma의 MySQL/MariaDB 조합, MariaDB E2E, 대체 ORM의 Docker E2E.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Qkwp4T95EwEXeoDozJ6Y4m