diff --git a/context/agents/integration-v2/report.md b/context/agents/integration-v2/report.md index 28ee4a99..f360faf0 100644 --- a/context/agents/integration-v2/report.md +++ b/context/agents/integration-v2/report.md @@ -6,7 +6,7 @@ model_pi: openai/gpt-5.6-luna effort_pi: low model_sdk: claude-sonnet-4-6 effort_sdk: high -skills: [integration-v2-report, integration-v2-notebook, integration-v2-mcp] +skills: [integration-v2-report, integration-v2-mcp] allowedTools: [Read, Glob, Grep] disallowedTools: [enqueue_task] dependsOn: [dashboard, review] @@ -18,9 +18,9 @@ Compose the setup report summarizing what this integration did, drawing only on what the run itself recorded: the queue log and event plan in `.posthog-wizard-cache/` (`queue.json` and `.posthog-events.json`), and the handoff each step left behind. Then publish it with a single `publish_handoff` -call, and mirror the same markdown into a shareable PostHog notebook, emitting -its URL with the `[NOTEBOOK_URL]` marker. Do not write a report file — the -handoff call and the notebook are how the report reaches the user. +call. That call is how the report reaches the user: the tool creates the +shareable PostHog notebook from it and hands the wizard the link. Do not write a +report file, and do not create the notebook yourself. Separate what the run verified from what it did not. A passing build proves the code compiles, not that events flow — never write that an event was captured @@ -42,6 +42,5 @@ One `publish_handoff` call went through with the full report: what was installed and initialized, the events captured, whether identify was wired or skipped, error tracking added, the dashboard link, any build conflict in full, and the next steps for the user. Every claim in it traces to a handoff, and -what the run could not confirm reads as unconfirmed. The same report is -mirrored into a PostHog notebook whose URL is emitted with the -`[NOTEBOOK_URL]` marker. +what the run could not confirm reads as unconfirmed. Nothing else is needed — +the notebook comes from that one call. diff --git a/context/skills/audit-attribution/references/1-presence.md b/context/skills/audit-attribution/references/1-presence.md index ec6c3e9a..993fd4e0 100644 --- a/context/skills/audit-attribution/references/1-presence.md +++ b/context/skills/audit-attribution/references/1-presence.md @@ -50,7 +50,7 @@ The ledger lives at `.posthog-audit-checks.json` and renders live in the wizard { "id": "attribution-cross-subdomain-cookie", "area": "Attribution — Configuration", "label": "cross_subdomain_cookie configured for multi-subdomain projects", "status": "pending" }, { "id": "attribution-cookieless-mode-impact", "area": "Attribution — Configuration", "label": "cookieless_mode tradeoff acknowledged", "status": "pending" }, { "id": "attribution-consent-integration", "area": "Attribution — Configuration", "label": "Consent banner wired to PostHog opt_in/opt_out", "status": "pending" }, - { "id": "write-report", "area": "Write report", "label": "Render posthog-audit-attribution-report.md", "status": "pending" } + { "id": "write-report", "area": "Write report", "label": "Publish the attribution audit report", "status": "pending" } ] } ``` diff --git a/context/skills/audit-attribution/references/4-report.md b/context/skills/audit-attribution/references/4-report.md index bb9e1aa7..b1590a9f 100644 --- a/context/skills/audit-attribution/references/4-report.md +++ b/context/skills/audit-attribution/references/4-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 4 — Generate the audit report +# Step 4 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing attribution audit report +[STATUS] Publishing attribution audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-attribution-report.md` at the project root with the structure shown below. After the markdown lands on disk, resolve the `write-report` ledger row to `pass` so the wizard sidebar advances. Then delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, resolve the `write-report` ledger row to `pass` so the wizard sidebar advances. Then delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -119,8 +119,10 @@ Re-run `posthog-wizard audit attribution` after applying fixes to refresh the le -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit-autocapture/references/4-report.md b/context/skills/audit-autocapture/references/4-report.md index 68a1dec7..d627e862 100644 --- a/context/skills/audit-autocapture/references/4-report.md +++ b/context/skills/audit-autocapture/references/4-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 4 — Generate the audit report +# Step 4 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing autocapture audit report +[STATUS] Publishing autocapture audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-autocapture-report.md` at the project root with the structure shown below. After the report is written, delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -115,8 +115,10 @@ Re-run `posthog-wizard audit-autocapture` after applying fixes to refresh the le -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit-events/references/4-report.md b/context/skills/audit-events/references/4-report.md index 5afb981e..c87c744c 100644 --- a/context/skills/audit-events/references/4-report.md +++ b/context/skills/audit-events/references/4-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 4 — Generate the audit report +# Step 4 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing event capture audit report +[STATUS] Publishing event capture audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-events-report.md` at the project root with the structure shown below. After the report is written, delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -115,8 +115,10 @@ Re-run `posthog-wizard audit-events` after applying fixes to refresh the ledger. -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit-feature-flags/references/4-report.md b/context/skills/audit-feature-flags/references/4-report.md index b1ad9122..7e67b95d 100644 --- a/context/skills/audit-feature-flags/references/4-report.md +++ b/context/skills/audit-feature-flags/references/4-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 4 — Generate the audit report +# Step 4 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing feature flag audit report +[STATUS] Publishing feature flag audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-feature-flags-report.md` at the project root with the structure shown below. After the report is written, delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -118,8 +118,10 @@ Re-run `posthog-wizard audit-feature-flags` after applying fixes to refresh the -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit-identify/references/6-report.md b/context/skills/audit-identify/references/6-report.md index 0449fb24..90d6a024 100644 --- a/context/skills/audit-identify/references/6-report.md +++ b/context/skills/audit-identify/references/6-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 6 — Generate the audit report +# Step 6 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing identify audit report +[STATUS] Publishing identify audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-identify-report.md` at the project root with the structure shown below. After the report is written, delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -136,8 +136,10 @@ Re-run `posthog-wizard audit-identify` after applying fixes to refresh the ledge -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit-session-replay/references/4-report.md b/context/skills/audit-session-replay/references/4-report.md index c63802c0..82bad3b2 100644 --- a/context/skills/audit-session-replay/references/4-report.md +++ b/context/skills/audit-session-replay/references/4-report.md @@ -2,7 +2,7 @@ next_step: null --- -# Step 4 — Generate the audit report +# Step 4 — Compose and publish the audit report The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded for this skill ends up in the report, even passes; nothing is invented. @@ -11,14 +11,14 @@ The audit report is rendered **directly from `.posthog-audit-checks.json`** — Emit: ``` -[STATUS] Writing session replay audit report +[STATUS] Publishing session replay audit report ``` ## Action `Read` the ledger once, then transform every entry into the report below. Use `area`, `label`, `status`, `file`, and `details` from each entry verbatim where the report calls for them. -`Write` `posthog-audit-session-replay-report.md` at the project root with the structure shown below. After the report is written, delete `.posthog-audit-checks.json`. +Compose the report as markdown with the structure shown below and publish it in **one** `publish_handoff` call — that call is how the report reaches the user, and the tool creates the shareable PostHog notebook copy and surfaces its URL for you. Do not write a report file. After the call goes through, delete `.posthog-audit-checks.json`. The report has four sections in this order: @@ -117,8 +117,10 @@ Re-run `posthog-wizard audit-session-replay` after applying fixes to refresh the -After the report is written, emit a final line so the wizard can surface the path to the user: +Compose the whole report in one model turn — starting with the `#` H1 heading — then call `publish_handoff` once, passing the full markdown as `content`: ``` -Created audit report: +publish_handoff({ "content": "" }) ``` + +Do not fall back to writing a file. That one call is the whole handoff: the tool stores the report on the wizard session, mirrors it into a PostHog notebook, and surfaces the notebook URL in the wizard outro. diff --git a/context/skills/audit/description.md b/context/skills/audit/description.md index 926b1c4c..1fb0fb6e 100644 --- a/context/skills/audit/description.md +++ b/context/skills/audit/description.md @@ -1,18 +1,18 @@ # PostHog Audit -This skill audits an existing PostHog integration for **data integrity** in event capture and identification. **Read-only** — the only file you create is the final audit report. +This skill audits an existing PostHog integration for **data integrity** in event capture and identification. **Read-only** — it creates no files; the audit report is published to the wizard session instead. Perform the checks described in the referenced skills and only the events referenced in the skills. ## Workflow -The audit runs as a 5-step chain: Installation (SDK + version) → init correctness → identification → event capture → report (which also uploads the report to a PostHog notebook). Each step file ends with a pointer to the next. Follow them in the order they are written. You must resolve them in order before any source-tree exploration. +The audit runs as a 5-step chain: Installation (SDK + version) → init correctness → identification → event capture → report (which publishes the report with a single `publish_handoff` call). Each step file ends with a pointer to the next. Follow them in the order they are written. You must resolve them in order before any source-tree exploration. -The audit ledger is already seeded with the 11 pending checks (10 correctness checks plus `upload-notebook`, which the report step resolves after mirroring the markdown into a PostHog notebook). Use `mcp__wizard-tools__audit_resolve_checks` to patch each one as you finish it. +The audit ledger is already seeded with the 12 pending checks (10 correctness checks plus `write-report` and `upload-notebook`, both of which the report step resolves after publishing the report). Use `mcp__wizard-tools__audit_resolve_checks` to patch each one as you finish it. **Start by reading the path relative to this file at `references/1-version.md`.** Do not Glob, ls, or find the skill directory. Do not preload future steps. Do not re-read a step file once you've moved past it. Do not re-read SKILL.md. -`ToolSearch` is only for loading a tool by exact name when the SDK has it deferred (e.g. `select:Grep`). Do **not** use it to browse for other tools — every tool the audit needs (`Glob`, `Grep`, `Read`, `Write`, `Bash`, and the named `mcp__wizard-tools__audit_*` tools) is already named in this skill. +`ToolSearch` is only for loading a tool by exact name when the SDK has it deferred (e.g. `select:Grep`). Do **not** use it to browse for other tools — every tool the audit needs (`Glob`, `Grep`, `Read`, `Bash`, `publish_handoff`, and the named `mcp__wizard-tools__audit_*` tools) is already named in this skill. **Do not call `TaskCreate` / `TaskUpdate` / `TaskGet` / `TaskList`.** The audit doesn't track its own task list — progress comes from the audit ledger plus `[STATUS]` lines. @@ -43,7 +43,7 @@ All audit ledger calls are atomic and serialize internally — **concurrent call - `file` — optional `path:line` for findings tied to a location. - `details` — optional one-line explanation. -After the report is written (Step 5), delete `.posthog-audit-checks.json`. +After the report is published (Step 5), delete `.posthog-audit-checks.json`. ## Severity levels @@ -53,7 +53,7 @@ After the report is written (Step 5), delete `.posthog-audit-checks.json`. ## Key principles -- **Read-only**: Do not edit project source files. The only file you create is the audit report. +- **Read-only**: Do not edit project source files. The audit writes no report file — it publishes the report with `publish_handoff`. - **Evidence-based**: Reference specific `file:line` for every non-pass finding. - **Actionable**: Every finding states what to fix and how. diff --git a/context/skills/audit/references/5-report.md b/context/skills/audit/references/5-report.md index a829de6f..d9a4a0fa 100644 --- a/context/skills/audit/references/5-report.md +++ b/context/skills/audit/references/5-report.md @@ -2,69 +2,54 @@ next_step: null --- -# Step 5 — Generate the audit report (and upload it to a notebook) +# Step 5 — Compose and publish the audit report -The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded ends up in the report, even passes; nothing is invented. After the markdown is written to disk, this step also writes the report into a PostHog notebook so it's shareable from inside PostHog. +The audit report is rendered **directly from `.posthog-audit-checks.json`** — that file is the source of truth. Every check the wizard seeded ends up in the report, even passes; nothing is invented. The finished markdown goes out in a single `publish_handoff` call; that call is how the report reaches the user, and the tool creates the shareable PostHog notebook from it. Nothing is left in the project. ## Status Emit, in order: ``` -[STATUS] Writing audit report -[STATUS] Building notebook payload -[STATUS] Uploading report to notebook +[STATUS] Composing audit report +[STATUS] Publishing audit report ``` -## MCP tools - -{{> mcp-tool-calling}} - -| MCP tool | When | Use | -|----------|------|-----| -| `notebooks-create` | (a) of "Upload to a PostHog notebook" | Create the notebook with a small placeholder skeleton (title + section headings + placeholder paragraphs). One call. | -| `notebook-edit` | (b) of "Upload to a PostHog notebook" | Replace one placeholder paragraph in the cloud notebook with a real ProseMirror node. **Called many times** (one per placeholder). Required because the model can't emit the full assembled tree in a single `notebooks-create` tool_use input — it self-truncates. | -| `notebooks-retrieve` | (c) of "Upload to a PostHog notebook" | Read the cloud notebook back to verify every placeholder has been replaced. | - -Run `info ` on each of these before its first `call`, right before the upload sub-step. `mcp__wizard-tools__audit_resolve_checks` is already loaded — you'll use it again after the upload. - -If `info notebook-edit` returns a not-found error, the project's `notebooks-collaboration` feature flag isn't enabled. Skip the notebook-upload sub-step entirely; emit `Notebook upload skipped: notebook-edit unavailable. The local report at posthog-audit-report.md is still the source of truth.` and resolve `upload-notebook` to `suggestion` with that reason. - ## Action -`Read` the ledger once, then build the report **incrementally** — `Write` a skeleton with placeholder markers, then `Edit` each placeholder with its real section in a separate turn. **Do not compose the whole report in one turn.** A single sustained generation of the full document routinely drops the LLM streaming connection around the 10-minute mark; chunking via Write + Edit keeps every turn short and resets the SSE timer at each tool call. The on-disk file is the source of truth, so a dropped turn loses at most one section, not the whole report. +`Read` the ledger once, then build the report **incrementally into scratch state** — `Write` a skeleton with placeholder markers to `.posthog-wizard-cache/audit-report.md`, then `Edit` each placeholder with its real section in a separate turn. **Do not compose the whole report in one turn.** A single sustained generation of the full document routinely drops the LLM streaming connection around the 10-minute mark; chunking via Write + Edit keeps every turn short and resets the SSE timer at each tool call. A dropped turn then loses at most one section instead of the whole report. -**Do not delete `.posthog-audit-checks.json` yet** — the notebook-upload sub-step still resolves a ledger row. The cleanup happens at the very end of this step. +That path is deliberate: `.posthog-wizard-cache/` is the wizard's scratch directory, not something the user reads, and you delete the file once the report is published. Do not write a report to the project root, and do not call `notebooks-create` — `publish_handoff` creates the notebook. + +**Do not delete `.posthog-audit-checks.json` yet** — the publish sub-step still resolves two ledger rows. The cleanup happens at the very end of this step. The report has four sections in this order: 1. **Summary** — one-paragraph overview, severity counts, and a problematic-items table. 2. **Recommended actions** — prioritized fixes with `file:line` and a docs link per item. 3. **Full audit** — every check the wizard ran, grouped by `area`, including passes. -4. **About this audit** — a short closing block explaining what the audit covered and how to interpret the report. *Static text — already baked into the skeleton.* +4. **About this audit** — a short closing block explaining what the audit covered and how to interpret the report. *Static text — use the canonical copy below verbatim.* For the Full audit section, group rows dynamically by each distinct `area` value in the ledger, preserving first-seen area order from the JSON. Today the core audit produces three areas — **Installation**, **Identification**, **Event Capture** — but the report must not hard-code that list; render whatever areas appear. For each area, write a one-paragraph framing immediately under the area heading, then the table. Use the canonical copy below verbatim when the area name matches; otherwise write a one-sentence summary derived from the area's check labels. -### a. Write the skeleton - -One `Write` to `posthog-audit-report.md` with section headings and HTML-comment placeholders for the body of each non-static section. The About-this-audit text is identical every run, so it's baked in directly. +## Report shape ```markdown # PostHog Audit Report ## Summary - + ## Recommended actions - + ## Full audit - + ## About this audit @@ -77,37 +62,8 @@ The PostHog wizard runs a five-stage chain: SDK installation → init correctnes Re-run `posthog-wizard audit` after applying fixes to refresh the ledger. ``` -This Write should be small — just the structure above. Don't compose section bodies yet. - -### b. Fill the Summary section - -One `Edit`: - -- `old_string`: `` -- `new_string`: the Summary body — one-paragraph overview, then the counts list, then the problematic-items table (or the "no issues" line). See the Summary template below for the exact shape. - -Output for this turn is bounded by the Summary content alone (~500 tokens for most projects). - -### c. Fill the Recommended actions section - -One `Edit`: - -- `old_string`: `` -- `new_string`: the numbered list of actions in the format below, or `_Nothing to fix._` if there are none. - -### d. Fill the Full audit section - -One `Edit`: - -- `old_string`: `` -- `new_string`: the per-area headings + paragraphs + tables + per-area `#### Assumptions and blind spots` subsection, in ledger order. The blind-spots subsection lives directly under each area's table, following the per-area body template below. - -If the Full audit section is large (many areas, many checks), you may split it across multiple Edits by including per-area placeholders in the original skeleton and filling each with one Edit. Most audits fit in one Edit. - ## Section body templates -Use these shapes when computing the `new_string` for each Edit above. - ### Summary body ```markdown @@ -163,216 +119,41 @@ For each `area` from the ledger, in first-seen order: [Per the investigation standards in `posthog-best-practices/references/investigation-standards.md`, standard 3. ≤4 sentences answering: which code paths were not checked, which runtime assumptions are unproven by static code, what alternative explanations exist for the patterns found, and what to verify in the live PostHog project to confirm the most important findings. When the area produced only `pass` rows, write `_No findings to qualify; the standard checks for this area passed cleanly._` instead.] ``` -After the report is written, emit a line so the wizard can surface the path to the user: - -``` -Created audit report: -``` - -### Resolve `write-report` - -Flip the `write-report` row to `pass` now that the markdown file exists on disk. The notebook-upload sub-step that follows can take a while (large ProseMirror payload), and resolving this row first lets the wizard sidebar advance to "Upload notebook" so the user can see what's happening. - -```json -{ - "updates": [ - { "id": "write-report", "status": "pass" } - ] -} -``` - -## Upload to a PostHog notebook - -The markdown report on disk is the source of truth. The notebook is a shareable, in-PostHog mirror so the reader can comment, link to it from insights, and discuss it without leaving the product. - -### Why two MCP tools instead of one - -Earlier versions of this skill called `notebooks-create` once with the full assembled ProseMirror tree as the `content` argument. The assistant turn that emits that tool_use has to *generate the tree as output tokens*, even if it's just copying from a file it just read. For a 12-check audit with tables and bullet lists the full tree is several thousand tokens — past the per-turn output budget for some runs. The model self-truncates and the notebook ships with sections missing. +## Publish the report -The fix is to **build the cloud notebook incrementally**. `notebooks-create` carries only a small skeleton (title + section headings + placeholder paragraphs). Then `notebook-edit` replaces one placeholder paragraph at a time with the real ProseMirror node. Each `notebook-edit` tool_use input is bounded — never more than one block-level node — so it always fits in one turn. The notebook is complete only after the last edit lands. +Once every placeholder in `.posthog-wizard-cache/audit-report.md` has been replaced, `Read` the finished file and call `publish_handoff` once with its exact contents as `content`: -There's no local notebook payload scratch file in this design. Section content is computed on demand from the ledger and the on-disk report. - -### Orientation: re-read the report - -`Read` `posthog-audit-report.md` once. You'll use it as a reference for what content to send in each edit. Don't translate the whole thing up front — translate per placeholder, as you fill each one. - -### Node mapping (apply per placeholder as you `notebook-edit`) - -| Markdown | ProseMirror node | -|---|---| -| `# / ## / ### heading` | `{"type":"heading","attrs":{"level":},"content":[{"type":"text","text":""}]}` | -| paragraph | `{"type":"paragraph","content":[{"type":"text","text":"<...>"}]}` | -| bulleted list | `{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":""}]}]}, ...]}` | -| numbered list | `{"type":"orderedList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":""}]}]}, ...]}` | -| inline `code` | text node with a `code` mark: `{"type":"text","marks":[{"type":"code"}],"text":""}` | -| `**bold**` | text node with a `bold` mark | -| `[label](url)` | text node with a `link` mark: `{"type":"text","marks":[{"type":"link","attrs":{"href":""}}],"text":"