Skip to content

feat(staged): add generate_pikchr MCP tool for note-writing sessions#841

Open
matt2e wants to merge 2 commits into
mainfrom
pikchar-grammar-corrections
Open

feat(staged): add generate_pikchr MCP tool for note-writing sessions#841
matt2e wants to merge 2 commits into
mainfrom
pikchar-grammar-corrections

Conversation

@matt2e

@matt2e matt2e commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a generate_pikchr MCP tool to the Pikchr MCP server used by note-writing sessions (project notes and local branch notes). It turns a natural-language description into validated Pikchr source so agents no longer have to hand-write Pikchr and iterate blindly.

What it does

  • generate_pikchr tool — takes a freeform description (boxes, arrows, labels, layout, relationships), an optional previous_pikchr (the current diagram's source, for revisions so intent is edited rather than redrawn), and an optional scale. Returns the validated Pikchr source plus a rendered PNG preview. If overlaps or intent can't be fully resolved, it returns the best diagram reached with a note.
  • pikchr_subsession module — a focused internal ACP sub-agent writes the diagram, renders it through the same engine as preview_pikchr, and re-prompts itself to repair parse errors and box overlaps. The loop is bounded (parse errors always block; overlaps are advisory and accepted after a couple of retries). The sub-session is not persisted — in-memory Store/MessageWriter stubs keep its transcript out of the user's session messages.
  • Provider reusesession_runner now tracks the provider id the driver actually resolved to and threads it into the MCP server, so the sub-agent runs under the same agent the user chose without re-running login-shell discovery. An empty id is tolerated: generate_pikchr fails per-call while preview_pikchr keeps working.
  • Note guidance — updated to point agents at generate_pikchr for creating/revising diagrams, with preview_pikchr reserved for checking hand-written source.

Cancellation

The sub-session's CancellationToken is owned by the parent MCP request future (held via a DropGuard). If the MCP client abandons the tool call, the future is dropped, the token is cancelled, and the provider subprocess is torn down promptly instead of running detached. A 180s wall-clock cap arms the same token, so client-cancel and timeout converge on one path — preventing orphaned provider subprocesses.

matt2e added 2 commits July 1, 2026 21:06
Add a `generate_pikchr` MCP tool alongside `preview_pikchr` that turns a
natural-language description into validated Pikchr. It runs a focused
internal ACP sub-session that emits a diagram, renders it through the
same engine as `preview_pikchr`, and re-prompts itself to fix parse
errors and box overlaps before returning the final source plus a PNG
preview. Revisions pass the existing diagram's source in via
`previous_pikchr` so the sub-agent edits real Pikchr rather than
redrawing from scratch — no id-addressable storage; the note's ```pikchr
fence remains the store.

How:
- Expose `run_preview` + `PreviewOutcome` as pub(crate) so the loop
  reuses the render/overlap path directly (no MCP round-trip).
- New `pikchr_subsession` module: in-memory Store/MessageWriter stubs
  (the sub-session transcript never touches the user's session), prompt
  templates, and a bounded `generate_pikchr_source` loop written against
  the `AgentDriver` trait. Parse errors always block; overlaps are
  advisory and accepted after a couple of retries. The store stub
  captures the agent session id so repair turns resume in-context.
- `generate_pikchr` drives the loop on a dedicated LocalSet thread
  (the ACP driver needs `spawn_local`), bounded by a wall-clock timeout
  via the sub-session's cancel token.
- Carry the parent session's provider id + AppHandle on the pikchr
  handler; `start_pikchr_mcp_server` now takes both, and `session_runner`
  threads the resolved provider through so the sub-agent matches the
  agent the user chose.
- Update `pikchr_note_guidance` to point the note agent at
  `generate_pikchr` as the primary path, keeping `preview_pikchr` for
  checks and hand-tweaks.

Unit-tested with a fake driver: bad -> overlapping -> clean converges,
persistent overlaps return best-effort, and never-rendering errors out —
all within the attempt cap, with resumption asserted.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
The generate_pikchr worker ran on a detached thread that minted its own
CancellationToken, so the parent MCP request had no way to signal it. If
the note agent dropped (or retried) the tool call, only the receiving
end of the result channel was dropped — the worker kept driving the
provider subprocess until it finished or hit the 180s wall-clock cap.
With no cap on concurrent invocations, a note agent that fired several
of these could leave multiple orphaned provider subprocesses alive for
up to three minutes each.

Move the token into the parent future and hand the worker a clone. The
parent holds a DropGuard, so when the future is dropped (the client
abandons the call) the token is cancelled and the sub-session's provider
subprocess is torn down promptly. The worker still arms this same token
on timeout, so both the client-cancel and wall-clock paths converge on
one token.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e matt2e requested review from baxen and wesbillman as code owners July 1, 2026 11:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41246c6c52

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +100 to +101
if let Some(id) = store_capture.agent_session_id() {
agent_session_id = Some(id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid requiring load_session for diagram repairs

When a first candidate needs a parse/overlap correction, this saves the ACP session id and the next loop passes it back into driver.run, which forces AcpDriver down its load_session path before the repair prompt is sent. For any selected provider that supports the parent HTTP MCP tool but advertises load_session = false, generate_pikchr will fail on the first retry instead of doing the promised repair, even though a fresh one-shot session with the failure details would still work.

Useful? React with 👍 / 👎.

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.

1 participant