froid-loop 0.11.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. froid_loop/__init__.py +11 -0
  2. froid_loop/__main__.py +12 -0
  3. froid_loop/adapters/__init__.py +3 -0
  4. froid_loop/adapters/base.py +254 -0
  5. froid_loop/adapters/entrypoints.py +63 -0
  6. froid_loop/adapters/env_fault.py +290 -0
  7. froid_loop/adapters/generic.py +2013 -0
  8. froid_loop/adapters/mock.py +49 -0
  9. froid_loop/adapters/multiplexer.py +914 -0
  10. froid_loop/adapters/opencode_http.py +1687 -0
  11. froid_loop/adapters/profile.py +650 -0
  12. froid_loop/adapters/psmux_backend.py +1428 -0
  13. froid_loop/adapters/registry.py +322 -0
  14. froid_loop/adapters/tmux_backend.py +35 -0
  15. froid_loop/adapters/tmux_base.py +630 -0
  16. froid_loop/checks.py +187 -0
  17. froid_loop/cli.py +5041 -0
  18. froid_loop/data/__init__.py +0 -0
  19. froid_loop/data/froid_loop_hook.py +228 -0
  20. froid_loop/data/froid_loop_probe_hook.py +88 -0
  21. froid_loop/data/plugins/example/plugin.toml +21 -0
  22. froid_loop/data/plugins/tea/plugin.toml +184 -0
  23. froid_loop/data/plugins/tea/tea_plugin.py +258 -0
  24. froid_loop/data/plugins/unity/plugin.toml +140 -0
  25. froid_loop/data/plugins/unity/unity_assets/FroidLoop.Unity.Editor.asmdef +16 -0
  26. froid_loop/data/plugins/unity/unity_assets/FroidLoop.Unity.Editor.asmdef.meta +7 -0
  27. froid_loop/data/plugins/unity/unity_assets/SceneAutoSaveGuard.cs +221 -0
  28. froid_loop/data/plugins/unity/unity_assets/SceneAutoSaveGuard.cs.meta +11 -0
  29. froid_loop/data/plugins/unity/unity_assets/_folders/Editor.meta +8 -0
  30. froid_loop/data/plugins/unity/unity_assets/_folders/FroidLoop.meta +8 -0
  31. froid_loop/data/plugins/unity/unity_cleanup.py +125 -0
  32. froid_loop/data/plugins/unity/unity_dialog_probe.py +239 -0
  33. froid_loop/data/plugins/unity/unity_facts.md +17 -0
  34. froid_loop/data/plugins/unity/unity_plugin.py +415 -0
  35. froid_loop/data/plugins/unity/unity_quiesce.py +234 -0
  36. froid_loop/data/plugins/unity/unity_ready.py +230 -0
  37. froid_loop/data/plugins/unity/unity_seed_assets.py +298 -0
  38. froid_loop/data/plugins/unity/unity_setup.py +551 -0
  39. froid_loop/data/plugins/unity/unity_teardown.py +362 -0
  40. froid_loop/data/profiles/antigravity.toml +52 -0
  41. froid_loop/data/profiles/claude.toml +85 -0
  42. froid_loop/data/profiles/codex.toml +22 -0
  43. froid_loop/data/profiles/copilot.toml +52 -0
  44. froid_loop/data/profiles/gemini.toml +26 -0
  45. froid_loop/data/profiles/opencode.toml +54 -0
  46. froid_loop/data/settings/core.toml +458 -0
  47. froid_loop/data/skills/README.md +93 -0
  48. froid_loop/data/skills/froid-loop-resolve/SKILL.md +288 -0
  49. froid_loop/data/skills/froid-loop-setup/SKILL.md +161 -0
  50. froid_loop/data/skills/froid-loop-setup/assets/module-help.csv +3 -0
  51. froid_loop/data/skills/froid-loop-setup/assets/module.yaml +19 -0
  52. froid_loop/data/skills/froid-loop-sweep/SKILL.md +100 -0
  53. froid_loop/data/skills/froid-loop-sweep/automation-mode.md +127 -0
  54. froid_loop/data/skills/froid-loop-sweep/deferred-work-format.md +302 -0
  55. froid_loop/data/skills/froid-loop-sweep/migration-mode.md +86 -0
  56. froid_loop/decisions.py +202 -0
  57. froid_loop/deferredwork.py +2282 -0
  58. froid_loop/devcontract.py +892 -0
  59. froid_loop/diagnostics.py +1104 -0
  60. froid_loop/documents.py +532 -0
  61. froid_loop/engine.py +7732 -0
  62. froid_loop/envvars.py +111 -0
  63. froid_loop/escalation.py +225 -0
  64. froid_loop/events.py +266 -0
  65. froid_loop/fences.py +103 -0
  66. froid_loop/froidconfig.py +226 -0
  67. froid_loop/frontmatter.py +526 -0
  68. froid_loop/gates.py +133 -0
  69. froid_loop/install.py +2936 -0
  70. froid_loop/journal.py +178 -0
  71. froid_loop/machine.py +148 -0
  72. froid_loop/model.py +898 -0
  73. froid_loop/operatoractions.py +474 -0
  74. froid_loop/platform_util.py +1490 -0
  75. froid_loop/plugins/__init__.py +64 -0
  76. froid_loop/plugins/bus.py +259 -0
  77. froid_loop/plugins/context.py +319 -0
  78. froid_loop/plugins/loader.py +145 -0
  79. froid_loop/plugins/manifest.py +279 -0
  80. froid_loop/plugins/model.py +296 -0
  81. froid_loop/plugins/registry.py +245 -0
  82. froid_loop/plugins/trust.py +75 -0
  83. froid_loop/policy.py +1569 -0
  84. froid_loop/probe.py +1044 -0
  85. froid_loop/process_host.py +408 -0
  86. froid_loop/recovery_flow.py +1561 -0
  87. froid_loop/resolve.py +283 -0
  88. froid_loop/runs.py +4715 -0
  89. froid_loop/runsetup.py +1293 -0
  90. froid_loop/sanitize.py +593 -0
  91. froid_loop/settings_schema.py +276 -0
  92. froid_loop/signals.py +160 -0
  93. froid_loop/sprintstatus.py +609 -0
  94. froid_loop/statemachine.py +57 -0
  95. froid_loop/stories.py +615 -0
  96. froid_loop/stories_engine.py +796 -0
  97. froid_loop/sweep.py +1892 -0
  98. froid_loop/tokens.py +196 -0
  99. froid_loop/tui/__init__.py +11 -0
  100. froid_loop/tui/app.py +1584 -0
  101. froid_loop/tui/data.py +840 -0
  102. froid_loop/tui/launch.py +1003 -0
  103. froid_loop/tui/screens/__init__.py +1 -0
  104. froid_loop/tui/screens/dashboard.py +1071 -0
  105. froid_loop/tui/screens/modals.py +943 -0
  106. froid_loop/tui/screens/settings_screen.py +477 -0
  107. froid_loop/tui/settings.py +135 -0
  108. froid_loop/tui/widgets.py +981 -0
  109. froid_loop/verify.py +4545 -0
  110. froid_loop/workspace.py +320 -0
  111. froid_loop/worktree_flow.py +2301 -0
  112. froid_loop-0.11.1.dist-info/METADATA +728 -0
  113. froid_loop-0.11.1.dist-info/RECORD +116 -0
  114. froid_loop-0.11.1.dist-info/WHEEL +4 -0
  115. froid_loop-0.11.1.dist-info/entry_points.txt +2 -0
  116. froid_loop-0.11.1.dist-info/licenses/LICENSE +30 -0
