Open-source readiness: engine bootstrap, branding, release infra#3
Merged
Conversation
…ease infrastructure - Add uv-managed Python engine bootstrap (bridge/engine.ts) — zero system dependencies - Add `altimate-code engine status/reset/path` CLI subcommand - Add unified release workflow (npm + PyPI + GitHub Release on v* tags) - Add CI workflow (TypeScript + Python 3.10/3.11/3.12 matrix) - Add engine-only publish workflow (engine-v* tags → PyPI) - Add version sync script (bump-version.ts) - Add open-source files: LICENSE (MIT), README, CONTRIBUTING, CODE_OF_CONDUCT, CHANGELOG, SECURITY - Add GitHub templates: bug report, feature request, PR template, dependabot - Fix build defines: OPENCODE_* → ALTIMATE_CLI_* (was breaking version detection) - Fix binary/package names: opencode → altimate-code throughout - Fix Docker/Homebrew/AUR: anomalyco/opencode → AltimateAI/altimate-code - Rename SDK exports: OpencodeClient → AltimateClient, createOpencodeClient → createAltimateClient - Update theme $schema URLs: opencode.ai → altimate-code.sh - Update pyproject.toml with full PyPI metadata - Update ping protocol to return engine version - Strip v prefix from ALTIMATE_CLI_VERSION in Script module - Update 30+ test files: env vars, paths, config names Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ry test - Agent config keys in tests: build → builder (matches actual agent name) - Read test context: agent "build" → "builder" - defaultAgent tests: disable all primary agents (builder, analyst, validator, migrator, plan) - Registry test: handle cowsay import failure gracefully in sandboxed envs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep both our dev dependencies and main's new security/docker/tunneling optional dependency groups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines
+59
to
+62
| // 4. Production: uv-managed engine | ||
| const { ensureEngine, enginePythonPath } = await import("./engine") | ||
| await ensureEngine() | ||
| return enginePythonPath() |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
- Add duckdb to dev dependencies so CI installs it (fixes 19 test failures) - Add pytest skip markers for tests requiring sqlguard (proprietary Rust extension not available in CI) and boto3 (optional warehouse dependency) - Wrap ensureEngine() in try-catch in resolvePython() to show a clear error message instead of crashing the CLI on bootstrap failure (Sentry review) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-fixed 18 unused import and redefinition warnings in server.py, cache.py, and feedback_store.py. Added noqa for keyring availability check import in credential_store.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines
74
to
76
| child = spawn(pythonCmd, ["-m", "altimate_engine.server"], { | ||
| stdio: ["pipe", "pipe", "pipe"], | ||
| }) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
Add an `error` event handler on the child process so the CLI doesn't crash with an unhandled exception if the Python executable can't be found (e.g., corrupted venv). Flagged by Sentry review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
engine.ts:
- Add promise-based mutex to prevent concurrent ensureEngine() calls
from corrupting state (race condition on venv/manifest)
client.ts:
- Add re-entrancy guard on start() to prevent recursive start->ping->
call->start loops when the Python process dies immediately
- Clear setTimeout on successful response to prevent timer accumulation
over long sessions (thousands of leaked timer callbacks)
- Reset restartCount on successful start so transient failures don't
permanently disable the bridge
- Add error handler on child.stdin to prevent unhandled exception when
writing to a closed pipe
publish.ts:
- Pass GitHub token via GIT_CONFIG env vars instead of embedding it in
the git clone URL, which leaked it in process args and .git/config
release.yml:
- Validate tag format with strict semver regex before use in shell
- Quote all variable expansions to prevent shell injection via crafted
tag names (e.g., v$(malicious-command))
- Pass github.ref_name via env var instead of inline ${{ }} expansion
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Docker distribution is not needed. The unconditional docker buildx call would have blocked the AUR/Homebrew updates that follow it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This was referenced Mar 5, 2026
4 tasks
This was referenced Mar 17, 2026
anandgupta42
added a commit
that referenced
this pull request
Mar 22, 2026
- Track loops by `(tool, inputHash)` not just tool name (#2) - Use "Failed after" narrative for error traces (#3) - Add keyboard accessibility to viewer tabs (role, tabindex, Enter/Space) (#4) - Use full command as dedup key, not `slice(0,60)` (#5) - Sort timeline events by time before rendering (#6) - Pass `tracesDir` to footer text in `listRecaps` (#7) - Increase `MAX_RECAPS` to 100, add eviction warning log (#8) - Resolve assistant `parentID` for recap enrichment (#9) - Remove unused `tracer` variable in test (#10) - Clarify `--no-trace` backward-compat flag in docs (#1) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
anandgupta42
added a commit
that referenced
this pull request
Mar 23, 2026
…381) * feat: rename tracer to recap with loop detection, post-session summary, and enhanced viewer - Rename `Tracer` class to `Recap` with backward-compat aliases - Rename CLI command `trace` to `recap` (hidden `trace` alias preserved) - Add loop detection: flags repeated tool calls with same input (3+ in last 10) - Add post-session summary: `narrative`, `topTools`, `loops` in trace output - New Summary tab (default) in HTML viewer with: - Truncated prompt with expand toggle - Files changed with SQL diff previews - Tool-agnostic outcome extraction (dbt, pytest, Airflow, pip, SQL) - Deduped dbt commands with pass/fail status, clickable to waterfall - Smart command grouping (boring ls/cd collapsed, meaningful shown) - Error details with resolution tracking - Cost breakdown in collapsible section - Virality: Share Recap (self-contained HTML download), Copy Summary (markdown), Copy Link, branded footer - Fix XSS: timeline items escaped with `e()` - Fix memory leak: per-session `sessionUserMsgIds` with cleanup on eviction - Fix JS syntax: onclick quote escaping in collapsible section - Bound `toolCallHistory` to prevent unbounded growth (cap at 200) - Summary view wrapped in try-catch for visible error messages - Update all 13 test files for rename + 8 new adversarial viewer tests - Update docs: `tracing.md` → `recap.md`, CLI/TUI references updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: share/copy buttons scoping bug + `t.text` undefined + adversarial viewer tests - Fix critical bug: Share Recap and Copy Summary buttons referenced variables from Summary IIFE scope — rewrote `buildMarkdownSummary` to be self-contained - Fix `t.text` → `t.result` in narrative (was rendering "undefined") - Fix `sessionUserMsgIds` not cleaned on MAX_RECAPS eviction (memory leak) - Fix zero cost display: show `$0.00` instead of em-dash - Add try-catch error boundary around Summary view rendering - Add 8 adversarial viewer tests: XSS, NaN/Infinity, null metadata, 200+ spans, JS syntax validation, tool-agnostic outcomes, backward compat Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address all 10 CodeRabbit review comments - Track loops by `(tool, inputHash)` not just tool name (#2) - Use "Failed after" narrative for error traces (#3) - Add keyboard accessibility to viewer tabs (role, tabindex, Enter/Space) (#4) - Use full command as dedup key, not `slice(0,60)` (#5) - Sort timeline events by time before rendering (#6) - Pass `tracesDir` to footer text in `listRecaps` (#7) - Increase `MAX_RECAPS` to 100, add eviction warning log (#8) - Resolve assistant `parentID` for recap enrichment (#9) - Remove unused `tracer` variable in test (#10) - Clarify `--no-trace` backward-compat flag in docs (#1) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add screenshots and update recap viewer documentation - Add Summary tab and full-page screenshots to docs - Update viewer section with 5-tab description - Detail what Summary tab shows: files changed, outcomes, timeline, cost - Add screenshot at top of recap.md for quick visual reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: move Recap to Use section, Telemetry to Reference - Move Recap from Configure > Observability to Use (peer to Commands, Skills) - Move Telemetry from Configure > Observability to Reference (internal analytics) - Remove the Observability section entirely Recap is a feature users interact with after sessions, not a config setting. Telemetry is internal product analytics, not user-facing observability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: viewer UX improvements from 100-trace analysis - Collapse Files Changed after 5 entries with "Show all N files" toggle - Rename "GENS" → "LLM Calls" in header cards - Hide Tokens card when cost is $0 (not actionable without cost context) - Hide Cost metric card when $0.00 (wasted space) - Add prominent error summary banner right after header metrics - Improved dbt outcome detection: catch [PASS], [ERROR], N of M, Compilation Error - Outcome detection rate improved from 18% → 33% across 100 real traces - Updated doc screenshots with cleaner samples Tested across 100 real production traces: 0 crashes, 0 JS errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: always show Cost and Tokens cards $0.00 is a valid cost (Anthropic Max plan). Hiding it implies we don't support cost tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: tool-agnostic outcome extraction for schema, validation, SQL, lineage tools 500-trace analysis revealed: - Schema tasks: 0% outcome visibility → 100% - Validation tasks: 0% outcome visibility → 100% - SQL tasks: 55% outcome visibility → 100% Added outcome extraction for: - schema_inspect, lineage_check, altimate_core_validate results - SQL error messages (not just row counts) - Improved empty session display (shows prompt if available) Tested across 500 diverse synthetic traces (SQL, Airflow, Dagster, Python, schema, validation, migration, connectors) + 100 real traces. 0 crashes, 0 JS errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address 4 new CodeRabbit review comments - Add `inputHash` to `TraceFile.summary.loops` schema type (#11) - Replace `startTrace()` API name with plain language in docs (#12) - Use `CSS.escape()` for spanId in querySelector to handle special chars (#13) - Sort spans by startTime before searching for error resolution (#14) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: round 3 review — sort spans once, clean narrative for 0 LLM calls - Sort spans once before error resolution loop instead of per-error (perf) - Narrative omits "Made 0 LLM calls" for tool-only sessions (UX) - Updated tests to match new narrative format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add missing `altimate_change` markers for recap rename in upstream-shared files Wrap renamed code (Tracer→Recap, trace→recap) with markers so the Marker Guard CI check passes. The diff-based checker uses -U5 context windows per hunk — markers must be close enough to added lines to appear within each hunk's context. Files fixed: - `trace.ts` — handler body, option descriptions, viewer message, compat alias - `app.tsx` — recapViewerServer return, openRecapInBrowser function - `dialog-trace-list.tsx` — error title, Recaps title, compat alias - `worker.ts` — getOrCreateRecap, part events, session title/finalization - `index.ts` — .command(RecapCommand) registration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add altimate_change markers to all upstream-shared files Marker Guard CI was failing — 5 upstream-shared files had custom code (recap rename) without altimate_change markers. Fixed: trace.ts, app.tsx, dialog-trace-list.tsx, worker.ts, index.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: type errors in training-import.test.ts from main merge Pre-existing type issues from main: mock missing `context`/`rule` fields and readFile return type mismatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kulvirgit
pushed a commit
that referenced
this pull request
Mar 30, 2026
…381) * feat: rename tracer to recap with loop detection, post-session summary, and enhanced viewer - Rename `Tracer` class to `Recap` with backward-compat aliases - Rename CLI command `trace` to `recap` (hidden `trace` alias preserved) - Add loop detection: flags repeated tool calls with same input (3+ in last 10) - Add post-session summary: `narrative`, `topTools`, `loops` in trace output - New Summary tab (default) in HTML viewer with: - Truncated prompt with expand toggle - Files changed with SQL diff previews - Tool-agnostic outcome extraction (dbt, pytest, Airflow, pip, SQL) - Deduped dbt commands with pass/fail status, clickable to waterfall - Smart command grouping (boring ls/cd collapsed, meaningful shown) - Error details with resolution tracking - Cost breakdown in collapsible section - Virality: Share Recap (self-contained HTML download), Copy Summary (markdown), Copy Link, branded footer - Fix XSS: timeline items escaped with `e()` - Fix memory leak: per-session `sessionUserMsgIds` with cleanup on eviction - Fix JS syntax: onclick quote escaping in collapsible section - Bound `toolCallHistory` to prevent unbounded growth (cap at 200) - Summary view wrapped in try-catch for visible error messages - Update all 13 test files for rename + 8 new adversarial viewer tests - Update docs: `tracing.md` → `recap.md`, CLI/TUI references updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: share/copy buttons scoping bug + `t.text` undefined + adversarial viewer tests - Fix critical bug: Share Recap and Copy Summary buttons referenced variables from Summary IIFE scope — rewrote `buildMarkdownSummary` to be self-contained - Fix `t.text` → `t.result` in narrative (was rendering "undefined") - Fix `sessionUserMsgIds` not cleaned on MAX_RECAPS eviction (memory leak) - Fix zero cost display: show `$0.00` instead of em-dash - Add try-catch error boundary around Summary view rendering - Add 8 adversarial viewer tests: XSS, NaN/Infinity, null metadata, 200+ spans, JS syntax validation, tool-agnostic outcomes, backward compat Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address all 10 CodeRabbit review comments - Track loops by `(tool, inputHash)` not just tool name (#2) - Use "Failed after" narrative for error traces (#3) - Add keyboard accessibility to viewer tabs (role, tabindex, Enter/Space) (#4) - Use full command as dedup key, not `slice(0,60)` (#5) - Sort timeline events by time before rendering (#6) - Pass `tracesDir` to footer text in `listRecaps` (#7) - Increase `MAX_RECAPS` to 100, add eviction warning log (#8) - Resolve assistant `parentID` for recap enrichment (#9) - Remove unused `tracer` variable in test (#10) - Clarify `--no-trace` backward-compat flag in docs (#1) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add screenshots and update recap viewer documentation - Add Summary tab and full-page screenshots to docs - Update viewer section with 5-tab description - Detail what Summary tab shows: files changed, outcomes, timeline, cost - Add screenshot at top of recap.md for quick visual reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: move Recap to Use section, Telemetry to Reference - Move Recap from Configure > Observability to Use (peer to Commands, Skills) - Move Telemetry from Configure > Observability to Reference (internal analytics) - Remove the Observability section entirely Recap is a feature users interact with after sessions, not a config setting. Telemetry is internal product analytics, not user-facing observability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: viewer UX improvements from 100-trace analysis - Collapse Files Changed after 5 entries with "Show all N files" toggle - Rename "GENS" → "LLM Calls" in header cards - Hide Tokens card when cost is $0 (not actionable without cost context) - Hide Cost metric card when $0.00 (wasted space) - Add prominent error summary banner right after header metrics - Improved dbt outcome detection: catch [PASS], [ERROR], N of M, Compilation Error - Outcome detection rate improved from 18% → 33% across 100 real traces - Updated doc screenshots with cleaner samples Tested across 100 real production traces: 0 crashes, 0 JS errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: always show Cost and Tokens cards $0.00 is a valid cost (Anthropic Max plan). Hiding it implies we don't support cost tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: tool-agnostic outcome extraction for schema, validation, SQL, lineage tools 500-trace analysis revealed: - Schema tasks: 0% outcome visibility → 100% - Validation tasks: 0% outcome visibility → 100% - SQL tasks: 55% outcome visibility → 100% Added outcome extraction for: - schema_inspect, lineage_check, altimate_core_validate results - SQL error messages (not just row counts) - Improved empty session display (shows prompt if available) Tested across 500 diverse synthetic traces (SQL, Airflow, Dagster, Python, schema, validation, migration, connectors) + 100 real traces. 0 crashes, 0 JS errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address 4 new CodeRabbit review comments - Add `inputHash` to `TraceFile.summary.loops` schema type (#11) - Replace `startTrace()` API name with plain language in docs (#12) - Use `CSS.escape()` for spanId in querySelector to handle special chars (#13) - Sort spans by startTime before searching for error resolution (#14) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: round 3 review — sort spans once, clean narrative for 0 LLM calls - Sort spans once before error resolution loop instead of per-error (perf) - Narrative omits "Made 0 LLM calls" for tool-only sessions (UX) - Updated tests to match new narrative format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add missing `altimate_change` markers for recap rename in upstream-shared files Wrap renamed code (Tracer→Recap, trace→recap) with markers so the Marker Guard CI check passes. The diff-based checker uses -U5 context windows per hunk — markers must be close enough to added lines to appear within each hunk's context. Files fixed: - `trace.ts` — handler body, option descriptions, viewer message, compat alias - `app.tsx` — recapViewerServer return, openRecapInBrowser function - `dialog-trace-list.tsx` — error title, Recaps title, compat alias - `worker.ts` — getOrCreateRecap, part events, session title/finalization - `index.ts` — .command(RecapCommand) registration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add altimate_change markers to all upstream-shared files Marker Guard CI was failing — 5 upstream-shared files had custom code (recap rename) without altimate_change markers. Fixed: trace.ts, app.tsx, dialog-trace-list.tsx, worker.ts, index.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: type errors in training-import.test.ts from main merge Pre-existing type issues from main: mock missing `context`/`rule` fields and readFile return type mismatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10 tasks
suryaiyer95
added a commit
that referenced
this pull request
Apr 17, 2026
Fixes five correctness, reliability, and portability issues surfaced by the consensus code review of this branch. CRITICAL #1 — Cross-dialect partitioned diff (`data-diff.ts`): `runPartitionedDiff` built one partition WHERE clause with `sourceDialect` and passed it as shared `where_clause` to the recursive `runDataDiff`, which applied it to both warehouses identically. Cross-dialect partition mode (MSSQL → Postgres) failed because the target received T-SQL `DATETRUNC`/`CONVERT(DATE, …, 23)`. Now builds per-side WHERE using each warehouse's dialect and bakes it into dialect-quoted subquery SQL for source and target independently. The existing side-aware CTE injection handles the rest. MAJOR #2 — Azure AD token caching and refresh (`sqlserver.ts`): `acquireAzureToken` fetched a fresh token on every `connect()` and embedded it in the pool config with no refresh. Long-lived sessions silently failed when the ~1h token expired. Adds a module-scoped cache keyed by `(resource, client_id)` with proactive refresh 5 min before expiry, parsing `expiresOnTimestamp` from `@azure/identity` or the JWT `exp` claim from the `az` CLI fallback. Exposes `_resetTokenCacheForTests` for isolation. MAJOR #3 — `joindiff` + cross-warehouse guard (`data-diff.ts`): Explicit `algorithm: "joindiff"` combined with different warehouses produced broken SQL (one task referencing two CTE aliases with only one injected). Now returns an early error with a clear message steering users to `hashdiff` or `auto`. Cross-warehouse detection switched from warehouse-name string compare to dialect compare, matching the underlying SQL-divergence invariant. MAJOR #4 — Dialect-aware identifier quoting in CTE wrapping (`data-diff.ts`): `resolveTableSources` wrapped plain-table names with ANSI double-quotes for all dialects. T-SQL/Fabric require `QUOTED_IDENTIFIER ON` for this to work; default for `mssql`/tedious is ON, but user contexts (stored procs, legacy collations) can override. Now accepts source/target dialect parameters and delegates to `quoteIdentForDialect`, which was hoisted to module scope so it can be reused across partition and CTE paths. MAJOR #5 — Configurable Azure resource URL (`sqlserver.ts`, `normalize.ts`): Token acquisition hardcoded `https://database.windows.net/`, blocking Azure Government, Azure China, and sovereign-cloud customers. Now honours an explicit `azure_resource_url` config field and otherwise infers the URL from the host suffix (`.usgovcloudapi.net`, `.chinacloudapi.cn`). Adds the usual camelCase/snake_case aliases in the SQL Server normalizer. Also surfaces Azure auth error causes: if both `@azure/identity` and `az` CLI fail, the thrown error includes both hints (redacted) so users know why rather than seeing the generic "install @azure/identity or run az login" message. Tests: adds `data-diff-cross-dialect.test.ts` covering the cross-dialect partition WHERE routing and the `joindiff` guard; extends `data-diff-cte.test.ts` with dialect-aware quoting assertions for tsql, fabric, and mysql; extends `sqlserver-unit.test.ts` with cache hit / expiry refresh / client-id keyed cache tests, commercial/gov/china/custom resource URL resolution, and the combined-error-hints surface. All 41 sqlserver driver tests, 24 data-diff orchestrator tests, and 214 normalize/connections tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
anandgupta42
pushed a commit
that referenced
this pull request
Apr 21, 2026
* fix: use synchronous DuckDB constructor to avoid bun runtime timeout Bun's runtime never fires native addon async callbacks, so the async `new duckdb.Database(path, opts, callback)` form would hit the 2-second timeout fallback on every connection attempt. Switch to the synchronous constructor form `new duckdb.Database(path)` / `new duckdb.Database(path, opts)` which throws on error and completes immediately in both Node and bun runtimes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * revert: restore async DuckDB constructor — sync change was bogus The async callback form with 2s fallback was already working correctly at e3df5a4. The timeout was caused by a missing duckdb .node binary, not a bun incompatibility. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add MSSQL/Fabric dialect mapping and data-parity support - Add `warehouseTypeToDialect()` mapping: sqlserver→tsql, mssql→tsql, fabric→fabric, postgresql→postgres, mariadb→mysql. Fixes critical serde mismatch where Rust engine rejects raw warehouse type names. - Update both `resolveDialect()` functions to use the mapping - Add MSSQL/Fabric cases to `dateTruncExpr()` — DATETRUNC(DAY, col) - Add locale-safe date literal casting via CONVERT(DATE, ..., 23) - Register `fabric` in DRIVER_MAP (reuses sqlserver TDS driver) - Add `fabric` normalize aliases in normalize.ts - Add 15 SQL Server driver unit tests (TOP injection, truncation, schema introspection, connection lifecycle, result format) - Add 9 dialect mapping unit tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Azure AD authentication to SQL Server driver (7 flows) - Support all 7 Azure AD / Entra ID auth types in `sqlserver.ts`: `azure-active-directory-password`, `access-token`, `service-principal-secret`, `msi-vm`, `msi-app-service`, `azure-active-directory-default`, `token-credential` - Force TLS encryption for all Azure AD connections - Dynamic import of `@azure/identity` for `DefaultAzureCredential` - Add normalize aliases for Azure AD config fields (`authentication`, `azure_tenant_id`, `azure_client_id`, `azure_client_secret`, `access_token`) - Add `fabric: SQLSERVER_ALIASES` to DRIVER_ALIASES - Add 10 Azure AD unit tests covering all auth flows, encryption, and `DefaultAzureCredential` with managed identity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add MSSQL and Microsoft Fabric documentation to data-parity SKILL.md - Add SQL Server / Fabric schema inspection query in Step 2 - Add "SQL Server and Microsoft Fabric" section with: - Supported configurations table (sqlserver, mssql, fabric) - Fabric connection guide with Azure AD auth types - Algorithm behavior notes (joindiff vs hashdiff selection) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: delegate Azure AD credential creation to tedious and remove underscore column filter - **Azure AD auth**: Pass `azure-active-directory-*` types directly to tedious instead of constructing `DefaultAzureCredential` ourselves. Tedious imports `@azure/identity` internally and creates credentials — avoids bun CJS/ESM `isTokenCredential` boundary issue that caused "not an instance of the token credential class" errors. - **Auth shorthands**: Map `CLI`, `default`, `password`, `service-principal`, `msi`, `managed-identity` to their full tedious type names. - **Column filter**: Remove `_.startsWith("_")` filter from `execute()` result columns — it stripped legitimate aliases like `_p` used by partition discovery, causing partitioned diffs to return empty results. - **Tests**: Remove `@azure/identity` mock (no longer imported by driver), update auth assertions, add shorthand mapping tests, fix column filter test. - **Verified**: All 97 driver tests pass. Full data-diff pipeline tested against real MSSQL server (profile, joindiff, auto, where_clause, partitioned). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: upgrade `mssql` to v12 with `ConnectionPool` isolation and row flattening - Upgrade `mssql` from v11 to v12 (`tedious` 18 → 19) - Use explicit `ConnectionPool` instead of global `mssql.connect()` to isolate multiple simultaneous connections - Flatten unnamed column arrays — `mssql` merges unnamed columns (e.g. `SELECT COUNT(*), SUM(...)`) into a single array under the empty-string key; restore positional column values - Proper column name resolution: compare `namedKeys.length` against flattened row length, fall back to synthetic `col_0`, `col_1`, etc. - Update test mock to export `ConnectionPool` class and `createMockPool` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve TypeScript spread-type errors in Azure AD conditional options Use ternary expressions (`x ? {...} : {}`) instead of short-circuit (`x && {...}`) to avoid spreading a boolean value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve cubic review findings on MSSQL/Fabric PR - P1: restrict `flattenRow` to only spread the empty-string key (`""`) where mssql merges unnamed columns, preserving legitimate array values - P2: escape single quotes in `partitionValue` for date-mode branches in `buildPartitionWhereClause` (categorical mode already escaped) - P2: add `fabric` to `PASSWORD_DRIVERS` set in registry for consistent password validation alongside `sqlserver`/`mssql` - P2: fallback to `"(no values)"` when `d.values` is nullish to prevent template literal coercing `undefined` to the string `"undefined"` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add fabric connection path and flattenRow coverage - sqlserver-unit: 3 tests for unnamed column flattening — verifies only the empty-string key is spread, legitimate named arrays are preserved - driver-normalize: fabric type uses SQLSERVER_ALIASES (server → host, trustServerCertificate → trust_server_certificate) - connections: fabric type is recognized in DRIVER_MAP and listed correctly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: document minimum versions and make @azure/identity optional - Add "Minimum Version Requirements" table to SKILL.md covering SQL Server 2022+, mssql v12, and @azure/identity v4 with rationale for each - Document auth shorthands (CLI, default, password, service-principal, msi) - Move @azure/identity from dependencies to optional peerDependencies so it is NOT installed by default — only required for Azure AD auth - Add runtime check in sqlserver driver: if Azure AD auth type is requested but @azure/identity is missing, throw a clear install instruction error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: acquire Azure AD tokens directly to bypass Bun browser-bundle resolution - For `azure-active-directory-default` (CLI/default auth), acquire token ourselves instead of delegating to tedious's internal `@azure/identity` - Strategy: try `DefaultAzureCredential` first, fall back to `az` CLI subprocess - Bypasses Bun resolving `@azure/identity` to browser bundle where `DefaultAzureCredential` is a non-functional stub - Also bypasses CJS/ESM `isTokenCredential` boundary mismatch - All 31 driver unit tests pass, verified against real Fabric endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-acquire Azure AD token for `azure-active-directory-access-token` when none supplied The `azure-active-directory-access-token` branch passed `token: config.token ?? config.access_token` to tedious. When neither field was set on a connection (e.g. a `fabric-migration` entry that declared the auth type but no token), tedious threw: TypeError: The "config.authentication.options.token" property must be of type string This blocked any Fabric/MSSQL config that relied on ambient credentials (Azure CLI / managed identity) but used the explicit `azure-active-directory-access-token` type instead of the `default` shorthand. Refactor token acquisition (`DefaultAzureCredential` → `az` CLI fallback) into a shared `acquireAzureToken()` helper used by both the `default` path and the `access-token` path when no token was supplied. Callers that pass an explicit token are unchanged. Also harden `mock.module("node:child_process", ...)` in `sqlserver-unit.test.ts` to spread the real module so sibling tests in the same `bun test` run keep access to `spawn` / `exec` / `fork`. Tests: 110 pass, 0 fail in `packages/drivers`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: side-aware CTE injection for cross-warehouse `data_diff` SQL-query mode When `source` and `target` are both SQL queries, `resolveTableSources` wraps them as `__diff_source` / `__diff_target` CTEs and the executor prepends the combined `WITH …` block to every engine-emitted task. T-SQL and Fabric parse-bind every CTE body even when unreferenced, so a task routed to the source warehouse failed to resolve the target-only base table referenced inside the unused `__diff_target` CTE (and vice versa), producing `Invalid object name` errors from the wrong warehouse. Return side-specific prefixes from `resolveTableSources` alongside the combined one, and have the executor loop in `runDataDiff` pick the source or target prefix per task when `source_warehouse !== target_warehouse`. Same-warehouse behaviour is unchanged. Adds `data-diff-cte.test.ts` covering plain-name passthrough, both-query wrapping, side-specific CTE isolation, and CTE merging with engine-emitted `WITH` clauses (10 tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: regenerate `bun.lock` to match drivers `peerDependencies` layout Commit 333a45c moved `@azure/identity` from `optionalDependencies` to `peerDependencies` with `optional: true` in `packages/drivers/package.json`, but the lockfile was not regenerated. That left CI under `--frozen-lockfile` broken and made fresh installs silently diverge from the committed state. Running `bun install` brings the lockfile in sync: `@azure/identity` is recorded as an optional peer, and its transitive pins (`@azure/msal-browser`, `@azure/msal-common`, `@azure/msal-node`) re-resolve to the versions required by `tedious` and `snowflake-sdk`, matching the reachable runtime surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address all CRITICAL/MAJOR findings from multi-model review Fixes five correctness, reliability, and portability issues surfaced by the consensus code review of this branch. CRITICAL #1 — Cross-dialect partitioned diff (`data-diff.ts`): `runPartitionedDiff` built one partition WHERE clause with `sourceDialect` and passed it as shared `where_clause` to the recursive `runDataDiff`, which applied it to both warehouses identically. Cross-dialect partition mode (MSSQL → Postgres) failed because the target received T-SQL `DATETRUNC`/`CONVERT(DATE, …, 23)`. Now builds per-side WHERE using each warehouse's dialect and bakes it into dialect-quoted subquery SQL for source and target independently. The existing side-aware CTE injection handles the rest. MAJOR #2 — Azure AD token caching and refresh (`sqlserver.ts`): `acquireAzureToken` fetched a fresh token on every `connect()` and embedded it in the pool config with no refresh. Long-lived sessions silently failed when the ~1h token expired. Adds a module-scoped cache keyed by `(resource, client_id)` with proactive refresh 5 min before expiry, parsing `expiresOnTimestamp` from `@azure/identity` or the JWT `exp` claim from the `az` CLI fallback. Exposes `_resetTokenCacheForTests` for isolation. MAJOR #3 — `joindiff` + cross-warehouse guard (`data-diff.ts`): Explicit `algorithm: "joindiff"` combined with different warehouses produced broken SQL (one task referencing two CTE aliases with only one injected). Now returns an early error with a clear message steering users to `hashdiff` or `auto`. Cross-warehouse detection switched from warehouse-name string compare to dialect compare, matching the underlying SQL-divergence invariant. MAJOR #4 — Dialect-aware identifier quoting in CTE wrapping (`data-diff.ts`): `resolveTableSources` wrapped plain-table names with ANSI double-quotes for all dialects. T-SQL/Fabric require `QUOTED_IDENTIFIER ON` for this to work; default for `mssql`/tedious is ON, but user contexts (stored procs, legacy collations) can override. Now accepts source/target dialect parameters and delegates to `quoteIdentForDialect`, which was hoisted to module scope so it can be reused across partition and CTE paths. MAJOR #5 — Configurable Azure resource URL (`sqlserver.ts`, `normalize.ts`): Token acquisition hardcoded `https://database.windows.net/`, blocking Azure Government, Azure China, and sovereign-cloud customers. Now honours an explicit `azure_resource_url` config field and otherwise infers the URL from the host suffix (`.usgovcloudapi.net`, `.chinacloudapi.cn`). Adds the usual camelCase/snake_case aliases in the SQL Server normalizer. Also surfaces Azure auth error causes: if both `@azure/identity` and `az` CLI fail, the thrown error includes both hints (redacted) so users know why rather than seeing the generic "install @azure/identity or run az login" message. Tests: adds `data-diff-cross-dialect.test.ts` covering the cross-dialect partition WHERE routing and the `joindiff` guard; extends `data-diff-cte.test.ts` with dialect-aware quoting assertions for tsql, fabric, and mysql; extends `sqlserver-unit.test.ts` with cache hit / expiry refresh / client-id keyed cache tests, commercial/gov/china/custom resource URL resolution, and the combined-error-hints surface. All 41 sqlserver driver tests, 24 data-diff orchestrator tests, and 214 normalize/connections tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address PR #705 bot review findings (coderabbitai + cubic + copilot) Addresses the remaining issues raised by coderabbitai, cubic-dev-ai, and the Copilot PR reviewer on top of the multi-model consensus fix. ### CRITICAL - **`@azure/identity` peer dep removed** (`drivers/package.json`) `mssql@12` → `tedious@19` bundles `@azure/identity ^4.2.1` as a regular dependency. Declaring it here as an optional peer was redundant and caused transitive-version-drift concerns. Users get the correct version automatically through the tedious chain; our lazy import handles the browser-bundle edge case itself. ### MAJOR - **Cross-dialect date partition literal normalization** (`data-diff.ts`) `buildPartitionDiscoverySQL` on MSSQL returns a JS `Date` object, stringified upstream as `"Mon Jan 01 2024 …"`. `CONVERT(DATE, …, 23)` rejects that format. Normalize `partitionValue` to ISO `yyyy-mm-dd` before dialect casting so the T-SQL/Fabric path works end-to-end on dates discovered from MSSQL sources. - **`crossWarehouse` uses resolved warehouse identity** (`data-diff.ts`) Previous commit gated on dialect compare, which treated two independent MSSQL instances as "same warehouse" and would have let `joindiff` route a JOIN through a warehouse that can't resolve the other side's base tables. Now resolves both sides' warehouse name (falling back to the default warehouse when a side is omitted) and compares identities — identity-based gating handles both the "undefined vs default" case (cubic) and the "same-dialect, different instance" case (Copilot). - **Drop `mssql.connect()` fallback** (`sqlserver.ts`) `mssql@^12` guarantees `ConnectionPool` as a named export. The fallback silently re-introduced the global-shared-pool bug this branch was added to fix. Now throws a descriptive error if `ConnectionPool` is missing — cross-database pool interference cannot regress. - **Non-string `config.authentication` guarded** (`sqlserver.ts`) Caller passing a pre-built `{ type, options }` block (or `null`) previously crashed with `TypeError: rawAuth.toLowerCase is not a function`. Now only applies the shorthand lookup when `rawAuth` is a string; other values pass through so tedious can handle them or reject them with its own error. - **Unknown `azure-active-directory-*` subtype fails fast** (`sqlserver.ts`) Typos or future tedious subtypes previously dropped through all `else if` branches, producing a config with `encrypt: true` but no `authentication` block. tedious then surfaced an opaque error far from the root cause. Now throws with the offending subtype and the supported list. - **`execSync` replaced with async `exec`** (`sqlserver.ts`) The `az account get-access-token` CLI fallback previously blocked the event loop for up to 15s. Switched to `util.promisify(exec)` so the connection path stays non-blocking. - **Mixed named + unnamed column derivation preserves headers** (`sqlserver.ts`) Previously `SELECT name, COUNT(*), SUM(x)` produced either `["name", ""]` (blank header) or `["col_0", "col_1", "col_2"]` (lost `name`). Rewrote column/row derivation to iterate in one pass, preserving known named columns and synthesizing `col_N` only for expanded `""`-key positions. ### MINOR - **`(no values)` fallback for empty `diff_row.values` array** (`tools/data-diff.ts`) `[].join(" | ") ?? "(no values)"` never fires because `""` is falsy-but-not- nullish. Gate on `d.values?.length` instead. ### Test / docs - `sqlserver-unit.test.ts`: token-cache client-id test now counts actual `getToken` invocations (previous version only verified both got the same mocked token, which proved nothing about keying). - `sqlserver-unit.test.ts`: "empty result" test now mirrors the real mssql shape (`recordset.columns` is a property *on* the recordset array, not a sibling key). - `sqlserver-unit.test.ts`: added mixed-column regression tests — "name + COUNT + SUM" and "single unnamed column" — to lock in the derivation fix. - `sqlserver-unit.test.ts`: stubbed async `exec` via `util.promisify.custom` so tests drive both the `execSync` legacy path and the new async path. - `SKILL.md`: Fabric config fenced block now declares `yaml` (markdownlint MD040). All tests: 43/43 sqlserver driver + 238/238 opencode test suite. Attribution: findings identified by coderabbitai, cubic-dev-ai, and the Copilot PR reviewer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: drop stale `@azure/identity` peer-dep entries from `bun.lock` Commit 38cfb0e removed `@azure/identity` from the drivers package's `peerDependencies` (tedious already bundles it), but the lockfile's `packages/drivers` workspace section still carried the corresponding `peerDependencies` and `optionalPeers` blocks. CI running `bun install --frozen-lockfile` would fail on the drift. Minimal edit — just removes the two stale blocks. No resolution changes (`bun install --frozen-lockfile` passes with "no changes"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: CI — isolate `data-diff-cross-dialect` tests from other files The prior integration-style test mocked the Registry module globally with `mock.module(".../registry", ...)`, which leaks across all test files in bun:test's single-process runner. That caused 14 unrelated tests in `connections.test.ts`, `telemetry-safety.test.ts`, and `dbt-first-execution.test.ts` to fail in CI. Additionally, the test relied on `mock.module("@altimateai/altimate-core")` to supply a fake `DataParitySession`. The npm-published 0.2.6 of that package does not export `DataParitySession` (sessions are only in the locally-built `altimate-core-internal` binary), and Bun's `mock.module` cannot override a package that another test file has already imported — so the integration test was structurally unreliable. Resolution: 1. **Export pure SQL-builder helpers** from `data-diff.ts` (`dateTruncExpr`, `buildPartitionWhereClause`) and unit-test them directly. No module mocking required; the test directly exercises the logic the CRITICAL/MAJOR fix changed. 2. **Move the `joindiff` + cross-warehouse guard earlier** in `runDataDiff` — before the NAPI import. Semantically identical for callers (guard still fires, same error message, `steps: 0`), but now it can be integration-tested without any NAPI mock. Preserves end-to-end wiring coverage for the guard. 3. **Rewrite `data-diff-cross-dialect.test.ts`** as pure-function unit tests for the partition WHERE logic + a real `runDataDiff` call for the joindiff guard. No more cross-file mock pollution. Functionality unchanged: - `runDataDiff` behavior for real callers is identical. The only observable difference is error-ordering: if a caller simultaneously omits NAPI and passes `joindiff + cross-warehouse`, they now get the "joindiff requires same warehouse" error instead of the NAPI-missing error. That's strictly better UX — NAPI availability is a deployment concern, `joindiff`+cross-warehouse is a user error. - `buildPartitionWhereClause` and `dateTruncExpr` are now exported but semantically unchanged — same inputs, same outputs. Test results: - 2821 altimate tests pass, 0 fail - 43 sqlserver driver tests pass, 0 fail - The 19 remaining full-suite failures (`mcp/`, `tool/project-scan`, `plan-approval-phrase`) are pre-existing on `main` and unrelated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: follow-up PR bot review findings (cubic P1/P2 + coderabbit MAJOR/MINOR) Addresses 5 substantive issues raised by the latest round of bot reviews. ### P1 / MAJOR - **MySQL/MariaDB week-partition values no longer corrupted** (cubic P1, data-diff.ts:610) — the prior ISO `yyyy-mm-dd` normalization applied to every dialect silently rewrote MySQL `DATE_FORMAT(%Y-%u)` outputs like `"2024-42"` into invalid dates, producing WHERE clauses that never match. Scope the normalization to T-SQL / Fabric only — those use `CONVERT(DATE, …, 23)` which is the only code path that requires ISO. Postgres, MySQL, ClickHouse, BigQuery, Oracle all get the raw value verbatim, matching their own `DATE_TRUNC`/`toStartOf*` output. - **Partitioned diff no longer drops extra_columns** (coderabbit MAJOR, data-diff.ts:824) — the partition fix wraps each side as a SELECT subquery before recursing. `discoverExtraColumns` skips SQL queries (only inspects plain table names), so the recursive `runDataDiff` fell through to key-only comparison, silently losing value-level diffs. Now `runPartitionedDiff` runs discovery ONCE on the plain source table up-front and passes the resolved `extra_columns` explicitly to each recursive call. Audit-column exclusion metadata is also propagated to the aggregated result for user reporting. ### P2 / MINOR - **`azure_resource_url` trailing slash normalized** (cubic P2, sqlserver.ts:50) — an explicit `"https://custom-host"` (no slash) would produce an invalid OAuth scope `"https://custom-host.default"`. Enforce a trailing slash in `resolveAzureResourceUrl`. - **`az account get-access-token` uses `execFile`** (coderabbit, sqlserver.ts:200) — replaces `exec(<shell command string>)` with `execFile("az", [args])` so user-supplied `azure_resource_url` can't introduce shell metacharacters into the command string. Also updates the test harness to stub both `exec` and `execFile`. ### Test isolation / coverage - **Added same-dialect cross-warehouse joindiff test** (cubic, data-diff-cross-dialect.test.ts:97) — two MSSQL servers with different hosts must still be gated by the joindiff guard; previous tests only exercised mixed dialects. - **Added MySQL week-partition regression tests** — prevent future revivals of the dialect-unaware ISO rewrite. - **Added trailing-slash `azure_resource_url` test.** Test results: - 44/44 sqlserver driver tests pass - 2824/2824 altimate tests pass, 0 fail - Remaining full-suite failures (`mcp/`, `tool/project-scan`, `plan-approval-phrase`) are pre-existing on `main`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Merged
11 tasks
This was referenced May 20, 2026
sahrizvi
pushed a commit
that referenced
this pull request
Jun 5, 2026
…uncation constant Three follow-ups from the multi-LLM consensus review of PR #895, codex-validated: * Worker user-text branch now skips parts with `synthetic` or `ignored` set (Major #1 in the review, codex-confirmed). `Session.createUserMessage` in prompt.ts attaches many synthetic text parts to the user messageID for MCP resource banners, decoded file contents, retry/reminder text, plan-mode reminders, and agent-handoff tags. Without the gate they pass the `sessionUserMsgIds.has(messageID)` check, `metadata.prompt` ends up holding the LAST synthetic part (typically a file blob), and the chat tab renders one fake "▶ You" bubble per synthetic span — defeating the two display surfaces this PR fixes. Gated on an `isAuthoredText` predicate so the symmetric assistant-text branch is also protected. Continue via predicate rather than `continue` keyword so the outer event loop still forwards the event downstream via `Rpc.emit`. * `Trace.rehydrateFromFile` now marks any open generation span as interrupted (Major #2). The transient state `logStepStart` populated (`currentGenerationSpanId`, `generationText`, `generationToolCalls`, `pendingToolResults`) is memory-only and can't be reconstructed from disk. If we leave open spans open, the next `step-finish` for that turn drops at the `!this.currentGenerationSpanId` guard and follow-up `logToolCall` mis-parents tool spans to the root, silently degrading the trace shape. Closing the span with `status: "error"` and a `statusMessage` describing the interruption preserves the partial data and makes the boundary visible in the viewer. * Extracted the 4000-char truncation cap as `USER_MESSAGE_INPUT_MAX_CHARS` exported from tracing.ts (new cubic P3 on viewer.ts:1331). The viewer's chat-tab dedupe now interpolates the same constant so the two sides can't drift if the truncation cap ever changes. Also reused a single `Date.now()` call for the user-message span's start/end timestamps (cosmetic, addresses review nit #16). Skipped from the review: - Major #3 (no per-messageID dedupe in logUserMessage): codex confirmed user text doesn't stream/chunk — message.part.delta is assistant-only — so the symptom the review described is subsumed by Major #1's synthetic gate. No separate fix needed. - Major #5 (Path A `setTitle(text, text)` couples title and prompt): codex grep-verified that no in-repo code path populates `userMsg.summary.title` or `summary.body`; the branch is inert. Cleanup risk only, tracked in #896. Tests - New behavioral test in tracing-rehydrate.test.ts asserts that an open generation span (no `step-finish` before reconstruction) ends up with `endTime` set, `status: "error"`, and a statusMessage matching `/interrupted/i` after rehydrate + a snapshot-triggering call. - New source-grep test in worker-trace-clearing.test.ts locks both the synthetic-gate literal (`!part.synthetic && !part.ignored`) and the requirement that both `trace.setPrompt` and `trace.logUserMessage` sit inside the `isAuthoredText &&` guard. 42 affected tests pass; typecheck clean.
anandgupta42
pushed a commit
that referenced
this pull request
Jun 6, 2026
…ta after each agent turn (#895) * fix(tracing): waterfall view collapses to system-prompt span on agent-finish Symptom: opening `/traces` mid-session and watching the waterfall view during agent execution shows the rich trace populating correctly, but the moment the agent finishes its turn the view collapses to a single "system-prompt" span — and the data is genuinely lost on disk, not just from the viewer. Chain (verified by reading worker.ts + routes/session/index.tsx + tracing.ts): 1. `routes/session/index.tsx` has `createEffect(() => session()?.workspaceID && sdk.setWorkspace(...))`. SolidJS dirty-tracks any signal read inside the effect — so it re-runs on EVERY `session()` change (message count, status, parts updates), including the cascade at agent-finish. 2. Each fire calls `worker.setWorkspace` via RPC. 3. `worker.setWorkspace` unconditionally calls `startEventStream`. 4. `startEventStream`: for (const [, trace] of sessionTraces) { void trace.endTrace().catch(() => {}) // fire-and-forget } sessionTraces.clear() 5. On the next event for the same session, `getOrCreateTrace` hits a cache miss, calls `Trace.create()` + `startTrace(sessionID, {})`, which pushes a single root span into a freshly-empty `this.spans` and calls `this.snapshot()`. The snapshot path is derived purely from sessionID (`tracing.ts:836`), so it overwrites the rich `ses_<id>.json` with a 1-span file. Distinct from #867 — that PR fixed intra-Trace-instance concurrency (snapshot debounce M2; FileExporter ↔ flushSync race M3). This bug is at the worker-level cache lifecycle: a new Trace instance gets constructed and its near-empty initial state clobbers the previous instance's rich state on disk. Two minimal fixes lock the contract: * `worker.ts` — make `setWorkspace` idempotent. Track `currentWorkspaceID` at module scope; early-return when the incoming value matches. Spurious calls from the UI no longer destroy traces. * `routes/session/index.tsx` — switch the workspaceID effect to `createEffect(on(() => session()?.workspaceID, ...))`. The `on()` projector restricts SolidJS dirty-tracking to that one field, so the effect only fires when workspaceID actually changes — defense in depth at the upstream trigger. Regression test in `test/cli/tui/worker-trace-clearing.test.ts` locks both contracts via source-grep (the worker-import side has top-level side effects that make in-process unit testing awkward). Out of scope (follow-up): `getOrCreateTrace` on cache miss does not rehydrate `this.spans` from the existing `ses_<id>.json` file. After the two fixes above, this matters only on worker restart or MAX_TRACES=100 eviction — both uncommon. Worth tracking as defense in depth so the disk file is always authoritative. Typecheck clean. 152 TUI tests pass; 35 existing tracing tests pass unchanged. * fix(tracing): real root cause — session.status=idle was destroying the Trace every turn Previous commit on this branch was correct but not load-bearing. The actual hot path that collapsed the on-disk trace after every agent turn is in `worker.ts`: if (event.type === "session.status") { if (status === "idle" && sid) { const trace = sessionTraces.get(sid) if (trace) { void trace.endTrace().catch(() => {}) sessionTraces.delete(sid) // ← every turn sessionUserMsgIds.delete(sid) } } } `session.status === "idle"` fires after every busy→idle transition, which happens once per turn — not once per session. Each fire ended the trace AND deleted the cache entry. The next event for the same session in the next turn hit a cache miss, constructed a fresh `Trace.create()`, called `startTrace(sessionID, {})` (which pushes a single root span into empty `this.spans`), and the immediate `snapshot()` clobbered the rich on-disk `ses_<id>.json` with a 1-span file. This also explains the "What was asked / No prompt recorded" symptom: `metadata.prompt` was captured on the now-destroyed first instance and never persisted into the replacement. Fixes in this commit: * `worker.ts`: removed the destructive `session.status === "idle"` handler. Sessions in altimate-code are long-lived; the Trace lives as long as the worker has the session in cache. Finalization happens on `shutdown` and on MAX_TRACES eviction only — both already correct. * `tracing.ts`: new `Trace.rehydrateFromFile(sessionId)` that reads the existing on-disk file and restores `this.spans`, `this.metadata`, `this.rootSpanId`, `this.startTime`, counters, and clears the root's endTime so the trace renders as still-in-progress. Returns true on success; false on missing/mismatched/malformed file. * `worker.ts:getOrCreateTrace`: on cache miss, calls `rehydrateFromFile` before falling back to `startTrace`. Defense in depth for the worker-restart / MAX_TRACES-eviction paths — even if some future path destroys the in-memory instance, the new instance loads disk state instead of overwriting. Verification * Behavioral test (`test/altimate/tracing-rehydrate.test.ts`, 4 cases): proves end-to-end that rehydrate preserves spans+metadata across Trace-instance reconstruction, returns false on missing/mismatched files, and clears the root endTime so re-opened traces accept new events. * Source-grep regression tests for worker.ts continue to lock the no-idle-clobber and rehydrate-before-startTrace contracts. * 449 pass / 0 fail across the full tracing + TUI suites (1 network flake in tracing-adversarial-2 unrelated to this change, passes on re-run). What I traced before declaring done * Inventoried every `sessionTraces.delete`, `sessionTraces.clear`, `endTrace()`, `Trace.create()`, `Trace.withExporters()`, and direct trace-file write across the whole repo. * Confirmed `this.spans` is never reassigned mid-instance (only pushed) except by the new `rehydrateFromFile`. * Confirmed no external callers of `trace.snapshot()` outside `tracing.ts`. Post-fix, the only ways a new `Trace` instance can replace an existing session's in-memory Trace are: worker boot (once), `setWorkspace` with an actually-changed workspaceID (rare, also idempotency-guarded), and MAX_TRACES eviction (uncommon at 100 sessions). All three now go through `rehydrateFromFile` first. * fix(tracing): capture user prompt via setPrompt; drop time.end gate Per codex review (2026-06-05): the previous user-text branch in worker.ts gated on `part.time?.end` (which user-input parts never have set) AND called `trace.setTitle(text, text)` which would have regressed the auto-generated session title from Path C (`session.updated`) back to the raw user input ('Greeting' → 'hi') if the ordering went sideways. * New `Trace.setPrompt(prompt)` method that only mutates `metadata.prompt`. Decouples prompt capture from title mutation. * Path B in worker.ts now: (a) accepts user text parts without `part.time?.end` (it's an assistant-side concept only); (b) calls `setPrompt(text)` only — never `setTitle` — for user-identified messages. Assistant text still requires `time.end` for `logText`. * Source-grep regression tests lock both contracts: no more `part.time?.end` on the user branch, no `setTitle` from the user branch, `setPrompt` exists and doesn't touch title. * fix(tracing): record user messages as spans so chat tab renders multi-turn The chat tab in the trace viewer renders 'metadata.prompt' as a single 'You' bubble at the top, then iterates 'kind: generation' spans for assistant replies. There's no place for any user message beyond the first to land — `setPrompt` overwrites on every call, and the viewer only reads the latest value. Symptom: a 3-turn session shows only the LAST user prompt followed by all earlier assistant responses, with the older user messages dropped. Fix splits the data and the rendering: * New `Trace.logUserMessage(text)` pushes a `kind: 'user-message'` span with the user text as input. Snapshots immediately like other log* methods. * New 'user-message' variant added to the TraceSpan.kind union. * worker.ts Path B: now also calls `logUserMessage(text)` alongside `setPrompt(text)` for user-identified text parts (the first one populates metadata.prompt for the Summary tab; all of them populate per-turn spans for the Chat tab). * viewer.ts chat-view: builds a chronologically sorted list of user-message + generation spans and walks it in startTime order. Older traces without user-message spans fall back to rendering metadata.prompt as before. * Behavioral test in tracing-rehydrate.test.ts proves the spans are written, ordered chronologically, and preserve the user text. 10 unit tests in worker-trace-clearing + 8 in tracing-rehydrate pass; 35 existing tracing tests pass unchanged; typecheck clean. * docs: trace-bugs followup to #867 — what this branch actually fixes * fix(tracing): address PR #895 bot review feedback * Trace.rehydrateFromFile (cubic P2): restore traceId from the on-disk file so post-rehydrate snapshots/exports preserve trace identity across instance lifetimes. Without this, every snapshot after rehydration writes a fresh random traceId. * Trace.rehydrateFromFile (cubic P2): normalize sessionId via the same sanitization buildTraceFile applies before comparing to trace.sessionId. Previously, sessions with /, \, ., or : in the id would be falsely rejected and recreated. * viewer chat-view (cubic P2): always render metadata.prompt at the top unless an existing user-message span carries the same text. Pre-fix traces (only metadata.prompt) and mixed traces (rehydrated pre-fix data + new turns) now render the first user turn correctly. Previously, the fallback was gated on userMsgs.length === 0 and dropped the legacy first turn in mixed traces. * worker-trace-clearing.test.ts (CodeRabbit, cubic P3): broaden the negative regression guards to catch all three flagged bug spellings — inline expression, inline ternary, block body with if — for the workspaceID effect; and to reject part.time?.end nested inside the user-text branch (identified by sessionUserMsgIds.get(...).has(...)). * routes/session/index.tsx: paraphrased the bug-shape literal that was in a comment so the broadened test regex doesn't catch our own documentation as the bug. * tracing-rehydrate.test.ts: behavioral tests for the traceId preservation and sanitized-sessionId match. Skipped CodeRabbit's tmpdir-fixture-style suggestion — the convention isn't followed by the existing tracing tests (tracing-display-crash, tracing-rename-race) so changing this one file alone would be inconsistent and out of scope for this PR. 48 affected tests pass; typecheck clean. * test(tracing): migrate tracing-rehydrate to tmpdir() fixture CodeRabbit pointed out that `packages/opencode/test/AGENTS.md` documents `tmpdir()` from `fixture/fixture.ts` as the project convention. My earlier reply skipped the migration on the grounds that sibling tracing tests don't follow the convention — but for code I'm adding fresh, the right move is to follow the documented standard. The sibling tests predate it and should be swept in a separate cleanup PR. Replaces the manual `os.tmpdir() + beforeEach/afterEach` pattern with `await using tmp = await tmpdir()` per test. Helpers `makeTrace` and `readTraceFile` now take `dir` as a first parameter so each test threads its own tmp directory through. 46 affected tests pass. * fix(viewer): dedupe metadata.prompt against truncated user-message input cubic flagged that `Trace.logUserMessage` slices user text at 4000 chars when persisting to the span, while `metadata.prompt` keeps the full string. The strict equality check in viewer chat-view (`u.input === t.metadata.prompt`) misses the dedupe for prompts longer than 4000 chars and renders the same text twice — once as the top-level "You" fallback bubble, once as the first user-message span. Match against both the full and the truncated form. Same fix shape cubic suggested. * docs: remove redundant spec/trace-bugs-followup-867.md The PR description already carries the bug-by-bug origin table and the explanation of why #867's scope was disjoint from these bugs. Keeping a 295-line spec file in the repo to say the same thing again is bloat — the PR is the right place for this content. * fix(tracing): synthetic-part gate, in-flight gen interrupt, shared truncation constant Three follow-ups from the multi-LLM consensus review of PR #895, codex-validated: * Worker user-text branch now skips parts with `synthetic` or `ignored` set (Major #1 in the review, codex-confirmed). `Session.createUserMessage` in prompt.ts attaches many synthetic text parts to the user messageID for MCP resource banners, decoded file contents, retry/reminder text, plan-mode reminders, and agent-handoff tags. Without the gate they pass the `sessionUserMsgIds.has(messageID)` check, `metadata.prompt` ends up holding the LAST synthetic part (typically a file blob), and the chat tab renders one fake "▶ You" bubble per synthetic span — defeating the two display surfaces this PR fixes. Gated on an `isAuthoredText` predicate so the symmetric assistant-text branch is also protected. Continue via predicate rather than `continue` keyword so the outer event loop still forwards the event downstream via `Rpc.emit`. * `Trace.rehydrateFromFile` now marks any open generation span as interrupted (Major #2). The transient state `logStepStart` populated (`currentGenerationSpanId`, `generationText`, `generationToolCalls`, `pendingToolResults`) is memory-only and can't be reconstructed from disk. If we leave open spans open, the next `step-finish` for that turn drops at the `!this.currentGenerationSpanId` guard and follow-up `logToolCall` mis-parents tool spans to the root, silently degrading the trace shape. Closing the span with `status: "error"` and a `statusMessage` describing the interruption preserves the partial data and makes the boundary visible in the viewer. * Extracted the 4000-char truncation cap as `USER_MESSAGE_INPUT_MAX_CHARS` exported from tracing.ts (new cubic P3 on viewer.ts:1331). The viewer's chat-tab dedupe now interpolates the same constant so the two sides can't drift if the truncation cap ever changes. Also reused a single `Date.now()` call for the user-message span's start/end timestamps (cosmetic, addresses review nit #16). Skipped from the review: - Major #3 (no per-messageID dedupe in logUserMessage): codex confirmed user text doesn't stream/chunk — message.part.delta is assistant-only — so the symptom the review described is subsumed by Major #1's synthetic gate. No separate fix needed. - Major #5 (Path A `setTitle(text, text)` couples title and prompt): codex grep-verified that no in-repo code path populates `userMsg.summary.title` or `summary.body`; the branch is inert. Cleanup risk only, tracked in #896. Tests - New behavioral test in tracing-rehydrate.test.ts asserts that an open generation span (no `step-finish` before reconstruction) ends up with `endTime` set, `status: "error"`, and a statusMessage matching `/interrupted/i` after rehydrate + a snapshot-triggering call. - New source-grep test in worker-trace-clearing.test.ts locks both the synthetic-gate literal (`!part.synthetic && !part.ignored`) and the requirement that both `trace.setPrompt` and `trace.logUserMessage` sit inside the `isAuthoredText &&` guard. 42 affected tests pass; typecheck clean. * test: tighten worker-trace-clearing regex to scope-bounded match (CodeRabbit) CodeRabbit flagged that the previous source-grep assertions matched across the entire `worker.ts` file, so unrelated code positioning could satisfy them without the synthetic-gate fix being present in the actual user-text branch. * The `isAuthoredText` declaration check now asserts the const is built from BOTH flags (`!part.synthetic && !part.ignored`), not just that the literal exists somewhere. * The two write-path checks now require `trace.setPrompt` and `trace.logUserMessage` to sit inside the same `if (text) { ... }` body within the `sessionUserMsgIds...has(part.messageID)` branch. The `[^{}]` bounds on the inner spans prevent the match from extending past the closing brace of that body, so calls elsewhere in the file can't false-green the assertion. * fix(tracing): make rehydrateFromFile async to unblock the event-loop hot path cubic flagged that `fsSync.readFileSync` in `rehydrateFromFile` blocks the worker event loop during a trace cache miss. The hot path is bounded (cache miss only fires on worker restart, MAX_TRACES eviction, or initial-boot resumption of a stale session), but a multi-MB trace file makes the pause visible. * `Trace.rehydrateFromFile` now returns `Promise<boolean>` and uses the async `fs.readFile`. * `getOrCreateTrace` in worker.ts becomes `async` and the three call sites in the event-stream loop now `await` it. The loop body was already async (`for await (const event of events.stream)`), so the conversion is local. * Behavioural tests in `tracing-rehydrate.test.ts` converted to `await` the now-async method (7 call sites). * The source-grep contract test for `getOrCreateTrace`'s rehydrate-before-startTrace shape now matches the `await` form. Not addressed in this commit (will reply on PR): - cubic also flagged a theoretical event-ordering race where `message.part.updated` could arrive before `message.updated`, leaving `sessionUserMsgIds` empty when the part handler runs. The producer (`Session.createUserMessage` → `updateMessage` THEN `updatePart` for each part) emits in order, and the consumer reads the event stream sequentially. The race is theoretical given the current producer; if we ever reorder, the defensive fix is to buffer unrouted parts by messageID. Out of scope for this PR. --------- Co-authored-by: Haider <haider@altimate.ai>
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.
Summary
Prepares the monorepo for open-source release at
github.com/AltimateAI/altimate-code. This PR covers four areas:bridge/engine.ts). The CLI auto-downloadsuv, creates an isolated Python 3.12 venv at~/.local/share/altimate-code/engine/, and installsaltimate-enginewith zero system dependencies required. Addsaltimate-code engine status/reset/pathsubcommand.opencode/anomalyco/OPENCODE_*references toaltimate-code/AltimateAI/ALTIMATE_CLI_*across 100+ files (source, build scripts, Docker, Homebrew, AUR, SDK exports, tests, theme URLs). Fixes a critical bug where build defines (OPENCODE_VERSION) didn't match consumer declarations (ALTIMATE_CLI_VERSION), causing version detection to fall back to "local" in production builds.v*tag → npm + PyPI + GitHub Release), engine-only publish workflow (engine-v*→ PyPI), version sync script (bump-version.ts), andRELEASING.mdguide.Files changed: 120 (101 modified + 18 new + 1 snapshot)
Key new files
packages/altimate-code/src/bridge/engine.tspackages/altimate-code/src/cli/cmd/engine.tsaltimate-code engineCLI subcommandpackages/altimate-code/script/bump-version.ts.github/workflows/release.yml.github/workflows/ci.yml.github/workflows/publish-engine.ymlRELEASING.mdRemaining
opencodereferences (all legitimate)@gitlab/opencode-gitlab-auth— third-party npm packageproviderID: "opencode"in test fixture — external OpenCode Zen AI providersst/opencodein test — generic GitHub URL parser test datamodels-api.json— external provider definition from models.devBefore publishing checklist
These steps must be completed before the first release:
1. npm token
NPM_TOKENin GitHub repository secrets (Settings > Secrets and variables > Actions)2. PyPI trusted publishing (OIDC — no API tokens needed)
<pypi-account>)altimate-engineAltimateAIaltimate-coderelease.ymlpypipypienvironment in GitHub repo settings (Settings > Environments > New environment >pypi)3. Homebrew tap
AltimateAI/homebrew-taprepository on GitHubGITHUB_TOKENused in Actions has write access to this repo (or add a PAT asHOMEBREW_TAP_TOKEN)4. First release
5. Optional: AUR (Linux)
altimate-code-binpackage on AUR6. Verify after first release
Test plan
bun run packages/altimate-code/script/build.ts --singlesucceeds andALTIMATE_ENGINE_VERSIONis injectedcd packages/altimate-engine && pytestpasses (283+ tests)cd packages/altimate-engine && python -m build --sdistproduces valid packageOPENCODE_oranomalycoreferences remain:grep -rn "OPENCODE_\|anomalyco" --include="*.ts" --include="*.py" --include="*.yml"🤖 Generated with Claude Code