fix: a workspace's first prompt and job are sent from any bound session

Merged
RA
14h ago
+710-19

Issue for this PR

Closes #1698

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

first_prompt_sent and first_job_completed came only from onboarding sessions, and the job row only for the job picked from the onboarding menu. Someone who links a project and starts working sent neither, so telemetry couldn't say when a workspace first got a prompt or first finished a job.

first-events.ts now sends each row once per workspace per machine, from any session bound to that workspace, with workspace_id:

  • first_prompt_sent on the first typed prompt. A slash command in the TUI or run, or a subagent's prompt, doesn't count; the next typed message does. The VS Code chat sends a command as its expanded text through prompt_async (vscode-altimate-mcp-server, src/chatPanel.ts:614-640), so there a command counts as the first prompt. That is kept, because the expanded command runs a real model turn (R14 on the review log).
  • first_job_completed on the first turn that ends completed with at least one tool call that didn't fail. Nothing says which job it was, so job is something_else.

What has been sent is an empty marker file per tenant, workspace and event under the CLI's state directory, created exclusively, so a later session, a new process or a second serve doesn't send it again. With telemetry off nothing is marked. Onboarding sessions keep their own rows, and they mark the workspace's too. An onboarding job row is still sent if the workspace's came first.

How did you verify your code works?

Tests. workspace-first-events-loop.test.ts runs the real prompt loop: both rows from a bound session, neither again from a later session or a new process, no job from a turn with no tool call, whose only tool call failed, or that errored after one that worked, no prompt from a slash command, even one sent while the session is busy, or from a subagent's session, nothing from an unbound session, onboarding rows unchanged. first-events.test.ts covers the marker: per event, workspace and tenant, two claims racing with no shared memory, telemetry off, no account, and a mark that can't be written. onboarding.test.ts checks that an onboarding job row marks its workspace.

E2E. altimate-code run from this branch in a container, in a project with an on-disk link, with rows going to a local catcher and the API host blocked so the local link is kept. A turn whose only tool call failed sent first_prompt_sent naming the linked workspace, and no first_job_completed. The next process, whose read worked, sent first_job_completed with job: something_else and no second prompt row. A third process sent neither. The two marks are mode 0600 under the state directory, and the turns wrote nothing in the project.

Suites. test/altimate/workspace, test/altimate/telemetry, test/altimate/plugin and test/telemetry pass locally, typecheck is clean and Marker Guard is ok. The full suite runs in CI.

Cost side effects

None. No model calls, queries or cloud resources are added. Each workspace gets at most two empty files in the CLI's state directory.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR
  • I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"

🤖 Generated with Claude Code

https://claude.ai/code/session_01VEmKQswEEpkYFmMEngESx8

Summary by CodeRabbit

  • Telemetry
    • First-prompt events cover eligible messages in onboarding and linked projects. Linked-project events are recorded once per workspace.
    • Onboarding first-job events apply only to the selected job. Linked projects can report a completed turn after a successful tool call, once per workspace.
    • Linked-project prompt and job events are tracked separately from onboarding events. Jobs without observable completion signals are excluded.
    • Telemetry event data remains in memory until it is flushed, so events may be lost if the process stops beforehand.