@@ -0,0 +1,288 @@
1
+ ---
2
+ name: froid-loop-resolve
3
+ description: 'Interactive escalation-resolution workflow for the froid-loop orchestrator. A froid-loop run paused on a CRITICAL escalation (a contradiction or gap a dev/review session could not safely resolve alone); you and the human disambiguate the frozen spec so the story can be re-driven. Invoked as /froid-loop-resolve <story-key>. Unlike the automated dev/review sessions this session is interactive — a human is present and you SHOULD ask.'
4
+ ---
5
+
6
+ # froid-loop Escalation Resolution
7
+
8
+ A `froid-loop` run drove a story through dev → review, a session raised a
9
+ **CRITICAL escalation** (work could not proceed safely — usually a contradiction
10
+ or an unanswered question in the _frozen spec_), and the orchestrator paused the
11
+ whole run for a human. The session that escalated is gone; you are a fresh
12
+ interactive session whose job is to **resolve the ambiguity with the human and
13
+ update the frozen spec**, so the orchestrator can re-arm the story and re-drive
14
+ it against a corrected spec.
15
+
16
+ This is **interactive**: a human IS present. Ask questions, present options,
17
+ recommend — but the human makes the call. (`$FROID_LOOP_MODE` is intentionally
18
+ unset for this session; the never-ask automation rules do NOT apply.)
19
+
20
+ ## Identity & I/O contract
21
+
22
+ These environment variables are set:
23
+
24
+ - `$FROID_LOOP_RUN_DIR` — the paused run's directory.
25
+ - `$FROID_LOOP_STORY_KEY` — the escalated story key (also your invocation argument).
26
+ - `$FROID_LOOP_RESOLVE_CONTEXT` — path to a `context.json` written for you.
27
+
28
+ **Read `$FROID_LOOP_RESOLVE_CONTEXT` FIRST.** Its schema:
29
+
30
+ ```json
31
+ {
32
+ "story_key": "6-4-cli-list-command",
33
+ "run_id": "20260613-111429-6a14",
34
+ "spec_file": "/abs/path/to/_froid-output/implementation-artifacts/spec-<story>.md",
35
+ "spec_reaches_the_redrive": true,
36
+ "redrive_base_ref": "<branch the re-drive reads, or HEAD>",
37
+ "baseline_commit": "<sha>",
38
+ "paused_reason": "CRITICAL escalation from review session: ...",
39
+ "escalations": [
40
+ {
41
+ "type": "<kind>",
42
+ "severity": "CRITICAL",
43
+ "detail": "<what's ambiguous/contradictory>"
44
+ }
45
+ ],
46
+ "resolution_path": "/abs/path/to/<run>/resolve/<story>/resolution.json"
47
+ }
48
+ ```
49
+
50
+ **`spec_reaches_the_redrive` says whether your edit has a future.** The re-drive
51
+ reads one tree; `spec_file` may name another. Under worktree isolation the run's mount
52
+ is discarded before the re-drive reads anything, so a spec inside that mount is
53
+ destroyed with it. When this field is `false`, every write to `spec_file` still
54
+ SUCCEEDS and is then thrown away — worse than not editing at all, because the session
55
+ looks resolved. `null` means the task has no spec on record: there is nothing to edit
56
+ and step 4 does not apply.
57
+
58
+ **`redrive_base_ref` tells you which of the two remedies applies.** Read it before you
59
+ tell the human anything: a branch name and `HEAD` mean opposite things.
60
+
61
+ Do not skip the edit when it is `false` — the corrected spec is what gets carried
62
+ over, and it is the clearest statement of what you and the human agreed. Do step 4 as
63
+ usual, then tell the human, in the same breath as the resolution, **where the
64
+ correction has to land to be read** — which the field decides: when `redrive_base_ref`
65
+ names a branch, committed on `redrive_base_ref`; when it is `HEAD`, re-applied in the
66
+ main checkout, uncommitted. The two paragraphs below carry each arm.
67
+
68
+ Be precise about this, because the two obvious moves both fail silently:
69
+
70
+ - Committing from the **main checkout** cannot include the file you edited — it lives
71
+ in a linked unit worktree, which is a separate working tree.
72
+ - Committing on the **unit's own branch** does not reach the re-drive either. The
73
+ replacement worktree is cut fresh from `redrive_base_ref`, not from the branch of
74
+ the mount that was discarded.
75
+
76
+ So the correction has to reach `redrive_base_ref` itself: make the same edit to that
77
+ tree's copy of the spec and commit it there. The orchestrator names the same ref when
78
+ it re-arms; say it here so they hear it before they close the session rather than
79
+ after.
80
+
81
+ **When `redrive_base_ref` is `HEAD`, do not tell them to commit anything.** That means
82
+ the re-drive runs in the **main checkout** and reads its WORKING TREE, so an edit there
83
+ is read as-is. `spec_reaches_the_redrive: false` beside a `HEAD` base is the opposite
84
+ problem from the one above: `spec_file` points into a worktree this run has STOPPED
85
+ using, because its isolation policy changed while the story sat escalated. The remedy
86
+ is to make the same edit to the main checkout's copy of the spec — no commit, no
87
+ branch. Telling them to commit here sends them to a tree the re-drive does not read,
88
+ which is the same lost work in the other direction.
89
+
90
+ In **stories mode** (folder+id dispatch) the context also carries a `stories`
91
+ block — the manifest intent for this story, so you can see WHAT it is meant to do
92
+ without hunting for it:
93
+
94
+ ```json
95
+ {
96
+ "stories": {
97
+ "spec_folder": "_froid-output/epic-1",
98
+ "story": {
99
+ "id": "6-4-cli-list-command",
100
+ "title": "CLI list command",
101
+ "description": "…",
102
+ "spec_checkpoint": false,
103
+ "done_checkpoint": false,
104
+ "invoke_dev_with": "…free-text planner→dev note, or ''…"
105
+ },
106
+ "sentinel": {
107
+ "kind": "unresolved",
108
+ "path": ".../stories/6-4-cli-list-command-unresolved.md",
109
+ "blocking_condition": "…the reason planning halted…"
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ The `sentinel` sub-block is present ONLY when the escalated story is a **sentinel**
116
+ (see the stories-mode section below); an ordinary escalation omits it.
117
+
118
+ Your **output marker** is the file at `resolution_path`. Writing it is the LAST
119
+ action of a successful resolution. Schema:
120
+
121
+ ```json
122
+ {
123
+ "story_key": "<key>",
124
+ "decision": "<one or two sentences: the rule you and the human chose>",
125
+ "spec_file": "<the spec you edited>",
126
+ "spec_updated": true,
127
+ "restore_patch": "<optional: path to a saved intent-gap patch to re-apply>"
128
+ }
129
+ ```
130
+
131
+ `restore_patch` is **optional** and used only for the intent-gap patch-restore
132
+ case below — omit it entirely for an ordinary resolution.
133
+
134
+ ## What you MUST do
135
+
136
+ 1. **Read the context**, then read the **frozen spec** at `spec_file` in full —
137
+ especially its `<frozen-after-approval>` block (the intent the dev/review
138
+ sessions treat as authoritative). The escalation is almost always that this
139
+ block is silent on, or contradicts, a case the implementation hit.
140
+ 2. **Present the escalation plainly** to the human: what is ambiguous or
141
+ contradictory, why it blocks safe implementation, and **2–4 concrete
142
+ resolution options** with a clear recommendation and its trade-offs. Keep it
143
+ tight — quote the relevant spec lines.
144
+ 3. **Get the human's decision.** Ask follow-ups if the choice is unclear. Do not
145
+ invent requirements; if the human is unsure, help them reason, don't guess.
146
+ 4. **Update the frozen spec** to encode the decision unambiguously: amend the
147
+ `<frozen-after-approval>` block and any affected acceptance criteria / test
148
+ matrix rows so a fresh dev session has exactly one correct reading. Make the
149
+ smallest change that removes the ambiguity. You MAY use the `froid-spec` or
150
+ `froid-correct-course` skills if a larger spec change is warranted. **If
151
+ `spec_reaches_the_redrive` is `false`, make the same edit and then say plainly
152
+ that this copy is not the one the re-drive reads, and name the remedy that
153
+ `redrive_base_ref` selects: on a branch, the correction must be
154
+ committed on `redrive_base_ref`; on `HEAD`, it must be re-applied in the main
155
+ checkout, uncommitted** — an unflagged edit here is lost work, and a commit in the
156
+ wrong tree or on the wrong branch is lost work that looks done.
157
+ 5. **Write the resolution marker** at `resolution_path` (schema above), then tell
158
+ the human the resolution is recorded and they can exit this session — the
159
+ orchestrator will offer to **re-arm the story and resume the run** (a clean
160
+ rebuild against the corrected spec).
161
+
162
+ ## Special case: a review-stage `intent gap` with a saved patch
163
+
164
+ When the escalation came from the **review step** halting on an `intent gap`, the
165
+ dev session first **saved its attempted change as a patch file** (in the
166
+ implementation-artifacts folder) before reverting the tree — the escalation
167
+ `detail` and the spec's `## Review Triage Log` reference the patch path. That
168
+ patch is concrete evidence: it shows exactly which reading of the intent the run
169
+ implemented.
170
+
171
+ **First check `restore_supported` in the context file.** When it is `false`
172
+ (worktree-isolation runs: the re-drive discards and re-mounts the unit's
173
+ worktree, so an in-place restore can never land; an escalation with no recorded
174
+ spec: a restored patch has no review to resume; a pre-planning sentinel wedge:
175
+ there is no attempted implementation to restore), **never offer the restore
176
+ option and never record `restore_patch`** — the orchestrator would reject the
177
+ resolution and this whole session's negotiation would be wasted. The patch is
178
+ still available as evidence.
179
+
180
+ Use the patch two ways:
181
+
182
+ - **As evidence.** Read the patch (and the diff it represents) to see what the
183
+ guessed reading produced — often clearer input for writing the clarification
184
+ than the questions alone.
185
+ - **When the attempted reading was actually correct.** Sometimes the run's guess
186
+ is the right one and only the _intent_ was silent. Present this as an explicit
187
+ option to the human: _"the implementation read it as X, which is in fact what we
188
+ want — amend the intent to say X, and resume **review** on the already-written
189
+ change instead of re-implementing it."_ If the human chooses this:
190
+ 1. Still **amend the intent** in the spec so it unambiguously says X (step 4
191
+ above is unchanged — the frozen intent must match the restored code).
192
+ 2. Add **`"restore_patch": "<the saved patch path>"`** to `resolution.json`
193
+ (copy the path verbatim from the escalation detail / triage log).
194
+
195
+ The orchestrator then re-arms the spec to `in-review`, re-applies the patch onto
196
+ the baseline, and re-dispatches — the session resumes at the review step on the
197
+ restored diff. **Do NOT `git apply` the patch yourself and do NOT set the spec
198
+ status** — the orchestrator does both deterministically at re-arm.
199
+
200
+ **The restore must not overlap resolution commits.** Re-arm advances the
201
+ re-drive's baseline to the branch's post-resolve HEAD, but the saved patch was
202
+ diffed from the ORIGINAL baseline — so if this session left commits that touch
203
+ the patch's own files, the restore's `git apply` fails and the story
204
+ re-escalates (loudly, by design: the orchestrator never silently merges the
205
+ resolution with the stale attempt). If the resolution work already includes or
206
+ supersedes the attempted change, **omit `restore_patch`** — the commits survive
207
+ re-arm as the re-drive's starting point, so a from-scratch re-drive builds
208
+ directly on them.
209
+
210
+ If the attempted reading was wrong (the common case), omit `restore_patch`
211
+ entirely: the orchestrator re-drives from scratch against the corrected intent.
212
+
213
+ ## What you MUST NOT do
214
+
215
+ - **Do NOT** write the orchestrator's `result.json` — that is a dev/review
216
+ artifact; this is not one of those sessions.
217
+ - **Do NOT** change `sprint-status.yaml`, and **do NOT** set the spec's `status:`
218
+ field — the orchestrator deterministically re-arms the spec status on resume.
219
+ Edit spec **content** only.
220
+ - **Do NOT** implement the story, write feature code, run tests, or commit. Your
221
+ job ends at a corrected spec + the resolution marker. That holds when
222
+ `spec_reaches_the_redrive` is `false` too: landing the corrected spec where the
223
+ re-drive reads it is the HUMAN's step — committing it on `redrive_base_ref` when that
224
+ names a branch, re-applying it in the main checkout when it is `HEAD`. Tell them it
225
+ is required, and which one; do not do it yourself.
226
+ - **Do NOT** widen scope. Resolve exactly the escalated ambiguity; if you notice
227
+ unrelated problems, note them to the human but leave them alone.
228
+
229
+ ## Stories mode: sentinels and the preserved copy
230
+
231
+ In stories mode a story that could not even be **planned** — the dev session hit
232
+ a contradiction or gap before it could write a real spec — leaves a fixed-slug
233
+ **sentinel** file instead of a frozen spec: `<id>-unresolved.md` (the intent was
234
+ too ambiguous to plan) or `<id>-ambiguous.md` (more than one story spec matched
235
+ the id). The context's `stories.sentinel` block names it and carries the
236
+ `blocking_condition` the session recorded.
237
+
238
+ A sentinel is **not a spec you edit** — there is no plan or `<frozen-after-approval>`
239
+ block inside it. So for a sentinel:
240
+
241
+ - **Do not try to amend a frozen spec** (step 4's "edit the frozen spec" does not
242
+ apply — there isn't one). Instead resolve the _upstream_ ambiguity so a fresh
243
+ planning pass can succeed: usually that means clarifying `SPEC.md` (the epic
244
+ spec) or this story's entry in `stories.yaml` — the `title` / `description` /
245
+ `invoke_dev_with` the planner reads — with the human.
246
+ - **`redrive_base_ref` decides where that upstream edit has to land, exactly as it
247
+ does for a spec.** `spec_reaches_the_redrive` does not answer this — it is about
248
+ `spec_file`, which for a sentinel is the file being deleted. The artifacts you
249
+ actually edit are `SPEC.md` / `stories.yaml`, and they face the same question: when
250
+ `redrive_base_ref` names a **branch**, the re-drive mounts a fresh worktree and
251
+ re-plans from that branch's COMMITTED tree, so an uncommitted edit is invisible and
252
+ the re-plan mints the same sentinel again — tell the human it has to be committed
253
+ there. When it is `HEAD`, the re-drive re-plans in the main checkout's working tree
254
+ and the edit is read as-is — do not tell them to commit. The orchestrator re-arms on
255
+ the same rule and will hold the resume until the branch carries it.
256
+ - On **re-arm** the orchestrator does NOT flip the sentinel to `ready-for-dev`
257
+ (there is no plan to route to). It **preserves a copy** of the sentinel under
258
+ `{run}/sentinels/<id>-<kind>.md` as a breadcrumb, **deletes** the sentinel, and
259
+ the next dispatch re-plans the story from scratch (leg 1 again for a
260
+ `spec_checkpoint` story). You do not touch the sentinel file yourself.
261
+ - Write the resolution marker as usual once the human has decided how to
262
+ disambiguate; set `spec_file` to whatever you edited (e.g. `SPEC.md`), or omit
263
+ it if the fix was entirely in `stories.yaml`.
264
+
265
+ ### Not a sentinel: more than one file matches the id
266
+
267
+ Distinct from the single-file `<id>-ambiguous.md` sentinel above: when **more
268
+ than one** file in `stories/` matches `<id>-*.md` (say `3-login.md` AND
269
+ `3-signup.md`), the id itself is ambiguous _on disk_ and the orchestrator wedges
270
+ the story without picking either file. You can recognize this state by the
271
+ escalation reason (`ambiguous story file match: <names>`) and by what the
272
+ context does NOT have: no `stories.sentinel` block and no single spec path.
273
+
274
+ The auto-clear above does **not** apply — there is no sentinel to preserve and
275
+ delete, and re-arming alone just re-wedges on the same duplicates. The
276
+ resolution IS the cleanup: with the human, decide which file is the story's real
277
+ spec and remove or rename the other (merge content first if both carry real
278
+ work; renaming must move it out of the `<id>-*` pattern or to another id).
279
+ Exactly one match re-dispatches that spec; zero matches re-plans from scratch.
280
+ Then write the resolution marker as usual — re-arm + resume takes it from there.
281
+
282
+ ## If you cannot resolve it
283
+
284
+ If the human defers, the information needed is genuinely unavailable, or the
285
+ right fix is out of scope for a spec edit (e.g. it needs a PRD/architecture
286
+ change), say so plainly and **do not** write the resolution marker. Exiting
287
+ without the marker leaves the story escalated and the run paused — the safe
288
+ default. The orchestrator will not re-arm a story with no recorded resolution.
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: 'froid-loop-setup'
3
+ description: Sets up FROID Loop Skills module in a project. Use when the user requests to 'install froid-loop module', 'configure FROID Loop Skills', or 'setup FROID Loop Skills'.
4
+ ---
5
+
6
+ # Module Setup
7
+
8
+ ## Overview
9
+
10
+ Installs, configures, **and upgrades** the froid-loop module in a project.
11
+
12
+ This module is unusual: alongside its automation skills it relies on the **froid-loop orchestrator tool** — the Python program that actually drives the loop — installed as the `froid-loop` package from its public Git repository. The skills do nothing on their own.
13
+
14
+ **The FROID installer owns `_froid/` registration; this skill does not.** When froid-loop is installed through the FROID installer (`npx froid-plane install`), the installer stages the skills under `_froid/froid-loop/`, writes that module's own config and help files, records it in `_froid/_config/` (manifest, skill manifest, files manifest), and rebuilds the `/froid-help` catalog. It regenerates the central `_froid/config.toml` on every run, so nothing outside the installer should write there.
15
+
16
+ So this skill's job is the part the installer structurally **cannot** do:
17
+
18
+ 1. Install **or upgrade** the orchestrator tool (the installer copies skill directories only — it cannot carry a Python package).
19
+ 2. Run `froid-loop init` to register the per-CLI hooks, lay down the bundled skills, and write `.froid-loop/policy.toml` + gitignore entries.
20
+ 3. Preflight with `froid-loop validate` and point the user at per-role adapter config.
21
+
22
+ It also refreshes one file — `_froid/froid-loop/module-help.csv` — so the module's help entries are present even on a project that installed the tool directly rather than through the FROID installer. Nothing else under `_froid/` is written or deleted.
23
+
24
+ Module identity (name, code, version) comes from `./assets/module.yaml`.
25
+
26
+ `{project-root}` is a **literal token** in FROID config _values_ — never substitute it there. **This does not apply to the filesystem path _arguments_ in the commands below**: those are real paths, so resolve `{project-root}` to the actual project root before running.
27
+
28
+ ## On Activation
29
+
30
+ 1. Read `./assets/module.yaml` for module metadata (the `code` field is the module identifier).
31
+ 2. Check whether `{project-root}/_froid/` exists. If it does not, this project has no FROID install — say so, and note that the automation skills expect one. Setup can still install the orchestrator tool.
32
+ 3. Check whether `{project-root}/_froid/froid-loop/` exists. If it does, the module was installed by the FROID installer and is already registered. If it does not, tell the user the module is not registered with FROID, and that installing it via `npx froid-plane install` (selecting **froid-loop**) adds it to `/froid-help`. Do **not** gate setup on this — continue either way.
33
+
34
+ **Decide fresh-install vs upgrade.** This drives whether the tool is upgraded and whether the per-project skills are refreshed. Treat it as an **upgrade** when either holds:
35
+
36
+ - The user asked for one in their arguments — `upgrade`, `update`, `upgrade tool and skills`, or similar.
37
+ - The orchestrator tool is already installed under uv: run `uv tool list` and look for a `froid-loop` entry. (A bare `froid-loop --version` is **not** sufficient on its own — it can be satisfied by a source checkout or unrelated virtualenv; see step 1 below.)
38
+
39
+ Otherwise it is a **fresh install**. State the decision to the user before proceeding — e.g. "Detected an existing froid-loop install — running an upgrade: tool + skills" or "No existing install detected — running a fresh setup".
40
+
41
+ If the user provides arguments (e.g. `accept all defaults`, `--headless`, `upgrade`), use them and skip interactive prompting. Still display the full confirmation summary at the end.
42
+
43
+ ## Register Help Entries
44
+
45
+ Refresh the module's help entries. This is the only file this skill writes under `_froid/`, and it is exactly what the FROID installer itself places there — so it is a no-op on an installer-installed project and idempotent on every re-run.
46
+
47
+ ```bash
48
+ mkdir -p "{project-root}/_froid/froid-loop"
49
+ cp ./assets/module-help.csv "{project-root}/_froid/froid-loop/module-help.csv"
50
+ ```
51
+
52
+ `/froid-help` reads the assembled catalog at `_froid/_config/froid-help.csv`, which the FROID installer rebuilds from every `_froid/<module>/module-help.csv`. So on a project that never ran the FROID installer, these entries become visible the next time it runs — not immediately. Say that plainly in the Confirm step rather than claiming the help system was updated.
53
+
54
+ Skip this step entirely if `{project-root}/_froid/` does not exist.
55
+
56
+ ## Install the Orchestrator Tool
57
+
58
+ The orchestrator is what spawns fresh coding CLI sessions through the selected adapter(s) to invoke `froid-build-auto` (the upstream dev primitive; `froid-dev-auto` on pre-rename releases) for the dev pass — then re-invokes it on the `done` spec for the follow-up review pass — and `froid-loop-sweep`, watches their hook signals, and verifies their artifacts. Installing it is therefore part of setup, not an optional extra.
59
+
60
+ > **Why from Git?** The FROID installer copies only skill directories into a project — it does not carry sibling files, so the tool can't ride along in the skill folder. The canonical source is <https://github.com/cordya-ai/froid-loop>. (The reverse holds, though: the tool's wheel **bundles** the skills, so `froid-loop init` lays them down into a project's skill trees on its own — see step 3.)
61
+
62
+ Unless the user explicitly asked to skip it (e.g. `skills only` / `--no-tool`), install **or upgrade** and bootstrap now. Resolve `{project-root}` to the real project path before running.
63
+
64
+ 1. **Check what's already on PATH:** run `froid-loop --version`. A version printing here does **not** mean this project is set up — it only means _some_ `froid-loop` is importable in the current environment. Before trusting it, run `uv tool list` and look for `froid-loop`: if it's absent (the on-PATH copy comes from a source checkout or an unrelated virtualenv), warn the user that the active environment is shadowing a clean install and that the project would be relying on that checkout. Unless the user explicitly declines, install/upgrade from the canonical source below so the project doesn't depend on an incidental dev environment.
65
+
66
+ 2. **Install or upgrade from the Git repository** (the `[tui]` extra pulls in the Textual dashboard so `froid-loop tui` works). `uv tool install` puts `froid-loop` in uv's own managed environment, so there's no PEP 668 externally-managed conflict and no need for `--user`, an activated virtualenv, or `--break-system-packages`.
67
+
68
+ - **Fresh install** (no uv-managed `froid-loop`):
69
+
70
+ ```bash
71
+ uv tool install "froid-loop[tui] @ git+https://github.com/cordya-ai/froid-loop.git"
72
+ ```
73
+
74
+ Pin a release tag for reproducibility by appending `@v<X.Y.Z>` to the Git URL.
75
+
76
+ - **Upgrade** (uv already manages `froid-loop`):
77
+
78
+ 1. Record the current version first so you can report the delta: `froid-loop --version`.
79
+ 2. Default — follow `main` (or the currently pinned tag):
80
+
81
+ ```bash
82
+ uv tool upgrade froid-loop --reinstall
83
+ ```
84
+
85
+ The `--reinstall` is **required** for a Git source: a plain `uv tool upgrade` reuses the cached commit and won't pull new code. Then **offer to pin a release tag** for reproducibility — if the user wants a specific version, move to it with:
86
+
87
+ ```bash
88
+ uv tool install --force "froid-loop[tui] @ git+https://github.com/cordya-ai/froid-loop.git@v<X.Y.Z>"
89
+ ```
90
+
91
+ 3. Re-run `froid-loop --version` and note the before → after for the confirmation step.
92
+
93
+ 3. **Bootstrap the project** — install the coding-CLI hooks, the bundled `froid-loop-*` skills, the `.froid-loop/policy.toml` template, and the gitignore entry (idempotent).
94
+
95
+ First decide **which coding CLI(s)** the orchestrator should drive. The supported adapters are `claude` (default), `codex`, `gemini`, `copilot`, and `antigravity` (Google's `agy`). Hooks are registered per CLI, so the choice matters — register every CLI you intend to use for dev/review/triage. Ask the user (unless they already specified it in their setup args, e.g. `cli: claude, codex`, or accepted defaults — then default to `claude` only):
96
+
97
+ > "Which coding CLI(s) should the orchestrator drive — `claude`, `codex`, `gemini`, `copilot`, and/or `antigravity`? You can pick more than one. [claude]"
98
+
99
+ Build the command with one `--cli <name>` per selected CLI (the flag is repeatable). **On an upgrade, append `--force-skills`** so the per-project skill copies are actually refreshed — without it `init` skips every existing skill dir and the project keeps stale skills against the upgraded tool. On a fresh install, omit it.
100
+
101
+ ```bash
102
+ # fresh install, claude only (default)
103
+ froid-loop init --project "{project-root}" --cli claude
104
+
105
+ # fresh install, multiple, e.g. claude + codex + gemini
106
+ froid-loop init --project "{project-root}" --cli claude --cli codex --cli gemini
107
+
108
+ # upgrade — refresh the bundled skills in place
109
+ froid-loop init --project "{project-root}" --cli claude --force-skills
110
+ ```
111
+
112
+ Names must be exactly `claude`, `codex`, `gemini`, `copilot`, or `antigravity` — `init` errors on an unknown profile and lists the valid ones. `init` prints any one-time first-run notes per CLI (e.g. start `claude` once in the project and accept the workspace-trust + hooks-approval dialogs before `froid-loop run` — spawned sessions can't answer first-run dialogs). Relay those notes to the user.
113
+
114
+ **Skills are installed automatically:** `init` lays the bundled `froid-loop-*` skills into the right tree for each selected CLI — `.claude/skills/` for `claude`, `.agents/skills/` for `codex`/`gemini`/`copilot`/`antigravity`. On a fresh install, existing skill dirs are left untouched; on an upgrade, `--force-skills` overwrites them with the bundled copies from the upgraded tool (use `--no-skills` to skip the step and manage skills yourself).
115
+
116
+ > **Note:** `--force-skills` also overwrites `froid-loop-setup` itself (it ships in the same bundle). That's expected and safe — the freshly laid-down setup skill takes effect on the **next** invocation, and your `_froid/custom/*.toml` overrides (keyed by skill directory name) are untouched.
117
+
118
+ 4. **Preflight** — verify config, sprint-status, git, tmux, and the coding CLI:
119
+
120
+ ```bash
121
+ froid-loop validate --project "{project-root}"
122
+ ```
123
+
124
+ `validate` exits non-zero when the project isn't fully ready (e.g. no `sprint-status.yaml` yet, or `froid-sprint-planning` hasn't run). On a fresh project that is **expected** — report its findings to the user as a readiness checklist, not as an install failure.
125
+
126
+ 5. **Point the user at per-role adapter config.** `--cli` in step 3 only registers _hooks_ for each CLI. Which CLI actually **runs** each stage is governed by `{project-root}/.froid-loop/policy.toml`, written from a template by `init`. The `[adapter] name` (default `claude`) applies to every stage; optional `[adapter.dev]`, `[adapter.review]`, and `[adapter.triage]` tables override individual stages (each takes its own `name` and `extra_args`). So a mixed setup — e.g. `claude` for dev, `codex` for review — needs both the hooks registered (step 3) **and** the role pointed at that CLI in `policy.toml`:
127
+
128
+ ```toml
129
+ [adapter]
130
+ name = "claude" # default for all stages
131
+
132
+ [adapter.review]
133
+ name = "codex" # review runs on codex instead
134
+ ```
135
+
136
+ Tell the user where the file is and that any CLI named in `policy.toml` must also have been registered with `--cli` in step 3 (re-run `froid-loop init --cli <name>` to add one later). Leave `policy.toml` untouched if they only use a single CLI — the default is correct.
137
+
138
+ ## Confirm
139
+
140
+ Report:
141
+
142
+ - **Fresh install:** the installed `froid-loop --version`, that `froid-loop init` registered hooks, installed the `froid-loop-*` skills, and wrote policy/gitignore for the selected coding CLI(s) (name each one — e.g. "hooks + skills installed for claude, codex").
143
+ - **Upgrade:** the before → after `froid-loop --version` (e.g. "upgraded 0.3.1 → 0.3.2", or "already current at 0.3.2"), and that the `froid-loop-*` skills were **refreshed** (not skipped) with `--force-skills` in each CLI tree.
144
+
145
+ Also report:
146
+
147
+ - The `froid-loop validate` preflight result (pass, or the readiness checklist of what's still missing).
148
+ - That `_froid/froid-loop/module-help.csv` was refreshed. If `_froid/froid-loop/` did not exist before this run (i.e. the FROID installer never installed the module), say that the help entries will appear in `/froid-help` after the FROID installer next runs, and that installing the module via `npx froid-plane install` registers it properly.
149
+ - That this skill wrote nothing else under `_froid/` — module registration, `config.toml`, and the help catalog are owned by the FROID installer.
150
+
151
+ Then display the `module_greeting` from `./assets/module.yaml` to the user.
152
+
153
+ ## Outcome
154
+
155
+ Use the user's configured name and language for the remainder of the session. Read them from FROID's central config via its own resolver (four-layer TOML merge; needs FROID v6.10+ and Python 3.11+):
156
+
157
+ ```bash
158
+ uv run "{project-root}/_froid/scripts/resolve_config.py" --project-root "{project-root}" --key core
159
+ ```
160
+
161
+ Take `user_name` and `communication_language` from the `core` table. If the script is absent or exits non-zero, fall back to addressing the user neutrally in English — do not write or repair any config file.
@@ -0,0 +1,3 @@
1
+ module,skill,display-name,menu-code,description,action,args,phase,preceded-by,followed-by,required,output-location,outputs
2
+ FROID Loop Skills,froid-loop-setup,Setup Loop Module,SA,Install or update FROID Loop module config and help entries.,configure,{-H: headless mode},anytime,,,false,{project-root}/_froid,config.yaml entries
3
+ FROID Loop Skills,froid-loop-sweep,Sweep Triage,ST,Read-only triage of the deferred-work ledger into a machine-readable partition. Automation-only; froid-loop sweep invokes it.,triage,{--feedback path},anytime,,,false,implementation_artifacts,result.json triage plan
@@ -0,0 +1,19 @@
1
+ code: froid-loop
2
+ name: FROID Loop Skills
3
+ description: "Automation-mode skills driven by the froid-loop orchestrator: interactive escalation resolution (froid-loop-resolve) and deferred-work sweep triage (froid-loop-sweep) — the inner dev primitive (which self-reviews and commits) is the upstream froid-build-auto skill (froid-dev-auto on pre-rename releases)"
4
+ module_version: 0.11.1
5
+ default_selected: false
6
+ module_greeting: >
7
+ FROID Loop installed — both the automation skills and the
8
+ froid-loop orchestrator tool (the Python program that drives the loop). Setup
9
+ installed the `froid-loop` package from its Git repository with
10
+ `uv tool install`, ran `froid-loop init` (skills + hooks + policy + gitignore),
11
+ and `froid-loop validate`
12
+ (preflight). The skills
13
+ are invoked by the orchestrator and must stay installed together — the
14
+ froid-loop-sweep skill normalizes and migrates the deferred-work ledger per its
15
+ own deferred-work-format.md. They require the Froid Plane (bmm) module:
16
+ _froid/bmm/config.yaml and a sprint-status.yaml from froid-sprint-planning. Run
17
+ `froid-loop run` to start, `froid-loop tui` for the dashboard. To upgrade later,
18
+ just re-run `/froid-loop-setup` (or `/froid-loop-setup upgrade`) — it upgrades the
19
+ orchestrator tool and refreshes the per-project skills for you.
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: froid-loop-sweep
3
+ description: 'Triage the deferred-work ledger for the froid-loop orchestrator: verify every open entry against the actual codebase and return a machine-readable partition (bundles, already-resolved, blocked, skip, human decisions). Also migrates legacy pre-DW-format ledgers when invoked with --migrate. Automation-only — invoked by froid-loop sweep runs, not by humans.'
4
+ ---
5
+
6
+ # Deferred-Work Sweep Triage
7
+
8
+ **Goal:** Classify every open entry in `{implementation_artifacts}/deferred-work.md`
9
+ into a machine-readable triage plan the orchestrator can validate and execute.
10
+
11
+ This workflow is **read-only and automation-native**: it runs only inside a
12
+ `froid-loop` sweep session. You never edit the ledger, never edit code, never
13
+ ask questions. Your sole output is the result file. (The one exception is
14
+ migration mode, which edits exactly one file — the ledger.)
15
+
16
+ ## On Activation
17
+
18
+ ### Step 0: Automation Check & Mode Dispatch
19
+
20
+ Run: `echo "${FROID_LOOP_MODE:-}"`
21
+
22
+ If the output is not `1`, state that this skill only runs inside a froid-loop
23
+ sweep and end your turn. Otherwise read `./automation-mode.md` fully — its
24
+ rules and result schema govern this entire run.
25
+
26
+ If the invocation carries `--migrate <manifest-path>`, this is a **migration
27
+ session**, not triage: read `./migration-mode.md` and follow it instead of
28
+ Steps 1–4 below.
29
+
30
+ ### Step 1: Locate the ledger
31
+
32
+ Read `{project-root}/_froid/bmm/config.yaml` to resolve `implementation_artifacts`,
33
+ then read `{implementation_artifacts}/deferred-work.md` in full. Open entries
34
+ are `### DW-<n>:` blocks whose `status:` line is `open`. If the ledger is
35
+ missing or unreadable, escalate `CRITICAL` (`type: missing-ledger`) per
36
+ automation-mode.md and end your turn.
37
+
38
+ An entry carrying an `archived:` line keeps only a stub here — its full body
39
+ lives in the sibling `deferred-work-archive.md`, keyed by the same DW- id; read
40
+ it there before classifying that entry. An `archived-body:` line says the same
41
+ of an entry that was archived and later reopened: it is live work again, but the
42
+ body it carried before that close is still in the archive file, in the block
43
+ stamped with the date the line carries.
44
+
45
+ If the invocation carries `--feedback <path>`, read that file FIRST — it lists
46
+ the deterministic validation errors your previous attempt's result.json failed
47
+ on. Fix exactly those defects in this attempt's output.
48
+
49
+ ### Step 2: Verify every open entry against the code
50
+
51
+ Ledger statuses are known-unreliable: entries are often resolved by later work
52
+ but never marked done. For EACH open entry:
53
+
54
+ 1. Read its `location:` (file/component) in the current tree.
55
+ 2. Check whether the described issue still exists — read the code, grep for
56
+ the symptom, check `git log` for commits that touched the area since the
57
+ entry's `origin:` date.
58
+ 3. Record concrete evidence either way. "Probably fixed" is not evidence;
59
+ a file:line or commit hash is.
60
+
61
+ Use sub-agents for parallel verification when available; never ask permission.
62
+
63
+ ### Step 3: Partition
64
+
65
+ Classify each open entry into exactly ONE category:
66
+
67
+ - **already_resolved** — the issue no longer exists in the code. Requires
68
+ concrete `evidence` (file:line that now handles it, or the commit that fixed
69
+ it). The orchestrator closes the ledger entry deterministically.
70
+ - **bundles** — buildable now: every file the fix needs already exists and no
71
+ future story has to land first. Group entries that share a touchpoint (same
72
+ file, same subsystem, same validator pattern) into cohesive single-goal
73
+ bundles sized for one dev session; an entry that stands alone is a
74
+ one-entry bundle. `name` matches `^[a-z0-9][a-z0-9-]{1,39}\Z` (kebab-case,
75
+ at most 40 characters; e.g. `unicode-string-hardening`), and `intent` is
76
+ 2–6 sentences describing the one cohesive goal.
77
+ - **blocked** — the fix is only meaningful (or meaningfully easier) after a
78
+ named future story/epic lands. Name the blocker verbatim.
79
+ - **skip** — superseded, moot, or tied to a scenario the project explicitly
80
+ excludes. Give the reason.
81
+ - **decisions** — a human must choose. ALWAYS a decision, never a bundle:
82
+ renegotiating anything inside a spec's `<frozen-after-approval>` block,
83
+ reversing a deliberate human-approved scope decision, API-shape changes that
84
+ ripple to unbuilt consumers, and entries deferred with reason
85
+ "auto-mode: needs human decision". Each decision gets 2–4 concrete options
86
+ (each with an `effect`: `build` + an implementation intent, `close` +
87
+ optional resolution, or `keep-open`) and your `recommendation`. The only
88
+ exception: a frozen-block renegotiation that is clearly net-positive,
89
+ non-destructive, and behavior-tightening may be bundled — when in doubt,
90
+ make it a decision.
91
+
92
+ Every open entry appears in exactly one category. The orchestrator validates
93
+ this deterministically; a missed or double-counted entry fails the whole
94
+ result and burns a retry.
95
+
96
+ ### Step 4: Write the result and end your turn
97
+
98
+ Write the result.json per `./automation-mode.md` and state in one line how
99
+ many entries went to each category. End your turn. Do not edit any file other
100
+ than the result file.