skills: replace edit-services with an update-service front door#240
Open
gnguralnick wants to merge 9 commits into
Open
skills: replace edit-services with an update-service front door#240gnguralnick wants to merge 9 commits into
gnguralnick wants to merge 9 commits into
Conversation
added 4 commits
July 1, 2026 15:30
Changing an existing service had no clear entry point. edit-services only covered the supervisord [program:*] plumbing and was usually skipped when an agent just edited service code, so nothing reliably prompted refreshing the user's tab or the turn-end hardening. update-service is a front door that triggers on editing/fixing/restyling/ extending an existing service -- web service or background daemon. It owns the live change loop (apply the change so it takes effect, refresh the user's tab for web services, verify) and the turn-end handoff to update-artifact / heal-artifact. Larger-scope changes route back through the same interactive-delivery (mock-confirm) flow used to build the service, so heavy work never happens against an unconfirmed shape. The supervisord program mechanics move into update-service/references/service-processes.md. Cross-links in build-web-service, libs/bootstrap/README.md, and CLAUDE.md now point at update-service.
telegram-bot runs in a tmux window in this deployment, not as a supervisord [program:*], so it can't be `supervisorctl restart`ed -- a misleading example for a skill whose daemon path is built on supervisorctl. Swap it for runtime-backup and cloudflared, which are genuine supervisord programs.
…to gabriel/knowing-to-refresh
The recurring failure mode when editing a service is verifying a change by writing test data into the live store and then deleting it as cleanup, with a too-broad delete taking real records with it. - Scaffold now generates a DATA_DIR constant that defaults to runtime/<name>/ but honors a per-service <PACKAGE_UPPER>_DATA_DIR env var, so a service's data location can be redirected without code edits. - build-web-service documents routing all persistent state through DATA_DIR and why it keeps later edits safe. - update-service adds a 'protect the user's data' section: verify read-only or against a disposable copy pointed at by the override, never delete from the live store to clean up, snapshot before any in-place change, and leave the data dir in place on teardown.
gnguralnick
commented
Jul 9, 2026
| nothing to refresh; the change takes effect when the process restarts. | ||
|
|
||
| **This skill is the front door for editing a service's code.** You do not | ||
| need to have loaded it to start typing an edit -- but the moment you are |
Contributor
Author
There was a problem hiding this comment.
if an agent is reading this text it has already loaded the skill - only the description is seen without loading it
gnguralnick
commented
Jul 9, 2026
| web tab keeps showing the old page until it's refreshed. | ||
|
|
||
| If you are doing something other than editing an existing service, you are | ||
| in the wrong skill: |
Contributor
Author
There was a problem hiding this comment.
we're probably hammering this point too much - don't need it both here and in the description. in general we can probably make this whole skill a bit more concise
added 5 commits
July 9, 2026 11:38
Extract the "boot a throwaway instance of a service on a spare port" motion -- previously hand-rolled in the update-service data-isolation prose and baked into the system-interface preview -- into one shared, unopinionated script, .agents/shared/scripts/serve_isolated_instance.py (up/down). It takes the launch command, cwd, and env overrides as parameters, picks a free port, injects it, waits for health, and either returns the loopback URL (for the agent's own data-safe testing) or, given the preview flags, registers it and wraps it in the labeled "preview" frame as a tab. The frame wrapper (preview_wrapper_server.py) moves into shared alongside it. update-system-interface's preview/unpreview become thin adapters that delegate boot/teardown to the shared script, passing the system-interface specifics (neuter layout persistence, probe /api/agents, register inner app + wrapper). The merge and reveal/auto-rollback machinery -- the part whose failure strands the user with no UI -- stays owned by reveal_system_interface.py and was intentionally not generalized. Scaffolded Flask services now read their listen port from a <PKG>_PORT env override (mirroring <PKG>_DATA_DIR), so a throwaway can bind a spare port beside the live one. update-service documents the shared-script invocation for data-safe testing and the optional user-facing preview, and tells agents to retrofit both overrides onto older services.
Two chats editing the same service produced four collision shapes: live edits interleaving in the shared working tree, duplicate harden workers (name/branch/runtime dir all derive from the service name), a hardened branch merging over a moved base, and merges landing mid-edit. Serialize the foreground with an advisory per-service editing lease (a cross-agent tk ticket taken by update-service before touching code, released each turn end; another agent's lease is surfaced to the user, never silently broken). Make harden passes single-flight per artifact with coalescing, specified in the new shared harden-contention.md reference: a lead finding a live pass leaves a note instead of dispatching a sibling; before merging, the lead waits out the foreground lease and runs a merge-base freshness check over the artifact's footprint; stale or conflicted passes are never hand-resolved but discarded and superseded by one pass covering everything since the last hardened merge. The foreground always wins: a live edit never waits on a pass, it just makes the pass stale.
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.
What
Editing an existing service had no clear entry point in the skill set. The old
edit-servicesskill only documented the supervisord[program:*]plumbing, so an agent that just edited a service's backend or frontend logic never had a reason to load it -- and nothing reliably prompted the two things that leave the user looking at stale state: reloading the process so the change takes effect, and refreshing the open web tab so they can see it.This replaces
edit-serviceswithupdate-service, a front-door skill whose description triggers on editing/fixing/restyling/extending an existing service -- web service or background daemon -- and, on top of that, encodes how to change a service without ever risking the user's data.The new skill
use_reloader=False; nothing for frontend-only;reread/updatefor definition changes), refresh the user's tab for web services (layout.py refresh <name>), then verify.update-artifact/heal-artifacthardening core, mirroring howupdate-system-interfacewraps that core for the workspace UI.The supervisord program mechanics move into
update-service/references/service-processes.md, cited only for definition-level changes. Cross-links inbuild-web-service(SKILL +cleanup.md),libs/bootstrap/README.md, andCLAUDE.mdnow point atupdate-service.Protecting user data on service edits
A recurring, expensive failure mode when editing a service is not the code edit: it's verifying the change by writing test data into the live store and then "cleaning up" with a delete/reset whose predicate is too broad and takes real user records with it. The skill now encodes data isolation as the safeguard -- deliberately lighter than deferring every edit to an isolated worktree, which isolates the wrong thing (code) and doesn't even protect the data, since the running service still reads and writes the same
runtime/<name>/.runner.pynow defines aDATA_DIRconstant that defaults toruntime/<name>/but honors a per-service<PACKAGE_UPPER>_DATA_DIRenv var. Routing all persistent state through it lets an editing agent run a throwaway instance against a copy of the data with a single env var, never touching the live store. It's a per-service variable (not a sharedSERVICE_DATA_DIR) so it can't leak into supervisord's inherited environment and silently repoint every service at once.build-web-servicedocuments theDATA_DIRconvention where services are created, and why a hardcodedruntime/<name>/at a call site silently bypasses the override and re-exposes the live data.update-servicegains a "Protect the user's data while you verify" section, ordered cheapest-first: read-only verification needs no ceremony (reading, including to render a preview, is safe -- only writes are dangerous); if verification must write, run against a copy outsideruntime/via the override and delete the copy; never delete from the live store to clean up (leave a stray test record rather than run a broad delete); snapshot before any genuine in-place change to the real store, strictly as a recovery net and never as a routine wipe-and-restore (overwriting a running store tears its state and loses any real writes that land during the test window); retrofit older hardcoded services when you touch them; and teardown leaves the data dir in place unless the user explicitly asks to remove it.Scope
Skills plus one scaffold script -- no runtime application code changed. The scaffolder change was verified by generating services and confirming the output lints clean (
ruff check/ruff format), imports correctly, and thatDATA_DIRredirects to the env-var path while defaulting toruntime/<name>/. The skill validator passes on the new skill, and the code-guardian diff/verify and conversation gates ran clean on the front-door replacement.Notes
runtime/*store. A PreToolUse hook that blocks or confirms such ops would make it a hard backstop if the guidance alone proves insufficient. Existing services are retrofitted lazily (when next edited) rather than in a big-bang migration; until then they fall back to read-only verification plus the snapshot net.