diff --git a/context/agents/error-tracking/capture-exceptions.md b/context/agents/error-tracking/capture-exceptions.md new file mode 100644 index 00000000..5c14e6d5 --- /dev/null +++ b/context/agents/error-tracking/capture-exceptions.md @@ -0,0 +1,42 @@ +--- +type: capture-exceptions +flow: error-tracking +label: Wire up exception capture +model_pi: openai/gpt-5.6-sol +effort_pi: medium +model_sdk: claude-sonnet-4-6 +effort_sdk: high +skills: [integration-v2-error-tracking-step, posthog-best-practices] +allowedTools: [Read, Write, Edit, Glob, Grep] +disallowedTools: [enqueue_task] +dependsOn: [install, init] +--- + +## Goal + +Make the errors the app does not catch reach PostHog, by whatever means the +SDK offers for that. Which means depends on the SDK: some autocapture +exceptions once you enable it at init, some wire into the framework's own +error handler, some give you a boundary to mount at the app entry. Follow the +docs and the reference example for this one, and set it up in one place — +never manual capture calls sprinkled across files. + +The SDK is installed and initialized — either it already was, or the install +and init tasks before you did it (see their handoffs); build on that, do not +re-check it. + +This is an instrument-only task. Do not install dependencies, run the build, +run tests, or start the app — the user-driven test-setup step at the end of +the flow verifies, when the user wants it. Do not touch the build config +either way; when the flow includes a configure task, it owns those files. +Stay inside this project's directory and set up that one place; that is the +whole job. + +## How you know you succeeded + +An error the app does not catch reaches PostHog, through the mechanism this +SDK gives you rather than one you invented. You did not install anything, run +a build, lint, or tests, search outside the project, or read through the whole +app or hand-wrap individual components or routes. Your handoff names the files +you changed and the capture mechanism, so the report can explain it to the +user. diff --git a/context/agents/error-tracking/configure.md b/context/agents/error-tracking/configure.md new file mode 100644 index 00000000..4cbbf3bc --- /dev/null +++ b/context/agents/error-tracking/configure.md @@ -0,0 +1,96 @@ +--- +type: configure +flow: error-tracking +label: Apply build-config changes +model_pi: openai/gpt-5.6-sol +effort_pi: medium +model_sdk: claude-sonnet-4-6 +effort_sdk: medium +skills: [] +allowedTools: [Read, Write, Edit, Glob, Grep, Bash, load_skill_menu, install_skill, check_env_keys] +disallowedTools: [enqueue_task] +dependsOn: [capture-exceptions] +--- + +## Goal + +Make this project's production build emit and upload source maps (or, for Go +and Rust, native debug symbols). Install the skill your task input names +(`install_skill` with the `skillId`) and read it — it is the source of truth +for the per-framework build-config and the uploader wiring. + +Two of the skill's steps are yours: + +- **"Apply build-config changes"** — make the bundler / build-config edits the + skill instructs for this platform, so the build produces and injects the + chunk IDs PostHog needs and runs the uploader. +- **"Make credentials available at build time"** — do the skill's step so the + build can read the upload credentials from the environment. If it calls for a + loader (e.g. `dotenv`), install it SILENTLY with the project's package + manager. Skip this step entirely when the platform already auto-loads `.env`. + +Install every dependency with the project's own package manager: call +`detect_package_manager` before the first install and use its answer, +translating any `npm install` the skill or docs show (`pnpm add -D …`, +`yarn add -D …`). In a pnpm or yarn workspace, npm fails outright on +`workspace:*` dependencies (`EUNSUPPORTEDPROTOCOL`) — that error means the +wrong manager, never a flag to retry with. + +When your changes make the build emit a bundle to a new directory (`dist/`, +`build/`), check that some script actually runs that output. A project whose +`build` writes `dist/index.js` while `start` still runs the original source +never executes the bundle the maps were uploaded for, so every uploaded map +goes unused. Add or fix the script that serves the built output. + +A run script also has to reach the values the app reads. When the credentials +live in an env file and the platform does not load it on its own, the script you +add or fix has to load it — export it before the binary, pass the runtime's own +flag for it, whatever that platform offers. Skip that and the artifact starts, +prints its own "variable missing" guard, and reports nothing: the build is +green, the symbols are uploaded, and the one command you told the user to run is +the one that cannot capture. + +Match the bundle's module format to the package's type while you write the +command — you cannot run it, so it has to be right by construction. `esbuild +--platform=node` emits CommonJS unless you pass `--format=esm`, so in a package +whose `package.json` sets `"type": "module"` the bundle dies at boot with +`ReferenceError: module is not defined in ES module scope`. Read the `type` +field before writing the command. + +Types are the same kind of trap. In a config the compiler checks — a `.ts` +config, or JS under `checkJs` — `process.env.ANYTHING` is `string | undefined`, +while a plugin's options usually require `string`. Where your skill's example +asserts or defaults that lookup, keep that part exactly: dropping a `!` or a +`?? ''` turns a working example into a build that fails type checking. The +upload step often runs before the type check, so the maps land and the build +still exits non-zero — a broken build that looks half-successful in the log. + +## The names are a contract + +An env variable only works if the name the code reads is the name in the file. +You write the code that reads them; the `credentials` task writes the file, and +you two run in parallel. So never invent a name that already exists somewhere +else — look for the other half of the contract first, and adopt it: + +- **Call `check_env_keys` before you write the config.** It returns names, never + values. If the PostHog upload variables are already there, make your config + read exactly those names, whatever they are, even when they are not the ones + your skill's example shows. +- **Only when they are absent yet** are you the one deciding. Use the names your + skill documents for the mechanism you are wiring, and name them in your + handoff in full so `credentials` and `wire-ci` can match them. + +Names that merely look plausible are the failure here. A build reading +`POSTHOG_API_KEY` beside an env file holding `POSTHOG_CLI_API_KEY` throws no +error anywhere — the upload is skipped silently, the build looks clean, and +every stack trace stays minified. + +Do not write any credential values and do not create env files — the +`credentials` task owns that, in parallel with you. Do not run the build. + +## How you know you succeeded + +The build config carries the skill's source-map / debug-symbol changes and can +read its credentials from the environment at build time. Your handoff names +every file you changed and the exact build-config keys you added, so the CI +task can wire the same variables through the pipeline. diff --git a/context/agents/error-tracking/credentials.md b/context/agents/error-tracking/credentials.md new file mode 100644 index 00000000..ae86250d --- /dev/null +++ b/context/agents/error-tracking/credentials.md @@ -0,0 +1,80 @@ +--- +type: credentials +flow: error-tracking +label: Get and write the upload credentials +model_pi: openai/gpt-5.6-sol +effort_pi: medium +model_sdk: claude-sonnet-4-6 +effort_sdk: medium +skills: [] +allowedTools: [Read, Write, Edit, Glob, Grep, Bash, load_skill_menu, install_skill, wizard_ask] +disallowedTools: [enqueue_task] +dependsOn: [] +--- + +## Goal + +Put the PostHog source-map upload credentials into this project's environment. +Install the skill your task input names (`install_skill` with the `skillId`) +and follow its **"Write credentials to the env file"** step for the variable +names and the env file to pick. + +The upload needs a PostHog **personal API key** at build time. Only the user can +mint one — never call the PostHog API or any tool to create it. Get it and +write it in this one task (the key never survives across tasks): + +1. Ask with `wizard_ask`, exactly: + `{ id: "api-key", prompt: "Paste your PostHog personal API key below.\n\nDon't have one yet? Create one here:\n\n\nWhen creating the key, choose the 'Source map upload' preset, then come back and paste it here.", kind: "text", sensitive: true }` + You receive `{ secretRef: "secret:..." }` — a vaulted reference, never the raw + value. If `wizard_ask` is unavailable (non-interactive run), report this task + with status `not needed` and say in your handoff that the user must create + the key and set the variables themselves; do not block. +2. Pick the env file per the skill (reuse the one PostHog's SDK already writes + its `POSTHOG_*` vars to, when there is one). Call `check_env_keys` on it + first (it returns present/absent, never values — never read the file + directly). +3. Settle the variable names before you write them — see "The names are a + contract" below. Then call `set_env_values`, passing the secretRef as a + value object, not a literal string — e.g. + `values: { "POSTHOG_CLI_API_KEY": { secretRef: "" }, "POSTHOG_CLI_PROJECT_ID": "", "POSTHOG_CLI_HOST": "" }` + with whichever names you settled on. The wizard resolves the ref locally, so + you never see the key value. +4. Document the same variable names for other developers: append them to + `.env.example` (create it if the project has none) with empty or + placeholder values — never a real value, and never the key itself. The + example file is committed and is the only `.env*` you may write directly; + it is how the next developer, and the next wizard run's `check_env_keys`, + learns the project expects these variables. + +Replace ``, ``, and `` from your project +context. Do not touch the build config — the `configure` task owns that. + +## The names are a contract + +An env variable only works if the name in the file is the name the code reads. +You write the file; the `configure` task writes the code that reads it, and you +two run in parallel. So never invent a name that already exists somewhere else +— look for the other half of the contract first, and adopt it: + +- **Read the build config before you choose.** If it already references PostHog + env variables, use exactly those names, whatever they are, even when they are + not the ones your skill would suggest. Reading config is safe — it holds + names, not secrets. +- **Only when nothing references them yet** are you the one deciding. Use the + names your skill documents for the mechanism that was actually wired. +- **Either way, say which set you chose in your handoff, in full.** That is how + `configure` and `wire-ci` learn what to match. A handoff that says "the usual + variables" hands the next task the same guess you just made. + +Names that merely look plausible are the failure here. A build reading +`POSTHOG_API_KEY` beside an env file holding `POSTHOG_CLI_API_KEY` throws no +error anywhere — the upload simply never runs, and every stack trace stays +minified. + +## How you know you succeeded + +The env file holds the upload variables (the key as a resolved secret, the +non-secret project id and host as literals), written through the wizard tools, +never hardcoded in source, and `.env.example` documents the same names with +placeholders. Your handoff names the env file and every variable name — never +a value — so the CI task carries the same names into the pipeline. diff --git a/context/agents/error-tracking/init.md b/context/agents/error-tracking/init.md new file mode 100644 index 00000000..0e8d96dd --- /dev/null +++ b/context/agents/error-tracking/init.md @@ -0,0 +1,101 @@ +--- +type: init +flow: error-tracking +label: Set up PostHog initialization +model_pi: openai/gpt-5.6-sol +effort_pi: low +model_sdk: claude-sonnet-4-6 +effort_sdk: medium +skills: [integration-v2-init, posthog-best-practices] +allowedTools: [Read, Write, Edit, Glob, Grep, Bash, check_env_keys, set_env_values] +disallowedTools: [enqueue_task] +dependsOn: [] +--- + +## Goal + +Make sure PostHog is initialized. If the project already has a working +`posthog.init(...)` (or the framework's equivalent) with its env keys wired, +leave it alone and say so in your handoff. If it doesn't, create it following +your skill — it owns the how: the framework's init point, the env-var wiring +through the wizard tools, and `.env.example`. + +You exist in this flow because the user asked for error tracking on a repo +whose PostHog init is missing or unproven. Initialize the SDK so exceptions can +flow and stop — no instrumentation, no extras. Don't set up exception capture +either way; the capture-exceptions task after you owns that. + +## An existing init still needs its variable defined + +"Already initialised" is a property of the pair, not of the call. An init point +that reads `process.env.SOMETHING` only works when `SOMETHING` is defined in the +env file the project loads. A repo can carry an init that has never once run: +the call is there, the variable it names is nowhere, and the client is built +from an empty string. + +So before you leave an existing init alone, call `check_env_keys` on that env +file and look for the exact name the init reads. It returns names, never values. + +- **Present** — the pair is complete. Leave it alone and say so. +- **Absent** — the init is not wired yet, whoever wrote it. Write that variable + with `set_env_values` under the name the code already reads, and document it + in `.env.example`. Do not rename the code to match a name you would rather + have written; the code is the half that already exists. + +An empty key is the quiet failure here. A client constructed from `''` throws +nothing and logs nothing. The build is clean, the app starts, every capture call +returns — and no event ever arrives. + +## Make the environment actually reachable + +Writing the keys to `.env` is only half the job — something has to load that +file at runtime, or the app throws on boot and captures nothing. + +Most frameworks do it for you: Next, Nuxt, Astro and SvelteKit auto-load `.env`, +and Vite auto-loads it for client code. Nothing to do there. + +A plain Node backend does not — Express, Fastify, Hono, Koa, a raw `node:http` +server — and neither does a bare Rollup or webpack config. When your init point +reads `process.env` on one of those, wire the loading too, either way: + +- install `dotenv` with the project's own package manager (detect it from the + lockfile) and import it above the PostHog init — `require('dotenv').config()`, + or `import 'dotenv/config'` for ESM; or +- add `--env-file=.env` to the `start` and `dev` scripts, when the project is on + Node 20.6+ and would rather not take a new dependency. + +You cannot run the app, so this has to be right by construction. Writing the +guard without the loader is what produces +`POSTHOG_… variable required by PostHog is missing or un-configured` at module +load: the guard you wrote firing against an env file nothing reads. + +Some platforms have no environment to read at all, and there the answer is not +a loader. Angular on the stock `@angular/build` builder is the common one: +nothing populates `process.env`, `import.meta.env`, or the project's own +`src/environments/.env.ts` with your keys. + +The trap is that each of those *looks* like a mechanism. `import.meta.env` and +the `NG_APP_` prefix only exist with `@ngx-env/builder` installed, and a +generated `.env.ts` usually carries one unrelated key such as +`npm_package_version` and nothing else. Read from either and your key is +`undefined`: in development the guard throws while the module evaluates and the +app renders a blank page, and in a production build the guard returns quietly, +so the app looks fine while PostHog never initialises at all. + +So do not wire a lookup unless you have opened the thing it reads from and seen +your key defined there. When nothing populates it, write the real public project +token as a literal in the committed `src/environments/*` files. This is the +skill's "no valid environment to read from" case, and the public token is +publishable — it ships inside the browser bundle either way. + + +## How you know you succeeded + +An init point exists with the PostHog env keys present — whether it already +did or you just created it — keys in the env file and confirmed there with +`check_env_keys`, never hardcoded. On a platform that does not auto-load +`.env`, the loading is wired; on a platform with no environment at all, the +token is a literal rather than a lookup into something that never defines it. +Your handoff names the files involved, how the client is constructed, and how +`.env` reaches it, so the capture-exceptions task can find the init options +without re-discovering them. diff --git a/context/agents/error-tracking/install.md b/context/agents/error-tracking/install.md new file mode 100644 index 00000000..14a2e7ff --- /dev/null +++ b/context/agents/error-tracking/install.md @@ -0,0 +1,31 @@ +--- +type: install +flow: error-tracking +label: Add the PostHog SDK to the manifest +model_pi: openai/gpt-5.6-sol +effort_pi: low +model_sdk: claude-haiku-4-5-20251001 +skills: [integration-v2-install] +allowedTools: [Read, Edit, Glob, Grep, Bash] +disallowedTools: [enqueue_task] +dependsOn: [] +--- + +## Goal + +Make sure the PostHog SDK is in the manifest. If it's already installed, +leave it alone and say so in your handoff. If it isn't, install it following +your skill — it owns the how: the package manager rules, the version rules, +what counts as an environment failure, and the fallback. + +You only exist in this flow because the user asked for error tracking on a +repo without PostHog. Install the SDK the errors will report through (the +server library too, if the app runs server-side code) and stop — no +instrumentation, no extras. + +## How you know you succeeded + +The SDK is declared in the manifest at a real version — whether it already +was or you just installed it — or your handoff plainly says why the +environment stopped you. Your handoff names the manifest and the package, so +later steps import it under the name they will actually get. diff --git a/context/agents/error-tracking/report.md b/context/agents/error-tracking/report.md new file mode 100644 index 00000000..366611a0 --- /dev/null +++ b/context/agents/error-tracking/report.md @@ -0,0 +1,65 @@ +--- +type: report +flow: error-tracking +label: Summarise and hand off +sink: true +model_pi: openai/gpt-5.6-luna +effort_pi: low +model_sdk: claude-sonnet-4-6 +effort_sdk: medium +skills: [] +allowedTools: [Read, Glob, Grep, Write, posthog_exec] +disallowedTools: [enqueue_task] +dependsOn: [capture-exceptions, wire-ci, test-setup] +--- + +## Goal + +Tell the user what error tracking now does for them and what they still have +to do, from the handoffs of every task in the run. `read_handoffs` gives you +each task's report — the capture mechanism, the files changed, the env +variable names, the CI secret to create, and any deploy path that could not be +traced. Do not re-derive any of it from the project. + +First, turn on the Error Tracking product for the team (`products-enable` +through `posthog_exec`) so the captured exceptions have a UI to land in. If +the call fails or the tool is missing, carry it as a follow-up — never fail +the report over it. + +Write the hand-off to `posthog-error-tracking-report.md` at the top level of +this project's directory. When the run wired source-map upload, START it with +a **"What you still need to do"** section — numbered, copy-pasteable: + +1. Create a personal API key with the 'Source map upload' preset at + `/settings/user-api-keys` (skip when the credentials handoff says + the key is already written). +2. Add it as the CI secret the wire-ci step referenced, named exactly as in + the pipeline config. +3. Any other manual follow-up the handoffs carry (an untraceable deploy path, + provider-side settings). + +Then cover, briefly and concretely: + +- How uncaught errors reach PostHog now — the capture mechanism and the files + that carry it. +- If the run also installed and initialized the SDK, say so — the user + started this command without PostHog and now has it. +- When source-map upload was wired: the files changed (paths only), the exact + production build command, and that every production build now uploads. +- When it was skipped: one line saying why — readable stack traces on this + platform, or that Astro is not supported by the uploader. An outcome, not an + apology. Say plainly that the build command was left untouched. +- How to verify: trigger any error and look at + `/project//error_tracking`; uploaded symbol sets appear + at `/project//error_tracking/configuration`. + +Never write a secret value into the report — only variable names. Replace +`` and `` from your project context. Give the same +summary in chat. + +## How you know you succeeded + +`posthog-error-tracking-report.md` exists and a user who reads only it knows +how errors reach PostHog, the follow-ups they still owe (the API key and the +CI secret named exactly, when upload was wired), and where in PostHog to see +the first captured exception. diff --git a/context/agents/error-tracking/setup-error-tracking.md b/context/agents/error-tracking/setup-error-tracking.md new file mode 100644 index 00000000..168c247c --- /dev/null +++ b/context/agents/error-tracking/setup-error-tracking.md @@ -0,0 +1,109 @@ +--- +type: setup-error-tracking +flow: error-tracking +seed: true +model_pi: openai/gpt-5.6-terra +effort_pi: medium +model_sdk: claude-sonnet-4-6 +effort_sdk: high +skills: [] +allowedTools: [Read, Glob, Grep, posthog_exec] +disallowedTools: [Write, Edit, Bash, complete_task] +dependsOn: [] +--- + +## Goal + +Plan a PostHog Error Tracking setup and seed the task queue. The end state: +errors the app does not catch reach PostHog, and — where the platform ships +minified bundles or stripped binaries — production builds upload the source +maps or debug symbols that make the stack traces readable. + +First establish two facts from the repo: + +**1. Is PostHog already integrated?** Look for `posthog-js` or a server SDK in +the dependency manifests, or a `posthog.init(...)` / snippet in the source. +Check the project state for existing events if the repo is ambiguous. + +Integrated means the init runs, not that the package is listed. An init point +is a pair: the call, and the key it is constructed from. A repo can carry the +call while the key it names is defined nowhere — then the client is built from +an empty string and captures nothing, however complete the manifest looks. So +when you find an init that reads a variable, look for that same name in the +repo's committed env template or its build config. Found, or the project state +shows real events arriving: integrated. Named nowhere, or you cannot tell: +**queue `init`**. It re-checks the pair itself and leaves a complete init +alone, so queuing it when you are unsure costs one cheap task, while skipping +it on a keyless init costs the whole run. + +**2. Which uploader variant is this project — or none?** Read the manifests +and pick at most one, by this precedence (first match wins): + +- `pubspec.yaml` → `flutter` +- an `.xcodeproj`, `Podfile`, or `Package.swift` → `ios` +- a Gradle build file (`build.gradle`, `build.gradle.kts`, `settings.gradle`) → `android` +- `go.mod` → `go` +- `Cargo.toml` → `rust` +- `astro` in `package.json` dependencies → **none**. Astro is not supported by + the uploader: it inlines scripts below its asset limit into the HTML, so a + build routinely emits a `.map` with no `.js` beside it, and the upload step + then fails the whole build. This rule wins over every `package.json` match + below — an Astro project that also depends on `vite` is still **none**. +- otherwise read `package.json` dependencies, first match wins: + `react-native` → `react-native`; `nuxt` → `nuxt`; `next` → `nextjs`; + `@angular/core` → `angular`; `vite` → `vite`; `webpack` → `webpack`; + `rollup` → `rollup`; `react` → `react`; server-only Node → `node`; + any other browser JS → `web` +- **none** for platforms whose stack traces are already readable: plain + Python (Django, Flask, FastAPI), Ruby, PHP, Elixir, JVM servers, .NET. + Skip the whole upload subgraph for them — a skipped upload on such a + platform is an outcome, not a gap. + +When a variant matched, the uploader skill id is +`error-tracking-upload-source-maps-`. Pass it to the four upload +tasks as `inputs: { skillId: "", displayName: "" }` +so no task re-detects. + +The two facts are independent — settle BOTH before you enqueue anything. +"PostHog is already integrated" answers fact 1 only; it never decides fact 2, +and an already-integrated project still gets the upload subgraph when a +variant matches. A compiled or bundled JS project normally has one: a Node +service built with `tsc` ships minified/compiled output, so it is the `node` +variant, not "none". Only two kinds of project skip the subgraph — the +readable-stack platforms listed above, and Astro. + +Then seed the graph: + +- `install`, only when the SDK is missing from the manifest. +- `init`, independent of `install` — whenever fact 1 did not show a complete + pair. Do not stop on an uninstrumented repo, integrate. +- `capture-exceptions`, after whichever of `install` and `init` you queued + (with no dependencies when you queued neither). +- When an uploader variant matched, add the upload subgraph: + - `credentials`, no dependencies — it stops to ask the user for a personal + API key, so keep it a root task: the prompt reaches the user early while + the code tasks run. + - `configure`, after `capture-exceptions` — build-config changes; it runs + after the code edits so the two never fight over the same files. + - `wire-ci`, after `configure` and `credentials`. + - `test-setup`, after `wire-ci` — offers the user a local end-to-end test + last, once everything is wired. +- `report`, after every other queued task. It writes the handoff last, so it + describes what actually shipped. + +Never plan an identify, capture, dashboard, or session-replay task — this run +sets up error tracking, not the full integration. The minimal SDK footprint +that `install` and `init` leave behind is enough for exceptions to flow. + +## How you know you succeeded + +Every task in the chosen graph is queued with that dependency shape, the four +upload tasks (when queued) share the same `{ skillId, displayName }` inputs, +`report` depends on the rest (directly or transitively), and the first task is +runnable. Your plan states both facts explicitly: whether PostHog was +integrated — and, when you called it integrated, the name of the key you found +defined — and which uploader variant matched — or, when you queue no upload +tasks, why no variant applies: which readable-stack platform this is, or that +Astro is not supported by the uploader. A +plan that never mentions fact 2 is an incomplete plan, not a decision. Keep +labels short — the action in a few words. diff --git a/context/agents/error-tracking/test-setup.md b/context/agents/error-tracking/test-setup.md new file mode 100644 index 00000000..b5d6dfb2 --- /dev/null +++ b/context/agents/error-tracking/test-setup.md @@ -0,0 +1,41 @@ +--- +type: test-setup +flow: error-tracking +label: Offer to test the local setup +model_pi: openai/gpt-5.6-sol +effort_pi: medium +model_sdk: claude-sonnet-4-6 +effort_sdk: high +skills: [] +allowedTools: [Read, Write, Edit, Glob, Grep, Bash, load_skill_menu, install_skill, wizard_ask] +disallowedTools: [enqueue_task] +dependsOn: [wire-ci] +--- + +## Goal + +Offer the user a one-time, end-to-end check that errors reach PostHog with +readable stack traces. Install the skill your task input names (`install_skill` +with the `skillId`) and follow its **"Test the local setup"** step for the +platform-appropriate affordance, the `captureException` shape, the placement, +and the read-before-edit / always-revert rules. + +First ask with `wizard_ask`: +`{ id: "test-affordance", prompt: "Want me to help you test your local setup? I'll add a temporary test button (or route) to your app so you can confirm errors show up in Error Tracking with readable stack traces after your next build. I'll remove it once you've confirmed it works.", kind: "single", options: [{ label: "Yes, help me test it", value: "yes" }, { label: "No, I'll test on my own later", value: "no" }] }` + +- **"no"** (or `wizard_ask` unavailable): do nothing to the code and report this + task done, noting the test was offered and declined. +- **"yes"**: add the affordance per the skill, then pause with a second + `wizard_ask` (id `"test-done"`, a single `Continue (revert test code)` + option) whose prompt gives the build, run, and Error-Tracking-check as + literal numbered steps (build first — it uploads the maps — then trigger the + affordance, then confirm the error resolves to real source in Error + Tracking). After the user continues, REVERT every test edit per the skill's + rules. Never leave the affordance in place, even if the user says it didn't + work — revert first, then carry the failure into your handoff. + +## How you know you succeeded + +Either the user declined and no code changed, or the affordance was added, +tested, and fully reverted. Your handoff says which, and carries any failure +the user reported for the report to surface. diff --git a/context/agents/error-tracking/wire-ci.md b/context/agents/error-tracking/wire-ci.md new file mode 100644 index 00000000..981c690d --- /dev/null +++ b/context/agents/error-tracking/wire-ci.md @@ -0,0 +1,89 @@ +--- +type: wire-ci +flow: error-tracking +label: Set up CI for automatic uploads +model_pi: openai/gpt-5.6-sol +effort_pi: high +model_sdk: claude-sonnet-4-6 +effort_sdk: high +skills: [] +allowedTools: [Read, Write, Edit, Glob, Grep, load_skill_menu, install_skill] +disallowedTools: [enqueue_task] +dependsOn: [configure, credentials] +--- + +## Goal + +Make the credentials reach the production build wherever it actually runs, so +source maps upload on every deploy — not just on a local build. Install the +skill your task input names (`install_skill` with the `skillId`) and follow its +**"Set up CI for automatic uploads"** step — it owns tracing where the +production build runs and wiring the credentials through every layer, whatever +the CI provider. + +The `configure` and `credentials` handoffs already name the build-config keys +and the exact environment-variable names in use — carry those same names into +the pipeline; do not invent new ones. Trace the deploy path by reading the +project's own files (CI workflows, Dockerfiles, deploy scripts) — never invent +config that is not there. You cannot create the CI secret that holds the API +key; reference it by name and carry that follow-up, plus any deploy path you +could not trace, into your handoff for the report. + +## Two things cross the boundary, not one + +Credentials are half of it. The uploader also needs a **release identity** — a +name and a version — and it derives that from the CI's own git variables or +from a `.git` directory. A container build sees neither: `.git` is almost +always in `.dockerignore`, and the CI's variables stop at the `docker build` +command. Nor does a hardcoded release name rescue you; the uploader wants both +halves, and stops the build when it has only one. + +So forward the provider's git variables into the build the same way you forward +credentials, and declare each one as `ARG` **and** `ENV` — `ARG` alone is not +visible to the uploader's environment lookup. Your skill's "Associate the +release with a git commit" step lists the variables per provider. Read that +step even though it is not the CI step: this boundary is where it applies. + +Forward them only where a provider actually sets them. A project with no +pipeline — an image built and run by hand — has nothing to inherit from, and +variables declared but never filled resolve to no release at all: it reads as +wired and still fails the build. There the identity has to be supplied outright, +both halves, from something the build itself holds — a build argument the +operator passes, or the manifest's own version — so name and version are always +present. Decide which case you are in by reading the repo, not by assuming a +provider exists. + +That `ARG`-plus-`ENV` shape is for the git variables and the non-secret settings +only. The API key is a secret and keeps whatever secret-carrying mechanism the +build system offers — a build secret mounted for the one step that needs it, a +masked variable, a secret file. A secret in `ARG` or `ENV` is recorded in the +build history, and the builder itself will warn you: *do not use ARG or ENV +instructions for sensitive data*. Widening the git-variable pattern to cover the +key is a downgrade, not consistency. + +## The runtime boundary has names too + +Upload credentials are a build-time concern. The app also reads its own +variables at **run** time, and the step that starts it is another place a name +has to match — a `docker run -e`, a compose file, a systemd unit, a platform's +environment settings. That step was written before PostHog existed here, so the +name it passes is whatever the project used back then. If `init` settled on a +different one, the deployed process starts with an undefined key. + +Nothing fails loudly. In production the init guard returns quietly instead of +throwing, so the process boots, serves traffic, and reports nothing — the same +silent shape as a mismatched upload variable, one boundary later. + +So read the names the app's own source reads, and make the start step pass +exactly those. Rename the deploy step's variable, not the code: the code is the +half that already exists. Name any secret the user must create under its new +name in your handoff. + +## How you know you succeeded + +The pipeline that runs the production build carries the upload credentials by +the same names the credentials task used and reaches the uploader with a +resolvable release identity, and the step that starts the app passes the +variable names the app's own source reads. Every secret the user still has to create is named +in your handoff. Your handoff lists the CI files you changed +and every manual follow-up, so the report can hand them to the user. diff --git a/context/skills/error-tracking-upload-source-maps/description.md b/context/skills/error-tracking-upload-source-maps/description.md index bd355029..90078145 100644 --- a/context/skills/error-tracking-upload-source-maps/description.md +++ b/context/skills/error-tracking-upload-source-maps/description.md @@ -34,6 +34,7 @@ Wire source map generation, chunk-ID injection, and upload into your **productio - **Don't ship source maps publicly**: omit `.map` files from the deployed artifact, or use hidden source maps. Uploaded maps live in PostHog, not on your origin. - **Link each release to its commit.** The CLI auto-detects the commit from the CI's git env vars — see "Associate the release with a git commit" for making those reachable in Docker/CI builds. - **Never write a dependency version from memory.** When a variant needs the CLI or a plugin *inside the project* — the JS/web build tools (Node, web, Next.js, React, Angular, Nuxt, Vite, Webpack, Rollup) — install it with the project's package manager pinned to `latest` (`npm install --save-dev @posthog/cli@latest`, `pnpm add -D @posthog/cli@latest`, `yarn add -D @posthog/cli@latest`) and let the manager write the resolved version into `package.json`. A version recalled from memory is usually far behind and silently breaks the commands in this skill — `--dotenv-file`, for one, is rejected outright by 0.5.x. iOS, Android, React Native, Flutter, Go and Rust are the exception: the wizard pre-installs a global `posthog-cli` for those, so add no project dependency for them at all. +- **Match the project's package manager, not the docs'.** Example commands in the docs install with `npm`. Before any install, detect the real manager from the lockfile — `pnpm-lock.yaml` → `pnpm add -D`, `yarn.lock` → `yarn add -D`, `package-lock.json` / none → `npm install --save-dev` (the wizard's `detect_package_manager` tool answers this) — and translate the command. This is load-bearing in monorepos: npm hard-fails on `workspace:*` dependencies with `EUNSUPPORTEDPROTOCOL`, so an npm install copied into a pnpm workspace cannot succeed, and no npm flag fixes it — switch the command to the detected manager instead of retrying. #### Examples - **Node / tsc** Emit maps with embedded sources by setting both in `tsconfig.json`: `"sourceMap": true` and `"inlineSources": true`. Add the CLI to the project with `npm install --save-dev @posthog/cli@latest` (or the project's package manager) so the build script and CI resolve the same binary — never hand-write the version string. Then run `posthog-cli sourcemap process` against the build output dir as a post-build step — it injects chunk IDs and uploads in one pass, and needs the upload credentials (see "Make credentials available at build time").