feat(infra): wire ts-rs + specta codegen pipeline (T-117)#51
Conversation
Replaces the Sprint 0 stub `scripts/codegen-types.ts` with a deterministic three-stage pipeline that emits TypeScript bindings for the IPC DTOs: ts-rs `#[derive(TS)]` exports per-type files, the new `src-tauri/src/bin/specta-export.rs` bin emits a placeholder `commands.ts` (T-118 will populate it once `tasks_list` lands), and the script writes a sorted barrel `index.ts`. Annotates `Task`, `Phase`, `Status`, `Priority`, `AppEvent`, the `uuid_newtype!` and `i64_newtype!` macros in `domain/shared/ids.rs` with `#[derive(TS)]` + `#[ts(export, export_to = "../../src/shared/types/")]`, plus field-level overrides for `time::OffsetDateTime` (string), `i64` (number), and the transparent newtypes (string / number). Adds a CI gate (`git diff --exit-code src/shared/types/` after `pnpm run codegen`) that fails when a Rust type changes without re-committing the regenerated TS bindings, and a vitest smoke suite asserting the documented file shapes (Task fields, Phase literal union, AppError adjacent-tagged kind/detail, AppEvent snake_case discriminator, TaskId flat string alias, commands.ts AUTO-GENERATED header). Closes #32. - `.oxfmtrc.json`: ignore `src/shared/types/**` (parallels oxlint ignorePatterns) so ts-rs's fixed output style does not fight oxfmt defaults. - `scripts/no-manual-deps.sh`: scope dep-table detection to `[*dependencies*]` sections via awk so adding a `[[bin]]` target no longer false-positives the lock-file gate. - `cargo test --workspace`: 165 passed (11 new ts-rs export tests). - `cargo clippy --workspace --all-targets -- -D warnings`: clean. - `pnpm exec tsc -b` / `pnpm exec oxlint . --max-warnings=0` / `pnpm exec vitest run`: clean.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughImplements deterministic Rust→TypeScript codegen: annotate Rust types with ts-rs, add specta-export binary, add orchestration script to run cargo generation and write a barrel, produce generated TS files under src/shared/types/, add CI drift-check gate, and add validation tests. ChangesRust→TypeScript IPC DTO Codegen Pipeline
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 110-115: The echo statements in the "codegen-diff gate (no
uncommitted Rust → TS drift)" run block include backticks inside double-quoted
strings, causing bash command substitution when printing the error; change those
echo lines to use single-quoted strings (or escape the backticks) so the
messages display the literal text "pnpm run codegen" instead of executing it,
e.g., update the echo calls that reference `pnpm run codegen` after the git diff
check.
In `@CHANGELOG.md`:
- Line 9: The changelog contains a stale sentence claiming “No ts-rs in domain”
that contradicts this PR which adds #[derive(TS)] exports (e.g., types in
domain/tasks/model.rs, domain/shared/events.rs, and domain/shared/ids.rs) and
the new codegen pipeline (scripts/codegen-types.ts and
src-tauri/src/bin/specta-export.rs); edit CHANGELOG.md to remove or replace that
phrase with a concise accurate note stating that ts-rs derives were added to
domain models and that generated TS files are created under src/shared/types/ so
the entry matches the actual changes.
In `@scripts/codegen-types.ts`:
- Around line 27-35: REQUIRED currently omits generated ID alias files so
verify() can falsely succeed; update the REQUIRED array in
scripts/codegen-types.ts to include the generated ID alias files for the domain
types (e.g., TaskId.ts and the corresponding ID files for AppError, AppEvent,
Phase/Priority/Status if applicable) so verify() will fail when those generated
type aliases are missing; modify the REQUIRED constant (not the verify logic) by
adding the missing ID filenames like TaskId.ts, AppErrorId.ts, AppEventId.ts,
etc., matching the actual generated filenames used elsewhere.
In `@scripts/no-manual-deps.sh`:
- Around line 23-29: The git diff invocation uses -U0 which removes section
context and causes the awk state machine (the DEP_DIFF capture logic) to miss
additions inside unchanged dependency tables; change the git diff call in
scripts/no-manual-deps.sh (the DEP_DIFF assignment) to include at least one line
of context (e.g., remove -U0 or use -U1) so the /^[@@]/ and dependency section
matches in the awk script can be set correctly, then run quick tests by staging
a change that adds a dependency to an existing [dependencies] table to confirm
DEP_DIFF now captures the addition.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 31859275-6748-431e-9b06-d7ea443b6bc0
📒 Files selected for processing (24)
.github/workflows/ci.yml.oxfmtrc.jsonCHANGELOG.mdscripts/codegen-types.tsscripts/no-manual-deps.shsrc-tauri/Cargo.tomlsrc-tauri/src/bin/specta-export.rssrc-tauri/src/domain/shared/events.rssrc-tauri/src/domain/shared/ids.rssrc-tauri/src/domain/tasks/model.rssrc/shared/types/AppError.tssrc/shared/types/AppEvent.tssrc/shared/types/Phase.tssrc/shared/types/PrId.tssrc/shared/types/Priority.tssrc/shared/types/ProfileId.tssrc/shared/types/RunId.tssrc/shared/types/Status.tssrc/shared/types/Task.tssrc/shared/types/TaskId.tssrc/shared/types/WorktreeId.tssrc/shared/types/__tests__/codegen.test.tssrc/shared/types/commands.tssrc/shared/types/index.ts
There was a problem hiding this comment.
4 issues found across 24 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/codegen-types.ts">
<violation number="1" location="scripts/codegen-types.ts:45">
P2: Codegen does not remove obsolete generated files, so stale TypeScript bindings can remain and continue to be exported after Rust types are removed or renamed.</violation>
</file>
<file name=".github/workflows/ci.yml">
<violation number="1" location=".github/workflows/ci.yml:112">
P2: The drift gate misses untracked generated files; `git diff --exit-code` only checks tracked-file changes, so newly generated type files can bypass CI.</violation>
<violation number="2" location=".github/workflows/ci.yml:113">
P2: Backticks inside double-quoted `echo` strings trigger command substitution in bash, so `` `pnpm run codegen` `` will actually execute rather than print as literal text. Use single quotes to prevent this.</violation>
</file>
<file name="scripts/no-manual-deps.sh">
<violation number="1" location="scripts/no-manual-deps.sh:23">
P1: Using `-U0` with section-tracking awk causes normal dependency additions to be missed, so manual Cargo dependency edits can bypass the lockfile check.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
CodeRabbit + Cubic-AI bot findings, all VALID: - ci.yml drift gate: switched from `git diff --exit-code` to `git status --porcelain --untracked-files=all` so a brand-new `<NewType>.ts` (untracked, no tracked-diff) cannot bypass the gate. Single-quoted the `echo '::error::...'` lines so the literal backticks around `pnpm run codegen` print instead of triggering command substitution (shellcheck SC2006 / actionlint). - scripts/codegen-types.ts: added `cleanGenerated()` that wipes every `*.ts` under `src/shared/types/` (test files live in `__tests__/`) before regen, so a renamed/removed Rust type cannot leave a stale binding behind. `REQUIRED` now lists every ID alias (TaskId, RunId, WorktreeId, ProfileId, PrId) so verify() catches missing IDs that Task / AppEvent depend on for compilation. - scripts/no-manual-deps.sh: replaced `-U0` with `-U999999` so the full file context lands in the diff and the awk state machine observes the unchanged `[dependencies]` header before any new `+key = value` line. Without context lines, additions to existing dep tables would slip past the gate. Section-tracker regex relaxed from `^[+-]` to `^[ +-]` to match context lines too. - CHANGELOG.md (T-109 entry): removed the stale "No ts-rs in domain" claim that contradicted T-117. Replaced with a note explaining the in-place ts-rs derive choice (derive-only crate, no runtime / I/O, preserves dependency-rule spirit without a parallel DTO hierarchy). Verified: cargo test 165, cargo clippy -D warnings, tsc -b, oxlint --max-warnings=0, vitest 14/14, codegen idempotent after wipe-and-regen. Awk patch sanity-checked against synthetic dep addition under unchanged `[dependencies]`: now flagged correctly.
… desktop binary Adding the `[[bin]] specta-export` codegen helper in 4f74013 left the Tauri package with two binaries (the implicit `src/main.rs` and the specta-export bin), so tauri-cli's `cargo metadata` lookup failed with failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file across all three OS matrix builds. Pinning `default-run = "forgent"` in `[package]` tells cargo / tauri-cli which binary is the app entrypoint. Verified: `cargo metadata` reports `default_run: forgent`, `pnpm exec tauri build --debug` exits 0 locally with both bins present.
Summary
scripts/codegen-types.tswith a deterministic Rust → TypeScript codegen pipeline (ts-rs export tests + newspecta-exportbin + sorted barrelindex.ts).Task,Phase,Status,Priority,AppEvent, theuuid_newtype!/i64_newtype!macros indomain/shared/ids.rswith#[derive(TS)]+#[ts(export, ...)]; field-level overrides preserve the wire contract (OffsetDateTime→string,i64→number, transparent newtypes flatten via#[ts(type = "string"|"number")]).git diff --exit-code src/shared/types/afterpnpm run codegenso any Rust → TS drift fails the frontend-checks job.src/shared/types/__tests__/codegen.test.ts) asserts file shapes (Task fields, Phase literal union, AppError adjacent{kind, detail}, AppEvent snake_case discriminator, TaskId flat string, commands.ts header).Why
T-117 is P0 in sprint 2026-05-04 — blocks T-121 (final commit step) and T-126 (frontend wrapper imports
Task). The renderer cannot consume IPC DTOs as long assrc/shared/types/is empty; CI cannot gate type drift while the codegen script is a no-op.Changes
scripts/codegen-types.ts: real three-stage orchestrator (ts-rs tests → specta-export bin → barrel + verify).src-tauri/src/bin/specta-export.rs+[[bin]]inCargo.toml: deterministiccommands.tsplaceholder until T-118 wirestasks_list.domain/tasks/model.rs,domain/shared/events.rs,domain/shared/ids.rs..github/workflows/ci.yml: add Linux deps + Rust toolchain +Swatinem/rust-cacheto frontend-checks job, thenpnpm run codegenfollowed by the diff gate..oxfmtrc.json: ignoresrc/shared/types/**(parallels oxlint).scripts/no-manual-deps.sh: scope dep-table detection to[*dependencies*]sections via awk so structural edits like[[bin]]no longer false-positive the lock gate.Task.ts,Phase.ts,Status.ts,Priority.ts,AppEvent.ts,AppError.ts,TaskId.ts,RunId.ts,WorktreeId.ts,ProfileId.ts,PrId.ts,commands.ts,index.ts.Acceptance criteria
pnpm codegenproducessrc/shared/types/{Task,Phase,Status,Priority,AppEvent,AppError,commands}.ts.pnpm codegenis byte-deterministic (verified viadiff -ron two consecutive runs).pnpm exec tsc -bcompiles with the generated types.Test plan
cargo test --workspace— 165 passed (added 11 ts-rsexport_bindings_*tests).cargo clippy --workspace --all-targets -- -D warnings— clean.cargo fmt --check— clean.pnpm exec tsc -b— clean.pnpm exec oxlint . --max-warnings=0— clean.pnpm exec vitest run— 14 / 14 passing.pnpm run codegentwice in a row — no diff (deterministic).Closes #32.
Summary by CodeRabbit
New Features
Tests
Chores
Documentation