@sjawhar/claude-envoy 0.0.0 → 1.0.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-plugin/plugin.json +6 -0
- package/.mcp.json +11 -0
- package/.omp-plugin/plugin.json +6 -0
- package/AGENTS.md +343 -0
- package/README.md +639 -0
- package/bin/dispatch +6 -0
- package/bin/envoy-channel.ts +9 -0
- package/bin/envoy-send.ts +33 -0
- package/dist/THIRD_PARTY_NOTICES +816 -0
- package/dist/dispatch.js +27707 -0
- package/dist/envoy-channel.js +57695 -0
- package/dist/session-hook.js +26328 -0
- package/dist/session-title-hook.js +116 -0
- package/hooks/hooks.json +60 -0
- package/hooks/session-hook.ts +512 -0
- package/hooks/session-title-hook.ts +46 -0
- package/legion/.claude-plugin/plugin.json +4 -0
- package/legion/agents/deep-worker.md +68 -0
- package/legion/agents/oracle.md +35 -0
- package/legion/agents/plan-gap-analyst.md +65 -0
- package/legion/agents/plan-reviewer.md +104 -0
- package/legion/agents/thermonuclear-code-quality.md +30 -0
- package/legion/agents/thermonuclear-deep-review.md +30 -0
- package/legion/hooks/hooks.json +89 -0
- package/legion/skills/ce-simplify-code/LICENSE +21 -0
- package/legion/skills/ce-simplify-code/SKILL.md +66 -0
- package/legion/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
- package/legion/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
- package/legion/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
- package/legion/skills/leading-a-work-stream/SKILL.md +96 -0
- package/legion/skills/leading-a-work-stream/references/daily-plan.md +8 -0
- package/legion/skills/leading-a-work-stream/references/decision-rights.md +6 -0
- package/legion/skills/leading-a-work-stream/references/direction-and-coaching.md +13 -0
- package/legion/skills/leading-a-work-stream/references/dispatch-upkeep.md +8 -0
- package/legion/skills/leading-a-work-stream/references/sources.md +9 -0
- package/legion/skills/leading-a-work-stream/references/speed-tick.md +44 -0
- package/legion/skills/leading-a-work-stream/references/user-quotes.md +7 -0
- package/legion/skills/legion-architect/SKILL.md +86 -0
- package/legion/skills/legion-architect/references/acceptance.md +208 -0
- package/legion/skills/legion-architect/references/decomposition.md +129 -0
- package/legion/skills/legion-architect/references/design-gate.md +206 -0
- package/legion/skills/legion-architect/references/merge-notices.md +91 -0
- package/legion/skills/legion-architect/references/wake-routing.md +41 -0
- package/legion/skills/legion-controller/SKILL.md +151 -0
- package/legion/skills/legion-controller/references/admission.md +22 -0
- package/legion/skills/legion-controller/references/backlog-stewardship.md +101 -0
- package/legion/skills/legion-controller/references/daily-report.md +75 -0
- package/legion/skills/legion-controller/references/escalation.md +82 -0
- package/legion/skills/legion-controller/references/finding-asks.md +181 -0
- package/legion/skills/legion-controller/references/parked-decisions.md +198 -0
- package/legion/skills/legion-controller/references/session.md +100 -0
- package/legion/skills/legion-controller/references/slack-mentions.md +154 -0
- package/legion/skills/legion-controller/references/triage.md +96 -0
- package/legion/skills/legion-controller/references/wakes.md +132 -0
- package/legion/skills/legion-oracle/SKILL.md +48 -0
- package/legion/skills/legion-retro/SKILL.md +112 -0
- package/legion/skills/legion-retro/references/learnings.md +60 -0
- package/legion/skills/legion-retro/references/pr-body-content.md +56 -0
- package/legion/skills/legion-worker/SKILL.md +69 -0
- package/legion/skills/legion-worker/references/cleanup-deletion.md +22 -0
- package/legion/skills/legion-worker/references/conflicts-and-rewrites.md +164 -0
- package/legion/skills/legion-worker/references/default-branch-check.md +53 -0
- package/legion/skills/legion-worker/references/findings.md +199 -0
- package/legion/skills/legion-worker/references/merge-gate.md +353 -0
- package/legion/skills/legion-worker/references/pr-body.md +218 -0
- package/legion/skills/legion-worker/references/review-threads.md +77 -0
- package/legion/skills/legion-worker/references/systematic-rename.md +19 -0
- package/legion/skills/legion-worker/references/tools.md +708 -0
- package/legion/skills/thermonuclear-code-quality/LICENSE +21 -0
- package/legion/skills/thermonuclear-code-quality/SKILL.md +60 -0
- package/legion/skills/thermonuclear-deep-review/LICENSE +21 -0
- package/legion/skills/thermonuclear-deep-review/SKILL.md +80 -0
- package/package.json +49 -1
- package/scripts/build-inputs.txt +84 -0
- package/scripts/build.ts +415 -0
- package/scripts/check-packed-skills.sh +50 -0
- package/scripts/pack-skills.sh +146 -0
- package/scripts/smoke-channel.sh +178 -0
- package/scripts/smoke-clear-rebind.sh +164 -0
- package/scripts/smoke-session-title.sh +245 -0
- package/scripts/verify-build.sh +88 -0
- package/skills/agent-secrets/SKILL.md +180 -0
- package/skills/asd-ste100/LICENSE +21 -0
- package/skills/asd-ste100/SKILL.md +140 -0
- package/skills/asd-ste100/examples/before-after.md +92 -0
- package/skills/asd-ste100/references/writing-rules.md +59 -0
- package/skills/dispatch/SKILL.md +89 -0
- package/skills/dispatch-first/SKILL.md +46 -0
- package/skills/dispatch-writing-specs/SKILL.md +80 -0
- package/skills/envoy/SKILL.md +82 -0
- package/skills/envoy/references/check-settlement.md +62 -0
- package/skills/envoy/references/deliveries.md +67 -0
- package/skills/envoy/references/legion.md +14 -0
- package/skills/envoy/references/roles.md +12 -0
- package/skills/envoy/references/sources.md +41 -0
- package/skills/outside-message/SKILL.md +111 -0
- package/skills/plain-words/SKILL.md +88 -0
- package/src/channel-forwarder.ts +262 -0
- package/src/claude-channel.ts +705 -0
- package/src/claude-session.ts +38 -0
- package/src/envoy-channel-server.ts +2115 -0
- package/src/envoy-client.ts +26 -0
- package/src/envoy-off.ts +62 -0
- package/src/legion-claim.ts +211 -0
- package/src/legion-inbox.ts +110 -0
- package/src/legion-phase-hook.ts +554 -0
- package/src/legion-tool.ts +69 -0
- package/src/replay-state.ts +97 -0
- package/src/send-arguments.ts +13 -0
- package/src/session-identity.ts +165 -0
- package/src/session-state.ts +142 -0
- package/src/session-title.ts +251 -0
- package/src/subscription-topics.ts +16 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# The merge gate: review, acceptance, retro, READY, the merge, and the production check
|
|
2
|
+
|
|
3
|
+
Part of `skill://legion-worker`. Read it when you are the reviewer submitting a review or an
|
|
4
|
+
approval, the implementer on a look of its closed issue's production check, or the merger
|
|
5
|
+
building READY. Every path it cites is in cybermonklabs/legion.
|
|
6
|
+
|
|
7
|
+
The order, in full: the tester's evidence green → the reviewer's approval of the head → the
|
|
8
|
+
architect's acceptance, in `accepting` → retro → the merger's READY → the merge, which closes the
|
|
9
|
+
issue → the implementer's production check, after the close. Every handoff is a record on the
|
|
10
|
+
Dispatch issue, never a commit, so the head that merges carries none. Retro commits nothing
|
|
11
|
+
(*Retro*, below), so the approved head is the head that merges. READY still lets a commit that
|
|
12
|
+
changes only `docs/solutions/` stand, and a file under `.legion/` that GitHub's compare lists as
|
|
13
|
+
removed, the commit `legion handoffs import` makes to remove the directory where handoffs lived
|
|
14
|
+
before they moved to Dispatch. A conflict-forced merge goes back to the reviewer for a
|
|
15
|
+
confirmation or a new round, as the fingerprint decides (*The reviewer*, below, and
|
|
16
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`), unless READY refused it, and any
|
|
17
|
+
other change to the head voids it.
|
|
18
|
+
|
|
19
|
+
## The reviewer
|
|
20
|
+
|
|
21
|
+
- The reviewer reads CI on GitHub and verifies the `Threads` facts and the issue's results there
|
|
22
|
+
and with `dispatch results --issue <issue>` — never from a handoff — then, when the diff
|
|
23
|
+
touches runtime code, runs
|
|
24
|
+
`task(agent="thermonuclear-deep-review")` and
|
|
25
|
+
`task(agent="thermonuclear-code-quality")` once at that head — the head the round reviews —
|
|
26
|
+
waits for each one's result with `wait`, since each runs as a job of its own, and records the
|
|
27
|
+
verdict from what those results say. A result that carries no report, or that names another
|
|
28
|
+
place for it, is a failed run, not a verdict: run that agent again, or read its transcript, and
|
|
29
|
+
record nothing from it until you have the findings text in front of you. Reading the agent's
|
|
30
|
+
diff yourself and recording that in its place is the same as answering the rubric from a bare
|
|
31
|
+
verdict — it is your own reading standing in for a review that did not happen, not the review
|
|
32
|
+
of your own that the round always asks for. When you can do neither — the agent will not run
|
|
33
|
+
again and its transcript holds no findings — that half of the round has no review: tell the
|
|
34
|
+
architect, do not approve, and wait with a `WAITING` line naming what you wait on until it
|
|
35
|
+
answers. The CI it reads is the head's own checks: no handoff is a
|
|
36
|
+
commit, so every push to the branch is work whose head gets GitHub's workflow runs of its own.
|
|
37
|
+
With `<o>/<r>`, `<n>`, `<base>` and `<headRefOid>` from
|
|
38
|
+
`gh pr view <n> --json baseRefName,headRefOid`, two reads. The required set is the
|
|
39
|
+
base branch's rulesets' required checks and its branch protection's, when it has any. Both
|
|
40
|
+
branch endpoints take the base as a URL path, each segment escaped and each `/` kept (a raw `#`
|
|
41
|
+
or `%` reads another branch), built in the same command from the name GitHub returns, never
|
|
42
|
+
pasted or carried over from an earlier command:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
base=$(gh pr view <n> -R <o>/<r> --json baseRefName -q .baseRefName) && \
|
|
46
|
+
base_path=$(jq -rn --arg b "$base" '$b | split("/") | map(@uri) | join("/")') && \
|
|
47
|
+
echo rulesets: && gh api --paginate "repos/<o>/<r>/rules/branches/$base_path" --jq '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context' && \
|
|
48
|
+
echo protection: && gh api "repos/<o>/<r>/branches/$base_path" --jq '.protection.required_status_checks.checks[].context'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A read GitHub refuses stops the command there, and is reported to the architect, never guessed.
|
|
52
|
+
The head's check-runs
|
|
53
|
+
(`gh api repos/<o>/<r>/commits/<headRefOid>/check-runs --paginate --jq '.check_runs[] | [.name, .status, .conclusion] | @tsv'`):
|
|
54
|
+
every required name present, `completed` and `success` (GitHub counts `neutral` and `skipped`
|
|
55
|
+
as passing for a required check). A required check
|
|
56
|
+
`queued` or `in_progress`: wait. One `completed` as `cancelled`, or absent: re-run that run's
|
|
57
|
+
failed and cancelled jobs, once, then wait — never the whole run, which runs again every job that
|
|
58
|
+
succeeded too. Find the run and job at the head: the head's runs
|
|
59
|
+
(`gh api 'repos/<o>/<r>/actions/runs?head_sha=<headRefOid>&per_page=100' --jq '.workflow_runs[] | [.id, .name, .event, .status, .conclusion] | @tsv'`),
|
|
60
|
+
then each candidate run's jobs
|
|
61
|
+
(`gh api repos/<o>/<r>/actions/runs/<run-id>/jobs --jq '.jobs[] | [.id, .name, .status, .conclusion] | @tsv'`).
|
|
62
|
+
A cancelled job: `gh run rerun <run-id> --failed`, which GitHub applies to the run's failed and
|
|
63
|
+
cancelled jobs and the jobs that depend on them, and to nothing that succeeded. Never
|
|
64
|
+
`--job <job-id>` alone: under a routing workflow, re-running only the final job reads the
|
|
65
|
+
cancelled results again and stays red. GitHub refuses a re-run while an attempt of that run is
|
|
66
|
+
still in progress: wait for it. When no
|
|
67
|
+
run at the head holds the job, or nothing will run it (a check only a commit status reports, a
|
|
68
|
+
workflow another repository defines), tell the architect and do not approve.
|
|
69
|
+
Approval is refused while any `before_merge` check of the issue's own Verification document has no passing result that is current at the head being reviewed: `REQUEST_CHANGES` naming each such check by its block id, and `dispatch results --issue <issue>` is the read that decides it.
|
|
70
|
+
An issue with no Verification document, or whose Verification document holds no `before_merge` check, is a gap you name in the review body, never a review that asks for changes: say which of the two it is, and tell the architect, who writes the checks. Your own findings and the checks that do exist still decide the review, and on an issue that has none the architect's acceptance is the gate.
|
|
71
|
+
A result is current at that head while the head is the head of an open pull request, or the final head of a merged one, attributed to the check's own issue. A child's pull request never makes a root check's result current, and a child never records the result that proves a root's check. A child that carries part of a root's requirement proves it with a check on the child's own Verification document whose `serves` names that requirement, and that result is current at the child's head. A root's own checks are proven at the root's own head, or on the default branch for an `after_merge` check, in the root's last round.
|
|
72
|
+
On a docs-only PR the `Thermo` line still records the simplify pass and the defects it
|
|
73
|
+
reported, and its pair half reads `thermonuclear pair: not run (docs-only)`.
|
|
74
|
+
Submit **one review per round** —
|
|
75
|
+
`REQUEST_CHANGES` when any correctness finding stands, otherwise `APPROVE` of the head you
|
|
76
|
+
reviewed — always named by SHA — carrying every inline comment in that single
|
|
77
|
+
call: `gh api --method POST repos/{owner}/{repo}/pulls/{number}/reviews --input body.json`
|
|
78
|
+
with `commit_id`, `event` (`REQUEST_CHANGES` or `APPROVE`), `body` (with the
|
|
79
|
+
Legion footer), and a `comments[]` array of `{path, line, side, body}`, one entry per
|
|
80
|
+
finding — never one `pr review` call per finding (each submission fires a `pr-review` wake).
|
|
81
|
+
A `COMMENT` decides nothing. After a
|
|
82
|
+
forward merge, compute the fingerprint (*The unchanged-diff check* in
|
|
83
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`) at the
|
|
84
|
+
`commit_id` of your last submitted review and at the new head. Equal and that review was
|
|
85
|
+
`APPROVE`: submit one more `APPROVE` naming the new head by SHA, its body naming both SHAs
|
|
86
|
+
and the fingerprint — a confirmation, not a round; no thermo pass, no thread pass. Equal and
|
|
87
|
+
that review was `COMMENT` or `REQUEST_CHANGES`: continue that round against the new head;
|
|
88
|
+
nothing restarts. Different, or either run printed no fingerprint: a new round — thermo again,
|
|
89
|
+
one review. A return whose task's `Reason:` names a READY refusal is never a confirmation, even
|
|
90
|
+
after a forward merge, and a no-fix `REQUEST_CHANGES` that answered one is never continued: the round
|
|
91
|
+
after it reviews the refused change in full from the last `APPROVE` (*Retro's commits do not
|
|
92
|
+
void the reviewer's approval* below).
|
|
93
|
+
- Answer every thread you opened, and every thread a bot opened that is none of Legion's role
|
|
94
|
+
Apps, as `skill://legion-worker/references/review-threads.md` says; the same reference says
|
|
95
|
+
when every thread is settled enough to approve, and resolving one never gates your approval.
|
|
96
|
+
- **Fix a prose-only finding yourself** — wording, a count, a tense, a citation — in your own
|
|
97
|
+
commit on the pull request, and list it in your review. Send the work back only for a finding
|
|
98
|
+
that touches code or behaviour. Push the fix **before** you submit the round's review, and
|
|
99
|
+
review the head that push made. A push above the head you approved leaves the approval
|
|
100
|
+
standing for nothing, since any push may change code: the round goes stuck and your architect
|
|
101
|
+
is told (`ApprovalStands` in `packages/daemon/internal/classify/decisions.go`,
|
|
102
|
+
`stuckApprovedOtherCode` in `packages/daemon/internal/workflow/review.go`, and the cases "the
|
|
103
|
+
approval, then a push" and "a push, then the approval of the head before it" in
|
|
104
|
+
`packages/daemon/internal/workflow/review_test.go`). GitHub runs the branch's workflows on that
|
|
105
|
+
push, as on every push. Wait for that head's required checks to be
|
|
106
|
+
green, and approve only then. The daemon sends nothing back for a red at a head the review App
|
|
107
|
+
pushed (`PlannedRed` in `classify/decisions.go`), so your own read is the only one. A red you
|
|
108
|
+
approve over reaches nobody: the tree runs on to the merge, where GitHub holds the pull request
|
|
109
|
+
under the base branch's required checks until someone reads them. A red there is a
|
|
110
|
+
`REQUEST_CHANGES` when the code is at fault, and your own commit to fix when your prose broke
|
|
111
|
+
it. The architect reads your prose commits at its acceptance (*The architect's acceptance*,
|
|
112
|
+
below), and that read is no new round. A round after a READY refusal is the one exception. Its
|
|
113
|
+
approval needs the tester's results to be current at the head, or at an ancestor with only files
|
|
114
|
+
under `.legion/` removed since. So there you fix no prose yourself, and you name each prose
|
|
115
|
+
finding in a `REQUEST_CHANGES`.
|
|
116
|
+
|
|
117
|
+
A reviewer's phase ends with its completion, not with its review. Every round records a handoff
|
|
118
|
+
and takes this order: read CI as above; write the handoff and record it with
|
|
119
|
+
`dispatch handoff-write`; submit the review of the head, by its SHA; then complete. A red at the
|
|
120
|
+
code is a `REQUEST_CHANGES` naming the check, never an approval. A request for changes does not
|
|
121
|
+
wait, since it stands whatever CI says and the issue leaves reviewing with it. The approval waits
|
|
122
|
+
on nothing either: it ends the round whatever CI says at the head it names, since the round reads
|
|
123
|
+
no CI verdict, and a red the daemon reads later — at that head or any above it — is the code's red
|
|
124
|
+
and resumes the implementer from wherever the issue is (reviewing, retro or merging) without the
|
|
125
|
+
reviewer. A head your own push changed is the exception. The daemon marks it a planned red and
|
|
126
|
+
sends nothing back for it, so wait for its checks yourself (*Fix a prose-only finding yourself*,
|
|
127
|
+
above). The daemon moves the issue once both are in — the decision GitHub reports and your
|
|
128
|
+
completion, in either order — so a review posted without a completion leaves the issue in
|
|
129
|
+
reviewing until you finish.
|
|
130
|
+
|
|
131
|
+
## The architect's acceptance
|
|
132
|
+
|
|
133
|
+
- Between the reviewer's approval and retro the issue sits in `accepting` (Dispatch status
|
|
134
|
+
`needs_review`), where no worker is started: the tree's architect reads the evidence (the
|
|
135
|
+
tester's handoff, `dispatch results --issue <issue>` at the approved head and the reviewer's
|
|
136
|
+
approval of the head by SHA; the daemon lets no child hold it; a parent's waits for its last
|
|
137
|
+
round, `skill://legion-architect`, section 4) and accepts with the
|
|
138
|
+
`legion` tool's `sign_off`, which moves the issue to retro, or sends it back to `implementing`
|
|
139
|
+
or `testing` with `request_backward_move` and a reason, which reaches the started worker's task
|
|
140
|
+
as its `Reason:` line and counts a review round. The acceptance comment names each check with
|
|
141
|
+
its result. An issue with no Verification document, or with no `before_merge` check, reaches
|
|
142
|
+
the architect as a gap the tester and the reviewer named and neither refused, so the
|
|
143
|
+
architect's acceptance is the only gate on it: the acceptance comment names that gap and
|
|
144
|
+
decides there, accepting the issue or sending it back with the checks the architect writes,
|
|
145
|
+
which closes no design gate while dispatch://INFRA-175 has not shipped. The architect also
|
|
146
|
+
reads the reviewer's own prose commits here, with the rest of the evidence, and that read is no
|
|
147
|
+
new round. It does not
|
|
148
|
+
accept while the issue holds a finding ask no controller has taken (`findings.md`, "When no
|
|
149
|
+
controller takes it"). The issue's approval is gated by the `before_merge` checks of its own
|
|
150
|
+
Verification document at its own head, so a child's result against a root's check proves
|
|
151
|
+
nothing for the root: the root's checks are proven at the root's own head, in its last round.
|
|
152
|
+
The acceptance asks for no `after_merge` result: that one is recorded
|
|
153
|
+
after the close (*The production check after the close*, below).
|
|
154
|
+
CI red at the head in `accepting` returns the issue to `implementing` as it does in review. A
|
|
155
|
+
head the review App pushed is the exception: the daemon marks it a planned red and acts on it
|
|
156
|
+
nowhere, and GitHub holds the merge under the base branch's own rules.
|
|
157
|
+
|
|
158
|
+
## Retro
|
|
159
|
+
|
|
160
|
+
- **Retro commits nothing.** It reads the evidence, writes what the work taught as a
|
|
161
|
+
`dispatch comment` on the issue for the skill librarian, brings the PR body up to date and
|
|
162
|
+
completes (`skill://legion-retro` gives the steps), so the approved head is the head READY
|
|
163
|
+
names. Anything above the approved
|
|
164
|
+
head voids the approval, except the handoff import's removal of `.legion/` files and a commit
|
|
165
|
+
that changes only `docs/solutions/`, which READY still lets through: READY is refused
|
|
166
|
+
`READY_UNREVIEWED_CHANGE`, naming the paths, and the merger tells the architect, who has it send
|
|
167
|
+
the head back to review instead of completing, the move's reason naming the refusal's code and
|
|
168
|
+
what it found. `READY_UNREVIEWED_CHANGE` has more causes than a path outside retro's own; every
|
|
169
|
+
one of them sends the head back to review and none is an error to retry, and the complete list,
|
|
170
|
+
with each remedy, is the merger's own role prompt part.
|
|
171
|
+
A conflict merge pushed above the approved head with no review round after it
|
|
172
|
+
brings the base's paths above that head, so READY refuses it the same way; one sent back through
|
|
173
|
+
`implementing` by the conflict procedure gets its confirmation before READY runs. A READY
|
|
174
|
+
refusal's return reaches the reviewer as a new round, never a fingerprint confirmation. Retro
|
|
175
|
+
removes nothing.
|
|
176
|
+
- **Retro brings the PR body's path-derived content up to date before it completes.** Whatever the
|
|
177
|
+
repository's instructions derive from the pull request's changed paths (a checklist named for
|
|
178
|
+
each class of path, read by a required check), retro recomputes for the whole diff at its head
|
|
179
|
+
and writes into the live body before it completes (`skill://legion-retro`), since the merger
|
|
180
|
+
reports a stale body rather than rewriting it. A body edit changes no commit, so the approval
|
|
181
|
+
stands.
|
|
182
|
+
|
|
183
|
+
## The merger
|
|
184
|
+
|
|
185
|
+
- The merger resolves no review thread: the reviewer's approval already judged them, and READY
|
|
186
|
+
requires no thread state beyond it. It quotes what sits above the approved head with
|
|
187
|
+
`cd -- "$LEGION_WORKSPACE" && jj -R "$LEGION_WORKSPACE" git fetch && jj -R
|
|
188
|
+
"$LEGION_WORKSPACE" diff --from <approved-sha> --to <tip-sha> --summary` in READY; the daemon,
|
|
189
|
+
not the merger, holds those commits to retro's: READY reads GitHub's compare of the round's
|
|
190
|
+
approved head with READY's head and refuses any other path (`READY_UNREVIEWED_CHANGE`), and a
|
|
191
|
+
round with no recorded approval (`READY_NOT_APPROVED`).
|
|
192
|
+
*The READY packet* is
|
|
193
|
+
`READY #<n> at <current sha> (approved at <approved sha>) for <KEY> (<pr url>)`
|
|
194
|
+
(the shape `packages/daemon/internal/prompts/roles/merger.md` defines), then the PR body's
|
|
195
|
+
`Outcome:` line and its `Not proven / risk:` value — every bullet under that label joined with
|
|
196
|
+
`; ` on the one READY line, or `none` — quoted from the `## For the reviewer` block at that same
|
|
197
|
+
head (or one line saying the body carries no brief — the packet still goes out), then the
|
|
198
|
+
`--summary` output and the PR body's gate facts. The READY packet names each `before_merge`
|
|
199
|
+
check with its latest result, as `dispatch results --issue <issue>` prints it; a check with no
|
|
200
|
+
current passing result is reported to the architect instead of completing.
|
|
201
|
+
- READY is `handoff_complete` with `ready: true` and the packet as its `summary`; the merger runs
|
|
202
|
+
no pre-flight of its own, and its completion reports the commit the workspace stands on. READY's
|
|
203
|
+
checks run in the daemon, which reads the pull request's head on GitHub and refuses, before
|
|
204
|
+
recording anything, on each ground the merger's role prompt names in its `Go daemon workflow`
|
|
205
|
+
part, by its code and with its remedy. That part is the one complete list of READY's refusals. A
|
|
206
|
+
refusal records nothing; the merger reports it to the architect unless it says to complete
|
|
207
|
+
again, and never pushes a commit to make CI run. On success the daemon posts the packet as a
|
|
208
|
+
`dispatch message` on the issue, publishes it to the project's merge queue role when one is set,
|
|
209
|
+
and says on the issue when that role has no live holder; the answer's `note` says when READY was
|
|
210
|
+
published on a pull request a person already merged, whose head can no longer change, or carries
|
|
211
|
+
the by-hand note of a pull request a person merges.
|
|
212
|
+
- **The daemon submits the merge the moment READY is accepted, unless the answer says a person
|
|
213
|
+
merges it.** It queues a `ready_merge` outbox row for the head READY named, and the row, as the
|
|
214
|
+
implement App, reads GitHub and then the record: while the issue still awaits that READY head, a
|
|
215
|
+
base with a merge queue gets the pull request enqueued once GitHub reports it mergeable now
|
|
216
|
+
(`CLEAN`, `UNSTABLE` or `HAS_HOOKS`); otherwise a pull request GitHub reports mergeable now is
|
|
217
|
+
squash-merged, and one in a repository that allows auto-merge is armed. Every request carries
|
|
218
|
+
READY's head as GitHub's expected head, so GitHub refuses it once the head has moved. On a
|
|
219
|
+
merge-queue base the row never arms auto-merge: while a required check still runs, or anything
|
|
220
|
+
else keeps GitHub from reporting the pull request mergeable, it waits, reading GitHub again on
|
|
221
|
+
the outbox's backoff, and enters the queue once GitHub does. In a repository that allows neither a
|
|
222
|
+
merge queue nor auto-merge, the row waits the same way until GitHub reports the pull request
|
|
223
|
+
mergeable — a required check finishing, a required review a person gives — and then merges it. An
|
|
224
|
+
arm or a queue entry is not the end: the row keeps reading GitHub until GitHub reports the pull
|
|
225
|
+
request merged at READY's head. Once it has seen the arm or entry, it never submits that head
|
|
226
|
+
again after someone else removes it: the merge queue taking the pull request out because the merge
|
|
227
|
+
failed (GitHub's reason `failed_checks`, or an unexplained reason with a check that failed, timed
|
|
228
|
+
out, failed to start, needed action or went stale on the commit the queue built for its
|
|
229
|
+
`merge_group` CI) sends the issue back to `implementing`, the implementer's task naming each
|
|
230
|
+
failing check and its run, as a red head does; any other removal (a person dequeues it,
|
|
231
|
+
cancelling its runs, or turns auto-merge off, GitHub turns it off, the queue finds a conflict)
|
|
232
|
+
ends the row and is told to the architect once
|
|
233
|
+
(`merge-removed`). Two removals are submitted again: the implement App's own disarm sent before
|
|
234
|
+
READY that GitHub applied late (auto-merge `manually_disabled`, or a `manual` dequeue, by that
|
|
235
|
+
App), and auto-merge a close turned off, which the reopen at READY's head undid. GitHub's
|
|
236
|
+
rules decide each answer: the daemon bypasses
|
|
237
|
+
none, and a person may merge it first. A refusal a retry would meet again (squash merges disabled,
|
|
238
|
+
a permission the App lacks) ends the row and is told to the architect once (`merge-refused`), with
|
|
239
|
+
GitHub's message. While the record still awaits READY's head, GitHub showing the pull request
|
|
240
|
+
closed or at another head is a webhook not yet applied, so the row waits for the two to agree.
|
|
241
|
+
Only once GitHub's last change to the pull request is more than 10 minutes old, however long the
|
|
242
|
+
row waited before, does the row end and tell the architect once (`merge-stalled`). A merge the
|
|
243
|
+
daemon submitted that GitHub has not completed within an hour of READY's acceptance tells the
|
|
244
|
+
issue's architect once (`merge-waiting`) and goes on waiting: nothing is withdrawn, the merge
|
|
245
|
+
stands, the issue stays in `awaiting_merge`, and the architect reads the pull request for what
|
|
246
|
+
GitHub waits on — a required check that never ran or failed, a review only a person can give, a
|
|
247
|
+
queue that has not landed it — and asks a person for what no worker can do. A reopen at
|
|
248
|
+
READY's head reuses the row, starts it over as though nothing had been armed, and takes it from a
|
|
249
|
+
run that read the record before the reopen. Any other failure retries.
|
|
250
|
+
No role runs `gh pr merge`. When the answer's `note` begins `MANUAL_MERGE_REQUIRED:` (the pull
|
|
251
|
+
request touches a path the project's `merge.by_hand` names), READY stands but the daemon submits
|
|
252
|
+
nothing: the merger tells the architect the note and the head, and a person merges under the
|
|
253
|
+
repository's rules. While an earlier auto-merge disarm for that pull request remains unfinished,
|
|
254
|
+
a by-hand READY refuses `READY_DISARM_PENDING`; retry it once the disarm finishes, so a stale
|
|
255
|
+
ordinary arm cannot merge the person-only head. Ordinary READY does not wait for the disarm. The
|
|
256
|
+
daemon tells that READY with the note at its head and the packet's `Merge:` line replaced, so the
|
|
257
|
+
issue and the merge queue role are never asked to arm it. A READY the design gate refused is
|
|
258
|
+
submitted by the daemon when the approval that opens the gate moves the issue to
|
|
259
|
+
`awaiting_merge`. A push that moves the head past a READY the gate holds voids it before then:
|
|
260
|
+
the architect is told `READY_HEAD_MOVED` and has the merger build a new READY at the new head.
|
|
261
|
+
The packet's second line records the step,
|
|
262
|
+
`Merge: the daemon submits pull request #<n> at <head> once READY is accepted`.
|
|
263
|
+
- **The daemon disarms a merge no READY covers.** Auto-merge stays enabled across pushes, and the
|
|
264
|
+
expected head is checked only when it is enabled, so the daemon itself disables the pull
|
|
265
|
+
request's auto-merge and takes it out of a merge queue, as the implement App, on any push or
|
|
266
|
+
reopen but one landing on an awaiting READY's head, when READY is withdrawn (a push or reopen
|
|
267
|
+
moved the head past it, its CI turned red, or it conflicts with its base, each of which sends the
|
|
268
|
+
issue back to `implementing`), when a child is set back to `todo`, and when the issue leaves the
|
|
269
|
+
workflow. A by-hand READY covers no arm, so whoever arms it is undone. A `ready_merge` row whose
|
|
270
|
+
READY the record has left, or whose pull request the record holds closed, submits nothing; a pull
|
|
271
|
+
request reopened at an awaiting READY's head gets that head's merge submitted again. No role runs
|
|
272
|
+
a disarm.
|
|
273
|
+
|
|
274
|
+
## The production check after the close
|
|
275
|
+
|
|
276
|
+
- **The merge closes the issue; the production check is the implementer's work after the close.**
|
|
277
|
+
When the pull request merges, the daemon writes `done` and, in the implementer's session, still
|
|
278
|
+
live, hands it the check task. On a root whose tree still runs the close, and so the check task,
|
|
279
|
+
comes later: the merge is recorded, the root waits in `awaiting_merge` until the last other
|
|
280
|
+
issue of its tree is done or parked, and the check opens with that close. Its first line is
|
|
281
|
+
`Production check of <KEY>: <title>.`, and it names the repository, the pull request, the merge
|
|
282
|
+
commit, the look's number, what woke the look (`the merge`, for the first) and whether it is the
|
|
283
|
+
last. A resumed session tells the check task
|
|
284
|
+
from its last phase task by that first line. The check carries no phase and no handoff: a look
|
|
285
|
+
ends with `handoff_complete` carrying a `verdict` and a `summary`, no commit, and the daemon
|
|
286
|
+
refuses a report on an issue with no open check (`CHECK_NOT_OPEN`), on a look already ended
|
|
287
|
+
(`CHECK_LOOK_NOT_OPEN`), or with no verdict of `pass`, `pending` or `fail`
|
|
288
|
+
(`CHECK_VERDICT_REQUIRED`).
|
|
289
|
+
- **Each look does three things.** Do what the deployment instructions say to reach the deploy:
|
|
290
|
+
when they name an agent or role for it, `envoy_publish` the merge commit to that role's topic
|
|
291
|
+
with `expects_reply: none`, and await no reply. Read whether production carries the merge. Then
|
|
292
|
+
report one of three outcomes:
|
|
293
|
+
- `pass`, once you drove the changed path in production through the user's own access path and
|
|
294
|
+
recorded the result: `dispatch result` against the issue's `after_merge` check, at the merge
|
|
295
|
+
commit, its `observed` ending `woken by <event>`, with the printed line in the PR body's
|
|
296
|
+
`Checks` block and one pull-request comment carrying the Legion footer. A closed issue takes a
|
|
297
|
+
result for an `after_merge` or `default_branch` check and refuses every other write. A staging
|
|
298
|
+
pass is not this, since a
|
|
299
|
+
staging gate does not run every resource production does.
|
|
300
|
+
- `pending`, when production does not carry the merge yet (a shared profile still holding the
|
|
301
|
+
previous release, a daemon still running the previous commit, a deploy nobody has run): the
|
|
302
|
+
summary names what is missing, nothing is recorded, and the check's line in the body stays at
|
|
303
|
+
`- Check <block id> (after_merge): not yet run`.
|
|
304
|
+
- `fail`, for a defect: record the failing result with `dispatch result --verdict fail`, file no
|
|
305
|
+
issue, and take the path
|
|
306
|
+
`skill://legion-worker/references/findings.md` gives it. A defect with a clear fix goes to the
|
|
307
|
+
controller topic, with its evidence comment on the controller's report issue. A defect that
|
|
308
|
+
needs a decision is a `dispatch ask` on the report issue, and you publish its reference to the
|
|
309
|
+
controller topic. Either names this issue, the pull request, the merge commit, what you drove
|
|
310
|
+
and what failed. Name the message or the ask (`dispatch://<REPORT KEY>/ask/<id>`) in the
|
|
311
|
+
result's `observed` and in the summary.
|
|
312
|
+
- **Numbers the daemon holds are read with the `legion` tool.** Where the verdict rests on what a
|
|
313
|
+
deployment's idle release has done, `read_release_records` answers a window of the daemon's own
|
|
314
|
+
records of its releases, resumes and holds: its one argument `since` is the RFC 3339 instant
|
|
315
|
+
the window starts at, and the tool's own description says what the answer carries. It reads
|
|
316
|
+
them with this session's own claim, so there is no credential to fetch and none on disk.
|
|
317
|
+
- **Nothing waits in the session for a deploy.** A `pending` ends the look. Where no wake is
|
|
318
|
+
queued yet, the daemon suspends your claim with its session kept, and the issue's Sandbox goes
|
|
319
|
+
with its pod and disk; where a wake arrived while the look was open, the next look starts at once
|
|
320
|
+
on the same pod. Either way the daemon wakes the check (after a suspension a new pod comes back
|
|
321
|
+
on a fresh disk, and your session resumes with a new check task) on Dispatch's
|
|
322
|
+
`issue.deployed` event for the pull request; when the project's `legion.yaml` names the
|
|
323
|
+
repository's deploy workflow (`projects.<KEY>.deploys`), at each completed run of that workflow
|
|
324
|
+
on the default branch whose named step (such as `Apply`) succeeded after the merge, once per such
|
|
325
|
+
job; or, for `cybermonklabs/legion`, on a Legion daemon's boot on a build containing the merge, on
|
|
326
|
+
Dispatch's `server.started` announcing that every server process serves such a build, or on the
|
|
327
|
+
build the daemon read from Dispatch at its boot. The woken look's task names the event
|
|
328
|
+
(`Dispatch's issue.deployed: run <id> completed <t>`,
|
|
329
|
+
`the deploy workflow's run: <repo> <workflow> run <id> attempt <n>, job <job name>: <step> succeeded at <t>`,
|
|
330
|
+
`the Legion daemon's boot on build <sha>`, `Dispatch serving build <sha> on every server process`,
|
|
331
|
+
`Dispatch's build <sha>`), and the result you record names it too, so the pull request carries
|
|
332
|
+
the cause. Never poll for the deploy, never wait on a reply, and never ask anyone for approval
|
|
333
|
+
to deploy. Open a `dispatch ask` to a person only when the instructions name nobody for that
|
|
334
|
+
deploy; it starts with the production gap and why it matters, then names the required install or
|
|
335
|
+
restart step, its risk, and outcome-named options, and the look still ends `pending`.
|
|
336
|
+
- **The bounds.** A look is bounded at 2 hours: one that reports nothing ends as a `pending`
|
|
337
|
+
would. The whole check is bounded by the project's `production_check_hours` (336, 14 days, by
|
|
338
|
+
default) from the close, and the wake at that bound is the last look, told so in its task
|
|
339
|
+
(`the bound: production did not carry the merge within <N days>`). A `pass` or `fail` there
|
|
340
|
+
closes the check as any look's would; a `pending` closes it as not run, and before reporting it
|
|
341
|
+
you write on the pull request, in the check's line and a comment, that the check did not
|
|
342
|
+
run because production did not carry the merge within the bound.
|
|
343
|
+
- **The record lives on the pull request and in the result.** Dispatch refuses a message, a
|
|
344
|
+
comment or an ask on a closed issue (`ISSUE_CLOSED`) and takes only a result for an
|
|
345
|
+
`after_merge` or `default_branch` check, so the deployment instructions' "on the pull request
|
|
346
|
+
and the issue" is the pull request and that result. A defect reaches Dispatch by the two paths above:
|
|
347
|
+
a clear defect as the message to the controller, which gives it to the tree that owns that code,
|
|
348
|
+
and any other as the ask on the
|
|
349
|
+
controller's report issue. The architect accepted the issue before the merge and is asked
|
|
350
|
+
nothing after it. `pass` or
|
|
351
|
+
`fail` closes the check: your claim is retired and the issue's Sandbox goes with its pod and
|
|
352
|
+
disk, for a root as for a child. Neither ending retires or suspends anything of an issue back in
|
|
353
|
+
the workflow (a root reopened inside its linger): its claim and pod are the new run's.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# PR body, recorded results, and the simplify pass
|
|
2
|
+
|
|
3
|
+
Part of `skill://legion-worker`. Read it before you write or edit any line of the pull request
|
|
4
|
+
body, record a result against one of the issue's checks, read another phase's results, or run the
|
|
5
|
+
simplify pass. Every path it cites is in cybermonklabs/legion.
|
|
6
|
+
|
|
7
|
+
## The pull request body template
|
|
8
|
+
|
|
9
|
+
The implementer writes the PR body from this template from the moment the PR opens, and every
|
|
10
|
+
later phase keeps it current rather than replacing it:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
## For the reviewer
|
|
14
|
+
|
|
15
|
+
**Outcome:** <one sentence a user of this repository would recognise: what someone can now do, or what stops going wrong>
|
|
16
|
+
**Why:** <the problem, one or two sentences, ending with the Dispatch key in parentheses — the key only, never a URL>
|
|
17
|
+
**Change:**
|
|
18
|
+
- <two to five bullets, each one behaviour a user or operator meets, never a file name>
|
|
19
|
+
**Look at first:** <one to three `path:line` places where a wrong decision would hurt> (the reviewer writes this line)
|
|
20
|
+
**Proven by:** <the surface and the run of the check the change turns on, one line>
|
|
21
|
+
**Not proven / risk:**
|
|
22
|
+
- <one line per claim recorded as unproven before READY>, or the single word `none` (the reviewer writes this line; `none` is invalid while such a claim stands)
|
|
23
|
+
**Size:** <files changed, +added/−removed>
|
|
24
|
+
|
|
25
|
+
## Verification
|
|
26
|
+
|
|
27
|
+
**Threads:** <n> resolved, 0 unresolved. Each answered and resolved individually by the implementer, the pull request's author, never in bulk (the reviewer names the bot threads it accepted; it resolves none itself):
|
|
28
|
+
- Thread <node id>: fixed in <commit-sha> — <one line>; resolved at <head-sha>.
|
|
29
|
+
- Thread <node id>: not a defect — <reason>; resolved at <head-sha>.
|
|
30
|
+
- Thread <node id>: accepted by the reviewer — <its reason, one line>; resolved at <head-sha>.
|
|
31
|
+
- Thread <node id>: still open — <what remains>; unresolved at <head-sha>.
|
|
32
|
+
|
|
33
|
+
**Thermo:** `ce-simplify-code` once at <head-sha>: <0 applied | applied → new head <sha>>; defects reported: <none | <n>, fixed in <sha>>; thermonuclear pair at the reviewed head <sha>:
|
|
34
|
+
<verdict>. (on a docs-only PR the simplify half and the defects are recorded as on any other diff, and the pair half reads `thermonuclear pair: not run (docs-only)`)
|
|
35
|
+
|
|
36
|
+
**Checks:** one line per `check` block of the issue's Verification document, in the document's
|
|
37
|
+
order, each the line `dispatch result` printed when the result was recorded:
|
|
38
|
+
- Check <block id> (<when>): <pass|fail> at <the commit's first 12 characters>, recorded by <role>. <the check's first words>
|
|
39
|
+
- Check <block id> (after_merge): not yet run
|
|
40
|
+
|
|
41
|
+
**Fast-follow:** "none", or one named cleanup item in a file this pull request does not touch, and where it will land.
|
|
42
|
+
|
|
43
|
+
**Chain:** stacked on <base bookmark> frozen at <sha> / not stacked.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Never waiting on CI
|
|
47
|
+
|
|
48
|
+
The implementer runs the repository's fast local checks for the paths it changed before the push,
|
|
49
|
+
then pushes and completes. It never waits for, watches or polls the pull request's remote CI
|
|
50
|
+
(`gh run watch`, `gh pr checks --watch`, `sleep` around `gh run view`) and never sleeps in a loop
|
|
51
|
+
(the step's spaced reads of the pull request's head after a push are the one sleep this rule
|
|
52
|
+
allows, and they read no check; `skill://legion-worker`, *Every role pushes its own commits*,
|
|
53
|
+
gives them).
|
|
54
|
+
Under the Go daemon a check or workflow the base branch requires that settles red on the head,
|
|
55
|
+
before the completion or after it, sends the work back to `implementing` with the failing checks
|
|
56
|
+
named, until `max_fix_attempts` is spent, when the architect decides. The one CI result an implementer waits for is a re-run: when its task names a red that
|
|
57
|
+
failed on its own, it re-runs the failed jobs once (`gh run rerun <run-id> --failed`) and completes once
|
|
58
|
+
the re-run settles green, woken by the pull request's checks event (`skill://envoy`, *Wait for
|
|
59
|
+
CI or a merge*: subscribe, then end the turn), never by a sleep loop. A completion while that red
|
|
60
|
+
still stands is sent back, at once for a required check and at the daemon's next read of the runs
|
|
61
|
+
(within two minutes) for a required workflow.
|
|
62
|
+
|
|
63
|
+
A pull-request body edit re-runs a check that judges the body at once, on the head as it is. While
|
|
64
|
+
the pull request conflicts with its base (GitHub shows it `CONFLICTING`), no workflow triggered
|
|
65
|
+
`on: pull_request` runs for any of its activity types, so a body edit re-judges nothing there; a
|
|
66
|
+
push cures it only once the pull request is mergeable, so the implementer forward-merges a
|
|
67
|
+
conflicting one first (*Reintegrating the base* in
|
|
68
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`).
|
|
69
|
+
|
|
70
|
+
## The brief for the human
|
|
71
|
+
|
|
72
|
+
`## For the reviewer` is written for the person who merges; `## Verification` below it stays the
|
|
73
|
+
ledger the reviewer and merger check against GitHub. The implementer writes `Outcome`, `Why`,
|
|
74
|
+
`Change`, `Proven by`, and `Size` when the pull request opens, and keeps them true after every
|
|
75
|
+
push; `Outcome` is a sentence a user of the repository would recognise, never "fix bug" or a file
|
|
76
|
+
name, and `Why` ends with the Dispatch key, never a URL. The reviewer writes `Look at first` and
|
|
77
|
+
`Not proven / risk` at each round, into the live body (`gh api
|
|
78
|
+
repos/{owner}/{repo}/pulls/{number} --jq .body`, edit, then `--method PATCH ... -F body=@body.md`);
|
|
79
|
+
`Not proven / risk` copies every claim recorded as unproven before READY — the tester's
|
|
80
|
+
`failures`, the reviewer's own review, any proof-check comment already on the pull request —
|
|
81
|
+
word for word, and `none` is a finding while one stands. The merger quotes `Outcome` and
|
|
82
|
+
`Not proven / risk` from the body at the head its READY packet names
|
|
83
|
+
(*The READY packet* in `skill://legion-worker/references/merge-gate.md`); a stale `Outcome` that no
|
|
84
|
+
longer describes the diff is a finding against the implementer, not a line the merger rewrites.
|
|
85
|
+
|
|
86
|
+
## What a result is
|
|
87
|
+
|
|
88
|
+
**A check** is a `check` block of the issue's Verification document (`verification.md`). It names
|
|
89
|
+
the requirements it serves, where a person reaches the change (`surface`: `browser`, `cli` or
|
|
90
|
+
`api`), what to `run`, `when` it runs (`before_merge`, `after_merge` or `default_branch`) and its
|
|
91
|
+
`negative_control`. The architect writes the checks with the spec; nothing else decides what must
|
|
92
|
+
be proved.
|
|
93
|
+
|
|
94
|
+
**A result** is one run of one check, recorded with
|
|
95
|
+
`dispatch result --issue <issue> --check <block id> --role <your role> --verdict pass --commit <sha>`,
|
|
96
|
+
with `--surface`, `--run`, `--observed` and `--negative-control` as the check asks for them. The
|
|
97
|
+
command prints the pull request body's line for it, and that line goes into the body unchanged.
|
|
98
|
+
The surface is **production-like** — the repository's real-process test harness and fixtures, a
|
|
99
|
+
sandbox repository, a real browser, a devN stack, staging, or a local stack with real migrations,
|
|
100
|
+
one that has the resource the change touches — and `--run` or `--observed` carries a **link** to
|
|
101
|
+
that run, screenshot, or e2e; human review does not replace user-facing verification, and a green
|
|
102
|
+
unit suite is not it. A unit or integration test is a regression lock, never the result of a
|
|
103
|
+
check. The agent that develops the change records its results this way before the merge, and
|
|
104
|
+
whatever blocks a check is fixed, not skipped (*When no surface reaches the changed path*, below).
|
|
105
|
+
|
|
106
|
+
**A check that cannot fail is not a result.** Before a check counts as evidence, show it
|
|
107
|
+
failing: the negative control is that demonstration, run on the same command with a deliberately
|
|
108
|
+
wrong input or call. A check whose pass survives empty output, a missing file or the wrong head
|
|
109
|
+
proves nothing about the change. A documented procedure counts only when it ran: reading it, or
|
|
110
|
+
reading the document that describes it, is not a run, and a procedure no surface available to
|
|
111
|
+
you can run is reported under *When no surface reaches the changed path* below, never passed.
|
|
112
|
+
|
|
113
|
+
**A result is current** while its commit is still the head of an open pull request of the check's
|
|
114
|
+
issue, or the final head of a merged one, and the check's words have not changed since; an
|
|
115
|
+
`after_merge` or `default_branch` result is current at a commit the default branch carries. A push
|
|
116
|
+
makes every result at the old head older, with no Dispatch write of anyone's, so each check is
|
|
117
|
+
recorded again at the new head. `dispatch results --issue <issue>` prints each check with its
|
|
118
|
+
latest result and whether that result is current, and `dispatch read --issue <issue>` names each
|
|
119
|
+
requirement of the spec no check serves.
|
|
120
|
+
|
|
121
|
+
**An issue is proved by its own checks.** Its approval is gated by the `before_merge` checks of
|
|
122
|
+
its own Verification document, read at its own head, so a child's pull request never makes a
|
|
123
|
+
root check's result current and a child never records the result that proves a root's check. A
|
|
124
|
+
child that carries part of a root's requirement proves it with a check on the child's own
|
|
125
|
+
Verification document whose `serves` names that requirement. A root's own checks are proven at
|
|
126
|
+
the root's own head, or on the default branch for an `after_merge` check, in the root's last
|
|
127
|
+
round.
|
|
128
|
+
|
|
129
|
+
**An issue with no `before_merge` check is a named gap.** An issue with no Verification document,
|
|
130
|
+
or with one that holds no `before_merge` check, has no evidence of its own. The tester names that
|
|
131
|
+
gap in its handoff and the reviewer in its review, and neither one sends the issue back for it;
|
|
132
|
+
both tell the architect, who writes the checks, and the architect's acceptance comment names the
|
|
133
|
+
gap and decides there, which is the only gate on such an issue.
|
|
134
|
+
|
|
135
|
+
## The rules every phase's evidence follows
|
|
136
|
+
|
|
137
|
+
- **The implementer records a result for every `before_merge` check before its phase completes,
|
|
138
|
+
and writes the `Checks` lines when the pull request opens.**
|
|
139
|
+
A check with no surface yet is the report of *When no surface reaches the changed path* below,
|
|
140
|
+
never a check left unrecorded. The results are on the Dispatch issue, and the lines are in the
|
|
141
|
+
PR body, because the reviewer and the merger verify facts on GitHub and never from a handoff.
|
|
142
|
+
The `implement` handoff carries no results: Dispatch holds them, and the tester reads them with
|
|
143
|
+
`dispatch results`.
|
|
144
|
+
- **The tester judges the implementer's results and records its own.** It re-runs the
|
|
145
|
+
implementer's command or drives the same surface independently, and records the verdict in
|
|
146
|
+
its `test` handoff as `implementerProof` (`{verdict, how}`). The tester runs the
|
|
147
|
+
application, not CI again. Try to break the changed surface — wrong and boundary inputs, the
|
|
148
|
+
second and the concurrent run, the party it must refuse — judge whether the implementer's tests
|
|
149
|
+
would catch the ways it breaks, writing the red test for one that none would, and
|
|
150
|
+
read CI on GitHub at the head you are judging for everything static: lint, types and the unit
|
|
151
|
+
and integration suites are CI's, read there and recorded for no later step, and no CI lane is
|
|
152
|
+
rerun as evidence. A lane GitHub shows `queued` or `in progress` is read as such: the tester
|
|
153
|
+
never waits for it, since a required red that settles sends the work back from `testing` and
|
|
154
|
+
the reviewer reads the head's checks before it approves.
|
|
155
|
+
A `before_merge` check the implementer left with no current result is a test failure, not a gap
|
|
156
|
+
for the tester to fill: record it in `failures` (each `{check, evidence}`, `check` the block id)
|
|
157
|
+
with `implementerProof.verdict: "rejected"`, complete the phase with
|
|
158
|
+
`verdict: "fail"`, and the daemon returns the issue to the implementer — the agent that developed the change owns
|
|
159
|
+
proving it (a rejected verdict, or `failed > 0`, always comes with the failure recorded in
|
|
160
|
+
`failures`). Otherwise record your own result for each `before_merge` check before you complete,
|
|
161
|
+
and put each printed line in the body. A code path whose first execution is after merge — a
|
|
162
|
+
deploy workflow's inline step, a post-merge helper, a production-only resource — is a check
|
|
163
|
+
marked `after_merge`, recorded after the merge, unless the implementer can execute it against a
|
|
164
|
+
devN stack before then; if no surface can reach it, the
|
|
165
|
+
tester names that missing surface as the blocker instead of passing the phase. Environment or
|
|
166
|
+
secret-scrub evidence (e.g. "`LEGION_*`/`DISPATCH_*`/`ENVOY_*` unset") is recorded once, in
|
|
167
|
+
its `test` handoff, and only when a check calls for it — never
|
|
168
|
+
re-pasted into the PR body each round. After a forward merge, compute the
|
|
169
|
+
fingerprint (*The unchanged-diff check* in
|
|
170
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`) at the commit your results name
|
|
171
|
+
and at the new head. Equal: re-run only the
|
|
172
|
+
bare gates — the repository's CI green at the new head and its smoke check — and record each
|
|
173
|
+
check again at the new head, its `--observed` ending
|
|
174
|
+
`forward-merge re-check <old-sha> → <new-sha>: fingerprint unchanged, bare gates only, class search of earlier findings clean`. A search that finds an instance is a failure, not a re-check: record it under `failures` and run a full test round. The
|
|
175
|
+
real-surface verification is not repeated, except in a parent's last round, which the architect
|
|
176
|
+
or the implementer's forward-merge comment names to you: then run a full test round. Different:
|
|
177
|
+
a full test round.
|
|
178
|
+
- **The implementer runs `skill://ce-simplify-code` once per pull request, in its first
|
|
179
|
+
implementing round, after its own results and before it completes that round,
|
|
180
|
+
whatever the diff touches, a docs-only diff included.** The daemon starts no implementing round
|
|
181
|
+
between a clean review and the approval, so this is the one slot before the tester and the
|
|
182
|
+
reviewer read the head; a later round answering a review runs no second pass. It is scoped to
|
|
183
|
+
the pull request's own diff at that head: nothing applied leaves the head as it is; applied →
|
|
184
|
+
the implementer re-runs the repository's checks and the issue's own checks on the applied head
|
|
185
|
+
for the surface the simplify diff touched, since a refactor that "preserves behaviour" is a
|
|
186
|
+
claim until it is executed, and records each result at that head before it completes.
|
|
187
|
+
That cost is why 0-applied is the expected outcome and a pass that applies is spent sparingly.
|
|
188
|
+
The pass covers every file of the diff, workflow YAML and documentation with the code, and a
|
|
189
|
+
defect it reports is fixed outside the pass with the results recorded again, never dropped as
|
|
190
|
+
out of scope; the `Thermo` line's `defects reported` half names how many and the commit that
|
|
191
|
+
fixed them, so a head that moved for a reported defect is not recorded as `0 applied` alone.
|
|
192
|
+
The reviewer's pair runs at the head each round reviews, when the diff touches runtime code.
|
|
193
|
+
Record both in the `Thermo` line.
|
|
194
|
+
- **No deferrals** is the body's rule (*PR body, review, and the merge gate* in
|
|
195
|
+
`skill://legion-worker`): a wording nit, and any clear fix in a file this pull request touches,
|
|
196
|
+
is made in the running round, because a later round costs a CI pass and a re-read of the whole
|
|
197
|
+
change (the operator's answer on that case: fix it while you are there, and defer no fix to
|
|
198
|
+
another issue). The `Fast-follow:` line reads
|
|
199
|
+
`none`, or names only cleanup in files this pull request does not touch.
|
|
200
|
+
A base frozen for others to stack on is never rewritten (*Rewriting pushed commits* in
|
|
201
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`); the `Chain` line records it.
|
|
202
|
+
- **GitHub refuses a pull request body over 65,536 characters.** Each verification round links its
|
|
203
|
+
evidence (the run, the comment) rather than inlining it once the body passes about 48,000
|
|
204
|
+
characters; a `Check` line keeps its evidence in the result on Dispatch and links the rest.
|
|
205
|
+
|
|
206
|
+
## When no surface reaches the changed path
|
|
207
|
+
|
|
208
|
+
No surface reaches the changed path is a report to the architect, never a reason to complete the phase.
|
|
209
|
+
Say which surface is missing and what it would have to do — a rig that can spawn the role, a
|
|
210
|
+
sandbox that holds the resource, a credential, a command that does not exist yet — and send it to
|
|
211
|
+
the architect with `envoy_publish` to its role topic. The architect builds it in this tree
|
|
212
|
+
(infrastructure, tooling, or a skill), in an open pull request or a child issue, and resumes you
|
|
213
|
+
once it lands. A code path
|
|
214
|
+
whose first execution would be after the merge — a deploy
|
|
215
|
+
workflow's inline step, a post-merge helper, a production-only resource — is a check marked
|
|
216
|
+
`after_merge`, recorded once the merge reaches production, and no reason to leave a
|
|
217
|
+
`before_merge` check unrecorded; completing with a unit suite as the only evidence is the
|
|
218
|
+
failure this rule exists to stop.
|