Prompt templates: reusable briefs for every loop type - #231
Merged
Conversation
Implements PROMPT_TEMPLATES.md (New Designs v4): a Templates button and ⌘T picker in the New loop dialog, an applied state that never gains a shape silently, markdown template files in ~/.graphcode/templates (read also from <project>/.graphcode/templates), Save as template, and follow-vs-snapshot — timed and composite loops re-read their template on the next run; main, goal and turn loops snapshot at creation. Includes the fixes from the design review of the first draft: Data loss and correctness - `TemplateStorage.move` wrote the copy and then deleted "the original" at the same path when a project relocation fell back to home, destroying the only copy. The move that isn't a move now stops before writing. - Saving no longer overwrites a different template that slugged onto the same filename; the second takes the next free name. Same guard on `move`. - A picker row click applied whatever the keyboard was on rather than the row clicked. - "Save as Template…" from a card's context menu set state that nothing presented — the only sheet host lived inside the New loop dialog. Both the canvas and the overview now host it, with a quiet notice bar for the path. - A following composite re-identified its whole sub-graph on every pilot, so the previous pass's child sessions were orphaned (node ids are zmx session names) and their memory stranded. It now compares what a human authored and only replaces when the template actually changed, tearing down the outgoing children when it does. Fidelity to the spec - Asking whether a project can take a template no longer creates `.graphcode/templates` in the checkout; nothing lands there by default. - The use count counts uses, not saves. - A resolve that flips a follow to `missing` now broadcasts, so the warning reaches the card; an unchanged resolve does not, so the timer-driven sweeps don't persist the graph every tick. - Templates carry an agent only when they name one, so "absent means the app's own default stands" is reachable and a shared template doesn't pin an agent a teammate may not have. - The relocation notice reports where the file actually landed. - A sheet rename reaches the filename (`PromptTemplate.renamed(to:)`). - The directory watch re-arms, so a `git pull` that creates `.graphcode/templates` is picked up rather than never seen. - An unwritable project is dropped from the save sheet rather than ineffectually `.disabled` on a Picker tag. - Tokens left in a done check, metric or branch block Start too; the save notice can be dismissed; ✕ restores the pre-template draft after two applies. Also: the repo-wide `cyclomatic_complexity` relaxation is dropped — the reducer is split into picker/apply/save switches instead, which is what the complexity was actually reporting. 17 new tests (1345 total, up from 1328), covering every fix above. make check: swiftlint 0 errors, swift-format clean on all touched files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af
…copy
Checked the implementation against `GraphCode New Loop v4.dc.html` (and
README.md § 3 / MAIN_LOOP.md) rather than PROMPT_TEMPLATES.md alone. The
markdown is terse where the canvas is exact, and three things were off.
- **`⇥` now walks the unfilled tokens.** The spec says tokens "are ⇥-navigable"
and the canvas prints "One token left to fill · ⇥ to jump to it"; nothing
implemented it, so the promise was going to be a lie. Tab cycles the fields
still holding a `{token}` in form order — brief, done check, metric, branch —
and is swallowed only while one is unfilled, so it stays the ordinary focus
walk everywhere else. The focus request is per-field and consumed by whichever
field answers it.
- **The applied strip says what the canvas says.** One shared verb ("sets a done
check and a worktree", not "sets … and sets …"), and the sentence finishes
with the design's own "Undo the shape to keep just the prompt."
- **`Detach` sits beside the Follows chip on the card**, which is where the
canvas puts it — the fact and the way out of it belong together. It stays in
the context menu too.
README § 3 confirms the Goal form's labels are "What does done look like?" and
"Done check"; the canvas's "Prompt" / "Done when" in the applied artboard is the
designer's shorthand, not a field rename, so those are left alone.
1348 tests (up from 1345). swiftlint 0 errors, swift-format clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af
…d-bearing Closes the last gap from the v4 design canvas. The ⌘T picker's "Manage…" footer pointed at a Settings list that could only reveal and delete; a template's own text could not be changed anywhere in the app. - `TemplateEditorView` — name, shape, brief, and the shape's own settings (done check + metric, cadence, pause scope) plus the branch. Saving goes through the new `TemplateStorage.update(_:replacing:)`, which **keeps the id**: that is what makes every timed and composite loop following the template go on following it across an edit. A rename moves the file, and removes the old one only once the new one is written and only when it is genuinely a different file — the trap `move` had. - `TemplateUsage` — the number behind the design's sentence, "3 scheduled loops use this — they'll pick up changes on their next run", counted across every known project and at any depth inside composites. It sits directly above Save, because the reach of an edit has to be read on the way to the button. The other half is stated too: loops that snapshotted keep their brief, so "3 loops use this" is never read as "and the other four change too". A project template adds the line about a committed edit reaching a teammate's machine. - The Settings list now shows **project templates as well as home's**, marked "in project". A list that hid them made "project templates are read too" invisible in the one place templates are managed. Project paths and graphs come from `ProjectPersistence` — Settings has no store, and wiring the whole app's state in to count integers would be the larger change. - Deleting a followed template now says what actually happens: the loops keep running on the brief they last read and warn on their cards. Deleting is not a way to stop a nightly run, and the dialog no longer lets anyone assume it is. `TemplatesSettingsSection` moved to its own file with the editor; `SettingsView.swift` is back under the file-length guideline. 1356 tests (up from 1348). swiftlint 0 errors, swift-format clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af
# Conflicts: # GraphcodeKit/Sources/Domain/LoopNode.swift # GraphcodeKit/Sources/GraphStore.swift # GraphcodeKit/Sources/ProjectRegistry.swift # graphcode/Sources/Features/Project/ProjectCanvasCards.swift
`swift build` compiles GraphcodeKit for CI on Linux, where `DispatchSource.makeFileSystemObjectSource` and `O_EVTONLY` do not exist. The watch is a UI affordance — it keeps the ⌘T picker's library live — and the only thing that reads templates on Linux is the daemon's by-id resolve, which re-reads the directory on every call. So the watcher is Darwin-only and the Linux stream finishes immediately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af
The Linux CI job builds GraphcodeKit through SwiftPM, which does not enable
bare-slash-regex; Xcode does, by default, which is why this compiled locally
and failed in CI. `{token}` is now an NSRegularExpression — the same type the
file already uses to recover token values — behind a `tokenRanges(in:)` helper
both call sites share.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af
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.
Implements
PROMPT_TEMPLATES.md(New Designs v4), checked againstGraphCode New Loop v4.dc.html,README.md§3 andMAIN_LOOP.md, and folds in the fixes from a design review of the first draft.What it adds
Undo the shapeand ✕.⇥walks the unfilled tokens across brief, done check, metric and branch — the canvas prints "One token left to fill · ⇥ to jump to it", so the key had to actually do it.~/.graphcode/templatesis where the app writes;<project>/.graphcode/templatesis also read and wins on a filename collision. Sharing is a git action — no sync layer, no.gitignoretouching.{token}positions come back as tokens.Detachsits beside the Follows chip on the card and in its context menu.Review fixes included
Data loss / correctness
movewrote the copy then deleted "the original" at the same path when a project relocation fell back to home — destroying the only copymoveFidelity to the spec
.graphcode/templatesin the checkout — nothing lands there by default.missingbroadcasts, so the warning reaches the card; an unchanged resolve doesn't, so the timer-driven sweeps don't persist the graph every tick.git pullthat creates.graphcode/templatesis picked up..disabledon aPickertag.The repo-wide
cyclomatic_complexityrelaxation the first draft added is dropped — the reducer is split into picker / apply / save switches instead, which is what the complexity was actually reporting.The usage line
§ Follow vs snapshot calls this load-bearing rather than decorative, and it is: a committed edit to a project template changes what runs on a teammate's machine.
TemplateUsagecounts followers across every known project and at any depth inside composites, and the editor puts the line directly above Save. The other half is stated too — loops that snapshotted keep their brief — so "3 loops use this" is never read as "and the other four change too". Deleting a followed template says what actually happens rather than letting someone assume it stops a nightly run.Note on the mock's field labels
The canvas's applied-state artboard labels the Goal fields "Prompt" and "Done when".
README.md§3 specifies "What does done look like?" and "Done check", andPROMPT_TEMPLATES.mdsays the fields are "exactly as specced in README.md § 3" — so those are read as designer shorthand, not a rename, and the labels are unchanged. Worth a second opinion.Verification
xcodebuild -workspace graphcode.xcworkspace -scheme graphcode build— succeedstest— 1356 tests in 147 suites pass (up from 1328/144 on the first draft; 28 new)swiftlint lint— 0 errors, with the original.swiftlint.ymlswift format lint --strict— clean on every file this branch touchesNot verified visually — this environment has no GUI automation, so the SwiftUI layout of the picker, the applied strip and the editor wants a human eye before merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MJR8Wuwc4qnaSBUMcYf7Af