Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
71d68d5
feat: [AI-7520] add Altimate LLM Gateway OAuth loopback signup to CLI
saravmajestic Jul 9, 2026
43fc015
feat: [AI-7520] onboarding UX — welcome panel, top-5 picker, inline a…
saravmajestic Jul 9, 2026
9c481ec
feat: [AI-7520] add /auth and /logout TUI commands
saravmajestic Jul 13, 2026
df61fdf
feat: [AI-7520] mark the selected provider with a green ✓ in the picker
saravmajestic Jul 14, 2026
d7f0f2c
refactor: [AI-7520] isolate onboarding into an altimate-owned file (r…
saravmajestic Jul 14, 2026
e46fdd4
fix: [AI-7520] harden CLI gateway loopback (PR review majors)
saravmajestic Jul 16, 2026
f5596b4
fix: [AI-7520] address PR #1001 review minors
saravmajestic Jul 16, 2026
7e7ac7c
feat: [AI-7520] exchange the login_token server-side (no api_key in l…
saravmajestic Jul 16, 2026
40c8036
fix: [AI-7520] PR #1001 re-review — loopback IPv4/UX hardening
saravmajestic Jul 16, 2026
e6ffd25
fix: [AI-7520] show Altimate LLM Gateway first in the full model list
saravmajestic Jul 17, 2026
a4ed360
Merge origin/main into feat/AI-7520-social-signup-authorize
saravmajestic Jul 17, 2026
364460a
feat: [AI-7520] port prototype-v2 scan gate + TUI polish
saravmajestic Jul 21, 2026
98a7deb
feat: [AI-7774] first-run picker gate + slim the welcome header
saravmajestic Jul 23, 2026
2d83c79
feat: [AI-7778] No on scan gate just closes to the chat input
saravmajestic Jul 24, 2026
2779649
fix: [AI-7520] wrap fork code in altimate_change markers (Marker Guard)
saravmajestic Jul 24, 2026
b7102b7
fix: [AI-7520] wrap remaining fork regions in altimate_change markers
saravmajestic Jul 24, 2026
7a898db
fix: [AI-7520] wrap update-gate + AutoMethod local in markers
saravmajestic Jul 24, 2026
70ee02f
fix: [AI-7520] wrap AutoMethod success-state flow in markers
saravmajestic Jul 24, 2026
fa3b980
feat(onboarding): ship jaffle-shop DuckDB starter sample
Jul 24, 2026
96533a4
feat(onboarding): resolve + ship starter sample from wrapper package
Jul 24, 2026
c15671f
fix(onboarding): sample-source-resolver + regenerate script — codex-r…
Jul 24, 2026
aa49db3
feat(onboarding): first-run activation — core logic (detection, marke…
Jul 24, 2026
1e8597f
fix(onboarding): Phase 4a codex-review refinements
Jul 24, 2026
8f727e8
feat(onboarding): activation dialog + /starter + /activation wiring
Jul 24, 2026
f524cb0
fix(onboarding): Phase 4b codex-review refinements
Jul 24, 2026
4848da6
test(onboarding): Phase 5a — marker, materialize, tool-detection, sam…
Jul 24, 2026
2449a89
docs(onboarding): VHS tape + helper script for /starter demo
Jul 24, 2026
c88fd76
feat(onboarding): starter_materialize tool — LLM-invoked wrapper arou…
Jul 27, 2026
2c1eba3
refactor(onboarding): rename starter_materialize tool → sample_setup
Jul 27, 2026
e89955f
feat(onboarding): activation menu as agent-appended text; drop the mo…
Jul 27, 2026
eb957f7
fix(onboarding): harden sample_setup against path traversal + atomic …
Jul 28, 2026
883cf21
fix(onboarding): template branches for sample_setup states + de-vacat…
Jul 28, 2026
ff35d93
fix(onboarding): consensus-review Round 1–3 — safety, correctness, pa…
Jul 28, 2026
5f67208
test(onboarding): add coverage for consensus-review test gaps (27–31)
Jul 28, 2026
ea6ec07
fix(onboarding): codex sweep — canonicalize HOME + lstat orphan sweep
Jul 28, 2026
ae31698
fix(onboarding): dbt runtime probe falls back through cmd.exe on Windows
Jul 28, 2026
89028ff
fix(onboarding): kilo-code-bot findings — alongside branch wording + …
Jul 28, 2026
6c8b445
fix(onboarding): cubic P1s + codex-sweep NEW-10 — marker identity, te…
Jul 28, 2026
33f6e3a
fix(onboarding): refuse ANY single-quote in pasted dbt path (kilo fol…
Jul 28, 2026
b477573
feat(onboarding): telemetry taxonomy + first batch of funnel events
Jul 29, 2026
f6bc248
fix(onboarding): telemetry review fixes — shutdown races, gateway gaps
Jul 29, 2026
ebd2e78
feat(onboarding): activation funnel events + first_prompt_sent
Jul 29, 2026
89c5651
fix(onboarding): activation-funnel review fixes — scoping, attributio…
Jul 29, 2026
b4252c5
feat(onboarding): TUI funnel events via an injected telemetry callback
Jul 29, 2026
eba46bf
fix(onboarding): TUI funnel review fixes — abandonment scope, guards,…
Jul 29, 2026
c10ce7e
fix(onboarding): add launch correlation id + full-review fixes
Jul 29, 2026
4221531
fix(onboarding): don't nest altimate_change markers inside the TUI cl…
Jul 29, 2026
935136a
test(onboarding): funnel emission coverage
Jul 29, 2026
2fc34e8
test(onboarding): cover the first-run provider picker
Jul 29, 2026
3704b42
fix(onboarding): actually share launch_id between the TUI and worker …
Jul 29, 2026
ecd94c8
fix(onboarding): attribute tool-emitted funnel events to their own se…
Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/meta/merge-commit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Merge origin/main into feat/AI-7520-social-signup-authorize

Resolve conflicts from upstream's TUI extraction (packages/opencode/src/cli/cmd/tui
→ packages/tui) and fork-features-as-plugins re-architecture, re-applying the
AI-7520 CLI onboarding onto the new structure:

- dialog-provider: export/reorder PROVIDER_PRIORITY (Altimate LLM Gateway first),
brand the gateway option, add WARNLIST + DialogAltimateAuth, harden the OAuth
authorize/AutoMethod UX (try/catch → toast, inline green success + auto-close)
- dialog-model: keep the READY / NEEDS-SETUP restructure + Big Pickle, wired through
upstream's new DialogVariant flow; gateway leads the full list
- use-connected + home tips: fix undefined-cost mislabeling OpenCode as connected
- app.tsx: /connect (welcome picker), /auth (gateway), /logout (plugin command +
resetSetupComplete); gate the update-available dialog on onboarding readiness
- home.tsx: render the WelcomePanel in the home_logo slot
- provider-credentials plugin: add altimate.provider.logout
- providers.ts: Altimate LLM Gateway first in the login picker
- relocate altimate-onboarding + welcome-panel into packages/tui with relative imports

Verified: `bun run typecheck` green in packages/tui and packages/opencode; the
onboarding/provider/model/app-lifecycle tests pass. Pre-existing sync*.test.tsx
failures come from origin/main's own merge (files untouched here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ tsconfig.tsbuildinfo
**/.github/meta/pr-body-*.md
.bridge-merge-report.md
/data/

# Rendered VHS demos — regenerable from .tape files, kept out of git
docs/media/*.gif
28 changes: 26 additions & 2 deletions docs/docs/reference/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,32 @@ We collect the following categories of events:
| `schema_complexity` | Warehouse schema structural metrics from introspection — bucketed table, column, and schema counts plus average columns per table. No schema names or content. |
| `validator_check` | A completion-gate validator ran on session end — validator name, `ok` boolean, step, retry count, `enforced` flag (false in shadow mode), and structured `details` (model counts, elapsed time, concurrency limit — no SQL or model content). Only emitted when `ALTIMATE_VALIDATORS_ENABLED=1` or `ALTIMATE_VALIDATORS_SHADOW=1`. See [Validators](../data-engineering/validators.md). |
| `validator_retries_exhausted` | A session terminated with unresolved validator failures after exhausting the synthetic-retry budget — names of the failing validators (no failure body content). |

Each event includes a timestamp, anonymous session ID, CLI version, and an anonymous machine ID (a random UUID stored in `~/.altimate/machine-id`, generated once and never tied to any personal information).
| `onboarding_started` | The first-run setup gate opened (fresh launch with no usable model). |
| `model_picker_shown` | The provider picker was displayed. `trigger` distinguishes the first run from `/connect`, from declining Big Pickle, and from the prompt gate. |
| `provider_selected` | A provider row was chosen (`altimate_gateway`, `anthropic`, `openai`, `google`, `big_pickle`, `search_all`). Recorded at the moment of choice, so a sign-in that is then cancelled still counts. |
| `big_pickle_confirm_shown` / `big_pickle_choice` | The Big Pickle interstitial was shown, and what the user decided (`accept`/`cancel`). |
| `gateway_device_code_issued` | The Altimate Gateway authorize URL was built and the browser open attempted. **Name note:** the flow is a browser loopback OAuth — there is no device code. The name follows the original event spec. |
| `gateway_auth_completed` / `gateway_auth_failed` | Gateway sign-in outcome. `reason` is `timeout`, `denied`, or `error` — never the underlying message, which can contain the instance name. An unrecognised callback state does not reject the pending attempt, so a CSRF mismatch surfaces as `timeout`. |
| `instance_connected` | Credentials received and saved. `time_to_connect_ms` runs from the start of the authorize call, so it includes the browser launch. No instance or tenant name is sent. |
| `onboarding_completed` | A model is ready and chat is live. |
| `scan_gate_shown` / `scan_gate_choice` | The "scan your environment?" gate appeared, and whether the user chose `scan` or `skip`. |
| `environment_scan_completed` | A `project_scan` finished — `has_dbt`, `has_warehouse`, `is_repo`, `connections_found`, and a bounded list of short `degraded` detection keys. No paths, hostnames, or connection details. Fires for every scan, including `/discover`, not just onboarding. |
| `sample_setup_completed` | The sample dbt project was materialised. `success`, `models`, `tables`, and `reused` — the tool is deliberately re-callable, so this is per invocation. The target path is never sent. |
| `activation_menu_shown` | The activation menu was (very likely) rendered. `variant` is `warehouse` or `no_data`. **Derived** — see the note below. |
| `activation_job_selected` / `first_job_completed` | Which activation job the user started and, where observable, finished. **Derived** — see the note below. |
| `first_prompt_sent` | The user's first typed message in an onboarding session. Slash commands are excluded, so the hidden `/onboard-connect` submission does not count. |
| `onboarding_abandoned` | The CLI exited during a first run without connecting. `last_stage` is the furthest point reached: `started`, `model_picker`, `provider_setup`, `big_pickle_confirm`, `gateway_auth`, or `connected`. Only emitted for a genuine first run — opening `/connect` as an existing user does not enter the funnel, and abandonment after setup completes is out of scope by definition. |

### A note on the derived activation events

`activation_menu_shown`, `activation_job_selected`, and `first_job_completed` are **inferred, not observed**. The activation menu is not a UI element: it is text the model writes from a prompt template, and the user picks a job by replying in free text. Nothing in the CLI can see either moment directly.

They are therefore inferred from the closest deterministic signals — the menu from the command dispatch or the completed environment scan, the job from the first matching tool or skill invocation that follows. Treat the counts as **lower bounds**, and note two specific gaps:

- The "something else" branch has no tool signature at all and is never counted.
- `first_job_completed` only fires for jobs with a real completion signal. Skill-driven jobs (downstream impact, SQL review, cost) load an instruction bundle and then do their work through other tools, so their completion is not observable and they are absent from this event rather than wrongly counted in it.

Each event includes a timestamp, anonymous session ID, a per-launch correlation ID (`launch_id` — a random value regenerated every process start, not persisted and not derived from your machine or identity; it exists only to group events from the same run), CLI version, and an anonymous machine ID (a random UUID stored in `~/.altimate/machine-id`, generated once and never tied to any personal information).

## Delivery & Reliability

Expand Down
1 change: 1 addition & 0 deletions packages/dbt-tools/test/fixture/.user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
id: 03b8c7b7-4b26-4408-9876-88d05b7da4f5
12 changes: 12 additions & 0 deletions packages/opencode/sample-projects/jaffle-shop-duckdb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Build/run artifacts that must NOT be committed. `target/*` (not `target/`)
# so the re-include below actually works — git ignores the CONTENTS of target/
# individually, then lets us un-ignore the one pre-compiled artifact we ship.
# Using `target/` here would exclude the directory as a whole and make the
# `!target/manifest.json` line inert (git will not descend into an ignored
# directory to re-include children).
target/*
!target/manifest.json

dbt_packages/
logs/
.user.yml
55 changes: 55 additions & 0 deletions packages/opencode/sample-projects/jaffle-shop-duckdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Jaffle Shop — altimate-code starter sample

Everything below runs against a local DuckDB file — no cloud warehouse, no
credentials, no network calls.

## What's in here

```
dbt_project.yml dbt project config
profiles.yml DuckDB profile — path is project-relative
sample-manifest.json version metadata used by altimate-code to detect
stale copies on upgrade
models/
staging/
stg_customers.sql renames raw customer columns to snake_case
stg_orders.sql renames raw order columns
schema.yml column descriptions + unique/not_null tests
marts/
customers.sql one row per customer, joins in order counts
orders.sql one row per order, joins in customer names
schema.yml column descriptions + tests + relationships
seeds/
raw_customers.csv 3 rows of test data
raw_orders.csv 4 rows of test data
target/
manifest.json PRE-COMPILED dbt manifest — ships with the sample so
altimate-code's static workflows (/discover, /review)
work without dbt-core / dbt-duckdb installed
```

## What to try (works with zero external tools)

- `/discover stg_customers` — walk the DAG and see what depends on this model
- `/review models/marts/customers.sql` — run the reviewer against a mart model
- Open any `.sql` file and ask altimate-code to explain the transformation
- Ask altimate-code "what tests would you recommend for `orders`?"

## What to try (needs `dbt-core` + `dbt-duckdb` installed)

```bash
pip install dbt-duckdb
cd ~/altimate-sample-dbt # or wherever you materialized the sample
dbt seed # load the CSVs into DuckDB
dbt build # run models + tests
duckdb target/jaffle.duckdb -c 'select * from customers'
```

Once `dbt-duckdb` is on your `$PATH`, altimate-code detects it automatically
and the "run" workflows appear in `/help`.

## Bringing your own project

When you're ready to switch to your real dbt project, `cd` into it and run
altimate-code again. The scan will pick up your `dbt_project.yml` and offer
to connect its warehouse.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "jaffle_shop"
version: "1.0.0"

profile: "jaffle_shop"

model-paths: ["models"]
seed-paths: ["seeds"]
target-path: "target"
clean-targets: ["target", "dbt_packages"]

models:
jaffle_shop:
staging:
+materialized: view
marts:
+materialized: table
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
select
c.customer_id,
c.first_name,
c.last_name,
count(o.order_id) as order_count,
coalesce(sum(o.amount), 0) as total_amount
from {{ ref('stg_customers') }} c
left join {{ ref('stg_orders') }} o on c.customer_id = o.customer_id
group by c.customer_id, c.first_name, c.last_name
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
select
o.order_id,
o.customer_id,
c.first_name || ' ' || c.last_name as customer_name,
o.order_date,
o.amount
from {{ ref('stg_orders') }} o
join {{ ref('stg_customers') }} c on o.customer_id = c.customer_id
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: 2

models:
- name: customers
description: One row per customer with total order count and revenue.
columns:
- name: customer_id
description: Primary key.
data_tests:
- unique
- not_null
- name: order_count
description: Number of orders placed by the customer (0 when none).
data_tests:
- not_null
- name: total_amount
description: Sum of all order amounts (0 when none).
data_tests:
- not_null

- name: orders
description: One row per order with a joined customer name.
columns:
- name: order_id
description: Primary key.
data_tests:
- unique
- not_null
- name: customer_id
description: Foreign key to `stg_customers`.
data_tests:
- not_null
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
version: 2

models:
- name: stg_customers
description: Renamed customer columns from the raw seed.
columns:
- name: customer_id
description: Primary key of the customer.
data_tests:
- unique
- not_null

- name: stg_orders
description: Renamed order columns from the raw seed.
columns:
- name: order_id
description: Primary key of the order.
data_tests:
- unique
- not_null
- name: customer_id
description: Foreign key to `stg_customers`.
data_tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
select
id as customer_id,
first_name,
last_name
from {{ ref('raw_customers') }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
select
id as order_id,
customer_id,
order_date,
amount
from {{ ref('raw_orders') }}
14 changes: 14 additions & 0 deletions packages/opencode/sample-projects/jaffle-shop-duckdb/profiles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# DuckDB profile — everything runs locally against a single file at
# `target/jaffle.duckdb` (created on first `dbt build`). No cloud credentials.
# `path:` is unqualified, so dbt-duckdb resolves it against the PROCESS
# working directory at build time — NOT the project directory. Run
# `dbt build` from the materialized sample dir (`cd <sample-path>`) and
# the database lands at `<sample-path>/target/jaffle.duckdb`.
# Run it from anywhere else and dbt writes to `$PWD/target/jaffle.duckdb`.
jaffle_shop:
target: dev
outputs:
dev:
type: duckdb
path: "target/jaffle.duckdb"
threads: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$comment": "Metadata about this sample project. Distinct from dbt's target/manifest.json — this is our own version stamp for conflict detection on the materialized copy at ~/altimate-sample-dbt/. If you edit any source file below, run ./regenerate.sh and commit the refreshed target/manifest.json alongside your change.",
"name": "jaffle-shop-duckdb",
"version": "1.0.0",
"kind": "altimate-starter-sample",
"source": "packages/opencode/sample-projects/jaffle-shop-duckdb",
"requires": {
"dbt-core": ">=1.7 <2.0",
"dbt-duckdb": ">=1.7 <2.0"
},
"notes": [
"Renamed profile from the original test fixture: `test_jaffle_shop` → `jaffle_shop`.",
"DuckDB target file resolves project-relative at `target/jaffle.duckdb`; no host paths bake into the profile.",
"target/manifest.json ships pre-compiled so static workflows (/discover, /review) work without dbt installed."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
id,first_name,last_name
1,Alice,Smith
2,Bob,Jones
3,Carol,White
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,customer_id,order_date,amount
1,1,2024-01-15,100
2,1,2024-02-20,200
3,2,2024-01-10,150
4,3,2024-03-05,300
Loading
Loading