Skip to content

feat(wizard-tools): publish_handoff creates the notebook, outro opens it - #1047

Draft
gewenyu99 wants to merge 10 commits into
mainfrom
posthog-code/publish-handoff-notebook
Draft

feat(wizard-tools): publish_handoff creates the notebook, outro opens it#1047
gewenyu99 wants to merge 10 commits into
mainfrom
posthog-code/publish-handoff-notebook

Conversation

@gewenyu99

Copy link
Copy Markdown
Collaborator

Stacked on #1045.

publish_handoff now owns the whole handoff: it stores the text, creates the PostHog notebook itself, and sets the URL. The agent no longer makes a notebooks-create MCP call and echoes [NOTEBOOK_URL] back — that marker and AgentSignals.NOTEBOOK_URL are gone, along with both harnesses' parsing of it.

The notebook is where the report lives now, so the outro auto-opens it and no program advertises a local report file. A file is written only when the notebook could not be created, so the report never ends up existing nowhere.

Shape

  • wizard-tools/notebook.tsPOST /api/projects/:id/notebooks/, retry/backoff/429/401 semantics copied from the task-stream destination, never throws.
  • publishHandoff is async and takes a context (credentials, installDir, fallback filename, notebook title), built by buildHandoffContext at all four harness call sites so they can't drift.
  • New setReportFile UI seam alongside setNotebookUrl, merged in InkUI.setOutroData the same way. HeadlessUI now keeps both, so cloud runs stop dropping the notebook URL.
  • useAutoOpenNotebook — fires once, via openTrackedLink (same path OAuth uses), skipped when non-TTY. URL stays printed either way.
  • Every program's successMessage / prompt stops naming a .md file; skillPrompt() now orders a publish_handoff call instead of a report file, which was the largest remaining source of stray files.

Requires context-mill

The skills still tell agents to write files and create notebooks — that's the paired context-mill PR. Both audit ledger row ids (write-report, upload-notebook) stay seeded on purpose so the currently released skills keep resolving cleanly while this ships first.

Checks

tsc --noEmit 26 errors, byte-identical to the base branch. vitest run 119 files / 1669 tests, 0 failures (+1 new). check-screens passes. Prettier and eslint clean.


Created with PostHog Code

fercgomes and others added 7 commits July 30, 2026 15:54
The wizard session API accepts a handoff_text field (the run's markdown
setup report) that the PostHog app renders as a handoff dialog. Watch
the program's report file and mirror it into the wire payload:

- file-watcher grows a text format alongside JSON
- HandoffWatcher follows the report for the whole run (rewrites
  included, since follow-up features append to it), ignoring a stale
  file from a previous run, capped at the backend's 64 KB limit
- TaskStreamPush includes handoff_text on every push once captured and
  force-reads the file during the terminal flush
- posthog-integration declares its top-level reportFile so the default
  flow gets the watcher (self-driving and the skill programs already
  declared theirs)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…handoff

One explicit tool call replaces the three agent-IO paths the handoff ran
through: the agent Write-ing a report file, the agent hand-encoding ProseMirror
for notebooks-create over MCP, and relying on the passive file watcher to
mirror the file into handoff_text.

publish_handoff takes the full report markdown, mirrors it into a shareable
PostHog notebook via direct HTTP with the session's credentials, and — when the
program opts in via ProgramRun.uploadToPostHog (default false; only
posthog-integration and self-driving set it) — sets the text on the store so
the existing task-stream push carries handoff_text. No report file is written.

The usage contract lives in the tool description (shared between the MCP server
and the pi facade so they cannot drift), and the skill prompt now points at the
tool instead of hardcoding the report shape. The task-stream machinery is
untouched; the HandoffWatcher stays as the fallback for programs still writing
report files.

Analytics: `handoff published` / `handoff publish failed` via wizardCapture.

Coordinated with PostHog/context-mill (skills updated to call the tool after
this ships).

Generated-By: PostHog Code
Task-Id: 42ae71e9-0c89-4553-97a6-53c62e784fbb
…for the handoff"

This reverts commit e55f796, restoring the branch content to e9deb07 as originally authored. The publish_handoff implementation will be redone with a different approach.

Generated-By: PostHog Code
Task-Id: 6316a1d9-7a35-4a2b-8525-566e9931bcac
… file watcher

The handoff doc now reaches the session through one explicit tool call
instead of a report file mirrored by a watcher. The task-stream upload
itself (store → handoff_text on every push) is untouched.

