loki-mode 7.93.0 → 7.95.0
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.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/done-recognition.sh +823 -0
- package/autonomy/run.sh +98 -1
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/PRD-REUSE-DONE-RECOGNITION-PLAN.md +536 -0
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# PRD-Reuse Done-Recognition Gate -- Implementation Plan
|
|
2
|
+
|
|
3
|
+
Architect design. DESIGN ONLY: no production code in this doc, one file created
|
|
4
|
+
(this plan). Read-only against the engine. No emojis. No em dashes.
|
|
5
|
+
|
|
6
|
+
Repo: /Users/lokesh/git/loki-mode
|
|
7
|
+
Feature: when a no-PRD run reuses a previously generated PRD, model-verify
|
|
8
|
+
whether the codebase already satisfies that PRD before rebuilding a task queue
|
|
9
|
+
and re-running the RARV loop. If verified done, finish through the normal
|
|
10
|
+
completion path and tell the user clearly. If not, build only the unsatisfied
|
|
11
|
+
requirements. If inconclusive, build (safe default). Never fake-green.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. The bug, restated as a control-flow gap
|
|
16
|
+
|
|
17
|
+
On a no-PRD run over a project Loki already built and completed, the auto-detect
|
|
18
|
+
block in `run_autonomous()` (autonomy/run.sh:15140-15217) calls
|
|
19
|
+
`decide_generated_prd_action()` (autonomy/run.sh:5590). When the codebase
|
|
20
|
+
signature matches the stored one it returns `reuse`, and the block does exactly:
|
|
21
|
+
|
|
22
|
+
reuse) ... prd_path="$_gen_prd" ;;
|
|
23
|
+
|
|
24
|
+
Control then falls straight through to `populate_prd_queue "$prd_path"`
|
|
25
|
+
(autonomy/run.sh:15439), which extracts all `prd-*` features from the PRD
|
|
26
|
+
(autonomy/run.sh:14580, dedup only by `existing_ids` at 14878), and into the
|
|
27
|
+
main RARV loop. Nothing on the `reuse` path ever asks "is this reused PRD
|
|
28
|
+
already satisfied by the current codebase?" So a genuinely-done project gets a
|
|
29
|
+
fresh 11-task queue plus new iteration-N work, re-running RARV over completed
|
|
30
|
+
work. The prior run had already written `.loki/signals/COMPLETION_REQUESTED`,
|
|
31
|
+
`.loki/state/completion.json`, and `completion-evidence.md`; the reuse path
|
|
32
|
+
ignores all of it.
|
|
33
|
+
|
|
34
|
+
The fix is a single localized gate between the action decision and the
|
|
35
|
+
queue/loop, that re-verifies ground truth with the model and routes to one of
|
|
36
|
+
three outcomes.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. Integration point (exact function + line region)
|
|
41
|
+
|
|
42
|
+
Two-part, both inside `run_autonomous()`:
|
|
43
|
+
|
|
44
|
+
### 2a. Set up the gate inputs in the existing decision block (15140-15217)
|
|
45
|
+
No structural change. The block already computes `GENERATED_PRD_ACTION`,
|
|
46
|
+
`_gen_prd`, and `_prd_date`. We add nothing here except (optionally) reading the
|
|
47
|
+
gate's outcome variable later. Leave 15140-15217 byte-stable for the static
|
|
48
|
+
prompt prefix; the action variable continues to be set once per run.
|
|
49
|
+
|
|
50
|
+
### 2b. Add the gate as a self-contained function called AFTER load_state,
|
|
51
|
+
AFTER the restarted-run snapshot, and BEFORE the delegate-branch/start-sha block
|
|
52
|
+
and queue build.
|
|
53
|
+
|
|
54
|
+
Verified line ordering inside `run_autonomous()`:
|
|
55
|
+
- 15238 `load_state` (15239 already reads `ITERATION_COUNT` right after).
|
|
56
|
+
- ~15248 start-sha capture (`_start_sha_file`, `mkdir .loki/state`).
|
|
57
|
+
- 15259 delegate-branch block (`LOKI_DELEGATE_BRANCH`, spawns `loki/delegate-*`).
|
|
58
|
+
- 15313-15319 restarted-run snapshot + export.
|
|
59
|
+
- 15328-15331 `LOKI_TRUST_RUN_ID` minted (`record_trust_event_bash run_start`).
|
|
60
|
+
- 15439 `populate_prd_queue`.
|
|
61
|
+
|
|
62
|
+
Required call site: BETWEEN `load_state` (15238) and the start-sha / delegate
|
|
63
|
+
block (~15248-15259). At that window `ITERATION_COUNT` is live (15239 reads it,
|
|
64
|
+
so the gate's fresh-vs-resume disclosure works) AND neither the start-sha capture
|
|
65
|
+
nor the delegate branch has run yet, so a verified-done project never spawns a
|
|
66
|
+
stray `loki/delegate-*` branch or moves the diff window. Placing the gate at
|
|
67
|
+
15319 (after the delegate block) would defeat that, which is why the call site is
|
|
68
|
+
the pre-15248 window, not the restarted-snapshot line.
|
|
69
|
+
|
|
70
|
+
One ordering tradeoff to fold in at edit time: `LOKI_TRUST_RUN_ID` is minted at
|
|
71
|
+
15328-15331, AFTER this window. If the done path records a trust event, either
|
|
72
|
+
mint the run id inside the gate or hoist 15328-15331 above the gate. Prefer
|
|
73
|
+
hoisting the mint (15328-15331) to just after `load_state` so both the gate and
|
|
74
|
+
the existing call site see a stable id; verify no other consumer depends on its
|
|
75
|
+
current position before moving it.
|
|
76
|
+
|
|
77
|
+
Why not 15218 with a bare `exit 0`: 15218 is before `load_state`, so
|
|
78
|
+
`ITERATION_COUNT` is not yet restored, and a bare `exit` skips main's terminal
|
|
79
|
+
finalization (section 6.1). The gate must instead return through `run_autonomous`
|
|
80
|
+
so `main()` runs its terminal block.
|
|
81
|
+
|
|
82
|
+
- Before `populate_prd_queue` (15439): a done verdict must short-circuit the
|
|
83
|
+
ENTIRE queue-build + loop, and an incomplete verdict must hand the queue
|
|
84
|
+
builder its "only-unsatisfied" filter before it runs.
|
|
85
|
+
|
|
86
|
+
New function name: `reuse_done_recognition_gate` (call once, run-scoped).
|
|
87
|
+
The function returns control by either (a) running the council-parity
|
|
88
|
+
finalization subset and `return 0` from `run_autonomous`, so `main()`'s terminal
|
|
89
|
+
block (18595-18629) finishes the run (done -- see section 6.1), or (b) writing a
|
|
90
|
+
satisfied-requirements manifest and falling through (incomplete), or (c) doing
|
|
91
|
+
nothing and falling through (inconclusive / fast-path build).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 3. Which GENERATED_PRD_ACTION values arm the gate
|
|
96
|
+
|
|
97
|
+
The gate runs only on a no-PRD run that is reusing an existing generated PRD:
|
|
98
|
+
|
|
99
|
+
- `reuse` -> ARM the gate. Canonical bug case (codebase unchanged).
|
|
100
|
+
- `user_owned` -> ARM the gate. Codebase unchanged, PRD hand-edited; same
|
|
101
|
+
"already built" situation. The model verifies against the
|
|
102
|
+
hand-edited requirements.
|
|
103
|
+
- `update` -> ARM the gate, but FAST-STOP DISABLED. Codebase changed
|
|
104
|
+
since the PRD, so the PRD is stale by definition (that is why
|
|
105
|
+
the action is `update`). Judging "already done?" against a
|
|
106
|
+
spec that is about to be rewritten is a false-stop risk, so on
|
|
107
|
+
the `update` path the gate may resolve ONLY to incomplete or
|
|
108
|
+
inconclusive -- never to a fast-stop `done`. Its value here is
|
|
109
|
+
exclusively the incremental-queue case: mark which still-valid
|
|
110
|
+
requirements are met so the rebuild touches only the gap. A
|
|
111
|
+
model `done` on the `update` path is treated as inconclusive
|
|
112
|
+
(fall through to build / normal update flow).
|
|
113
|
+
- `generate` -> DO NOT arm. First run / forced regen (LOKI_PRD_REGEN). There
|
|
114
|
+
is no prior PRD to be "already satisfied"; maps to the
|
|
115
|
+
negative fast-path (build).
|
|
116
|
+
|
|
117
|
+
Guard condition at the call site:
|
|
118
|
+
|
|
119
|
+
case "${GENERATED_PRD_ACTION:-}" in
|
|
120
|
+
reuse|user_owned|update) reuse_done_recognition_gate "$prd_path" ;;
|
|
121
|
+
esac
|
|
122
|
+
|
|
123
|
+
Only `reuse` and `user_owned` (codebase unchanged) may terminate as a
|
|
124
|
+
fast-stop `done`. `update` may resolve only to incomplete/inconclusive. For
|
|
125
|
+
`reuse`/`user_owned`, "incomplete" is still possible (user deleted code, a
|
|
126
|
+
requirement regressed). The model decides the verdict; the action selects whether
|
|
127
|
+
to arm AND whether a `done` verdict is allowed to fast-stop.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 4. Fast-path hints (cheap, EXCLUSIVELY negative)
|
|
132
|
+
|
|
133
|
+
Before any model call, evaluate cheap deterministic signals. CRITICAL CONSTRAINT
|
|
134
|
+
(requirements 1 and 3): the fast path may only ever short-circuit toward BUILD
|
|
135
|
+
(skip the model, fall through to queue). There is NO deterministic
|
|
136
|
+
"checklist all-verified -> stop" shortcut. A positive done verdict is ALWAYS the
|
|
137
|
+
model's, grounded in re-verified reality. The very bug shows the deterministic
|
|
138
|
+
checklist artifact was stale/desynced (14-vs-19 discrepancy), so it can never be
|
|
139
|
+
trusted as a positive done signal.
|
|
140
|
+
|
|
141
|
+
Negative fast-path: skip the model and fall through to BUILD when ground-truth
|
|
142
|
+
"surely not done" signals hold, e.g.:
|
|
143
|
+
- No `.loki/signals/COMPLETION_REQUESTED` AND no `.loki/state/completion.json`
|
|
144
|
+
AND no `.loki/checklist/checklist.json`: the project was never completed by a
|
|
145
|
+
prior run; there is nothing plausibly done. Build. (Cheapest, most common
|
|
146
|
+
miss-avoidance: never pay for a model call on a project with zero completion
|
|
147
|
+
footprint.)
|
|
148
|
+
- Provider unavailable / degraded (`_loki_done_recog_provider_ok` mirrors
|
|
149
|
+
`_loki_prd_enrich_provider_ok`, autonomy/lib/prd-enrich.sh:65): cannot
|
|
150
|
+
model-verify -> inconclusive -> build (never assert done offline).
|
|
151
|
+
- Explicit opt-out (LOKI_REUSE_DONE_RECOG=0): build.
|
|
152
|
+
|
|
153
|
+
These are hints/inputs only. Their PRESENCE (a completion signal exists) does NOT
|
|
154
|
+
short-circuit to done; it merely makes the model call worthwhile. The model still
|
|
155
|
+
re-runs tests and re-checks each requirement.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 5. Model-intelligence verification design
|
|
160
|
+
|
|
161
|
+
Mirror the proven, mockable single-call pattern of prd-enrich
|
|
162
|
+
(autonomy/lib/prd-enrich.sh). New library: `autonomy/lib/done-recognition.sh`,
|
|
163
|
+
sourced at the gate call site the same way prd-enrich.sh is sourced at
|
|
164
|
+
autonomy/run.sh:14931-14937.
|
|
165
|
+
|
|
166
|
+
### 5.1 The single model-call primitive (the ONE provider touch, mockable)
|
|
167
|
+
|
|
168
|
+
_loki_done_recog_invoke <prompt> # echoes the raw model response
|
|
169
|
+
|
|
170
|
+
Implementation copies `_loki_prd_enrich_invoke` (autonomy/lib/prd-enrich.sh:43)
|
|
171
|
+
verbatim in shape: `command -v claude`, `timeout "${LOKI_DONE_RECOG_TIMEOUT}"`,
|
|
172
|
+
`claude --dangerously-skip-permissions -p "$prompt"`, return 1 on any
|
|
173
|
+
nonzero/empty. Tests stub THIS function to return canned JSON, exactly as
|
|
174
|
+
prd-enrich tests stub `_loki_prd_enrich_invoke`. This is the injection seam that
|
|
175
|
+
makes a bash+model gate deterministically testable.
|
|
176
|
+
|
|
177
|
+
Bounds (mirror prd-enrich):
|
|
178
|
+
: "${LOKI_DONE_RECOG_TIMEOUT:=180}"
|
|
179
|
+
: "${LOKI_DONE_RECOG_MAX_PRD_CHARS:=16000}"
|
|
180
|
+
: "${LOKI_DONE_RECOG_MAX_TEST_CHARS:=4000}"
|
|
181
|
+
|
|
182
|
+
### 5.2 Ground-truth re-verification BEFORE the model call
|
|
183
|
+
|
|
184
|
+
The model must judge against re-run reality, not artifacts alone. Before
|
|
185
|
+
building the prompt, the gate captures fresh ground truth:
|
|
186
|
+
|
|
187
|
+
1. Tests: reuse `ensure_completion_test_evidence` (autonomy/run.sh:9094) which
|
|
188
|
+
calls `enforce_test_coverage` (autonomy/run.sh:8595) and persists
|
|
189
|
+
`.loki/quality/test-results.json`. This is the SAME evidence axis the
|
|
190
|
+
completion council/evidence gate reads, so the gate cannot reach a verdict
|
|
191
|
+
that contradicts the council. `enforce_test_coverage` is CWD-safe (it anchors
|
|
192
|
+
every path on `${TARGET_DIR:-.}` internally, confirmed at 8595-8607), so it
|
|
193
|
+
is safe at this pre-loop site. Swallow rc with `|| true` (red tests are data,
|
|
194
|
+
not a crash). If no runner exists, the file records `runner:none` and the
|
|
195
|
+
test axis is honestly inconclusive (feeds "inconclusive -> build" unless the
|
|
196
|
+
model can establish done by code inspection alone, which it should treat
|
|
197
|
+
conservatively).
|
|
198
|
+
|
|
199
|
+
2. Requirements list: derive from the reused PRD (`$prd_path`, i.e.
|
|
200
|
+
`.loki/generated-prd.md`). Cap to LOKI_DONE_RECOG_MAX_PRD_CHARS.
|
|
201
|
+
|
|
202
|
+
3. Existing completion evidence (as INPUT/hint only): the prior
|
|
203
|
+
`completion-evidence.md`, `.loki/state/completion.json`, and
|
|
204
|
+
`.loki/checklist/checklist.json` are passed to the model as context labeled
|
|
205
|
+
"PRIOR CLAIMS, possibly stale -- verify against the code, do not trust."
|
|
206
|
+
|
|
207
|
+
### 5.3 The prompt and the structured return
|
|
208
|
+
|
|
209
|
+
The prompt instructs the model to: read the PRD requirements; for EACH
|
|
210
|
+
requirement, inspect the actual code (and the fresh test-results.json) and
|
|
211
|
+
decide whether it is met NOW; treat all prior Loki artifacts as unverified
|
|
212
|
+
claims; and return ONLY a JSON object (no prose, no fences), defensively parsed
|
|
213
|
+
(slice first `{` to last `}`, mirroring prd-enrich's array slice at
|
|
214
|
+
prd-enrich.sh:326):
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
"verdict": "done" | "incomplete" | "inconclusive",
|
|
218
|
+
"summary": "<one sentence for the user>",
|
|
219
|
+
"tests": { "passed": <int>, "total": <int>, "green": true|false },
|
|
220
|
+
"requirements": [
|
|
221
|
+
{ "id": "<stable id or title slug>",
|
|
222
|
+
"title": "<requirement>",
|
|
223
|
+
"status": "met" | "unmet" | "uncertain",
|
|
224
|
+
"evidence": "<file:line or test name proving it>" }
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
Verdict rules enforced in the prompt AND re-derived defensively in bash/python
|
|
229
|
+
(never trust the model's top-line verdict blindly):
|
|
230
|
+
- `done` requires ALL requirements `met` AND tests green in the fresh
|
|
231
|
+
test-results.json (or `runner:none` with the model citing concrete code
|
|
232
|
+
evidence for every requirement). If the model says `done` but the fresh
|
|
233
|
+
test-results.json shows red or any requirement is `unmet`/`uncertain`, the gate
|
|
234
|
+
DOWNGRADES to `incomplete` (or `inconclusive`). The trust decision is grounded
|
|
235
|
+
in re-verified reality, so a model overclaim cannot fake-green.
|
|
236
|
+
- `incomplete` when one or more requirements are `unmet` (with at least one
|
|
237
|
+
`met`, or none).
|
|
238
|
+
- `inconclusive` when the model could not establish ground truth (parse failure,
|
|
239
|
+
empty response, all `uncertain`, provider error). Falls to build.
|
|
240
|
+
|
|
241
|
+
This satisfies requirement 1 (model judgment, not a hardcoded checklist rule),
|
|
242
|
+
requirement 3 (trust moat: done is model-verified against re-run tests + code,
|
|
243
|
+
never asserted from a stale artifact; inconclusive -> build), and keeps the
|
|
244
|
+
verdict consistent with the council's evidence axis.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 6. Outcome routing
|
|
249
|
+
|
|
250
|
+
### 6.1 done -> finish through the normal completion path (NOT a bare exit)
|
|
251
|
+
|
|
252
|
+
A bare `exit 0` would skip finalization and risk leaving status/dashboard in a
|
|
253
|
+
"running" state with no fresh trust evidence. Instead, REFRESH the verified
|
|
254
|
+
completion record and exit through the same machinery a normal finish uses.
|
|
255
|
+
|
|
256
|
+
Verified exit path. The gate runs INSIDE `run_autonomous()` (the pre-loop
|
|
257
|
+
window in section 2b). `main()` calls `run_autonomous "$PRD_PATH"` at
|
|
258
|
+
autonomy/run.sh:18515 (`|| result=$?`) and then UNCONDITIONALLY runs its terminal
|
|
259
|
+
finalization at 18595-18629 -- `_advance_current_phase "COMPLETED"`, the COMPLETED
|
|
260
|
+
marker (18604-18606), proof-of-run (18597-18598), and HANDOFF.md (18614-18629).
|
|
261
|
+
That terminal block lives in `main()`, NOT in `run_autonomous()`. So when the
|
|
262
|
+
gate `return`s from `run_autonomous` (with or without entering the loop), control
|
|
263
|
+
returns to `main()` at 18515 and the terminal finalization runs exactly once.
|
|
264
|
+
This is the mechanism that makes step 3's receipt promise real -- a pre-loop
|
|
265
|
+
`return` reaches the terminal because the terminal is in the caller, not in the
|
|
266
|
+
loop body.
|
|
267
|
+
|
|
268
|
+
Concretely, on `done` the gate:
|
|
269
|
+
1. Writes a fresh verified-completion record reflecting the NOW re-run results.
|
|
270
|
+
The fresh `.loki/quality/test-results.json` is already on disk. Reuse the
|
|
271
|
+
standalone completion-summary writer `build_completion_summary`
|
|
272
|
+
(autonomy/run.sh:3026), which emits both `.loki/state/completion.json`
|
|
273
|
+
(3196-3210) and `completion-evidence.md` -- the SAME writer the council
|
|
274
|
+
approval path uses (called from completion-council.sh:3211). Confirm it is
|
|
275
|
+
callable standalone from the pre-loop site (it reads git/state, not loop
|
|
276
|
+
locals); if it requires loop-set vars, pass them explicitly or factor the
|
|
277
|
+
write. The per-requirement `requirements[]` array is recorded as the evidence
|
|
278
|
+
body, so the record is reconciled-and-refreshed, not a parallel fabricated one.
|
|
279
|
+
2. Runs the same finalization subset the council approval path runs (mirroring
|
|
280
|
+
autonomy/run.sh:17688-17704), then RETURNS from `run_autonomous` so `main()`'s
|
|
281
|
+
terminal block (18595-18629) does the COMPLETED marker, phase-advance, proof,
|
|
282
|
+
and handoff. To avoid double-writing the COMPLETED marker / phase, the gate
|
|
283
|
+
should do the council-parity pieces that the main terminal does NOT
|
|
284
|
+
(`council_write_report`, `run_memory_consolidation`, `on_run_complete`,
|
|
285
|
+
`emit_completion_summary complete`, `save_state ... reuse_already_satisfied`)
|
|
286
|
+
and let main own `_advance_current_phase "COMPLETED"` + the COMPLETED marker
|
|
287
|
+
(18604-18606). Verify at edit time which subset main already covers so neither
|
|
288
|
+
is run twice:
|
|
289
|
+
type council_write_report &>/dev/null && council_write_report
|
|
290
|
+
run_memory_consolidation
|
|
291
|
+
on_run_complete # autonomy/run.sh:3516 (optional PR/summary/ping)
|
|
292
|
+
emit_completion_summary complete # autonomy/run.sh:3367
|
|
293
|
+
save_state ${RETRY_COUNT:-0} "reuse_already_satisfied" 0
|
|
294
|
+
return 0 # back to main() -> terminal finalization at 18595-18629
|
|
295
|
+
Do NOT `exit`. Do NOT enter the loop. A `return 0` (clean) is correct here; a
|
|
296
|
+
`return 2` is unnecessary because there is no loop to stop -- the pre-loop
|
|
297
|
+
return already skips queue-build and iteration.
|
|
298
|
+
|
|
299
|
+
Alternative (if `build_completion_summary` proves loop-coupled): have the gate
|
|
300
|
+
only write the COMPLETED marker + refreshed completion.json + advance phase,
|
|
301
|
+
then `return 0`; main's `is_completed()` check (17205) is INSIDE the loop and
|
|
302
|
+
so is irrelevant on the pre-loop return -- the loop never runs. Either way the
|
|
303
|
+
single guaranteed terminal is main's 18595-18629. Pick the
|
|
304
|
+
`build_completion_summary`-reuse route if it is standalone-safe (preferred:
|
|
305
|
+
one writer, no divergence); fall back to the marker-only route otherwise.
|
|
306
|
+
3. The proof-of-run + HANDOFF.md generation (autonomy/run.sh:18597-18629) runs in
|
|
307
|
+
main's terminal block after `run_autonomous` returns, so the user gets a
|
|
308
|
+
receipt with the honest re-run headline.
|
|
309
|
+
|
|
310
|
+
This is the "interacts with completion machinery without contradicting it"
|
|
311
|
+
requirement: we reconcile WITH the prior evidence, refreshed against re-run
|
|
312
|
+
reality, and exit through the one true completion path. No double-gating: the
|
|
313
|
+
council never runs because the loop never starts; the gate produced the same
|
|
314
|
+
kind of evidence the council would have read.
|
|
315
|
+
|
|
316
|
+
### 6.2 incomplete -> incremental queue (only unsatisfied requirements)
|
|
317
|
+
|
|
318
|
+
`populate_prd_queue` (14580) builds ALL features; its only filter is the
|
|
319
|
+
`existing_ids` dedup at 14878. To make it incremental we add ONE read-point:
|
|
320
|
+
|
|
321
|
+
- The gate writes a satisfied-requirements manifest:
|
|
322
|
+
`.loki/state/satisfied-requirements.json`:
|
|
323
|
+
|
|
324
|
+
{
|
|
325
|
+
"prd_sha": "<hash of the PRD the verdict was computed against>",
|
|
326
|
+
"generated_at": "<iso8601>",
|
|
327
|
+
"satisfied": ["prd-001 title or stable id", "..."],
|
|
328
|
+
"source": "reuse-done-recognition"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
Identity is by the same requirement key the queue builder uses to mint task
|
|
332
|
+
ids (`prd-NNN` is positional at 14877; to be robust, key on the feature TITLE
|
|
333
|
+
and let the manifest store titles, matched case-insensitively in the builder).
|
|
334
|
+
|
|
335
|
+
- `populate_prd_queue` reads the manifest once (guarded: only when it exists AND
|
|
336
|
+
its `prd_sha` matches the current PRD hash, so a stale manifest is ignored) and
|
|
337
|
+
SKIPS any feature whose title is in `satisfied[]`, alongside the existing
|
|
338
|
+
`existing_ids` skip at 14878:
|
|
339
|
+
|
|
340
|
+
if task_title_satisfied(feat["title"]): # new, manifest-driven
|
|
341
|
+
continue
|
|
342
|
+
|
|
343
|
+
Result: only unmet requirements become `prd-*` tasks; the RARV loop works only
|
|
344
|
+
the remaining gap, not a from-scratch rebuild. If the manifest is absent or
|
|
345
|
+
stale, the builder behaves exactly as today (full build) -- safe default.
|
|
346
|
+
|
|
347
|
+
- Disclosure: the gate logs "N of M requirements already satisfied; building
|
|
348
|
+
only the K unmet (<titles>). Pass --fresh-prd to rebuild from scratch."
|
|
349
|
+
|
|
350
|
+
Then the gate falls through (no exit); the run proceeds into queue-build and the
|
|
351
|
+
loop normally, now scoped to the gap.
|
|
352
|
+
|
|
353
|
+
### 6.3 inconclusive -> build (safe default, never falsely done)
|
|
354
|
+
|
|
355
|
+
Do nothing: write no manifest, no completion. Log "Could not verify whether the
|
|
356
|
+
project already satisfies its spec (<reason>); proceeding to build." Control
|
|
357
|
+
falls through to the normal full queue-build + loop. This is the prd-enrich
|
|
358
|
+
graceful-fallback contract (provider missing / timeout / unparsable ->
|
|
359
|
+
deterministic fall-through), applied to the done decision. NEVER declare done on
|
|
360
|
+
inconclusive.
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 7. User-facing messaging (enterprise UX, requirement 2)
|
|
365
|
+
|
|
366
|
+
- done:
|
|
367
|
+
"This project already satisfies its spec. Verified N/N requirements met and
|
|
368
|
+
tests green (re-ran the suite now). Nothing to build. To rebuild from scratch
|
|
369
|
+
run `loki start --fresh-prd`; to extend it, edit the spec or pass a new/changed
|
|
370
|
+
PRD." (emitted via log + emit_completion_summary complete.)
|
|
371
|
+
Risk-mitigating clarity: a user who genuinely WANTS to extend a done project
|
|
372
|
+
must immediately see the two escape hatches (--fresh-prd, new/changed spec).
|
|
373
|
+
Make that the last line of the done message so it is unmissable.
|
|
374
|
+
|
|
375
|
+
- incomplete:
|
|
376
|
+
"This project partially satisfies its spec: K of M requirements still need
|
|
377
|
+
work (<short list>). Building only those. (Pass --fresh-prd to rebuild
|
|
378
|
+
everything.)"
|
|
379
|
+
|
|
380
|
+
- inconclusive:
|
|
381
|
+
"Could not confirm whether the existing code already satisfies the reused spec
|
|
382
|
+
(<reason: provider unavailable / tests inconclusive / unparsable verdict>).
|
|
383
|
+
Proceeding to build to be safe."
|
|
384
|
+
|
|
385
|
+
All strings: plain text, no emojis, no em dashes (repo convention).
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## 8. Interaction with existing council / completion machinery (no double-gate)
|
|
390
|
+
|
|
391
|
+
- On `done`: the loop never starts, so council_should_stop / council_vote /
|
|
392
|
+
council_evidence_gate never run. The gate produces the SAME evidence those
|
|
393
|
+
gates read (fresh test-results.json + completion.json), so there is no
|
|
394
|
+
contradictory or parallel notion of "done." The completion finalization
|
|
395
|
+
subset it runs is the SAME one the council approval path runs
|
|
396
|
+
(run.sh:17688-17704), so dashboards/markers/memory stay consistent.
|
|
397
|
+
- On `incomplete`/`inconclusive`: the gate adds no completion state; the normal
|
|
398
|
+
loop, council, and evidence gate are fully in force for the (possibly reduced)
|
|
399
|
+
remaining work. No behavior change to those paths.
|
|
400
|
+
- The fast-path uses the council's artifacts (COMPLETION_REQUESTED,
|
|
401
|
+
completion.json, checklist.json) only as NEGATIVE inputs (their absence ->
|
|
402
|
+
build), never as a positive done shortcut, so it cannot inherit the stale-
|
|
403
|
+
artifact bug.
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 9. Test strategy (deterministic bash + mocked model)
|
|
408
|
+
|
|
409
|
+
New test: `tests/test-reuse-done-recognition.sh`, style mirroring the prd-enrich
|
|
410
|
+
and stop-scoping tests (bash, set -u, ok/bad counters, mktemp -d under /tmp with
|
|
411
|
+
trap-rm on every exit, no network, no real agent). The model call is injected by
|
|
412
|
+
overriding `_loki_done_recog_invoke` to echo canned JSON, exactly as prd-enrich
|
|
413
|
+
tests override `_loki_prd_enrich_invoke`.
|
|
414
|
+
|
|
415
|
+
Cases:
|
|
416
|
+
- T1 done verdict + green test-results.json -> gate routes to completion:
|
|
417
|
+
assert `.loki/COMPLETED` written, `.loki/state/completion.json` refreshed,
|
|
418
|
+
NO `.loki/queue/pending.json` prd-* tasks built, normal-terminal return.
|
|
419
|
+
- T2 model says done but fresh test-results.json is RED -> DOWNGRADE: assert NOT
|
|
420
|
+
done (incomplete/inconclusive), no COMPLETED marker (no fake-green).
|
|
421
|
+
- T3 model says done but a requirement is `unmet` -> downgrade to incomplete;
|
|
422
|
+
assert satisfied-requirements manifest excludes the unmet one.
|
|
423
|
+
- T4 incomplete -> manifest written with the met titles; stub populate read-point
|
|
424
|
+
test asserts only unmet features become tasks (feed a 3-feature PRD, mark 2
|
|
425
|
+
satisfied, assert 1 task).
|
|
426
|
+
- T5 inconclusive (invoke returns nonzero / empty / unparsable) -> no manifest,
|
|
427
|
+
no completion, falls through to full build.
|
|
428
|
+
- T6 negative fast-path: no COMPLETION_REQUESTED + no completion.json + no
|
|
429
|
+
checklist -> model NOT called (assert the stub recorded zero calls), full
|
|
430
|
+
build.
|
|
431
|
+
- T7 provider not ok (stub `_loki_done_recog_provider_ok` false) -> inconclusive
|
|
432
|
+
fast-path, build, model NOT called.
|
|
433
|
+
- T8 action gating: GENERATED_PRD_ACTION=generate -> gate not armed (no model
|
|
434
|
+
call); reuse/user_owned/update -> armed.
|
|
435
|
+
- T9 manifest staleness: prd_sha mismatch -> populate_prd_queue ignores manifest
|
|
436
|
+
and does a full build (safe default).
|
|
437
|
+
- T10 no-emoji/no-em-dash static grep over the new lib and the new strings.
|
|
438
|
+
|
|
439
|
+
Register in scripts/local-ci.sh (mirror the prd-enrich registration):
|
|
440
|
+
run_check "tests/test-reuse-done-recognition.sh (reuse done-recognition gate)" \
|
|
441
|
+
"bash tests/test-reuse-done-recognition.sh 2>&1 | tail -3"
|
|
442
|
+
|
|
443
|
+
Sourceability: the gate logic lives in `autonomy/lib/done-recognition.sh` (like
|
|
444
|
+
prd-enrich.sh), which is independently sourceable in the test without sourcing
|
|
445
|
+
run.sh. The `populate_prd_queue` read-point change is tested by exercising
|
|
446
|
+
populate_prd_queue in a temp project (it is already invoked in integration-style
|
|
447
|
+
tests) or by awk-extracting the python heredoc filter, defaulting to the
|
|
448
|
+
sourceable-lib approach for the gate proper.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## 10. Rollout
|
|
453
|
+
|
|
454
|
+
- Phase 1 (one release): env-gated DEFAULT-ON with a documented opt-out, so the
|
|
455
|
+
intelligent default ships immediately (requirement 5: unified default, not a
|
|
456
|
+
new knob) but operators retain an escape hatch:
|
|
457
|
+
LOKI_REUSE_DONE_RECOG=1 # default (on)
|
|
458
|
+
LOKI_REUSE_DONE_RECOG=0 # opt out (legacy reuse-then-build behavior)
|
|
459
|
+
No new user-facing CLI flag is added; --fresh-prd / LOKI_PRD_REGEN already
|
|
460
|
+
exist as the rebuild escape hatch and are surfaced in every gate message.
|
|
461
|
+
- Because the safe default on any uncertainty is BUILD (the legacy behavior), the
|
|
462
|
+
blast radius of a wrong gate is bounded: worst case it behaves like today.
|
|
463
|
+
- After one release of field evidence (proof-of-run + trust metrics show the gate
|
|
464
|
+
is correctly recognizing done without false-done), drop the env gate to
|
|
465
|
+
hard-on (keep `=0` opt-out for one more release, then remove).
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## 11. Risks and mitigations
|
|
470
|
+
|
|
471
|
+
- R1 (highest, requirement 2): a genuinely-done project the user WANTS to extend.
|
|
472
|
+
Mitigation: the done message's final line names BOTH escape hatches
|
|
473
|
+
(`--fresh-prd` to rebuild, edit/pass a new spec to extend). The gate only
|
|
474
|
+
fires on a NO-PRD reuse run; the moment the user supplies a changed/new PRD,
|
|
475
|
+
the user-PRD path (found_prd at 15159, or persist_user_prd) takes precedence
|
|
476
|
+
and the gate's reuse arming does not apply.
|
|
477
|
+
- R2 false-done (fake-green): mitigated by re-running tests NOW
|
|
478
|
+
(ensure_completion_test_evidence) and DOWNGRADING any model `done` that the
|
|
479
|
+
fresh test-results.json or an `unmet`/`uncertain` requirement contradicts. The
|
|
480
|
+
positive verdict is never sourced from a stale artifact.
|
|
481
|
+
- R3 false-incomplete (rebuilds something already done): bounded -- worst case is
|
|
482
|
+
today's behavior (it rebuilds). The incremental manifest only ever REDUCES
|
|
483
|
+
work; if the model is unsure it marks `uncertain` (not satisfied) and that
|
|
484
|
+
requirement is rebuilt. No regression vs status quo.
|
|
485
|
+
- R4 model cost on every reuse run: the negative fast-path skips the call
|
|
486
|
+
entirely on projects with no completion footprint (the common case for a fresh
|
|
487
|
+
reuse that is clearly not done is rare; the common reuse over a built project
|
|
488
|
+
pays one bounded call). Timeout-bounded (LOKI_DONE_RECOG_TIMEOUT). On non-
|
|
489
|
+
claude/degraded providers the call is skipped (inconclusive -> build).
|
|
490
|
+
- R5 manifest/PRD identity drift: manifest is guarded by `prd_sha`; a mismatch is
|
|
491
|
+
ignored and a full build runs. Keying on feature title (not positional
|
|
492
|
+
prd-NNN) avoids index-shift misskips.
|
|
493
|
+
- R6 finalization ordering: the gate runs after load_state and before the
|
|
494
|
+
delegate-branch/start-sha block, so a done verdict never spawns a delegate
|
|
495
|
+
branch or moves the diff window. Verify final line placement at edit time
|
|
496
|
+
against the live ordering of 15238 (load_state), the delegate block (~15259),
|
|
497
|
+
and 15439 (populate_prd_queue).
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## 12. Files touched (for the eventual implementation, not this doc)
|
|
502
|
+
|
|
503
|
+
- autonomy/lib/done-recognition.sh (NEW): `_loki_done_recog_invoke`,
|
|
504
|
+
`_loki_done_recog_provider_ok`, `reuse_done_recognition_gate`, payload/parse
|
|
505
|
+
python heredocs. Mirrors autonomy/lib/prd-enrich.sh.
|
|
506
|
+
- autonomy/run.sh:
|
|
507
|
+
- new call site after ~15319 (source the lib like 14931-14937; arm the gate per
|
|
508
|
+
section 3 guard).
|
|
509
|
+
- populate_prd_queue (14580): one manifest read-point + per-feature skip near
|
|
510
|
+
14878 (section 6.2).
|
|
511
|
+
- reuse the existing finalization functions (3367, 3516, 1653, 13190, 17692)
|
|
512
|
+
and completion-summary writer `build_completion_summary` (3026, which writes
|
|
513
|
+
3196-3210) on the done path -- no new completion code.
|
|
514
|
+
- tests/test-reuse-done-recognition.sh (NEW).
|
|
515
|
+
- scripts/local-ci.sh: register the new test.
|
|
516
|
+
- CHANGELOG.md + version-bump locations: per the repo's standard bump list (see
|
|
517
|
+
the prior reuse design's section 9 for the canonical 14-location list).
|
|
518
|
+
- Docs: a short note in the no-PRD reuse documentation that a reuse run now
|
|
519
|
+
verifies whether the project is already done and stops fast if so.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## 13. Divergences from the prior design (loki-plan/v7734-prd-reuse-design.md)
|
|
524
|
+
|
|
525
|
+
That doc designed the reuse/update/generate DECISION and the codebase signature.
|
|
526
|
+
It is now implemented (decide_generated_prd_action at run.sh:5590). This plan
|
|
527
|
+
builds the next layer ON TOP of its `reuse` outcome:
|
|
528
|
+
- The prior doc's `reuse` ends at "point prd_path at the generated PRD and run."
|
|
529
|
+
This plan inserts the done-recognition gate between that and the queue/loop.
|
|
530
|
+
- The prior doc relied on a deterministic signature to decide reuse-vs-update.
|
|
531
|
+
This plan deliberately does NOT add any deterministic done-shortcut; the
|
|
532
|
+
done/incomplete TRUST decision is the model's, grounded in re-run tests + code,
|
|
533
|
+
precisely because the signature/checklist artifacts proved untrustworthy as a
|
|
534
|
+
done signal (the 14-vs-19 checklist desync in the live bug). The signature
|
|
535
|
+
still does its job (reuse-vs-update); the new gate adds the satisfied-or-not
|
|
536
|
+
judgment the signature was never meant to make.
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var z8=Object.defineProperty;var X8=($)=>$;function K8($,Q){this[$]=X8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)z8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:K8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var y1={};b(y1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as t,dirname as i$}from"path";import{fileURLToPath as q8}from"url";import{existsSync as E$}from"fs";import{homedir as J8}from"os";function V8(){let $=h1;for(let Q=0;Q<6;Q++){if(E$(t($,"VERSION"))&&E$(t($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return t(h1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(E$(t(Q,"VERSION"))&&E$(t(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function P(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(J8(),".loki")}var h1,h;var C=L(()=>{h1=i$(q8(import.meta.url));h=V8()});import{readFileSync as W8}from"fs";import{resolve as U8,dirname as H8}from"path";import{fileURLToPath as G8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var z8=Object.defineProperty;var X8=($)=>$;function K8($,Q){this[$]=X8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)z8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:K8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var y1={};b(y1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as t,dirname as i$}from"path";import{fileURLToPath as q8}from"url";import{existsSync as E$}from"fs";import{homedir as J8}from"os";function V8(){let $=h1;for(let Q=0;Q<6;Q++){if(E$(t($,"VERSION"))&&E$(t($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return t(h1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(E$(t(Q,"VERSION"))&&E$(t(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function P(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(J8(),".loki")}var h1,h;var C=L(()=>{h1=i$(q8(import.meta.url));h=V8()});import{readFileSync as W8}from"fs";import{resolve as U8,dirname as H8}from"path";import{fileURLToPath as G8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.95.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=H8(G8(import.meta.url)),Z=e$(Q);Q$=W8(U8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var u1={};b(u1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>f1,commandVersion:()=>N8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>m1});async function f1($,Q=m1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([f1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=E8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function E8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function N8($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var m1=16777216,Q1;var d=L(()=>{Q1=class Q1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return S8?"":$}var S8,T,S,_,lZ,I,k,y,J;var c=L(()=>{S8=(process.env.NO_COLOR??"").length>0;T=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),lZ=r("\x1B[0;34m"),I=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as u8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(u8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var q0={};b(q0,{runStatus:()=>W3});import{existsSync as v,readFileSync as U$,readdirSync as i1,statSync as e1}from"fs";import{resolve as D,basename as $3}from"path";import{homedir as Q3}from"os";function $0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function Q0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=$0($),U=$0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function z3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -802,4 +802,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
802
802
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
803
803
|
`),process.stderr.write(Z8),2}}r1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var FZ=await jZ(Bun.argv.slice(2));process.exit(FZ);
|
|
804
804
|
|
|
805
|
-
//# debugId=
|
|
805
|
+
//# debugId=184A380A5462265664756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.95.0",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.95.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|