@joshuafolkken/kit 1.1136.0 → 1.1178.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/.claude/settings.json +4 -4
- package/.claude/skills/epic-commands/SKILL.md +27 -239
- package/.claude/skills/epic-commands/epic-bundle.md +115 -0
- package/.claude/skills/epic-commands/execution-waves.md +126 -0
- package/.claude/skills/workflow-commands/SKILL.md +48 -194
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-child.md +12 -123
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +14 -63
- package/.claude/skills/workflow-commands/backlogrun-park.md +16 -15
- package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -173
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +155 -0
- package/.claude/skills/workflow-commands/backlogrun-steps.md +65 -144
- package/.claude/skills/workflow-commands/backlogrun.md +5 -4
- package/.claude/skills/workflow-commands/chain-rule.md +3 -2
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/entry-sequence.md +62 -0
- package/.claude/skills/workflow-commands/followup.md +104 -181
- package/.claude/skills/workflow-commands/fullrun-steps.md +8 -9
- package/.claude/skills/workflow-commands/fullrun.md +16 -73
- package/.claude/skills/workflow-commands/halfrun.md +10 -36
- package/.claude/skills/workflow-commands/into-target.md +4 -4
- package/.claude/skills/workflow-commands/issue-comments.md +7 -8
- package/.claude/skills/workflow-commands/issue-scout.md +6 -6
- package/.claude/skills/workflow-commands/kickoff.md +6 -6
- package/.claude/skills/workflow-commands/needs-human-review.md +6 -6
- package/.claude/skills/workflow-commands/observation-filing.md +21 -172
- package/.claude/skills/workflow-commands/observation-ledger.md +150 -0
- package/.claude/skills/workflow-commands/pre-gate-cut.md +35 -95
- package/.claude/skills/workflow-commands/prerequisite.md +6 -6
- package/.claude/skills/workflow-commands/progress-watcher.md +90 -0
- package/.claude/skills/workflow-commands/prrun.md +5 -5
- package/.claude/skills/workflow-commands/retrospective.md +9 -9
- package/.claude/skills/workflow-commands/split-assessment.md +2 -2
- package/.claude/skills/workflow-commands/target-repository.md +2 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +7 -5
- package/.codex/hooks.json +4 -4
- package/.github/workflows/ci.yml +15 -2
- package/AGENTS.md +1 -6
- package/CLAUDE.md +58 -120
- package/GEMINI.md +1 -6
- package/dist/CLAUDE.md +58 -120
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-NFZGFGOR.js → chunk-32GQTL4F.js} +14 -1
- package/dist/hooks/{chunk-HBKZLITF.js → chunk-DO6NWWHU.js} +4 -4
- package/dist/hooks/{chunk-KBXZLMRH.js → chunk-G6KRCKEC.js} +216 -76
- package/dist/hooks/{chunk-JKKTVDAG.js → chunk-GDS5N2KB.js} +186 -309
- package/dist/hooks/chunk-I4XSL4TT.js +56 -0
- package/dist/hooks/{chunk-GDU26K3J.js → chunk-S6X6WYKR.js} +886 -174
- package/dist/hooks/{chunk-FO2I7UT7.js → chunk-UAGLLIGZ.js} +1 -1
- package/dist/hooks/{chunk-RFWWUI5F.js → chunk-VESKRQOF.js} +3 -3
- package/dist/hooks/{chunk-KOKOAV6D.js → chunk-VQLLNFNT.js} +6 -4
- package/dist/hooks/{chunk-LJBLGHEH.js → chunk-ZAL3NRGL.js} +4 -6
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +178 -177
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +11 -10
- package/dist/hooks/stop-guard.js +20 -6
- package/dist/josh.js +29 -3
- package/dist/version/index.js +5 -1
- package/package.json +2 -1
- package/prompts/collaboration-workflow/file-edits.md +11 -10
- package/prompts/collaboration-workflow/gh-rest.md +2 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-citation.md +5 -9
- package/prompts/collaboration-workflow/issue-template.md +40 -115
- package/prompts/collaboration-workflow/operating-rules.md +4 -5
- package/prompts/collaboration-workflow/output-bounds.md +12 -78
- package/prompts/collaboration-workflow/overview.md +7 -13
- package/prompts/collaboration-workflow/plan-comment.md +11 -39
- package/prompts/collaboration-workflow/principles.md +3 -4
- package/prompts/collaboration-workflow/report-format.md +28 -74
- package/prompts/collaboration-workflow/residency.md +12 -109
- package/prompts/collaboration-workflow/rule-delivery.md +98 -97
- package/prompts/collaboration-workflow/shell-body.md +3 -13
- package/prompts/collaboration-workflow/turn-batching.md +14 -88
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -14
- package/prompts/collaboration-workflow/wip-cap.md +6 -45
- package/prompts/collaboration-workflow.md +1 -13
- package/prompts/review-rubric.md +8 -71
- package/prompts/review.md +25 -55
- package/prompts/testing-guide.md +18 -67
- package/scripts/agent/agent-role-profile.ts +1 -1
- package/scripts/agent/codex-hooks.ts +3 -12
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +4 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +10 -1
- package/scripts/backlog/backlog-next.ts +15 -4
- package/scripts/backlog/backlog-offer.ts +6 -6
- package/scripts/backlog/backlog-plan-cli.ts +6 -5
- package/scripts/backlog/backlog-rank.ts +18 -9
- package/scripts/backlog/backlog-restructure.ts +152 -0
- package/scripts/backlog/backlog-waves.ts +15 -17
- package/scripts/behavior/behavior-rules.ts +11 -4
- package/scripts/cost-runtime/context-cut-threshold.ts +1 -9
- package/scripts/cost-runtime/cost-verdict.ts +15 -28
- package/scripts/delegation/investigation-reads.ts +5 -5
- package/scripts/discovery/repo-discovery.ts +21 -7
- package/scripts/discovery/repo-origin.ts +26 -0
- package/scripts/discovery/repo-party.ts +42 -1
- package/scripts/document/backlogrun-parent-read-set.ts +14 -15
- package/scripts/document/document-byte-budget.ts +34 -20
- package/scripts/document/entry-read-budget.ts +21 -9
- package/scripts/document/lane-child-read-set.ts +12 -14
- package/scripts/document/read-set-cli.ts +4 -4
- package/scripts/document/read-set-point-of-use.ts +10 -2
- package/scripts/document/resident-budget.ts +47 -0
- package/scripts/epic/epic-shape.ts +10 -13
- package/scripts/epic/epic-solo.ts +1 -1
- package/scripts/exports/unused-members-cli.ts +7 -3
- package/scripts/followup/git-followup-stages.ts +9 -0
- package/scripts/followup/git-pr-followup.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +7 -0
- package/scripts/gh/git-gh-issue.ts +5 -3
- package/scripts/gh/git-gh-pr-read.ts +4 -0
- package/scripts/gh/git-pr-checks-eval.ts +8 -0
- package/scripts/gh/git-pr-checks.ts +11 -1
- package/scripts/git/constants.ts +10 -0
- package/scripts/git/main-merge.ts +57 -21
- package/scripts/git/schemas.ts +2 -0
- package/scripts/hooks/pretool-guard-cli.ts +2 -2
- package/scripts/hooks/pretool-guard.ts +45 -2
- package/scripts/hooks/run-hook.sh +41 -0
- package/scripts/init/hook-command-rewrite.ts +18 -35
- package/scripts/init/hook-launch.ts +21 -32
- package/scripts/issue/issue-auto-ok.ts +95 -0
- package/scripts/issue/issue-file-cli.ts +53 -7
- package/scripts/issue/issue-file.ts +28 -5
- package/scripts/issue/issue-labels.ts +12 -12
- package/scripts/issue/issue-rank.ts +1 -1
- package/scripts/issue/issue-read-cli.ts +3 -3
- package/scripts/issue/issue-read.ts +2 -2
- package/scripts/issue/issue-scout-cli.ts +2 -2
- package/scripts/issue/issue-state-cli.ts +1 -1
- package/scripts/issue/issue-state.ts +2 -2
- package/scripts/issue/issue-wip.ts +70 -0
- package/scripts/josh/hook-decision.ts +17 -0
- package/scripts/josh/josh-commands-development.ts +11 -1
- package/scripts/josh/josh-commands-issue.ts +2 -2
- package/scripts/lane/lane-launch-cli.ts +21 -3
- package/scripts/lines/bytes-command.ts +29 -6
- package/scripts/notify/telegram-notify.ts +26 -1
- package/scripts/observations/observation-ledger-line.ts +1 -1
- package/scripts/release/release-progress.ts +167 -0
- package/scripts/release/release-publish.ts +19 -8
- package/scripts/release/release-tag.ts +22 -8
- package/scripts/rules/decision-oracle-stage.ts +1 -1
- package/scripts/rules/decision-oracle.ts +6 -6
- package/scripts/rules/delivered-rules.ts +11 -11
- package/scripts/rules/direct-pr-create.ts +110 -0
- package/scripts/rules/file-body.ts +98 -42
- package/scripts/rules/filing-cap.ts +2 -2
- package/scripts/rules/filing-offer.ts +1 -1
- package/scripts/rules/gh-api.ts +18 -1
- package/scripts/rules/implementation-cut.ts +3 -1
- package/scripts/rules/lane-background.ts +67 -10
- package/scripts/rules/permission-guards.ts +10 -2
- package/scripts/rules/pre-gate-cut.ts +2 -2
- package/scripts/rules/stop-rules.ts +4 -4
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +56 -12
- package/scripts/run/run-carry.ts +11 -5
- package/scripts/run/run-cut-cli.ts +1 -1
- package/scripts/run/run-cut-handoff.ts +21 -1
- package/scripts/run/run-cut-report.ts +2 -2
- package/scripts/run/run-entry-cli.ts +11 -3
- package/scripts/run/run-hold-cli.ts +16 -2
- package/scripts/run/run-label.ts +50 -0
- package/scripts/run/run-merge-steps.ts +8 -3
- package/scripts/run/run-merge.ts +1 -1
- package/scripts/run/run-prep-cli.ts +6 -2
- package/scripts/run/run-prep.ts +17 -0
- package/scripts/run/run-ship-cli.ts +27 -6
- package/scripts/run/run-ship-next.ts +13 -0
- package/scripts/run/run-ship-pre-detach.ts +66 -0
- package/scripts/run/run-ship-preflight.ts +34 -3
- package/scripts/run/run-ship-return.ts +7 -4
- package/scripts/run/run-ship-review-steps.ts +1 -1
- package/scripts/run/run-ship-review.ts +26 -5
- package/scripts/run/run-ship-stage.ts +13 -0
- package/scripts/run/run-ship-steps.ts +10 -2
- package/scripts/run/run-ship-sync.ts +114 -0
- package/scripts/run/run-ship.ts +6 -0
- package/scripts/run/run-step-cli.ts +20 -7
- package/scripts/run/run-wake-cli.ts +8 -1
- package/scripts/run/run-wake-driver.ts +62 -8
- package/scripts/run/run-wake-session.ts +2 -1
- package/scripts/test/test-declared-logic.ts +1 -1
- package/scripts/test/test-red-logic.ts +17 -2
- package/scripts/test/test-red.ts +8 -7
- package/scripts/time-runtime/time-batch-guard.ts +52 -16
- package/scripts/time-runtime/time-density-hook.ts +20 -2
- package/scripts/time-runtime/time-density.ts +4 -3
- package/scripts/time-runtime/time-markers.ts +15 -4
- package/scripts/time-runtime/time-transcript-line.ts +27 -1
- package/scripts/version/npm-registry.ts +11 -1
- package/templates/workflows/ci.yml +9 -1
- package/.claude/skills/workflow-commands/followup-reference.md +0 -118
- package/dist/hooks/chunk-HANSKHES.js +0 -464
|
@@ -15,7 +15,7 @@ authorization, whether it came from a named epic or from the opted-in pool:
|
|
|
15
15
|
- **A stop that would end the run parks one issue and the run continues** — → "park
|
|
16
16
|
and continue", which is that rule's single source, including what happens to the issue's lane.
|
|
17
17
|
- **A prerequisite discovered mid-run is recorded as a dependency rather than parked** —
|
|
18
|
-
→ "A prerequisite discovered mid-run", and `
|
|
18
|
+
→ "A prerequisite discovered mid-run", and `prerequisite.md` for the three-way
|
|
19
19
|
distinction between a prerequisite, a split and an upstream defect. **One thing is genuinely
|
|
20
20
|
different**: with no epic, there is no `pnpm josh epic --add` to record the ordering into, so the
|
|
21
21
|
prerequisite is filed with `route:tier-a` and the blocked issue is parked with `needs-decision`
|
|
@@ -58,8 +58,8 @@ untouched**, for the same reason. **Name the lane directory in the stop report a
|
|
|
58
58
|
person told to look at a working tree and not told which one has been told nothing. The lanes already in
|
|
59
59
|
flight finish; no new lane is opened.
|
|
60
60
|
|
|
61
|
-
**Never apply or remove the label** —
|
|
62
|
-
`needs-decision` comparison: `
|
|
61
|
+
**Never apply or remove the label** — a person's alone. Full definition and the
|
|
62
|
+
`needs-decision` comparison: `needs-human-review.md`, which is the single source.
|
|
63
63
|
|
|
64
64
|
## park and continue
|
|
65
65
|
|
|
@@ -75,13 +75,11 @@ pnpm josh issue:comment <N> --body-file <path> # what needs deciding, and the
|
|
|
75
75
|
`needs-decision` precedence over `in-progress`, so the next child is offered normally. Then return to
|
|
76
76
|
step 1; the other children are unaffected unless they depend on this one.
|
|
77
77
|
|
|
78
|
-
**
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
stash, and closing would delete the local branch the resume needs. **A lost merge race is not one of
|
|
84
|
-
these rows** — it resolves in its lane ("Conflicts are not predicted" above), and parks only under that
|
|
78
|
+
**A parked child's lane is kept, whether or not it had committed.** **Parked before its commit**, the
|
|
79
|
+
uncommitted work stays in the lane's tree — never in the stash, which every work tree shares — and
|
|
80
|
+
`lane:close` would remove it by force. **Parked after its commit and push**, closing would delete the
|
|
81
|
+
local branch the resume needs. **A lost merge race is not one of
|
|
82
|
+
these rows** — it resolves in its lane (`backlogrun-recovery.md` → "Conflicts are not predicted"), and parks only under that
|
|
85
83
|
section's four conditions, which take the after-commit row. Both rows and the `pnpm josh run:release
|
|
86
84
|
<N>` every parked ending owes are "What happens to a lane" above, the single source.
|
|
87
85
|
|
|
@@ -101,7 +99,8 @@ gh api -X DELETE repos/{owner}/{repo}/issues/<N>/labels/needs-decision 2>/dev/nu
|
|
|
101
99
|
|
|
102
100
|
**The released child goes back to a lane, never into the parent's own context.** A parent that is
|
|
103
101
|
*already* running, and clears a label mid-run because a person just answered, dispatches it the same way
|
|
104
|
-
rather than picking up its diff itself — `pnpm josh lane:
|
|
102
|
+
rather than picking up its diff itself — `pnpm josh lane:launch <N>`, which dispatches into the lane the
|
|
103
|
+
park kept, or opens one where none is. A
|
|
105
104
|
parent that implements the released child inline is the failure the orchestrator rule is written against:
|
|
106
105
|
"The parent orchestrates and never implements a child in its own context" above.
|
|
107
106
|
|
|
@@ -110,8 +109,10 @@ human-in-the-loop cycle, not an optional tidy-up.
|
|
|
110
109
|
|
|
111
110
|
## `in-progress` is removed by whoever finds it stale
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
leaves it behind, and a child that carries it is
|
|
112
|
+
A run's own ending removes `in-progress` — `pnpm josh followup` on a merge, `pnpm josh run:release <N>`
|
|
113
|
+
on a stop. An interrupted run reaches neither and leaves it behind, and a child that carries it is
|
|
114
|
+
excluded from every future `epic:next` — permanently. The removal below is the one hand-written call
|
|
115
|
+
left, for a label no live run owns: another tree's record is not this tree's to release.
|
|
115
116
|
**A session that detects a stale child removes the label itself** (Tier A) and reports it, before
|
|
116
117
|
continuing the loop.
|
|
117
118
|
|
|
@@ -140,7 +141,7 @@ gh api -X DELETE repos/{owner}/{repo}/issues/<N>/labels/in-progress 2>/dev/null
|
|
|
140
141
|
|
|
141
142
|
Finding that something else in **this** repository has to land first is not a split, and not an upstream
|
|
142
143
|
defect. The child in hand is still one deliverable; it just needs another one before it. The three-way
|
|
143
|
-
distinction, the `route:tier-a` filing command and the filing ceiling are `
|
|
144
|
+
distinction, the `route:tier-a` filing command and the filing ceiling are `prerequisite.md`, the single
|
|
144
145
|
source; what follows is this entry's branch.
|
|
145
146
|
|
|
146
147
|
`<M>` below is the child being implemented when the prerequisite turned up; `<N>` is the new Issue.
|
|
@@ -166,7 +167,7 @@ source; what follows is this entry's branch.
|
|
|
166
167
|
3. `pnpm josh epic --add <E> <N> --before <M>` — one command writes the task-list row, the declaration
|
|
167
168
|
and the `blocked-by` relation together. Never edit the body by hand: the declaration and the relations
|
|
168
169
|
then disagree, `epic:next` returns `error`, and the unattended run stops.
|
|
169
|
-
4. **Remove `in-progress` from `<M>`** — `
|
|
170
|
+
4. **Remove `in-progress` from `<M>`** — `pnpm josh run:release <M>`, which removes the label with the hold.
|
|
170
171
|
This is what lets `<M>` run again: `epic:next` classifies a child carrying `in-progress` as waiting on
|
|
171
172
|
time **before** it looks at any blocker, so a child left labelled is never offered again.
|
|
172
173
|
5. **Do not park.** Go back to step 1 of the loop. `epic:next` classifies the original child as resolving
|
|
@@ -1,148 +1,18 @@
|
|
|
1
1
|
# `backlogrun` — progress, the hand-off, resume and waiting
|
|
2
2
|
|
|
3
|
-
**Read this file in full before the
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
and
|
|
3
|
+
**Read this file in full before the first child is dispatched** and before the hand-off check at a
|
|
4
|
+
child's merge (`pnpm josh cost --cut`). It is a point-of-use document, never an entry read: the entry
|
|
5
|
+
procedure is `backlogrun.md`, which points here at those steps (joshuafolkken/kit#2010). This file is
|
|
6
|
+
the single source of the session hand-off and cut/resume, waiting without waiting forever, and the
|
|
7
|
+
end-of-run summary and propagate. **The heartbeat is not here** — it binds every implementing run, so
|
|
8
|
+
its single source is `progress-watcher.md` → "Progress while the run is quiet" (joshuafolkken/kit#3172),
|
|
9
|
+
read before the watcher starts.
|
|
8
10
|
|
|
9
11
|
After a carry hand-off, the detached `run:wake` supervisor runs the deterministic `backlog:drive`
|
|
10
12
|
loop. References below to a parent's mechanical polling and lane collection describe work now done
|
|
11
13
|
by that supervisor; an AI parent is started only for a driver branch that requires judgment.
|
|
12
14
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
**Start the progress step before step 1 of the loop, and do it without being asked.**
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pnpm josh run:progress --wait --output <the transcript path of each delegated unit> # in the background
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Start it once; it reports by itself** (joshuafolkken/kit#3102). Each silence interval appends the
|
|
22
|
-
five labelled lines to the run's event stream as a heartbeat and to the ambient log, prints nothing to
|
|
23
|
-
standard output and keeps running — so a scheduled report never wakes the parent, and the parent
|
|
24
|
-
relays none. **It exits only on one of three, and the command computes which:**
|
|
25
|
-
|
|
26
|
-
| Exit | What the parent does |
|
|
27
|
-
| ------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
28
|
-
| Newly runnable backlog work (the `ready #N` line) | Read the ready line, act on it, and restart the watcher in that turn |
|
|
29
|
-
| `pnpm josh followup` removed the run's life record | Nothing — the run has ended |
|
|
30
|
-
| `--hours` elapsed (8 by default for `--wait`) | Restart the watcher in that turn |
|
|
31
|
-
|
|
32
|
-
**`pnpm josh run:watcher:guard` detects a missed restart** (joshuafolkken/kit#2113): it refuses when
|
|
33
|
-
lane children are in-flight but the watcher's life record has gone stale. **It is wired into
|
|
34
|
-
`pretool-guard`, the one `PreToolUse` hook** (joshuafolkken/kit#2353), so the next tool call is refused
|
|
35
|
-
until the watcher is restarted — once per run, so the `pnpm josh run:progress --wait` that fixes it is
|
|
36
|
-
not blocked.
|
|
37
|
-
|
|
38
|
-
**`run:progress` writes the five labelled lines itself (joshuafolkken/kit#2026).** It emits the observation instant (`at`, local first and UTC beside it), the elapsed figures
|
|
39
|
-
(`quiet` and `unchanged`), the children in flight, the run state (`lanes`, `load`, `record`), and the
|
|
40
|
-
scheduled `next`, each behind its own label and on its own line — so where a session does present them
|
|
41
|
-
(`--once`), it relays the output verbatim and rounds, rephrases or re-labels nothing. How each field reads when it has nothing to report (`record
|
|
42
|
-
unread`, `no in-progress child yet`, `lanes none`, an absent `next`) is fixed by the command's own unit
|
|
43
|
-
tests. **A value said that way is still an observation, never a state**: `record unread` means no
|
|
44
|
-
`--output` path was given, not *not stalled*; `lanes none` is *no lane is open*, not *nothing is
|
|
45
|
-
running*; and the `next` line is a schedule — the time *if the silence continues*, superseded when a
|
|
46
|
-
real report resets the clock through `--mark`.
|
|
47
|
-
|
|
48
|
-
**A report is not an exit, and neither is a decline** — with no run recorded it keeps waiting, and the
|
|
49
|
-
`--hours` exit says so on standard error.
|
|
50
|
-
|
|
51
|
-
**`--mark` at every real report.** Whenever this loop reports something of its own — a child merged,
|
|
52
|
-
parked, a stop — run `pnpm josh run:progress --mark` in the same turn to restart the silence clock. The clock is silence, never a timer
|
|
53
|
-
(`docs/josh-commands-automation.md` → "`josh run:progress`").
|
|
54
|
-
|
|
55
|
-
**Do not keep a progress clock of your own** — the hook refuses the arm rather than asking
|
|
56
|
-
(`scripts/rules/early-heartbeat.ts` → `decide`), though **a single correctly-spaced arm is allowed**. It
|
|
57
|
-
is still not the way to report; the step above prints without arming anything, and `--wait` is not a
|
|
58
|
-
wait timer. Rationale: `docs/maintainers/backlogrun-progress-rationale.md` → "Why the heartbeat reads
|
|
59
|
-
as it does".
|
|
60
|
-
|
|
61
|
-
**The default interval is twenty minutes, overridable — by the person, not the run.**
|
|
62
|
-
`JOSH_PROGRESS_INTERVAL_MINUTES` moves both sides (the guard reads it through the watcher's reader);
|
|
63
|
-
`josh` → `progress_interval_minutes` in `package.json` sits one step below the variable. **`--interval`
|
|
64
|
-
moves the watcher alone** — it makes the watcher quieter than the floor, never the guard stricter.
|
|
65
|
-
|
|
66
|
-
**An explicit ask is not a heartbeat, and it is exempt by construction.**
|
|
67
|
-
**Every unscheduled progress statement is answered by `pnpm josh run:progress --once`** — presented in
|
|
68
|
-
the same five field lines, the reply to an explicit ask and the note just after a run starts alike.
|
|
69
|
-
**Never write a clock time the command did not print** (not an approximation, not a placeholder — where
|
|
70
|
-
a field is missing, say so). **`--once` records the report**, so no `--mark` beside it.
|
|
71
|
-
|
|
72
|
-
**Every report opens with the time the observation was taken** — not only how long it has been quiet: an
|
|
73
|
-
absolute instant, `at YYYY-MM-DD HH:MM±HH:MM / YYYY-MM-DDTHH:MMZ`. **It is added, never substituted for
|
|
74
|
-
the elapsed figure**, and **the presented line needs no stamp computed for it** — the command prints the
|
|
75
|
-
`at` stamp and the `next` field.
|
|
76
|
-
|
|
77
|
-
**The line carries observations, never "still running", and nothing in it is a verification result** —
|
|
78
|
-
no gate, CI or check rollup, because the command reads none of them. Rationale:
|
|
79
|
-
`docs/maintainers/backlogrun-progress-rationale.md` → "Why the heartbeat reads as it does".
|
|
80
|
-
|
|
81
|
-
**The invariant is a tier, not a mechanism** (joshuafolkken/kit#2156, joshuafolkken/kit#2207). Three
|
|
82
|
-
tiers say how much of a person's attention a signal takes: **interrupt** reaches for it now (a Telegram),
|
|
83
|
-
**ambient** is seen without being asked for, and **requested** is read only when a person types for it.
|
|
84
|
-
**The heartbeat sits at the ambient tier, and stays there across a `backlogrun` session cut — never
|
|
85
|
-
promoted to interrupt, never demoted to requested.**
|
|
86
|
-
|
|
87
|
-
**The report surface belongs to the run, not to the session** (joshuafolkken/kit#2207). The run's
|
|
88
|
-
session-facing events — a plan posted, a child launched, a PR opened, a review round, a park, a cut, a
|
|
89
|
-
stop, a merge — are appended to one ordered stream keyed to the run's identity (`pnpm josh run:event`).
|
|
90
|
-
**Every session is a writer;
|
|
91
|
-
the reader is a script in a pane of the person's own, never a conversation** (#2492):
|
|
92
|
-
`pnpm josh run:event --watch` prints each event the moment it lands, in the session language, and the
|
|
93
|
-
`heartbeat` events say the run is alive when quiet. **No session relays the stream** — name the watch command once, at the first cut or when
|
|
94
|
-
asked. **It is the same reader before and after the cut**: a cut moves who executes, never where the
|
|
95
|
-
stream lives.
|
|
96
|
-
|
|
97
|
-
**Interrupt is withheld on purpose**: no Telegram for the heartbeat. **`pnpm josh run:wake --list` is the requested tier** — the last-event read of the stream the
|
|
98
|
-
watch pane reads, for a person who types for one line rather than watching. **`tail -F` on the
|
|
99
|
-
raw stream file is a recovery path, not the ambient surface** — the watch pane is how progress is seen
|
|
100
|
-
without asking, and the raw `tail` is the fallback when the pane has stopped, named for that role in
|
|
101
|
-
`--list`'s own output.
|
|
102
|
-
|
|
103
|
-
**A stop is an interrupt, and only a stop** (joshuafolkken/kit#2136). A run that has _stopped_ — every
|
|
104
|
-
remaining child blocked behind a parked one, the backlog drained of anything runnable, the failure
|
|
105
|
-
streak tripped — is pushed to the interrupt tier: `pnpm josh run:carry --end --stopped "<reason>"` sends one ⏸️ confirmation
|
|
106
|
-
as it ends the record, and because the record is gone by the second `--end` the same stop never notifies
|
|
107
|
-
twice. A parked child is pushed by the child itself, which records the park and sends its own
|
|
108
|
-
`confirmation` before it stops (`backlogrun-park.md`). Rationale for the tiers:
|
|
109
|
-
`docs/maintainers/backlogrun-progress-rationale.md` → "Why the signal tiers are split this way".
|
|
110
|
-
|
|
111
|
-
**A heartbeat is emitted from the moment a run has started, even before any child carries
|
|
112
|
-
`in-progress`** — "a run has started" is read from a mechanical record (a registered lane, a held work
|
|
113
|
-
tree, or a carried budget), and the line names that stage as an observed fact (`no in-progress child
|
|
114
|
-
yet`). **Only a checkout with no run recorded at all stays silent.**
|
|
115
|
-
|
|
116
|
-
**The scope is every implementing run, not this command alone.** `fullrun`, `halfrun` and `backlogrun`
|
|
117
|
-
start the same watcher under the same rules, and this section is the single source for all four; `kickoff`
|
|
118
|
-
starts none. **`halfrun` is included.**
|
|
119
|
-
|
|
120
|
-
**One watcher per run, and the outermost invocation is the one that starts it.** A `fullrun` running as
|
|
121
|
-
a `backlogrun` named issue or an `backlogrun` child starts none: the brief names the invocation it descends
|
|
122
|
-
from. `backlogrun` starts one for the whole batch. **A dispatched lane child is refused a watcher by its
|
|
123
|
-
`JOSH_LANE_CHILD` mark** — it still runs `--mark` for the parent's clock, but every reporting form
|
|
124
|
-
(`--wait`, `--once`, the default watch) exits at once with a notice.
|
|
125
|
-
|
|
126
|
-
**In a single-issue run it starts immediately after `pnpm josh run:hold` succeeds.** **It is started in the target repository's checkout, and
|
|
127
|
-
`--mark` is run there too.**
|
|
128
|
-
|
|
129
|
-
**What counts as a real report when the run has one issue.** A `fullrun` has exactly one child, so the
|
|
130
|
-
unit is **any turn that puts a progress statement in front of the person** — four of them, `--mark` run
|
|
131
|
-
in the same turn as each: **the Step 0 work summary, the pull request opening, each review round's
|
|
132
|
-
verdict, and any `confirmation` / `failure` / `completion` notification or stop.** **A tool result only
|
|
133
|
-
you read is not one** — a gate run, a `gh` read, an edit.
|
|
134
|
-
|
|
135
|
-
**`--output` is omitted in a single-issue run, and `record` reads `unread`.** Rationale for the
|
|
136
|
-
watcher's scope and placement: `docs/maintainers/backlogrun-progress-rationale.md` → "Why the watcher
|
|
137
|
-
runs where it does".
|
|
138
|
-
|
|
139
|
-
**A run that merges needs no teardown; a run that stops has to end the reporting itself.** **A stop
|
|
140
|
-
keeps the `in-progress` label on purpose** — so **no further `--wait` is started** after the stop
|
|
141
|
-
notification, and the running `--wait` is stopped in the same turn, since no report ends it. That
|
|
142
|
-
covers `halfrun`'s stop before commit, a `needs-human-review` stop, a split or prerequisite stop, and
|
|
143
|
-
a `backlogrun` named issue's failure stop.
|
|
144
|
-
|
|
145
|
-
### Running a named epic's children
|
|
15
|
+
## Running a named epic's children
|
|
146
16
|
|
|
147
17
|
`josh epic:next <E> --repo <this repository> --lanes` prints **one issue number per line** on standard
|
|
148
18
|
output — as many as that repository has free lanes — or, when there is no child to run, the verdict as a
|
|
@@ -196,39 +66,11 @@ run in lanes".
|
|
|
196
66
|
through the same `run:merge --output` classification. **The flag is what you saw, never what kind of
|
|
197
67
|
child it is.**
|
|
198
68
|
|
|
199
|
-
When the unit reports back,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
# state: CLOSED
|
|
205
|
-
# labels: (none)
|
|
206
|
-
# human_review: no
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**`--repo` is not optional for a cross-repository child.** Without it the read resolves `<N>` against
|
|
210
|
-
the repository this session runs in.
|
|
211
|
-
|
|
212
|
-
`state: CLOSED` is the only answer that means the child finished. **Read the `human_review:` and
|
|
213
|
-
`labels:` lines before calling anything else a failure.** **A non-zero exit is not `OPEN`**: the command exits non-zero
|
|
214
|
-
without a state when the number resolves to nothing (`does not resolve`) or the read failed (`could
|
|
215
|
-
not read`, a rate limit or expired auth). Re-read before deciding.
|
|
216
|
-
|
|
217
|
-
- **Open, carrying `needs-decision`** — the unit **parked** it. Not a failure: leave the label on, do
|
|
218
|
-
**not** count it against the consecutive-failure guard, and go back to step 1.
|
|
219
|
-
- **Open, and `human_review: yes`** — the child **stopped before its commit**, the run's own ending
|
|
220
|
-
(§2z). **`Open` is part of the test** — a CLOSED child carrying the label finished and merged.
|
|
221
|
-
**Read that line, not the `labels:` one.** Leave
|
|
222
|
-
`in-progress` **on**, do not park it, do not count it, and do **not** go back to step 1. Finish the
|
|
223
|
-
session and report. **Do not send a second `confirmation` Telegram** — the unit already sent one.
|
|
224
|
-
Where the child ran in this session's own context, that first notification is yours to send.
|
|
225
|
-
- **Open, without `needs-decision`** — it failed. Remove the stale `in-progress` here (Tier A), count
|
|
226
|
-
it against the consecutive-failure guard, and **park it**.
|
|
227
|
-
|
|
228
|
-
**Never ask `epic:next` in place of this read.**
|
|
229
|
-
|
|
230
|
-
**A merge is one event, and one call of the parent's — `pnpm josh run:merge <N>`**
|
|
231
|
-
(joshuafolkken/kit#2024). The composite command confirms the child from GitHub, does the post-merge steps, folds in the hand-off check, and prints
|
|
69
|
+
**When the unit reports back, a merge is one event, and one call of the parent's — `pnpm josh
|
|
70
|
+
run:merge <N>`** (joshuafolkken/kit#2024). It is the only reading of how the child ended: never
|
|
71
|
+
classify `pnpm josh issue:state` by hand, and never ask `epic:next` in its place. Rationale:
|
|
72
|
+
`docs/maintainers/backlogrun-progress-rationale.md` → "Why a child's ending is read by `run:merge` alone".
|
|
73
|
+
The composite command confirms the child from GitHub, does the post-merge steps, folds in the hand-off check, and prints
|
|
232
74
|
the next child number — or a control verdict — for the parent to read. Pass the merged child's
|
|
233
75
|
number and the offer's source — `--epic <E> --repo <owner/repo>` for a named epic,
|
|
234
76
|
nothing for the opted-in backlog — with `--owner "$PPID"` so it counts into the carry record under
|
|
@@ -257,7 +99,9 @@ run in lanes".
|
|
|
257
99
|
**Beyond the offer `epic:next` prints** (`run` becomes numbers; `wait` / `stop` / `complete` /
|
|
258
100
|
`error` pass through), the composite adds six verdict tokens: `over` — the merge crossed the budget,
|
|
259
101
|
so hand the lanes over and take the cut ("The hand-off" below); `human-review` — the child stopped
|
|
260
|
-
before its commit, the run's own ending (
|
|
102
|
+
before its commit, the run's own ending (needs-human-review.md), so stop — leave `in-progress` on, and send
|
|
103
|
+
no second `confirmation` Telegram, since the unit already sent one (where the child ran in this
|
|
104
|
+
session's own context, that first notification is yours to send); `stop` — the consecutive-failure
|
|
261
105
|
guard tripped; `environment` — the consecutive-**outage** guard tripped, so the API is down and the
|
|
262
106
|
run stops as an environment failure rather than the children's (joshuafolkken/kit#2240); `resumed` —
|
|
263
107
|
an unadopted cut was relaunched; `lane:await <N>` it again (joshuafolkken/kit#2484); and `retry` — the child's state could not be read, so re-read it.
|
|
@@ -522,7 +366,7 @@ closing with the release tail below. Its output is the Telegram body too — pas
|
|
|
522
366
|
`pnpm josh notify --body-file`.
|
|
523
367
|
|
|
524
368
|
**That same session asks `pnpm josh release:scope` once, after the last child has merged** — in the
|
|
525
|
-
primary checkout, after the last lane is closed, and never once per child. `followup
|
|
369
|
+
primary checkout, after the last lane is closed, and never once per child. `followup.md` →
|
|
526
370
|
"When `pnpm josh release` runs" is the single source for the position and the three answers. `run:report`
|
|
527
371
|
appends that answer as the summary's closing line: on `required` the request and the exact command, on
|
|
528
372
|
`unknown` the word `unknown`, never rounded to `skip`.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# `backlogrun` — recovering a child that went wrong
|
|
2
|
+
|
|
3
|
+
**Read the section you need at the moment it is needed, never at the entry or before the first child**
|
|
4
|
+
(joshuafolkken/kit#3175). Both sections below are reached only on failure — a unit that went silent, a
|
|
5
|
+
lane whose pull request conflicts with `main` — so a run in which nothing fails never pays for them.
|
|
6
|
+
`backlogrun-child.md`, `backlogrun-lanes.md` and `chain-rule.md` point here at those triggers.
|
|
7
|
+
|
|
8
|
+
## A delegated unit that stopped without reporting
|
|
9
|
+
|
|
10
|
+
**A unit can be stopped from outside, and a stop leaves no notification behind** — and a stopped unit
|
|
11
|
+
trips none of `backlogrun-child.md`'s guards, which all assume it is running. **So the parent checks
|
|
12
|
+
rather than waiting — which means it must not be waiting.** **Hand the child to the unit without
|
|
13
|
+
blocking on its return, and poll** at the loop's polling interval; ask once the unit's output has been
|
|
14
|
+
unchanged for the silent-unit window (`| Silent delegated unit | 30 min |` in `backlogrun-progress.md`).
|
|
15
|
+
|
|
16
|
+
**Note where the unit writes at hand-off; the modification time is read from the file, not carried.**
|
|
17
|
+
**Where the child runs in a lane, that note goes into the lane rather than the conversation** —
|
|
18
|
+
`pnpm josh lane:output <N> <path>`, in the same turn as the dispatch, so it comes back from
|
|
19
|
+
`pnpm josh lane:output <N>` in any session.
|
|
20
|
+
|
|
21
|
+
**Ask the command rather than combining the traces yourself.** Traces are read **in the checkout the
|
|
22
|
+
unit was given** — this session's own unless the unit was handed a separate work tree, and the stash in
|
|
23
|
+
the recovery below is taken there too.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm josh run:liveness <N> --output <path> --process none
|
|
27
|
+
pnpm josh run:liveness <N> --output <path> --process alive
|
|
28
|
+
pnpm josh run:liveness <N> --output <path> --process none --window 45 --repo <owner/repo>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Answer | What it found | What the parent does |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `alive` | The output moved, or a process of the child is running | Keep polling; touch nothing |
|
|
34
|
+
| `stopped` | The output has been frozen past the window and no process of the child is alive | The recovery below |
|
|
35
|
+
| `settled` | The child closed, or the unit parked it with `needs-decision` | Re-read it with `pnpm josh run:status <N>` — one read-only call whose state section says which branch and whose carry counters beside it feed the failure-streak decision — and take the branch its state section says |
|
|
36
|
+
| `undetermined` | A trace could not be read | Read the trace that failed and ask again — and see the two-in-a-row rule below |
|
|
37
|
+
|
|
38
|
+
**Two `undetermined` answers in a row is a fault in the check, not a slow unit.** The second
|
|
39
|
+
consecutive `undetermined` on the same child ends the polling: send a `confirmation` Telegram naming
|
|
40
|
+
the trace that failed, and stop. **It is never escalated to a `stopped`** — nothing was read.
|
|
41
|
+
|
|
42
|
+
**Silence and no process together — never either one alone.** **A trace that could not be read answers
|
|
43
|
+
`undetermined`, never `stopped`**, and a process trace nobody gave is an unasked question rather than
|
|
44
|
+
"no process". **Output that moved answers `alive` on its own; a live process is weighed only once every
|
|
45
|
+
trace has answered.** Rationale: `docs/maintainers/backlogrun-recovery-rationale.md` → "Why liveness needs
|
|
46
|
+
silence and no process together".
|
|
47
|
+
|
|
48
|
+
**The path passed to `--output` is absolute** — the command refuses a relative one. **The process
|
|
49
|
+
trace is the one the command does not read for itself**: run it in the unit's checkout and read the
|
|
50
|
+
command lines rather than the names — `pgrep -laf 'josh gate'` etc. — looking for one naming **that
|
|
51
|
+
checkout's path**, since several kit projects may run at once. **Read the file the path points at, not
|
|
52
|
+
the link**: a transcript path is a symlink whose own modification time never changes, so a `stat` typed
|
|
53
|
+
by hand needs `-L` (`run:liveness` follows the link itself).
|
|
54
|
+
|
|
55
|
+
**A clean checkout is not evidence that the unit is alive** — a stop can come while the unit is still
|
|
56
|
+
reading the skill and the issue. The checkout is read only for whether there is work to stash before
|
|
57
|
+
the child is parked; **"nothing was ever opened for the child" is `pnpm josh run:hold`'s preflight check
|
|
58
|
+
at the start of the next child, not this one's.**
|
|
59
|
+
|
|
60
|
+
**What follows is what a failed child already gets.** Re-read the child first with
|
|
61
|
+
`pnpm josh run:status <N>` — its state section carries the same `state:` / `labels:` /
|
|
62
|
+
`human_review:` lines `issue:state` prints, and folds in the carry counters this recovery reads
|
|
63
|
+
anyway; then, while it is still `state: OPEN` and not carrying `needs-decision`.
|
|
64
|
+
**A re-read carrying `needs-decision`** means the unit parked the child and then stopped, so fall
|
|
65
|
+
through to the loop's park branch: leave the label on, count nothing against the consecutive-failure
|
|
66
|
+
guard, and go back to step 1.
|
|
67
|
+
|
|
68
|
+
1. **Stash the half-finished work** — `git stash push -u -m "backlogrun: stopped unit for #<N>"` — and
|
|
69
|
+
record it on the Issue. `-u` is not optional, and the comment is what gets the stash popped — by
|
|
70
|
+
message, `pnpm josh stash:pop "backlogrun: stopped unit for #<N>"`, never a positional
|
|
71
|
+
`git stash pop` that a shared stack lets another lane divert.
|
|
72
|
+
2. **Classify how the child ended and act, in one call** — `pnpm josh run:merge <N> --output <path>`
|
|
73
|
+
(add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic). **It is the same composite a
|
|
74
|
+
returned child takes** (`backlogrun-progress.md` → "Running a named epic's children"), reached here
|
|
75
|
+
from the poll rather than from a return — **one** decision, never a second copy of it. It **reads the
|
|
76
|
+
child's exit record without waiting for the unit to return**, telling an `outage` — a child that
|
|
77
|
+
could not reach the API — apart from an `abandoned` one that stopped mid-implementation, and it drops
|
|
78
|
+
the stale `in-progress` itself, so there is no separate label-removal step:
|
|
79
|
+
- **outage** — the child never reached the API, so it is counted into its own outage streak and
|
|
80
|
+
**re-dispatched in the same run** by being offered again, **not** parked with `needs-decision` and
|
|
81
|
+
**not** counted against the consecutive-failure guard. **The re-dispatch resumes the child's
|
|
82
|
+
session**: `lane:dispatch` reads the `session_id` off the exit record and relaunches with
|
|
83
|
+
`--resume`; with no session id it falls back to a fresh `fullrun`, and the report says which path
|
|
84
|
+
it took. **Outages inside a two-minute window count once** toward the streak. The re-dispatch is
|
|
85
|
+
bounded by `CONSECUTIVE_OUTAGE_LIMIT` in `scripts/run/run-merge.ts`: *distinct* outages trip the
|
|
86
|
+
separate outage guard, at which point the command prints `environment` and the run stops. It never
|
|
87
|
+
re-dispatches into a dead API forever.
|
|
88
|
+
- **abandoned** — counted against the consecutive-failure guard and parked with `needs-decision`,
|
|
89
|
+
exactly as a failed child. Never retried silently.
|
|
90
|
+
3. **Read the token it printed and take that branch** — a next child number to run (the re-dispatched
|
|
91
|
+
outage child among them), `environment` / `stop` to end the run, or `busy` / `retry` to re-read —
|
|
92
|
+
the same tokens the merge event reads (`backlogrun-progress.md` → "Running a named epic's children").
|
|
93
|
+
Then go back to step 1 of the loop.
|
|
94
|
+
|
|
95
|
+
Rationale: `docs/maintainers/backlogrun-recovery-rationale.md` → "Why the poll routes through `run:merge`".
|
|
96
|
+
|
|
97
|
+
## Conflicts are not predicted
|
|
98
|
+
|
|
99
|
+
**The child merges `origin/main` into its lane before its gate now, so this path is the fallback rather
|
|
100
|
+
than the first line.** Every `fullrun` runs `pnpm josh main:merge` ahead of the gate
|
|
101
|
+
(`chain-rule.md` → "origin/main is merged in before the gate", the single source), so an overlap
|
|
102
|
+
already on `main` is resolved before the gate reads the tree. What this section covers is an overlap
|
|
103
|
+
that lands on `main` *after* it, which `followup` still reports as a conflict.
|
|
104
|
+
|
|
105
|
+
**Nothing here forecasts which children will overlap.** The overlap surfaces where GitHub already
|
|
106
|
+
reports it: a pull request that conflicts with its base comes back `mergeStateStatus: DIRTY`, which
|
|
107
|
+
`git-pr-checks-eval.ts` reads as a **failure** rather than polling through it — so `pnpm josh followup`
|
|
108
|
+
ends that child with a named conflict in about ten seconds.
|
|
109
|
+
|
|
110
|
+
**That child does not stop: it resolves the conflict in its own lane.** It is **not** counted against
|
|
111
|
+
the consecutive-failure guard either way.
|
|
112
|
+
|
|
113
|
+
**The merge direction is `origin/main` into the lane's branch, never a rebase**, and no step below
|
|
114
|
+
rewrites a pushed commit. Rationale: `docs/maintainers/backlogrun-recovery-rationale.md` → "Why a conflict
|
|
115
|
+
is resolved in the lane rather than handed to a person".
|
|
116
|
+
|
|
117
|
+
1. **Resolve in the lane.** `git fetch origin main`, merge `origin/main` into the lane's branch, resolve
|
|
118
|
+
in place. Do not close the lane, open another, or switch its branch.
|
|
119
|
+
2. **Re-run the whole gate.** The tree changed, so the green recorded before the conflict is void:
|
|
120
|
+
`pnpm josh lint:related` and `pnpm josh test:related`, then `pnpm josh gate`.
|
|
121
|
+
3. **Review the resolution, one round.** Brief it with `pnpm josh review:brief` and run `/code-review`
|
|
122
|
+
over the resolution diff, then require `pnpm josh review:attest --check` to answer `ok`. That round
|
|
123
|
+
is a different subject and does not spend one of the two the cap allows — `prompts/review.md` →
|
|
124
|
+
"Review round cap" carries the exception.
|
|
125
|
+
4. **Conclude the merge and push it, with `pnpm josh git -y`** — the only sanctioned way; without it
|
|
126
|
+
nothing changes on `origin` and condition 3 parks the child for good. **Record the resolution on the
|
|
127
|
+
Issue in this same step** — a comment naming the conflicting paths — because condition 3 counts
|
|
128
|
+
resolutions off the Issue comments, not off memory.
|
|
129
|
+
5. **Merge**, by re-running `pnpm josh followup` exactly as before.
|
|
130
|
+
|
|
131
|
+
**The safeguard is the re-run verification, not who holds the pen** — a conflict the run can resolve is
|
|
132
|
+
not routed to a person.
|
|
133
|
+
|
|
134
|
+
**The run steps back under these four conditions, and under no others. The list is exhaustive and
|
|
135
|
+
carries no judgement.**
|
|
136
|
+
|
|
137
|
+
1. **The resolution requires deleting the other side's change.** That decides intended behavior, not
|
|
138
|
+
text. It goes to the user as a **specification** question — "behavior A or behavior B" — never as a
|
|
139
|
+
diff.
|
|
140
|
+
2. **Both sides rewrote the same lines** — overlapping, not adjacent. Read off the structure of the
|
|
141
|
+
conflict hunks, so it needs no interpretation.
|
|
142
|
+
3. **A second conflict on the same child.** One resolution per child; the count is read off the Issue
|
|
143
|
+
comments step 4 writes, so it survives an interrupt.
|
|
144
|
+
4. **The re-run gate did not come back green, or the resolution review returned a High.** Not only a
|
|
145
|
+
High: a lint error, a failing test or a spell-check hit that merging a moved `main` introduced is
|
|
146
|
+
this condition too.
|
|
147
|
+
|
|
148
|
+
Meeting any of the four, the child is parked — `needs-decision` plus a comment naming which of the four
|
|
149
|
+
it was — and its lane is **kept**, because the pushed branch is the resume path
|
|
150
|
+
(`backlogrun-lanes.md` → "What happens to a lane").
|
|
151
|
+
|
|
152
|
+
**Leave the tree clean before parking: `git merge --abort` precedes a park under conditions 1, 2 or 4.**
|
|
153
|
+
Conditions 1 and 2 are read mid-merge with conflict markers still in the tree, condition 4 after a merge
|
|
154
|
+
that is resolved but not committed; condition 3 fires before any merge is started, so there is nothing
|
|
155
|
+
to abort.
|