- New publish_handoff wizard tool: takes the full report markdown, sets
  it on the store via getUI().setHandoffText(), capped at the backend's
  64 KB limit. Registered on both facades (MCP server + pi defineTool)
  with a shared description so they cannot drift, and included in the
  pi orchestrator's per-task wizard tools so the report task can call it.
- HandoffWatcher deleted, along with its TaskStreamPush/runner wiring
  and the file-watcher text mode added for it. No report file is
  written by the integration flow anymore.
- posthog-integration outro drops reportFile; the coding-agent handoff
  prompt now points at the report notebook URL (captured via
  [NOTEBOOK_URL]) and is omitted when the run never produced one. The
  orchestrator outro no longer checks for the report on disk.

Coordinated with PostHog/context-mill, where the integration skills
keep creating the notebook and switch from writing the file to calling
publish_handoff.

Generated-By: PostHog Code
Task-Id: 6316a1d9-7a35-4a2b-8525-566e9931bcac
publishHandoff takes string, not unknown — both facades already
guarantee it via their schemas (zod / typebox), so only the blank check
remains. Comments cut to one line except where the why genuinely needs
more; the store setter drops its stale watcher reference and logs like
setNotebookUrl does.

Generated-By: PostHog Code
Task-Id: 6316a1d9-7a35-4a2b-8525-566e9931bcac
Two logToFile lines so a local run can verify the handoff from the
verbose log: the payload assembly logs phase + handoff_text size, and
the PostHog destination logs successful sends (failures already log).

Generated-By: PostHog Code
Task-Id: 6316a1d9-7a35-4a2b-8525-566e9931bcac
publish_handoff now owns the whole handoff: it stores the text, creates the
PostHog notebook itself, and sets the URL. The agent no longer makes a
notebooks-create MCP call and echoes [NOTEBOOK_URL] back at us, so that marker
and AgentSignals.NOTEBOOK_URL are gone along with both harnesses' parsing of it.

The notebook is where the report lives now, so the outro auto-opens it and no
program advertises a local report file any more. A file is written only when the
notebook could not be created — the report never ends up existing nowhere.

Generated-By: PostHog Code
Task-Id: 3ab3f6ae-5b80-45a2-849d-ac6efdff3505
@gewenyu99
gewenyu99 requested review from a team as code owners July 31, 2026 03:03
@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci revenue
  • /wizard-ci self-driving

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit

Results will be posted here when complete.

…ng a handoff we didn't deliver

Three problems from reviewing the previous commit:

Removing the `[NOTEBOOK_URL]` parsing was premature. This ships before the
context-mill side, and the released skills still upload their own notebook and
emit the marker — with the parsing gone and no report file advertised either, an
audit run showed the user neither a notebook link nor a path. The marker is read
again, documented as legacy, and can go once every released skill publishes
through the tool.

The notebook title was built from the program id, so it read "PostHog
posthog-integration (wizard) – app". It takes the program's description now, and
`buildHandoffContext` keeps the id for the fallback filename only.

publish_handoff returned ok when the notebook failed AND the fallback write
failed, so the agent was told the report was published when it had reached
nobody. That case reports failure.

Generated-By: PostHog Code
Task-Id: 3ab3f6ae-5b80-45a2-849d-ac6efdff3505
A success now carries its url in the type, so the caller stops re-checking a
field the ok flag already implies and the error string stops needing a default
that could never be hit. buildNotebookContent had no caller outside the module.

Generated-By: PostHog Code
Task-Id: 3ab3f6ae-5b80-45a2-849d-ac6efdff3505
@gewenyu99
gewenyu99 marked this pull request as draft July 31, 2026 03:34
Base automatically changed from fcgomes/upload-handoff-text to main July 31, 2026 20:16
Every publish_handoff call now reports what happened to it. `handoff called`
fires before any work, so it is the denominator and shows calls that produced no
outcome at all; `handoff rejected`, `handoff notebook failed` and `handoff
published` cover the branches, with the notebook's duration and error string.

Each event carries program_id, so notebook success rate is answerable per
program rather than only in aggregate. Sizes and truncation ride along; the
report text never does — it quotes the user's own code.

Losing the notebook is recoverable and expected (a token without
notebook:write), so it stays an event. Losing the notebook AND the fallback
write means the report reached nobody, which raises an exception.

Generated-By: PostHog Code
Task-Id: 3ab3f6ae-5b80-45a2-849d-ac6efdff3505
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants