@ngockhoale/ukit 2.5.1 → 2.6.2
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/CHANGELOG.md +80 -0
- package/manifests/platform.full.yaml +16 -0
- package/package.json +1 -1
- package/src/cli/commands/install.js +49 -2
- package/src/cli/commands/update.js +5 -0
- package/src/core/output/index.js +77 -0
- package/src/core/update.js +36 -5
- package/templates/.claude/agents/code-reviewer.md +12 -1
- package/templates/.claude/agents/feature-implementer.md +4 -2
- package/templates/.claude/agents/handoff-planner.md +36 -1
- package/templates/.claude/commands/ukit/handoff-clear.md +4 -0
- package/templates/.claude/commands/ukit/handoff-create.md +23 -7
- package/templates/.claude/commands/ukit/handoff-fullstack.md +181 -17
- package/templates/.claude/commands/ukit/handoff-implement.md +9 -2
- package/templates/.claude/commands/ukit/handoff-review.md +4 -1
- package/templates/.claude/commands/ukit/handoff-status.md +6 -2
- package/templates/.claude/hooks/auto-allow-bash.sh +17 -5
- package/templates/.claude/hooks/block-dangerous.sh +18 -5
- package/templates/.claude/hooks/completion-gate.sh +17 -5
- package/templates/.claude/hooks/compress-output.sh +15 -6
- package/templates/.claude/hooks/context-hardcap-gate.sh +77 -46
- package/templates/.claude/hooks/context-window-guard.sh +41 -27
- package/templates/.claude/hooks/handoff-model-guard.sh +62 -14
- package/templates/.claude/hooks/handoff-resume.sh +20 -7
- package/templates/.claude/hooks/post-edit-verify.sh +17 -5
- package/templates/.claude/hooks/pre-edit-backup.sh +17 -5
- package/templates/.claude/hooks/project-important.sh +18 -1
- package/templates/.claude/hooks/protect-files.sh +18 -5
- package/templates/.claude/hooks/record-execution.sh +17 -5
- package/templates/.claude/hooks/sensitive-data-guard.sh +48 -9
- package/templates/.claude/hooks/skill-router.sh +124 -86
- package/templates/.claude/hooks/stale-spec-guard.sh +22 -6
- package/templates/.claude/hooks/task-watchdog.sh +22 -7
- package/templates/.claude/hooks/verification-guard.sh +17 -5
- package/templates/.claude/hooks/vision-router.sh +17 -5
- package/templates/.claude/settings.json +15 -10
- package/templates/.claude/ukit/index/provision-worktree.mjs +30 -2
- package/templates/.claude/ukit/runtime/execution-ledger.mjs +99 -1
- package/templates/.claude/ukit/runtime/hook-input.sh +25 -0
- package/templates/.claude/ukit/runtime/hook-telemetry.mjs +86 -2
- package/templates/.claude/ukit/runtime/output-compression.mjs +87 -0
- package/templates/.claude/ukit/runtime/stop-coordinator.mjs +201 -11
- package/templates/.omp/RULES.md +9 -1
- package/templates/.omp/agents/code-reviewer.md +12 -1
- package/templates/.omp/agents/feature-implementer.md +4 -2
- package/templates/.omp/agents/handoff-planner.md +36 -1
- package/templates/.omp/hooks/pre/ukit-bridge.js +110 -3
- package/templates/AGENTS.md +14 -0
- package/templates/CLAUDE.md +14 -0
- package/templates/docs/AI_HANDOFF/RULES.md +37 -4
- package/templates/docs/AI_HANDOFF/SPEC.md +98 -0
- package/templates/docs/AI_HANDOFF/tasks/_TEMPLATE.md +4 -1
- package/templates/ukit/storage/config.json +48 -9
|
@@ -66,9 +66,10 @@ numbered step completes:
|
|
|
66
66
|
Command: handoff-fullstack
|
|
67
67
|
Goal: <one sentence from the **Problem / feature** section above>
|
|
68
68
|
Base: <BASE branch>
|
|
69
|
-
Phase: <P1|P2|P2.5|P3|I1|I2|I3|I4|R1|R2|R3|R4|R5|done>
|
|
69
|
+
Phase: <P0|P1|P2|P2.5|P3|I1|I2|I3|I4|R1|R2|R3|R4|R4.5|R5|F|done|blocked>
|
|
70
70
|
Cursor: wave <N> batch <M> — <what just finished>
|
|
71
71
|
Next: <the exact next step to run>
|
|
72
|
+
QuietScans: <n>/<required> # only while sweeping for stragglers near the end
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
This file is the resume contract. It costs one small write per step and is what turns an
|
|
@@ -76,13 +77,99 @@ interrupted run into a continuable one.
|
|
|
76
77
|
|
|
77
78
|
### Resume — when the run is re-invoked mid-flight
|
|
78
79
|
|
|
79
|
-
If `docs/AI_HANDOFF/RUN.md` exists with `Phase:` not `done`, this is a
|
|
80
|
-
new cycle**. Do NOT re-plan, do NOT overwrite `PLAN.md`, do NOT ask
|
|
81
|
-
continue. Read the cursor, then jump straight to `Next:` and carry on.
|
|
82
|
-
or `pending_review` are skipped; only `ready`, `in_progress`,
|
|
83
|
-
`blocked` tasks are picked up.
|
|
80
|
+
If `docs/AI_HANDOFF/RUN.md` exists with `Phase:` not `done` or `blocked`, this is a
|
|
81
|
+
**continuation, not a new cycle**. Do NOT re-plan, do NOT overwrite `PLAN.md`, do NOT ask
|
|
82
|
+
the human whether to continue. Read the cursor, then jump straight to `Next:` and carry on.
|
|
83
|
+
Tasks already `done` or `pending_review` are skipped; only `ready`, `in_progress`,
|
|
84
|
+
`changes_requested` and `blocked` tasks are picked up.
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
`Phase: blocked` is terminal for the run — the machinery (Stop gate, hooks, stop-coordinator)
|
|
87
|
+
all treats it as closed. It is *paused*, not abandoned: an explicit `/ukit:handoff-fullstack`
|
|
88
|
+
re-invocation resumes it as a continuation; `ukit handoff-clear` abandons it. Only when
|
|
89
|
+
`RUN.md` is absent or `Phase: done` does a new request start a fresh cycle.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## §0 — Completion contract (terminal outputs)
|
|
94
|
+
|
|
95
|
+
This run has exactly **two** legal ways to end:
|
|
96
|
+
|
|
97
|
+
- `HANDOFF FULLSTACK COMPLETE` — the completion gate in Phase F passed. First line of the
|
|
98
|
+
final report, verbatim.
|
|
99
|
+
- `HANDOFF FULLSTACK BLOCKED` — every remaining task depends on the same external blocker
|
|
100
|
+
(missing credential, dead service, permission wall) AND no safe local work exists. Write
|
|
101
|
+
`Phase: blocked` to RUN.md before emitting it so the Stop gate releases the session.
|
|
102
|
+
|
|
103
|
+
Nothing else ends the run. A recap is a progress notification, not a stopping point. If you
|
|
104
|
+
emit a checkpoint it MUST use this shape and be followed by immediate work:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
CHECKPOINT — WORK CONTINUING
|
|
108
|
+
Cycle:
|
|
109
|
+
Completed since last checkpoint:
|
|
110
|
+
Currently executing:
|
|
111
|
+
Exact next action:
|
|
112
|
+
Verification status:
|
|
113
|
+
Run cursor updated: YES
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Never stop at `recap: ... Next: ...` while unfinished work remains — the Stop gate now
|
|
117
|
+
bounces that stop straight back into the pipeline, so stopping is not even restful; it
|
|
118
|
+
just costs a round-trip.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Phase 0 — Sweep & Inventory (before planning)
|
|
123
|
+
|
|
124
|
+
Before Phase 1, build the authoritative task inventory — the run owns **all** unfinished
|
|
125
|
+
work, not only the current plan:
|
|
126
|
+
|
|
127
|
+
1. `docs/AI_HANDOFF/INDEX.md` — every task not `done`/`cancelled_superseded`.
|
|
128
|
+
2. `docs/AI_HANDOFF/tasks/TASK-*.md` — stale `in_progress` from a dead session, `blocked`,
|
|
129
|
+
`changes_requested`, `needs_executor_report`, `needs_breakdown`.
|
|
130
|
+
3. Previous cycles — `docs/AI_HANDOFF/HISTORY.md` and `archive/` for cycles closed with
|
|
131
|
+
unfinished tasks; their leftovers join THIS cycle.
|
|
132
|
+
4. `docs/TASKS.md` — `Ready for AI` items are newly-assigned work; fold them into the plan.
|
|
133
|
+
5. `git status` / `git diff` — uncommitted work-in-progress that must be finished or
|
|
134
|
+
checkpointed, never silently dropped.
|
|
135
|
+
|
|
136
|
+
The inventory feeds P2: the planner either schedules every discovered item or records why
|
|
137
|
+
it is out of scope (§1/§2 of PLAN.md). Silent omission is a plan defect.
|
|
138
|
+
|
|
139
|
+
**Recovery rule — stuck task records.** For every `pending`, stale `in_progress`, or
|
|
140
|
+
`blocked` task whose record cannot be cleanly resumed (invalid state, orphaned worktree
|
|
141
|
+
gone, contradicting reports):
|
|
142
|
+
|
|
143
|
+
1. Inspect code/tests/git to see what is actually missing.
|
|
144
|
+
2. Recoverable → finish the original task in place.
|
|
145
|
+
3. Not recoverable → mark the old row `cancelled_superseded`, create `TASK-xxx-R1`
|
|
146
|
+
(`-R2`, …) with the same spec references, acceptance criteria and verification, link
|
|
147
|
+
both files' `## Discussion` threads, and execute the replacement in this cycle.
|
|
148
|
+
4. Never leave stranded work merely because a task record was pending.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Watchdog — surviving idle and new assignments
|
|
153
|
+
|
|
154
|
+
The run must keep itself alive without the human watching.
|
|
155
|
+
|
|
156
|
+
- **Stop gate (Claude Code, automatic):** while `RUN.md` `Phase:` is not `done`/`blocked`,
|
|
157
|
+
`completion-gate.sh` refuses the stop with the cursor's `Next:` step. Recaps and
|
|
158
|
+
premature stops cannot park the run. A stalled-cursor breaker
|
|
159
|
+
(`handoff.fullstack.stopGateMaxStalledBlocks`, default 12) releases the session if the
|
|
160
|
+
cursor has genuinely stopped advancing — the escape hatch, not the norm.
|
|
161
|
+
- **Scheduled wakeup (when the harness offers one):** arm it at run start — Claude Code:
|
|
162
|
+
`CronCreate` a ~`handoff.fullstack.idleWatchdogMin`-minute (default 5) session job with
|
|
163
|
+
prompt `Resume HANDOFF FULLSTACK from docs/AI_HANDOFF/RUN.md — continue the Next: step`;
|
|
164
|
+
`/loop`-style tools: the equivalent interval. Cancel/let it expire once `Phase: done`.
|
|
165
|
+
- **Harnesses with neither:** treat EVERY activation as a watchdog recovery turn — read
|
|
166
|
+
RUN.md first, rescan the inventory, continue `Next:`.
|
|
167
|
+
|
|
168
|
+
**Quiet-period rule.** The run may finish only after the backlog is empty AND
|
|
169
|
+
`handoff.fullstack.quietScansRequired` (default 2) consecutive watchdog scans find no new
|
|
170
|
+
assignment, no recoverable task, and no uncommitted intended change. Record each quiet
|
|
171
|
+
scan in RUN.md as `QuietScans: <n>/<required>`. A scan that finds anything resets the
|
|
172
|
+
counter and starts a new cycle automatically.
|
|
86
173
|
|
|
87
174
|
---
|
|
88
175
|
|
|
@@ -117,7 +204,15 @@ The planner agent does the following (use P1 summary — do NOT re-read files):
|
|
|
117
204
|
BASE=$(git symbolic-ref --short HEAD)
|
|
118
205
|
```
|
|
119
206
|
|
|
120
|
-
3. **Write `docs/AI_HANDOFF/
|
|
207
|
+
3. **Write `docs/AI_HANDOFF/SPEC.md`** — the detailed implementation spec, from
|
|
208
|
+
`templates/docs/AI_HANDOFF/SPEC.md`'s section list (or RULES.md §Spec). It must be
|
|
209
|
+
concrete enough that an executor implements without guessing: exact paths, module and
|
|
210
|
+
API names, schemas, validation rules, permissions, empty/error states, migration
|
|
211
|
+
behavior, and test expectations — every applicable section filled, open questions
|
|
212
|
+
resolved to chosen defaults recorded inline. Implementation begins from SPEC+PLAN,
|
|
213
|
+
never from the raw request.
|
|
214
|
+
|
|
215
|
+
4. **Write `docs/AI_HANDOFF/PLAN.md`** — all 6 sections mandatory:
|
|
121
216
|
- §1 Intent — problem + success definition
|
|
122
217
|
- §2 Scope — in / out of scope; same-wave tasks must not modify the same file (prevents merge conflicts)
|
|
123
218
|
- §3 Approach — solution, trade-offs, alternatives rejected
|
|
@@ -131,8 +226,9 @@ The planner agent does the following (use P1 summary — do NOT re-read files):
|
|
|
131
226
|
PLANNER_MODEL: <your exact model ID>
|
|
132
227
|
```
|
|
133
228
|
|
|
134
|
-
|
|
229
|
+
5. **Create `docs/AI_HANDOFF/tasks/TASK-001.md`, `TASK-002.md`...** from `_TEMPLATE.md`.
|
|
135
230
|
Every task MUST have:
|
|
231
|
+
- Spec references (SPEC.md section IDs this task implements)
|
|
136
232
|
- Target Files (exact paths — no two tasks in same wave share a file)
|
|
137
233
|
- Dependencies (`TASK-xxx` or `none` — wave structure inferred from this)
|
|
138
234
|
- Test Cases (Type | Name | Expected — ≥1 happy + ≥2 edge cases of different kinds)
|
|
@@ -141,17 +237,21 @@ The planner agent does the following (use P1 summary — do NOT re-read files):
|
|
|
141
237
|
- Acceptance Criteria (verifiable checklist)
|
|
142
238
|
Missing any field → status `needs_breakdown`, never `ready`.
|
|
143
239
|
|
|
144
|
-
|
|
240
|
+
6. **Update `docs/AI_HANDOFF/INDEX.md`** — one row per task, `status=ready`. Every
|
|
241
|
+
unfinished item from the Phase 0 sweep is either a row here or superseded by a
|
|
242
|
+
`-R<n>` recovery row.
|
|
145
243
|
|
|
146
|
-
|
|
244
|
+
7. **Update `docs/AI_HANDOFF/ACTIVE.md`:**
|
|
147
245
|
```
|
|
148
246
|
Cycle: <ID> Date: <YYYY-MM-DD> Base: <BASE>
|
|
149
247
|
Goal: <1 sentence>
|
|
248
|
+
Spec: docs/AI_HANDOFF/SPEC.md
|
|
150
249
|
Tasks: <N> total
|
|
151
250
|
Status: planning_done — ready for executor
|
|
152
251
|
```
|
|
153
252
|
|
|
154
|
-
|
|
253
|
+
8. **Report:** task IDs, dependency graph, recovery/superseded tasks, any
|
|
254
|
+
`needs_breakdown` tasks + reason.
|
|
155
255
|
|
|
156
256
|
### P2.5 — Independent plan review (strong model, separate agent)
|
|
157
257
|
|
|
@@ -164,9 +264,9 @@ The planner agent does the following (use P1 summary — do NOT re-read files):
|
|
|
164
264
|
The gate still does its job — two independent opus passes shape the plan before a line of code
|
|
165
265
|
is written. What it no longer does is hand a stalled plan back to a human who isn't there.
|
|
166
266
|
|
|
167
|
-
**Claude Code — MANDATORY, do this before anything else in P2.5:** call the Agent tool with `subagent_type: "code-reviewer"` (omp: the `task` tool with `agent: "code-reviewer"`), passing `REVIEW_TARGET_TYPE=plan` and the
|
|
267
|
+
**Claude Code — MANDATORY, do this before anything else in P2.5:** call the Agent tool with `subagent_type: "code-reviewer"` (omp: the `task` tool with `agent: "code-reviewer"`), passing `REVIEW_TARGET_TYPE=plan` and the paths to `docs/AI_HANDOFF/PLAN.md` AND `docs/AI_HANDOFF/SPEC.md`. This MUST be a separate agent invocation from P2's `handoff-planner` call (fresh context) — same-session self-review defeats the purpose of an independent gate.
|
|
168
268
|
|
|
169
|
-
1. Reviewer reads `PLAN.md` only (no diff, no task files, no executor report), checks Completeness / Consistency / Clarity / Scope / YAGNI — see `.claude/agents/code-reviewer.md` → Spec/Plan Review — and appends its verdict to PLAN.md's `## Plan Review Log` (new round entry, prior rounds kept).
|
|
269
|
+
1. Reviewer reads `SPEC.md` + `PLAN.md` only (no diff, no task files, no executor report), checks Completeness / Consistency / Clarity / Scope / YAGNI plus the spec quality gate — every requirement testable, every fullstack layer covered, dependencies explicit, legacy sweep included, no vague instruction left ("improve UI" without defined behavior fails) — see `.claude/agents/code-reviewer.md` → Spec/Plan Review — and appends its verdict to PLAN.md's `## Plan Review Log` (new round entry, prior rounds kept).
|
|
170
270
|
2. `Issues Found` → route back to P2: `handoff-planner` revises `PLAN.md` and the affected `TASK-xxx.md` files to address every finding, then re-submit for one more P2.5 review round — subject to the loop cap above.
|
|
171
271
|
3. `Approved` → append `PLAN_REVIEW: Approved by <reviewer model>` to PLAN.md's `## Planner Report` footer, then proceed to P3.
|
|
172
272
|
|
|
@@ -510,9 +610,50 @@ Force-push stays denied, so this can only ever fast-forward.
|
|
|
510
610
|
|
|
511
611
|
**If some tasks are `blocked`:** still push. The approved work is reviewed, tested, and
|
|
512
612
|
committed; withholding it helps no one, and the per-wave commits make any subset revertible.
|
|
513
|
-
Name the blocked tasks in the Final Report.
|
|
613
|
+
Name the blocked tasks in the Final Report. Tasks that stayed `blocked` only because their
|
|
614
|
+
record was unrecoverable get the Phase 0 recovery treatment — `cancelled_superseded` plus a
|
|
615
|
+
`-R<n>` replacement — before the run may close.
|
|
616
|
+
|
|
617
|
+
Do NOT set `Phase: done` yet — Phase F still has to close the books.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## Phase F — Finalize: quiet period, docs, archive
|
|
622
|
+
|
|
623
|
+
### F1 — Quiet-period scan
|
|
624
|
+
|
|
625
|
+
Run the sweep from Phase 0 again (INDEX, task files, `docs/TASKS.md`, git status).
|
|
626
|
+
`handoff.fullstack.quietScansRequired` (default 2) consecutive scans must each find:
|
|
627
|
+
no unfinished task, no new assignment, no uncommitted intended change. Record each scan
|
|
628
|
+
in RUN.md (`QuietScans: <n>/<required>`). Anything found resets the counter — fold it in
|
|
629
|
+
through the normal pipeline and keep going.
|
|
630
|
+
|
|
631
|
+
### F2 — Docs sync
|
|
632
|
+
|
|
633
|
+
Update only the docs the change actually touched: `docs/WORKLOG.md` (always — one entry
|
|
634
|
+
for the cycle), plus `docs/PROJECT.md` / `docs/CODE_MAP.md` / `docs/CHANGELOG.md` when the
|
|
635
|
+
cycle changed architecture, surface area, or shipped behavior. Record: behavior
|
|
636
|
+
implemented, API/schema/UI changes, migration steps, config changes, verification
|
|
637
|
+
evidence, rollback notes.
|
|
638
|
+
|
|
639
|
+
### F3 — Archive the cycle
|
|
514
640
|
|
|
515
|
-
|
|
641
|
+
When `handoff.fullstack.autoArchive` is true (default), archive per `handoff-clear`
|
|
642
|
+
conventions so a future session needs no transcript to understand the cycle:
|
|
643
|
+
|
|
644
|
+
1. Move the completed cycle's records (PLAN.md, SPEC.md, task files, ACTIVE.md snapshot)
|
|
645
|
+
into `docs/AI_HANDOFF/archive/cycle-<NN>/`.
|
|
646
|
+
2. If `archive/` holds more than 3 cycle folders → fold the oldest into a one-line
|
|
647
|
+
summary in `HISTORY.md` and remove it.
|
|
648
|
+
3. Reset `ACTIVE.md` to its empty template; clear `INDEX.md` rows to a fresh header;
|
|
649
|
+
remove `tasks/TASK-*.md`; clear `PLAN.md`/`SPEC.md` (templates stay).
|
|
650
|
+
4. Commit the docs + archive changes:
|
|
651
|
+
`git add -A && git commit -m "handoff: finalize + archive cycle <ID>"`
|
|
652
|
+
|
|
653
|
+
### F4 — Close the run
|
|
654
|
+
|
|
655
|
+
Set `Phase: done` in `docs/AI_HANDOFF/RUN.md`, disarm any watchdog job armed at the
|
|
656
|
+
start, and emit the Final Report below.
|
|
516
657
|
|
|
517
658
|
---
|
|
518
659
|
|
|
@@ -539,17 +680,40 @@ Re-run assertions after removal to confirm clean state.
|
|
|
539
680
|
|
|
540
681
|
## Final Report
|
|
541
682
|
|
|
683
|
+
The ONLY legal first lines are:
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
HANDOFF FULLSTACK COMPLETE
|
|
687
|
+
```
|
|
688
|
+
or, when every remaining task shares one external blocker and no safe local work exists
|
|
689
|
+
(RUN.md set to `Phase: blocked` first):
|
|
690
|
+
```
|
|
691
|
+
HANDOFF FULLSTACK BLOCKED
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Then the body:
|
|
695
|
+
|
|
542
696
|
```
|
|
543
|
-
handoff-fullstack complete:
|
|
544
697
|
Cycle: <ID>
|
|
698
|
+
Spec: docs/AI_HANDOFF/SPEC.md
|
|
545
699
|
Tasks: <N> approved, <M> blocked
|
|
700
|
+
Recovery/superseded: <TASK-xxx→TASK-xxx-R1, …> | none
|
|
701
|
+
Newly assigned processed: <n> | none
|
|
702
|
+
Quiet scans: <n>/<required>
|
|
546
703
|
Fix rounds used: <0|1|2>
|
|
704
|
+
Verification: <commands + results>
|
|
547
705
|
Git: <K> wave commits + pushed to <branch>
|
|
706
|
+
Docs updated: <files>
|
|
707
|
+
Archive: docs/AI_HANDOFF/archive/cycle-<NN>/
|
|
548
708
|
Worktrees: all cleaned
|
|
549
709
|
Branches: all cleaned
|
|
710
|
+
Remaining tasks: NONE | <list>
|
|
550
711
|
Blocked (needs you): <TASK-xxx — one-line reason> | none
|
|
551
712
|
```
|
|
552
713
|
|
|
714
|
+
A blocked report additionally lists: blocked tasks, exact blocker + evidence, attempted
|
|
715
|
+
fixes, local work completed, and the recovery instructions for the next session.
|
|
716
|
+
|
|
553
717
|
Then, as the last line of the run:
|
|
554
718
|
|
|
555
719
|
> Cycle finished. Run `/compact` before starting the next cycle — this session is carrying
|
|
@@ -59,8 +59,12 @@ If this file already exists with `Phase:` not `done` when the command starts, th
|
|
|
59
59
|
|
|
60
60
|
Read `docs/AI_HANDOFF/ACTIVE.md` → get `Base: <BASE>`.
|
|
61
61
|
Read `docs/AI_HANDOFF/INDEX.md` → collect `ready` tasks (or the specific task named in the request above).
|
|
62
|
-
If there are no `ready` tasks, check for `changes_requested`/`blocked`
|
|
63
|
-
instead.
|
|
62
|
+
If there are no `ready` tasks, check for `changes_requested`/`blocked`/stale `in_progress`
|
|
63
|
+
ones and run those instead. A stuck record that cannot resume (orphaned worktree,
|
|
64
|
+
contradicting reports) gets the recovery treatment: mark it `cancelled_superseded`, create
|
|
65
|
+
`TASK-xxx-R1` with the same spec references/acceptance/verification, and run the
|
|
66
|
+
replacement — never leave stranded work just because a record was pending.
|
|
67
|
+
Only if nothing is actionable → report and stop.
|
|
64
68
|
|
|
65
69
|
Verify working tree state:
|
|
66
70
|
```bash
|
|
@@ -125,6 +129,9 @@ Each spawned agent works independently in its own worktree — **NO git commit,
|
|
|
125
129
|
|
|
126
130
|
```
|
|
127
131
|
Read docs/AI_HANDOFF/tasks/TASK-xxx.md
|
|
132
|
+
Read the SPEC.md sections named in the task's `Spec references` — the spec is the contract;
|
|
133
|
+
the task file is the slice. If they disagree, implement the spec and note it in the task's
|
|
134
|
+
## Discussion.
|
|
128
135
|
|
|
129
136
|
TDD — mandatory:
|
|
130
137
|
1. Write tests from §Test Cases
|
|
@@ -86,7 +86,10 @@ git status # overview of modified/new/deleted files
|
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Review as one unified diff — correctness, regression risk, security, edge cases, maintainability.
|
|
89
|
-
Cross-reference
|
|
89
|
+
Cross-reference each task's intent in `docs/AI_HANDOFF/tasks/TASK-xxx.md` AND the
|
|
90
|
+
`Spec references` sections it names in `docs/AI_HANDOFF/SPEC.md` — spec compliance is the
|
|
91
|
+
primary correctness bar; a diff that drifts from the spec's contract fails even when it
|
|
92
|
+
matches the task file's wording.
|
|
90
93
|
|
|
91
94
|
### 2d — Append verdict to each task file
|
|
92
95
|
|
|
@@ -12,8 +12,10 @@ Read and display current handoff state. Do NOT modify any files.
|
|
|
12
12
|
|
|
13
13
|
1. `docs/AI_HANDOFF/ACTIVE.md` → cycle ID, goal, base branch
|
|
14
14
|
2. `docs/AI_HANDOFF/INDEX.md` → all tasks + statuses
|
|
15
|
-
3. `
|
|
16
|
-
4. `
|
|
15
|
+
3. `docs/AI_HANDOFF/RUN.md` → fullstack run cursor: `Phase:` ≠ `done`/`blocked` means a run is live and the Stop gate is holding the session to it
|
|
16
|
+
4. `docs/AI_HANDOFF/SPEC.md` → spec exists/fresh (one line)
|
|
17
|
+
5. `git worktree list` → open worktrees
|
|
18
|
+
6. `git branch | grep handoff/` → open handoff branches
|
|
17
19
|
|
|
18
20
|
## Report
|
|
19
21
|
|
|
@@ -21,6 +23,8 @@ Read and display current handoff state. Do NOT modify any files.
|
|
|
21
23
|
━━━ Handoff Status ━━━━━━━━━━━━━━━━━━━━━━━━
|
|
22
24
|
Cycle: <ID> Base: <branch> Date: <date>
|
|
23
25
|
Goal: <goal>
|
|
26
|
+
Run: <Phase — or "no live cursor">
|
|
27
|
+
Spec: <present | missing>
|
|
24
28
|
|
|
25
29
|
Tasks:
|
|
26
30
|
✅ done TASK-001 <name>
|
|
@@ -11,7 +11,15 @@ if source "$SCRIPT_DIR/../ukit/runtime/hook-input.sh" 2>/dev/null; then
|
|
|
11
11
|
source "$SCRIPT_DIR/../ukit/runtime/hook-telemetry.sh" 2>/dev/null || true
|
|
12
12
|
trap ukit_cleanup_hook_input EXIT
|
|
13
13
|
# Bounded stdin (H01): stage before anything reads it; the shell var is capped.
|
|
14
|
-
ukit_stage_hook_input 2097152 truncate
|
|
14
|
+
ukit_stage_hook_input 2097152 truncate
|
|
15
|
+
__ukit_main_stage_rc=$?
|
|
16
|
+
if [ "$__ukit_main_stage_rc" -ne 0 ]; then
|
|
17
|
+
# Infra failure during staging (mktemp/truncate): payload was never
|
|
18
|
+
# inspected — announce the degrade (SPEC §8), never a silent pass.
|
|
19
|
+
ukit_emit_input_degraded advisory "auto-allow-bash"
|
|
20
|
+
fi
|
|
21
|
+
# BUG-C21-01: a truncated/stalled staged payload must be announced (SPEC §8), never silently passed.
|
|
22
|
+
ukit_input_degraded && ukit_emit_input_degraded advisory "auto-allow-bash"
|
|
15
23
|
else
|
|
16
24
|
# Runtime helper missing (pre-install tree): the SAME bounded staging,
|
|
17
25
|
# inline - `cat >/dev/null` used to block forever on a producer that never
|
|
@@ -38,10 +46,14 @@ else
|
|
|
38
46
|
wait "$__ukit_waiter" 2>/dev/null
|
|
39
47
|
exec 8<&-
|
|
40
48
|
__ukit_size="$(wc -c < "$UKIT_INPUT_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if [ "$__ukit_stage_rc" -ne 0 ] || [ "$__ukit_size" -gt 2097152 ]; then
|
|
50
|
+
# BUG-C21-01 fallback parity (SPEC §8): a truncated/stalled staged payload
|
|
51
|
+
# must be announced, never silently acted on — same shape as the helper
|
|
52
|
+
# path's ukit_emit_input_degraded.
|
|
53
|
+
rm -f "$UKIT_INPUT_FILE"
|
|
54
|
+
UKIT_INPUT_FILE=""
|
|
55
|
+
printf '%s\n' '{"systemMessage":"UKit auto-allow-bash: tool-call payload was truncated or stalled during stdin staging; skipped this pass rather than act on incomplete input."}'
|
|
56
|
+
exit 0
|
|
45
57
|
fi
|
|
46
58
|
trap '[ -n "$UKIT_INPUT_FILE" ] && rm -f "$UKIT_INPUT_FILE"' EXIT
|
|
47
59
|
fi
|
|
@@ -11,7 +11,15 @@ if source "$SCRIPT_DIR/../ukit/runtime/hook-input.sh" 2>/dev/null; then
|
|
|
11
11
|
source "$SCRIPT_DIR/../ukit/runtime/hook-telemetry.sh" 2>/dev/null || true
|
|
12
12
|
trap ukit_cleanup_hook_input EXIT
|
|
13
13
|
# Bounded stdin (H01): stage before anything reads it; the shell var is capped.
|
|
14
|
-
ukit_stage_hook_input 2097152 truncate
|
|
14
|
+
ukit_stage_hook_input 2097152 truncate
|
|
15
|
+
__ukit_main_stage_rc=$?
|
|
16
|
+
if [ "$__ukit_main_stage_rc" -ne 0 ]; then
|
|
17
|
+
# Infra failure during staging (mktemp/truncate): payload was never
|
|
18
|
+
# inspected — announce the degrade (SPEC §8), never a silent pass.
|
|
19
|
+
ukit_emit_input_degraded failclosed "dangerous-command gate"
|
|
20
|
+
fi
|
|
21
|
+
# BUG-C21-01: a truncated/stalled staged payload must be announced (SPEC §8), never silently passed.
|
|
22
|
+
ukit_input_degraded && ukit_emit_input_degraded failclosed "dangerous-command gate"
|
|
15
23
|
else
|
|
16
24
|
# Runtime helper missing (pre-install tree): the SAME bounded staging,
|
|
17
25
|
# inline - `cat >/dev/null` used to block forever on a producer that never
|
|
@@ -38,10 +46,15 @@ else
|
|
|
38
46
|
wait "$__ukit_waiter" 2>/dev/null
|
|
39
47
|
exec 8<&-
|
|
40
48
|
__ukit_size="$(wc -c < "$UKIT_INPUT_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if [ "$__ukit_stage_rc" -ne 0 ] || [ "$__ukit_size" -gt 2097152 ]; then
|
|
50
|
+
# BUG-C21-01 fallback parity (SPEC §8): a truncated/stalled staged payload
|
|
51
|
+
# must be announced, never silently acted on — same shape as the helper
|
|
52
|
+
# path's ukit_emit_input_degraded.
|
|
53
|
+
rm -f "$UKIT_INPUT_FILE"
|
|
54
|
+
UKIT_INPUT_FILE=""
|
|
55
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"UKit could not fully read this tool-call payload (stdin staging was cut at the deadline/size bound), so dangerous-command gate cannot prove it safe. UKit defers this to a human decision."}}'
|
|
56
|
+
echo "BLOCKED: dangerous-command gate could not inspect a truncated/stalled payload; deferred to human." >&2
|
|
57
|
+
exit 2
|
|
45
58
|
fi
|
|
46
59
|
trap '[ -n "$UKIT_INPUT_FILE" ] && rm -f "$UKIT_INPUT_FILE"' EXIT
|
|
47
60
|
fi
|
|
@@ -22,7 +22,15 @@ if source "$SCRIPT_DIR/../ukit/runtime/hook-input.sh" 2>/dev/null; then
|
|
|
22
22
|
source "$SCRIPT_DIR/../ukit/runtime/hook-telemetry.sh" 2>/dev/null || true
|
|
23
23
|
trap ukit_cleanup_hook_input EXIT
|
|
24
24
|
# Bounded stdin (H01): stage before anything reads it; the shell var is capped.
|
|
25
|
-
ukit_stage_hook_input 2097152 truncate
|
|
25
|
+
ukit_stage_hook_input 2097152 truncate
|
|
26
|
+
__ukit_main_stage_rc=$?
|
|
27
|
+
if [ "$__ukit_main_stage_rc" -ne 0 ]; then
|
|
28
|
+
# Infra failure during staging (mktemp/truncate): payload was never
|
|
29
|
+
# inspected — announce the degrade (SPEC §8), never a silent pass.
|
|
30
|
+
ukit_emit_input_degraded advisory "stop gate"
|
|
31
|
+
fi
|
|
32
|
+
# BUG-C21-01: a truncated/stalled staged payload must be announced (SPEC §8), never silently passed.
|
|
33
|
+
ukit_input_degraded && ukit_emit_input_degraded advisory "stop gate"
|
|
26
34
|
else
|
|
27
35
|
# wrapper's own fail-loud behavior below alive.
|
|
28
36
|
# Runtime helper missing (pre-install tree): the SAME bounded staging,
|
|
@@ -50,10 +58,14 @@ else
|
|
|
50
58
|
wait "$__ukit_waiter" 2>/dev/null
|
|
51
59
|
exec 8<&-
|
|
52
60
|
__ukit_size="$(wc -c < "$UKIT_INPUT_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
if [ "$__ukit_stage_rc" -ne 0 ] || [ "$__ukit_size" -gt 2097152 ]; then
|
|
62
|
+
# BUG-C21-01 fallback parity (SPEC §8): a truncated/stalled staged payload
|
|
63
|
+
# must be announced, never silently acted on — same shape as the helper
|
|
64
|
+
# path's ukit_emit_input_degraded.
|
|
65
|
+
rm -f "$UKIT_INPUT_FILE"
|
|
66
|
+
UKIT_INPUT_FILE=""
|
|
67
|
+
printf '%s\n' '{"systemMessage":"UKit stop gate: tool-call payload was truncated or stalled during stdin staging; skipped this pass rather than act on incomplete input."}'
|
|
68
|
+
exit 0
|
|
57
69
|
fi
|
|
58
70
|
trap '[ -n "$UKIT_INPUT_FILE" ] && rm -f "$UKIT_INPUT_FILE"' EXIT
|
|
59
71
|
fi
|
|
@@ -18,7 +18,15 @@ if [ -f "$SCRIPT_PATH" ]; then
|
|
|
18
18
|
trap ukit_cleanup_hook_input EXIT
|
|
19
19
|
# Bounded stdin (H01): compressed tool output can be large, so the staged
|
|
20
20
|
# file is the transport and the shell var only mirrors the capped payload.
|
|
21
|
-
ukit_stage_hook_input 33554432 temp-file
|
|
21
|
+
ukit_stage_hook_input 33554432 temp-file
|
|
22
|
+
__ukit_main_stage_rc=$?
|
|
23
|
+
if [ "$__ukit_main_stage_rc" -ne 0 ]; then
|
|
24
|
+
# Infra failure during staging (mktemp/truncate): payload was never
|
|
25
|
+
# inspected — announce the degrade (SPEC §8), never a silent pass.
|
|
26
|
+
ukit_emit_input_degraded advisory "output compression"
|
|
27
|
+
fi
|
|
28
|
+
# BUG-C21-01: a truncated/stalled staged payload must be announced (SPEC §8), never silently passed.
|
|
29
|
+
ukit_input_degraded && ukit_emit_input_degraded advisory "output compression"
|
|
22
30
|
else
|
|
23
31
|
# Runtime helper missing (pre-install tree): the SAME bounded staging,
|
|
24
32
|
# inline — `cat >/dev/null` used to block forever on a producer that never
|
|
@@ -45,14 +53,15 @@ if [ -f "$SCRIPT_PATH" ]; then
|
|
|
45
53
|
wait "$__ukit_waiter" 2>/dev/null
|
|
46
54
|
exec 8<&-
|
|
47
55
|
__ukit_size="$(wc -c < "$UKIT_INPUT_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
48
|
-
if [ "$__ukit_stage_rc" -ne 0 ]; then
|
|
49
|
-
# Unstaged: the hook cannot inspect this payload
|
|
56
|
+
if [ "$__ukit_stage_rc" -ne 0 ] || [ "$__ukit_size" -gt 33554432 ]; then
|
|
57
|
+
# Unstaged or oversized: the hook cannot inspect this payload — announce
|
|
58
|
+
# the degrade (SPEC §8) instead of silently compressing an empty or
|
|
59
|
+
# truncated input (SUS-02 / BUG-C21-01 fallback parity).
|
|
50
60
|
rm -f "$UKIT_INPUT_FILE"
|
|
51
61
|
UKIT_INPUT_FILE=""
|
|
62
|
+
printf '%s\n' '{"systemMessage":"UKit output compression: tool-call payload was truncated or stalled during stdin staging; skipped this pass rather than act on incomplete input."}'
|
|
63
|
+
exit 0
|
|
52
64
|
else
|
|
53
|
-
if [ "$__ukit_size" -gt 33554432 ]; then
|
|
54
|
-
truncate -s 33554432 "$UKIT_INPUT_FILE" 2>/dev/null
|
|
55
|
-
fi
|
|
56
65
|
trap '[ -n "$UKIT_INPUT_FILE" ] && rm -f "$UKIT_INPUT_FILE"' EXIT
|
|
57
66
|
fi
|
|
58
67
|
fi
|