Repository navigation
feat(api): add opt-out usage metrics (spec 133) - #923
Merged
Merged
Conversation
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
…cases TelemetryEvent and TelemetryConfig in TypeSpec; pure domain rules for the enabled state, retry backoff, duration buckets and error frames; an ITelemetry port backed by a SQLite outbox (migration 166) with once-key dedupe; a PostHog /batch/ transport over fetch; agent account ids read through catalog descriptors; and the flush, status, preferences, preview, notice and heartbeat use cases wired by string token. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
…cher shep telemetry on|off|identity|contact|status|show; a one-time notice on stderr listing every collected field; cli.command recorded with the command path only; error.unhandled from the crash handlers; and a one-minute flush watcher in the daemon and shep ui that records the daily heartbeat and sends leased batches, so two servers never double-send. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
feature.created from CreateFeatureUseCase; feature.run.finished from the worker's guarded terminal writes (status, agent type, duration bucket); pr.opened and pr.merged from the merge node, both PR sync paths and the GitHub webhook, once-keyed per feature so a merge seen three times counts once. pr.merged says whether a pull request was merged or the branch was squash-merged locally. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Web onboarding shows the usage-metrics notice with one-click turn off, the identity toggle and the GitHub contact consent; Settings gains a Usage metrics section; AppShell records web.area.viewed as a route template built from Next params; onboarding steps are recorded. The README trust section, FAQ and CLAUDE.md now say what is sent, with the full list in docs/telemetry.md. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
…test The identity switch is on by default, so its hint now says to turn it off to stay anonymous, in all nine locales. The _serve command test stub names ITelemetry, which background sync now resolves for the PR sync watcher. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
The windows-latest runner now ships git 2.56, which rejects the device
name NUL as GIT_CONFIG_GLOBAL ("unable to access 'NUL': Invalid
argument"), so every harness builtin-tools integration test failed in
createTempGitRepo. Git for Windows maps /dev/null itself, and the
merge-step real-git harness already isolates that way and passes on the
same runner.
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5
Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
The settings page now fires other server actions on load (telemetry status and the route-view event). selectLanguage resolved on whichever answered first, so a reload could abort the real save and leave the language unset. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Contributor
Author
|
Unit Tests (windows-latest) on
The job is running again on the new head Generated by Claude Code |
1 task done
Brings in spec 134 (unified decisions, migration 166). Telemetry's migration moves to 168, and identity becomes opt-in by owner decision: includeIdentity defaults to false in settings, the migration and the mapper, and both first-run notices list the identity fields apart, with how to turn them on. Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
arielshad
added a commit
that referenced
this pull request
Oct 11, 2026
## What Adds `docs/competitors/t3code.md`, a product and architecture review of T3 Code (pingdotgg/t3code) compared with Shep, and links it from `docs/competitors/README.md`. The doc covers four topics: - **What to adopt:** the inline-options decision pattern, plan capture, hidden-ref checkpoints and capability records. - **What to skip.** - **What to cut or consolidate in Shep.** - **A telemetry design.** It ends with the owner's decision on every recommendation. ## Why This is the record behind the follow-up work: - spec 133 metrics (#923) - spec 134 unified decisions (#922, #924) - spec 135 cleanup (#925) - the Effect-TS evaluation issue (#921) ## Screenshots / Recording N/A — non-UI change. ## Testing Docs only. Markdown is excluded from Prettier (`*.md` in `.prettierignore`). I checked the claims in the doc against `main@0304cfc` and `pingdotgg/t3code@a4c9494b`. ## Checklist - [x] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) — `docs`, so no release 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SGhpsQ4T9PshbZFxzCQQMy --- _Generated by [Claude Code](https://claude.ai/code/session_01SGhpsQ4T9PshbZFxzCQQMy)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5 Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
arielshad
marked this pull request as ready for review
October 11, 2026 10:50
arielshad
pushed a commit
that referenced
this pull request
Oct 11, 2026
<p align="center"> <a href="https://github.com/shep-ai/shep"> <img src="https://raw.githubusercontent.com/shep-ai/shep/main/docs/screenshots/shep-card.jpg" alt="Shep — run multiple AI agents in parallel" width="720" /> </a> </p> # 🚀 Shep [v1.239.0](/compare/v1.238.0...v1.239.0) · _2026-10-11_ > Your organization does not have access to Claude. Please login again or contact your administrator. ### ✨ Features * **api:** add opt-out usage metrics (spec 133) ([#923](#923)) ([d97bc6f](d97bc6f)), closes [#922](#922) [#924](#924)     ## 📦 Install or update ```bash # upgrade an existing install npm i -g @shepai/cli@1.239.0 # or run instantly without installing npx @shepai/cli@latest ``` ## 💬 Join the community [💬 **Discord**](https://discord.gg/ES6tdVFfur) · [📖 **Docs**](https://github.com/shep-ai/shep#readme) · [⭐ **Star on GitHub**](https://github.com/shep-ai/shep) · [🐛 **Report an issue**](https://github.com/shep-ai/shep/issues) --- <sub>🤖 Released autonomously by Shep — built by parallel AI agents working in isolated git worktrees. Try it: `npx @shepai/cli`</sub> Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
arielshad
pushed a commit
that referenced
this pull request
Oct 11, 2026
…up-cuts Bring in telemetry (#923, migration 168); the feature-flag migration stays at 169. Take main's settings-rows extraction and stories, keeping the row's flex basis so switches stay beside long descriptions. Keep the i18n spec's save-matching without response.finished(), which hung in CI. Claude-Session: https://claude.ai/code/session_01TxtN2VcWzg6NTCLj4DbmfL Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
This was referenced Oct 11, 2026
arielshad
added a commit
that referenced
this pull request
Oct 11, 2026
…lt (#925) ## What This is the cleanup PR from the T3 Code product review. It covers the items the owner agreed to, with one commit per item: | Commit | Item | Change | | --- | --- | --- | | `bb81a67` | **C1** | The stale good-first-issue and monthly recap watchers no longer run in users' daemons (`_serve`, `shep ui`, `dev:web`). They now run from the new scheduled workflow `.github/workflows/contributor-maintenance.yml`, through two new commands, `shep contributors stale-issues` and `shep contributors recap`. Both reuse the existing use cases. The watcher services are deleted. The contributor view (leaderboard, contributor doctor) moves to `/contributors`, which is linked from CONTRIBUTING.md and not from the sidebar. `/onboarding` keeps only the collaboration tutorial and leaves the sidebar. | | `2810b71` | **C3** | `featureFlags.aspm` now defaults to `false` in TypeSpec, the defaults factory and the web env fallback. Values users already saved are kept; no migration touches them. ROADMAP.md is updated to match. The e2e tests that visit `/aspm` turn the flag on for the test and back off afterwards. | | `0b9221f` | **C4** | Supply-chain security is folded into ASPM, because folding was cheap and removing it was not. Its own `supplyChainSecurity` flag is gone, and every surface follows `aspm` through `isSupplyChainSecurityEnabled()`: the feature-agent pre-check, the canvas badge, the Settings section and `shep security enforce`. `SHEP_SUPPLY_CHAIN_SECURITY` still overrides the flag in either direction. Shep's own `security-enforce` CI job already sets it to `true`, so the release gate keeps running. The `feature_flag_supply_chain_security` column stays and is no longer read. | | `d3a2157` | **C7** | Removes `system.autoUpdate` and the placeholder agent types `aider` and `continue`. The `sys_auto_update` column is NOT NULL with no default, so it is still written as `1` but never read. A settings row that still holds `aider` or `continue` reads back as the default agent. `agentQuestionBridge`, `AgentQuestionExecutorBridge` and `InteractionBubble` are untouched. | | `ef9c9ae` | **C2** | Adds 12 software-factory flags, all **on by default**: `spaces`, `trackers`, `knowledge`, `signals`, `opportunities`, `feedback`, `discovery`, `incidents`, `outcomes`, `docsFirst`, `autopilot`, `factory`. They are stored in migration 169 (`DEFAULT 1`). Each flag gates its area's nav entry, pages, intake API, CLI group, daemon loop and, for `docsFirst`, the docs gate. A new view at **`/settings/feature-flags`** lists every flag with a one-line description, its default and an on/off switch; Settings links to it. **`shep settings flags [enable\|disable <flag>]`** does the same from the CLI. | | `1b681a2` | **C5** | Deletes the Vercel, Netlify, AWS Amplify and GCP Cloud Run stub providers, their enum members and all "Coming soon" UI. Cloudflare Pages is unchanged. An application that saved a removed provider reads it back as "no provider selected". | `b4e7420` renumbers the spec to 135: the telemetry and unified-decisions workstreams took 133 and 134. ## Why Implements `specs/135-review-cleanup-cuts/`, from the "Where Shep Is Today and What to Cut" section of `docs/competitors/t3code.md`. ## Screenshots / Recording I checked the feature-flags view at desktop and phone width in the running app with `pnpm dev:web` and Playwright. Turning `spaces` off removed the Spaces link from the sidebar and made `/spaces` return 404; turning it back on restored both. (The screenshots are local to the session; I can attach them if needed.) ## Testing Local runs, on the merge of `main` at `d97bc6f` (telemetry #923, decisions #922/#924): | Check | Result | | --- | --- | | `pnpm generate` | committed output matches what it generates | | `pnpm tsp:compile` | no warnings | | `pnpm lint`, `pnpm format:check` | pass | | `pnpm typecheck` | pass | | `pnpm test:unit` | 14,927 passed | | `pnpm test:int` | 2,139 passed | | `pnpm check:stories` | pass | | `pnpm build:release` | pass | | `pnpm build:storybook` | pass | | Web e2e (CI settings, a flaky test counts as a failure) | 86 of 86 passed | New tests: - feature-flag catalog completeness - List/SetFeatureFlag use cases - the CLI gate helper - `shep settings flags` - the background-sync gating - `requireFeaturePage` - feedback and alerts returning 404 while their flag is off - the docs-first gate - migration 169 - a repository round-trip of every new flag - mapper fallbacks for the removed agent and provider values - the new contributor commands Stories added: FeatureFlagsList, FeatureFlagsPageClient, ProviderList, CloudProviderIcons. `SettingsRows` (and its stories) now come from `main`; this PR keeps only the row's flex basis so switches stay beside long descriptions. ## Notes for the reviewer - **Recap data in the workflow:** the recap use case reads recognition events from the local database. In Actions that database starts empty, which matches what the committed `recaps/` files already show (zero events), since recognition is still recorded by hand (see CONTRIBUTING). The stale-issues job reads GitHub and works as is. - **Migration number:** 169. `main` holds 166 (agent-question decisions), 167 (decision timeout) and 168 (telemetry). - **i18n e2e:** the language spec waits for the language save by its request body and no longer on `response.finished()`, which hung for 60s in CI while the re-rendered page streamed. - **Windows test harness:** the git-config isolation now uses `main`'s empty-config-file approach; the pty teardown tolerates a lingering ConPTY host (`4d1b86f`). - **CLI name:** `shep security enforce` keeps its name. Moving it under `shep aspm` would have broken existing CI scripts. ## Checklist - [x] `pnpm lint` passes - [x] `pnpm format:check` passes - [x] `pnpm typecheck` passes - [x] `pnpm test:unit` and `pnpm test:int` pass - [x] `pnpm build` succeeds - [x] `pnpm build:storybook` succeeds and every new component has a colocated `.stories.tsx` - [x] `pnpm tsp:compile` ran and `output.ts` is committed - [x] Tests landed RED-first - [x] No `domain/` or `application/` file imports from `infrastructure/` - [x] Conventional Commits; `feat`/`fix` for user-visible changes - [x] LESSONS.md updated: the flag checklist points at the catalog, plus notes on agent worktrees, Windows git-config isolation and waiting for a specific server action in e2e 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01TxtN2VcWzg6NTCLj4DbmfL --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Shep Bot <shep-agent@users.noreply.github.com>
arielshad
pushed a commit
that referenced
this pull request
Oct 11, 2026
<p align="center"> <a href="https://github.com/shep-ai/shep"> <img src="https://raw.githubusercontent.com/shep-ai/shep/main/docs/screenshots/shep-card.jpg" alt="Shep — run multiple AI agents in parallel" width="720" /> </a> </p> # 🚀 Shep [v1.240.0](/compare/v1.239.0...v1.240.0) · _2026-10-11_ > Your organization does not have access to Claude. Please login again or contact your administrator. ### ✨ Features * **web:** review cleanup — per-area feature flags, ASPM off by default ([#925](#925)) ([58df74f](58df74f)), closes [#923](#923) [922/#924](#924)     ## 📦 Install or update ```bash # upgrade an existing install npm i -g @shepai/cli@1.240.0 # or run instantly without installing npx @shepai/cli@latest ``` ## 💬 Join the community [💬 **Discord**](https://discord.gg/ES6tdVFfur) · [📖 **Docs**](https://github.com/shep-ai/shep#readme) · [⭐ **Star on GitHub**](https://github.com/shep-ai/shep) · [🐛 **Report an issue**](https://github.com/shep-ai/shep/issues) --- <sub>🤖 Released autonomously by Shep — built by parallel AI agents working in isolated git worktrees. Try it: `npx @shepai/cli`</sub> Co-Authored-By: Shep Bot <shep-agent@users.noreply.github.com>
arielshad
added a commit
that referenced
this pull request
Oct 11, 2026
… (spec 136) (#929) ## What This is the telemetry follow-up noted in #922 and #924. Every recorded answer to an agent decision now emits `decision.answered`, and a background agent's question that reaches its deadline emits the new `decision.defaulted` event. Both go through the `ITelemetry` port from #923. | Event | Properties | Recorded by | | ----- | ---------- | ----------- | | `decision.answered` | `kind` (`DecisionKind`), `surface` (new `DecisionSurface`: web / cli / chat / supervisor / other), `latency` (bucket from asked to answered), `pickedRecommended` | `AnswerAgentQuestionUseCase`, once per answer it actually records | | `decision.defaulted` | `kind`, `timeout` (bucket of the deadline the agent set) | `AskAgentDecisionUseCase`, when its deadline settle wins | ## Why Spec 133 defined `decision.answered` but did not emit it, and its `kind` and `surface` were raw strings. The T3 Code review asks three things that need data: whether people answer agent questions, where they answer them, and whether they take the recommendation. It also asks how often agents proceed on their own at the deadline. - **Emit points:** each event is emitted right after the use case's atomic `settlePending` succeeds. Each decision is therefore counted exactly once without an `onceKey`. A lost race (CLI vs web), a refused answer, or agent questions being off records nothing. A person answering at the deadline records an answer, not a default. - **Surface:** each caller passes its own surface: the web action (`web`, which overrides anything the client sends), the CLI (`cli`), the chat bridge (`chat`) and the supervisor router (`supervisor`). Nothing parses the free-form `answeredBy` actor. - **Typing:** in the property map, `kind` and `surface` are now enums. That makes the compiler reject a content property at every emit site. - **Refactor while touching the file:** the `AskAgentDecisionOutcome` string union is now the `DecisionOutcome` enum, with a new `Disabled` member. The MCP tool's wire values are unchanged. Stored questions use `RecordedDecisionOutcome`, which excludes `Disabled`, so the activity log needs no UI for a state it can never show. - **Docs:** `docs/telemetry.md` lists both events. Spec: `specs/136-decision-telemetry/`. It was renumbered from 135, which belongs to #925; the branch name still says 135. ## Screenshots / Recording No UI changes. ## Testing - `answer-agent-question.use-case.test.ts`: - Properties for a decision answered on the web, including the latency bucket with a faked clock and `pickedRecommended: true`. - Typed text counts as not the recommendation. - A legacy question is reported as `Legacy`, with `Other` as the default surface. - Answering twice records one event. - Agent questions off, or a refused answer, records nothing. - `ask-agent-decision.use-case.test.ts`: - The deadline default records `decision.defaulted` with the timeout bucket. - A person answering at the deadline records no default. - Cancelled and disabled outcomes record no default. - Caller tests assert the surface they pass: web action, CLI `answer` (with `--answer` and interactive), chat bridge, supervisor router. Ran locally: `pnpm lint`, `format:check`, `check:stories`, `typecheck`, `test:unit` (14864 passed), `test:int` (2132 passed), `build`, `build:web`, `build:storybook`, and `generate` with a clean diff. After the renumbering: lint, format:check, typecheck and the affected test files. ## Checklist - [x] `pnpm lint` passes - [x] `pnpm format:check` passes - [x] `pnpm typecheck` passes - [x] `pnpm test:unit` and `pnpm test:int` pass - [x] `pnpm build` succeeds - [x] (Domain changes) `pnpm tsp:compile` ran and `packages/core/src/domain/generated/output.ts` is committed - [x] Tests landed RED-first per the TDD guide - [x] No `domain/` or `application/` file imports anything from `infrastructure/` - [x] Commit messages follow Conventional Commits 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014Zwb23kb4xTBxJtqc2hKbU --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Shep Bot <shep-agent@users.noreply.github.com>
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.
What
Opt-out usage metrics for Shep (spec 133).
Every process (CLI, daemon, feature-agent workers) records about ten content-free events into a local SQLite
telemetry_outbox. Only the long-lived server process sends them: theshep startdaemon orshep ui. Events go to PostHog's EU/batch/endpoint over plainfetch.Controls:
shep telemetry on|off|identity|contact|status|showWhy
This implements
specs/133-telemetry/, the "Metrics" section of the T3 Code product review. Today nobody can tell which areas, commands or agents are used, or whether runs end in merged PRs. The north star is merged PRs per weekly-active install.The product owner fixed these decisions:
CI,DO_NOT_TRACK=1,SHEP_TELEMETRY_DISABLED=1and test runners.installId.gh api user)shep telemetry identity on, or the switch in the notice and in Settings.accountIdSourceon the Claude and Codex entries. Core has noif (agentType === …).install.heartbeat,cli.command,web.area.viewed,feature.created,feature.run.finished,pr.opened,pr.merged,decision.answered(defined only),onboarding.step,error.unhandled.TelemetryEventPropertyMaptypes each event's properties, so a content property does not compile.CLAUDE.mdnow say what is sent. The full list is indocs/telemetry.md.Delivery details:
shep uinever send the same batch twice.pr.opened/pr.mergedare once-keyed per feature: the merge node, both PR-sync paths and the webhook all see a merge, and it counts once.Owner actions before release
SHEP_POSTHOG_KEYin the release environment, or fillBUILT_IN_POSTHOG_KEYinpackages/core/src/infrastructure/services/telemetry/telemetry-config.ts.The GDPR question in the spec is resolved by the owner's decision to make identity opt-in.
Screenshots / Recording
Storybook captures from
pnpm build:storybook, in light and dark.When the environment forces metrics off (
DO_NOT_TRACK), the switches are locked:The CLI first-run notice, printed once to stderr:
Testing
All of these ran locally on the final tree, after merging
main:pnpm lint,pnpm format:check,pnpm typecheck,pnpm typecheck:webandpnpm lint:webpass.pnpm generateleaves no diff.pnpm test:unit: 1,319 files, 14,830 tests, 0 failed.pnpm test:intpasses.pnpm buildandpnpm build:storybooksucceed, andcheck:storiesis OK.TDD, RED first. The identity default had RED tests in four places:
PRAGMA table_info)The split notice had its own RED tests: a domain field-list test, the use case, the CLI hook output order, and the web identity section.
Earlier coverage:
End to end: I posted the real wire body shape to
https://eu.i.posthog.com/batch/and got200; a malformed body gets400. PostHog answers 200 for any key, so this proves the format, not a project.Checklist
pnpm lintpassespnpm format:checkpassespnpm typecheckpassespnpm test:unitandpnpm test:intpasspnpm buildsucceedspnpm build:storybooksucceeds; every new component has a colocated.stories.tsxpnpm generateran;output.tsandapis/json-schema/are committeddomain/orapplication/file imports anything frominfrastructure/Notes for reviewers
168-create-telemetry.ts: feat(agents): one Decision primitive rendered inline everywhere (spec 134, part 1) #922 owns 166, and feat(agents): background agents ask with a recommendation and a deadline (spec 134, part 2) #924 takes 167.settings-page-client.tsx,settings.mapper.ts,create-feature.use-case.ts,feature-agent-worker.ts,merge.node.tsandagent-catalog.tswere already past the limit. This PR only adds injection and one call to each. New code lives in its own modules. Splitting the large files is left for a dedicated refactor.IClockregistration moved toregister-services, so every container has a clock.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y84mkYCEBtd3RMaSvpSLz5