[Work in progress]: Palette as a skill - #2
Draft
anupamamurthi wants to merge 25 commits into
Draft
Conversation
…ract Three fixes found by installing the skill into Claude Code for the first time and driving a deck through it end to end. install() left the checkout rendered for whichever host it installed. A `--host claude-code` install therefore made `make skill-check` fail for no visible reason, and committing the result would have shipped Bash-flavoured instructions to every CUGA user. check() already snapshotted payload/ for exactly this reason; install did not. Same fix, plus tests that the checkout is restored and the installed copy still carries its own host. run_deck's `next` and `dir` were attached to the stage transitions -- each seen once -- and missed on the two polling returns, which are the long middle of a build and precisely where an agent drifts into narrating instead of calling again. Every non-terminal result now carries both. Adds docs/skill-handbook.html: build, release, update, consume and verify on one page, with the failure modes that shaped the design. It links to GitHub by branch, so tests assert every link resolves to a file in this repo and that none points at main. Verified on both hosts: CUGA via the demo preset, Claude Code by installing to ~/.claude/skills and building a 9-slide deck from the vendored wheel -- verified: true, IBM Plex present in the OOXML. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two gaps the Claude Code run exposed. Nothing documented how to test on a host other than CUGA. TESTING.md and the cheatsheet now cover it: install from the checkout (running the installer from an installed package gives an empty vendor/), what the four tells are that the right variant landed, and the fact that installing for one host no longer disturbs another. Nothing said which Palette a deck talked to. The client resolves $PALETTE_URL then contract.DEFAULT_BASE_URL, which is http://127.0.0.1:18814 -- local. A deployed instance is the intended default and contract.py says so, but no URL is pinned there yet. Rather than asking anyone to reason about it, the docs point at the server log: the thread id from .palette-deck.json either appears there or the deck was served elsewhere. The handbook gains both, plus the install-leak bug in its failure table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs/skill-handbook.html was the fragment the Artifact publisher takes -- no doctype, no charset, no viewport, because the publisher supplies those. Served from a static host it renders in quirks mode, turns every arrow and em-dash into mojibake, and hands a phone a desktop-width page. So the fragment moves to docs/skill-handbook.body.html and stays the single source; scripts/build-handbook.py wraps it into the servable page. One copy of the prose, two outputs, and `make handbook-check` plus a test fail if they diverge -- the same contract as `make skill-check`. The pre-commit hook catches the likely mistake, which is editing the fragment and committing only that. Self-contained, so `python -m http.server -d docs` or any static host works with no build step: verified serving at 200, text/html, 49 KB. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The cheatsheet assumed a working checkout and never mentioned Node, LibreOffice, Poppler or the RITS key, so someone sent straight to it on a fresh machine would reach `serve ensure` before discovering what was missing. `serve doctor` reports it, but only if you know to run it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four docs told you to run
grep -E "draft_async|build_async" ~/.local/state/palette/server.log
That reports nothing for a deck that rendered perfectly well. app.py
installs a FileHandler per session, so build events land in
workspace/<tid>/session.log; server.log only ever gets startup and
shutdown. Demonstrated on a real deck: session.log holds both stages,
server.log holds zero mentions of the thread.
A verification step that lies is worse than none, and this one was
load-bearing in the cheatsheet, the guide, the testing ladder and the
handbook -- all of which spend paragraphs telling you to believe the files
over the agent's summary.
Corrected everywhere, with a test that fails any doc grepping server.log
for build events, and a note on why so it does not come back.
Also unbuffers the server's stdout (-u plus PYTHONUNBUFFERED). Stdout is a
file in every mode, so Python block-buffers it and the log can sit an 8 KB
buffer behind the server. That was not the cause here, but it is a real
way for the same class of confusion to happen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Palette is driven as a CLI now (build-plan / edit-plan / build-deck), so the
HTTP-era skill was the wrong abstraction end to end: a contract module, an
httpx client, per-host generated regions, a wheel to vendor, an installer and
a release pipeline -- all to reach a server the skill no longer talks to.
The skill is now what skills.sh installs look like: a folder with SKILL.md and
one script, no package, nothing generated.
skills/palette/
|- SKILL.md what the agent is told
`- scripts/deck.py starts a long build detached
deck.py exists for one reason. `build-deck` blocks for three to ten minutes and
CUGA kills a sandbox step at 120s, so a direct call is killed part-way while
the render keeps going -- the work completes and nobody collects it. `start`
returns in ~50ms; `status` polls. Claude Code's Bash allows ten minutes, so it
calls build-deck directly; the skill documents both and says which is which.
`status` reports done only when deck.pptx is on disk and over 20KB, never from
an exit code. Verified: a 5KB stub reports error, a missing build refuses
rather than guessing, and a failed build surfaces the log tail.
Removed with the old design: contract.py, client.py, build_skill.py, hosts.py,
install.py, release.py, payload/, four test modules and the doc set built
around them. service.py survives -- it supervises the web UI's server, which
still exists -- and no longer imports the deleted client. docs/skill-handbook
went too: it described the HTTP architecture in detail and would have been
confidently wrong.
tests/test_skill.py replaces all of it by reading palette.py's argparse setup
and failing if the skill names a command or flag that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A build that cannot reach the models retries every stage before failing -- measured at 51 minutes, with nothing but connection timeouts in the log. To a poller that is indistinguishable from a slow render, so the agent keeps reporting "still building" and the user waits. status already returns elapsed_seconds; the skill now says to speak up past about fifteen minutes and points at build.log, where the reason has been sitting the whole time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found by the first real run once RITS came back. The agent ran `skills/palette/palette.py` -- it lives in the checkout, not the skill folder, and the instruction asked it to hold both roots plus a cd in its head. deck.py now fronts plan/edit/start/status, resolves paths against the caller's cwd and runs palette.py from the checkout itself, so nobody has to keep them straight. SKILL.md says outright: do not run palette.py yourself. Two reported numbers were wrong. `progress` was always empty because build-deck prints only when it finishes -- its stage logging goes to a per-session file -- so the skill told the agent to relay a line it would never have; running builds now report elapsed time and a note. And `elapsed_seconds` measured wall clock since start, so polling a finished deck an hour later reported an hour, which would have tripped the fifteen-minute stall warning on a build that took nine minutes. It now measures duration. Verified end to end: plan 25s, edit 37s (applied the change), build ~8 min -> verified: true, 4 slides, IBM Plex in the OOXML. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
Goes hand in hand with - cuga-project/cuga-agent#551