@joshuafolkken/kit 1.1035.0 → 1.1050.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/skills/verify-ui/SKILL.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -2
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-child.md +2 -2
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
- package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
- package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
- package/.claude/skills/workflow-commands/chain-rule.md +2 -2
- package/.claude/skills/workflow-commands/followup-reference.md +2 -2
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +41 -86
- package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
- package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
- package/.github/workflows/auto-tag.yml +21 -0
- package/.github/workflows/publish.yml +9 -10
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-HU66PWWL.js → chunk-5BK3K7V4.js} +89 -41
- package/dist/hooks/{chunk-GLBDBJOK.js → chunk-7VPDUBRN.js} +49 -151
- package/dist/hooks/{chunk-GJT4BNVE.js → chunk-DMQN45FN.js} +3 -3
- package/dist/hooks/{chunk-D67KVGGY.js → chunk-EJDX2TOB.js} +13 -3
- package/dist/hooks/{chunk-KG6PZD72.js → chunk-GQ7KP45M.js} +2 -2
- package/dist/hooks/{chunk-Y27M6A7X.js → chunk-HJIISEAC.js} +4 -4
- package/dist/hooks/{chunk-XPLP27WA.js → chunk-NAS4C7GP.js} +4 -26
- package/dist/hooks/{chunk-ZXNE6NY3.js → chunk-NJQOBE6L.js} +2 -2
- package/dist/hooks/{chunk-EIFCAJRB.js → chunk-RLDX5SOC.js} +1184 -946
- package/dist/hooks/{chunk-HOYJU5RM.js → chunk-WQ4OSBSS.js} +10 -3
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +15 -34
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +23 -16
- package/dist/hooks/stop-guard.js +13 -188
- package/dist/josh.js +4 -106
- package/dist/version/index.js +381 -9
- package/package.json +6 -6
- package/prompts/collaboration-workflow/file-edits.md +6 -53
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +4 -35
- package/prompts/collaboration-workflow/shell-body.md +7 -9
- package/prompts/collaboration-workflow/turn-batching.md +21 -37
- package/prompts/collaboration-workflow/wip-cap.md +15 -18
- package/prompts/sonar-hotspot-handling.md +1 -1
- package/scripts/adopt/adopt-toolkits.ts +2 -0
- package/scripts/auto-ok/auto-ok-cli.ts +9 -9
- package/scripts/backlog/backlog-next.ts +24 -8
- package/scripts/backlog/backlog-plan-cli.ts +5 -1
- package/scripts/backlog/backlog-pool.ts +31 -11
- package/scripts/backlog/backlog-rank.ts +106 -0
- package/scripts/backlog/backlog-waves.ts +42 -29
- package/scripts/build/build-hooks.ts +7 -2
- package/scripts/delegation/delegation-policy.ts +11 -0
- package/scripts/document/document-byte-budget.ts +5 -5
- package/scripts/document/entry-read-budget.ts +5 -4
- package/scripts/epic/epic-lane-offer.ts +11 -22
- package/scripts/epic/epic-rank.ts +37 -0
- package/scripts/epic/epic-solo.ts +29 -28
- package/scripts/git/git-command.ts +1 -1
- package/scripts/git/git-execa-error.ts +6 -1
- package/scripts/git/git-next-issues.ts +34 -5
- package/scripts/git/git-preflight.ts +22 -4
- package/scripts/git/git-push-transport.ts +12 -69
- package/scripts/git/git-spawn.ts +79 -3
- package/scripts/git/git-ssh-keepalive.ts +74 -0
- package/scripts/git/gone-branch.ts +3 -2
- package/scripts/git/issue-labels.ts +12 -0
- package/scripts/git/issue-rank.ts +89 -0
- package/scripts/hooks/format-edited-cli.ts +15 -0
- package/scripts/hooks/format-edited-file.ts +2 -9
- package/scripts/hooks/pretool-guard-cli.ts +21 -0
- package/scripts/hooks/pretool-guard.ts +14 -15
- package/scripts/init/start-exec.ts +7 -1
- package/scripts/init/start-plan.ts +18 -5
- package/scripts/init/start-setup-pr.ts +46 -19
- package/scripts/init/start-steps.ts +41 -9
- package/scripts/init/start.ts +40 -10
- package/scripts/issue/issue-cite.ts +19 -1
- package/scripts/josh/josh-command-map.ts +0 -102
- package/scripts/josh/josh-commands-development.ts +2 -2
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/lane/lane-child-marker.ts +16 -2
- package/scripts/lane/lane-cli.ts +32 -14
- package/scripts/lane/lane-report.ts +10 -4
- package/scripts/propagate/propagate-plan.ts +185 -0
- package/scripts/propagate/propagate-run.ts +5 -1
- package/scripts/propagate/propagate-steps.ts +16 -1
- package/scripts/propagate/propagate-targets.ts +13 -2
- package/scripts/propagate/propagate.ts +60 -42
- package/scripts/registry-migration/migrate-logic.ts +1 -0
- package/scripts/release/github-release.ts +1 -1
- package/scripts/review/review-attest.ts +44 -0
- package/scripts/review/review-brief-cli.ts +49 -2
- package/scripts/review/review-brief.ts +46 -1
- package/scripts/review/review-stamps.ts +7 -0
- package/scripts/rules/implementation-cut.ts +1 -1
- package/scripts/rules/issue-fold.ts +1 -2
- package/scripts/rules/lane-carry-conflict.ts +1 -2
- package/scripts/rules/lane-park.ts +1 -2
- package/scripts/rules/pre-gate-cut.ts +2 -2
- package/scripts/run/run-headless.ts +22 -6
- package/scripts/run/run-parent-cut-hook.ts +144 -0
- package/scripts/run/run-progress-read.ts +2 -1
- package/scripts/run/run-progress.ts +21 -8
- package/scripts/run/run-ship-cli.ts +6 -74
- package/scripts/run/run-ship-preflight.ts +87 -0
- package/scripts/run/run-ship-review-steps.ts +5 -26
- package/scripts/run/run-ship-review.ts +9 -0
- package/scripts/run/run-ship-scoped.ts +44 -0
- package/scripts/run/run-ship-stage.ts +5 -1
- package/scripts/run/run-ship-steps.ts +105 -0
- package/scripts/run/run-ship.ts +2 -0
- package/scripts/run/run-wake-cli.ts +2 -2
- package/scripts/run/run-wake-describe.ts +19 -1
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/time-runtime/time-bundle-call.ts +2 -2
- package/scripts/version/npm-registry.ts +97 -0
- package/scripts/version/version-commands.ts +5 -4
- package/scripts/version/version-remote.ts +26 -12
|
@@ -7,7 +7,7 @@ from the gate onward. Rationale: `docs/maintainers/pre-gate-cut-rationale.md`
|
|
|
7
7
|
|
|
8
8
|
This file is the single source of the boundary. `run:cut` takes it, and `run:cut --resume` is the
|
|
9
9
|
check a fresh process makes at its own entry. **A second boundary is added below** — the
|
|
10
|
-
implementation-phase cut
|
|
10
|
+
implementation-phase cut, at a context threshold _during_ implementation.
|
|
11
11
|
Everything through "What is carried" describes the pre-gate cut; the later section states only what
|
|
12
12
|
differs.
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@ verification the fresh process runs on the tree.
|
|
|
23
23
|
|
|
24
24
|
The cut is for a **detached** `fullrun` a lane dispatched; a person's interactive `fullrun` never
|
|
25
25
|
relaunches itself. A dispatched child is told apart **mechanically, by a mark the dispatch sets** — never
|
|
26
|
-
by the model reading the shape of its own prompt
|
|
26
|
+
by the model reading the shape of its own prompt.
|
|
27
27
|
|
|
28
28
|
### The dispatch mark
|
|
29
29
|
|
|
@@ -44,16 +44,15 @@ checkout, a cut in a lane.
|
|
|
44
44
|
|
|
45
45
|
## Taking the cut
|
|
46
46
|
|
|
47
|
-
**The order is fixed in the chain
|
|
47
|
+
**The order is fixed in the chain**: `chain-rule.md` step 1 issues
|
|
48
48
|
`pnpm josh run:cut <N>` after `pnpm josh main:merge`, before the scoped pair and the gate. **The command
|
|
49
|
-
is issued unconditionally**; `run:cut` itself decides whether the cut is warranted
|
|
50
|
-
(joshuafolkken/kit#2312), so a short lane with nothing to drop is answered `under-threshold` and walks on
|
|
49
|
+
is issued unconditionally**; `run:cut` itself decides whether the cut is warranted, so a short lane with nothing to drop is answered `under-threshold` and walks on
|
|
51
50
|
to the gate rather than relaunching.
|
|
52
51
|
|
|
53
52
|
At the pre-gate boundary, issue:
|
|
54
53
|
|
|
55
54
|
```bash
|
|
56
|
-
pnpm josh run:cut <N>
|
|
55
|
+
pnpm josh run:cut <N>
|
|
57
56
|
```
|
|
58
57
|
|
|
59
58
|
- `cut` (0) — record written and handed to the lane's launch owner (OpenAI supervisor starts the
|
|
@@ -72,25 +71,25 @@ the current process to carry the run on itself.
|
|
|
72
71
|
|
|
73
72
|
### The gate refuses until the cut has been taken (joshuafolkken/kit#1864)
|
|
74
73
|
|
|
75
|
-
**This step was carried as prose and fired exactly never.**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
refusal is insurance, not the primary trigger. Rationale:
|
|
74
|
+
**This step was carried as prose and fired exactly never.** In measured lane children the cut was taken **0 times**, so
|
|
75
|
+
`pnpm josh rule:guard` **refuses `pnpm josh gate`** while this checkout is a lane and no cut record is
|
|
76
|
+
carried, handing back the command above. Rationale:
|
|
79
77
|
`docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
|
|
80
78
|
|
|
81
79
|
- **It fires for a marked child and nowhere else**: the working directory is a lane —
|
|
82
80
|
`<lane root>/<issue number>` — **and** `JOSH_LANE_CHILD` names that same issue. An interactive
|
|
83
81
|
`fullrun` carries no mark and runs its gate untouched. `run:cut` uses an open-lane lookup instead, so
|
|
84
82
|
the guard and the command need not agree byte for byte.
|
|
85
|
-
- **It fires only when the recent-window context warrants a cut
|
|
83
|
+
- **It fires only when the recent-window context warrants a cut**. The guard
|
|
86
84
|
reads the same `cost_cli.session_verdict` `run:cut` reads, so an `under`-threshold lane is let through
|
|
87
85
|
to the gate rather than refused.
|
|
88
86
|
- **It is silent once the cut is carried**, so the resumed process goes straight to the gate.
|
|
89
|
-
- **It fires once per run.**
|
|
87
|
+
- **It fires once per run.**
|
|
90
88
|
- **`--resume`, `--end` and `--json` do not count as taking the cut.**
|
|
91
89
|
|
|
92
90
|
The row, its trigger and the enumeration it joins are
|
|
93
|
-
`prompts/collaboration-workflow/rule-delivery.md
|
|
91
|
+
`prompts/collaboration-workflow/rule-delivery.md`. The suites that pin this and the three guards below:
|
|
92
|
+
`docs/maintainers/pre-gate-cut-rationale.md` → "Which suites pin each guard".
|
|
94
93
|
|
|
95
94
|
## Resuming — the fresh process's entry check
|
|
96
95
|
|
|
@@ -106,8 +105,7 @@ pnpm josh run:cut --resume <N>
|
|
|
106
105
|
- `resume` (0) — a declared pre-gate cut whose tree matches was verified and taken over. **Skip the
|
|
107
106
|
title, the plan, the fresh hold claim and the implementation**; re-read the issue body and comments
|
|
108
107
|
for the plan and the recorded decisions, then go straight to `pnpm josh gate`.
|
|
109
|
-
- `resume-impl` (0) — like `resume`, but the cut was taken **during** implementation
|
|
110
|
-
(joshuafolkken/kit#1933). Skip the title, the plan, the fresh hold claim and the split assessment,
|
|
108
|
+
- `resume-impl` (0) — like `resume`, but the cut was taken **during** implementation. Skip the title, the plan, the fresh hold claim and the split assessment,
|
|
111
109
|
re-read the plan, then **continue implementation** — do **not** go straight to the gate.
|
|
112
110
|
- `handed-off` (0) — a successor has already adopted this cut, `is_handed_off` is spent
|
|
113
111
|
(joshuafolkken/kit#1935). This process was woken **after** its own cut. **End the turn quietly and do
|
|
@@ -154,7 +152,7 @@ decisions are read back from the issue. Rationale:
|
|
|
154
152
|
|
|
155
153
|
## The implementation-phase cut — a lane child cuts before the gate too
|
|
156
154
|
|
|
157
|
-
The **implementation-phase cut**
|
|
155
|
+
The **implementation-phase cut** caps the context accumulated _during_
|
|
158
156
|
implementation — the child ends mid-implementation and a fresh one resumes the lane **back into
|
|
159
157
|
implementation**.
|
|
160
158
|
|
|
@@ -162,15 +160,15 @@ implementation**.
|
|
|
162
160
|
|
|
163
161
|
The guard below refuses on the same measurement the parent uses between children —
|
|
164
162
|
`cost_verdict.per_request_cost`, read through `cost_cli.session_verdict` (the read-only form of
|
|
165
|
-
`pnpm josh cost --cut
|
|
163
|
+
`pnpm josh cost --cut`), single-sourced at `backlogrun-progress.md` → "The
|
|
166
164
|
hand-off". The parent's seam and the child's `run_cut.IMPLEMENTATION_CONTEXT_THRESHOLD` share the
|
|
167
165
|
**135_000** `CONTEXT_CUT_THRESHOLD` (aliased so the tests cannot drift). No separate measurement is
|
|
168
166
|
built for the lane child.
|
|
169
167
|
|
|
170
168
|
### It is a guard, fired at the edit that crosses the threshold (joshuafolkken/kit#2310)
|
|
171
169
|
|
|
172
|
-
**This step was carried as prose and fired exactly never** —
|
|
173
|
-
|
|
170
|
+
**This step was carried as prose and fired exactly never** — the cut fired **0 times** while 33.9% of
|
|
171
|
+
lane requests ran past 200,000 tokens. Rationale:
|
|
174
172
|
`docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
|
|
175
173
|
|
|
176
174
|
So `pnpm josh rule:guard` **refuses an `Edit` / `Write`** while this checkout is a dispatched lane child
|
|
@@ -185,7 +183,7 @@ crossing one successor.
|
|
|
185
183
|
- **It fires for a marked child, or a run held outside a lane on a measured `over`**
|
|
186
184
|
(joshuafolkken/kit#2760); a person's tree holds no run. Outside a lane `cut` relaunches nothing: keep
|
|
187
185
|
the hold, send a `confirmation` Telegram naming `fullrun #<N>`, end the turn.
|
|
188
|
-
- **It fires on every threshold crossing, not once per run**
|
|
186
|
+
- **It fires on every threshold crossing, not once per run** — it re-asks on
|
|
189
187
|
each over-threshold edit, including after a `busy` / `failed` / `unready` verdict.
|
|
190
188
|
- **An edit reissued right after a refusal passes** — so a `busy` / `failed` cut cannot wedge the run:
|
|
191
189
|
the reissue lands inside a short window and goes through, a genuinely new crossing past it refuses
|
|
@@ -196,8 +194,8 @@ crossing one successor.
|
|
|
196
194
|
- **The verdict read is reused over a short window** — cached per checkout for a few seconds, so a
|
|
197
195
|
turn's burst of edits shares one read.
|
|
198
196
|
|
|
199
|
-
`scripts/rules/implementation-cut.ts` implements the trigger,
|
|
200
|
-
|
|
197
|
+
`scripts/rules/implementation-cut.ts` implements the trigger, and the row joins
|
|
198
|
+
`prompts/collaboration-workflow/rule-delivery.md`. **A child that ended
|
|
201
199
|
without cutting is still detected after the fact by `pnpm josh run:ending <N> --output <path>`**
|
|
202
200
|
(joshuafolkken/kit#2139). **A single check (`lint:related` / `test:related`) is run after a batch of
|
|
203
201
|
edits, not after each one.**
|
|
@@ -206,12 +204,10 @@ edits, not after each one.**
|
|
|
206
204
|
|
|
207
205
|
**`CONTEXT_CUT_THRESHOLD` (135_000) is one value, and `cost_verdict.per_request_cost` — the one
|
|
208
206
|
statistic the parent and the lane child both read — averages the most recent `RECENT_REQUEST_WINDOW`
|
|
209
|
-
(10) requests, not the whole session
|
|
210
|
-
|
|
211
|
-
cut's one-time preamble rewrite (`POST_CUT_CONTEXT`, ~60_000 tokens — a cut carries a request from ~130k
|
|
212
|
-
down to ~60k), the cache multipliers in `cost-pricing.ts`, and a 10-request horizon.
|
|
207
|
+
(10) requests, not the whole session**. The value is computed by
|
|
208
|
+
`scripts/cost-runtime/context-cut-payback.ts`, its single source.
|
|
213
209
|
|
|
214
|
-
**The cut is conditional
|
|
210
|
+
**The cut is conditional**: taken only when the recent-window per-request cost
|
|
215
211
|
is over `CONTEXT_CUT_THRESHOLD` — the same statistic and threshold the implementation-phase cut reads, so
|
|
216
212
|
no second threshold exists. Below it `run:cut` answers `under-threshold` and the gate runs uncut; the
|
|
217
213
|
guard reads the same verdict and stays silent. An **unmeasurable** session is always cut.
|
|
@@ -227,8 +223,8 @@ cut is the same pattern one boundary earlier.
|
|
|
227
223
|
## A lane child records its park before it stops
|
|
228
224
|
|
|
229
225
|
**A dispatched lane child records its park on the Issue before it stops for a decision**
|
|
230
|
-
(joshuafolkken/kit#2034). A headless child's only route to ask a person is to park the Issue
|
|
231
|
-
|
|
226
|
+
(joshuafolkken/kit#2034). A headless child's only route to ask a person is to park the Issue (#2012,
|
|
227
|
+
#2011). Rationale:
|
|
232
228
|
`docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
|
|
233
229
|
|
|
234
230
|
**The record, before the Telegram, is the park procedure exactly** — `needs-decision` plus a comment
|
|
@@ -255,21 +251,22 @@ lane child — the mark `JOSH_LANE_CHILD` names this lane's own issue — handin
|
|
|
255
251
|
on the compliant path is safe.
|
|
256
252
|
|
|
257
253
|
This section is the single source of the lane-child park rule; `scripts/rules/lane-park.ts` implements
|
|
258
|
-
the trigger,
|
|
254
|
+
the trigger, and the row joins the enumeration in
|
|
259
255
|
`prompts/collaboration-workflow/rule-delivery.md`.
|
|
260
256
|
|
|
261
257
|
### The interactive ask is refused one call earlier
|
|
262
258
|
|
|
263
259
|
(joshuafolkken/kit#2201) The harness refuses an interactive ask in a headless session by **ending the
|
|
264
|
-
turn**, so the notify guard never fires — #
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
260
|
+
turn**, so the notify guard never fires (#2178 — the failure joshuafolkken/kit#2034 set out to prevent,
|
|
261
|
+
one call upstream). So `pnpm josh rule:guard` **refuses `AskUserQuestion` for a marked lane child**; the
|
|
262
|
+
deny returns to the model, and the instruction is to park the question. It fires on **every occurrence**
|
|
263
|
+
and stays silent for a person working in a lane. Rationale:
|
|
264
|
+
`docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
|
|
268
265
|
|
|
269
266
|
**A child that slips past it is still recoverable** — `pnpm josh run:ending` lifts the refused ask out
|
|
270
267
|
of the exit record's `permission_denials` into the `abandoned` verdict's park basis, so the parent puts
|
|
271
|
-
it into the park comment. `scripts/rules/lane-interactive-ask.ts` implements the trigger,
|
|
272
|
-
`scripts/
|
|
268
|
+
it into the park comment. `scripts/rules/lane-interactive-ask.ts` implements the trigger, and
|
|
269
|
+
`scripts/agent/interactive-ask.ts` is the
|
|
273
270
|
shared extractor the guard and `run:ending` both read.
|
|
274
271
|
|
|
275
272
|
This file is the single source of the rule.
|
|
@@ -11,9 +11,9 @@ a `new` entry files its Issue**, because a run stopped after the filing has alre
|
|
|
11
11
|
artifact it should not have created.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pnpm josh run:hold <N> # a `#N` entry point
|
|
14
|
+
pnpm josh run:hold <N> # a `#N` entry point
|
|
15
15
|
pnpm josh run:hold # a `new` entry point, before the issue exists
|
|
16
|
-
pnpm josh run:release <N> # that same run releasing its own record
|
|
16
|
+
pnpm josh run:release <N> # that same run releasing its own record
|
|
17
17
|
pnpm josh run:release # the bare form releases the unnumbered run's own record
|
|
18
18
|
pnpm josh run:release --force # a record left behind by a run that has ended
|
|
19
19
|
```
|
|
@@ -9,6 +9,7 @@ jobs:
|
|
|
9
9
|
name: Create Git Tag if Version Changed
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
11
|
permissions:
|
|
12
|
+
actions: write
|
|
12
13
|
contents: write
|
|
13
14
|
steps:
|
|
14
15
|
- name: Checkout code
|
|
@@ -109,3 +110,23 @@ jobs:
|
|
|
109
110
|
with:
|
|
110
111
|
event-type: new-tag-created
|
|
111
112
|
client-payload: '{"tag": "${{ steps.create_tag.outputs.tag }}"}'
|
|
113
|
+
|
|
114
|
+
# A publish workflow run on the tag ref records the tag's commit in npm provenance; a
|
|
115
|
+
# repository_dispatch run records the default branch head, which may already be past the tag.
|
|
116
|
+
# A publish.yml that still listens for new-tag-created is left to the dispatch above, so one
|
|
117
|
+
# release never starts two publish runs. This step runs after that dispatch so its failure
|
|
118
|
+
# cannot keep the dispatch from reaching production.yml.
|
|
119
|
+
- name: Start publish workflow on the tag
|
|
120
|
+
if: steps.create_tag.outputs.completed == 'true'
|
|
121
|
+
env:
|
|
122
|
+
GH_TOKEN: ${{ github.token }}
|
|
123
|
+
GH_REPO: ${{ github.repository }}
|
|
124
|
+
TAG: ${{ steps.create_tag.outputs.tag }}
|
|
125
|
+
run: |
|
|
126
|
+
WORKFLOW=.github/workflows/publish.yml
|
|
127
|
+
if [ -f "$WORKFLOW" ] && grep -Eq '^[[:space:]]*workflow_dispatch:' "$WORKFLOW" && ! grep -q 'new-tag-created' "$WORKFLOW"; then
|
|
128
|
+
gh workflow run publish.yml --ref "$TAG"
|
|
129
|
+
echo "✅ Started publish.yml on $TAG"
|
|
130
|
+
else
|
|
131
|
+
echo "ℹ️ publish.yml is not a workflow_dispatch-only publisher, skipping"
|
|
132
|
+
fi
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
name: Publish
|
|
2
|
-
run-name: Publish ${{ github.
|
|
2
|
+
run-name: Publish ${{ github.ref_name }}
|
|
3
3
|
|
|
4
|
+
# auto-tag.yml starts this workflow with `gh workflow run publish.yml --ref <tag>`, so the run's
|
|
5
|
+
# commit and ref are the release tag's. npm provenance records the run's commit from the OIDC token,
|
|
6
|
+
# and a repository_dispatch run is pinned to the default branch head, which can be newer than the tag.
|
|
4
7
|
on:
|
|
5
|
-
|
|
6
|
-
types: [new-tag-created]
|
|
8
|
+
workflow_dispatch:
|
|
7
9
|
|
|
8
10
|
jobs:
|
|
9
11
|
publish-github:
|
|
12
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
10
13
|
concurrency:
|
|
11
14
|
group: package-publish
|
|
12
15
|
queue: max
|
|
@@ -17,8 +20,6 @@ jobs:
|
|
|
17
20
|
packages: write
|
|
18
21
|
steps:
|
|
19
22
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
|
-
with:
|
|
21
|
-
ref: ${{ github.event.client_payload.tag }}
|
|
22
23
|
|
|
23
24
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
24
25
|
with:
|
|
@@ -55,6 +56,7 @@ jobs:
|
|
|
55
56
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
56
57
|
|
|
57
58
|
publish-npm:
|
|
59
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
58
60
|
concurrency:
|
|
59
61
|
group: package-publish
|
|
60
62
|
queue: max
|
|
@@ -65,8 +67,6 @@ jobs:
|
|
|
65
67
|
id-token: write
|
|
66
68
|
steps:
|
|
67
69
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
68
|
-
with:
|
|
69
|
-
ref: ${{ github.event.client_payload.tag }}
|
|
70
70
|
|
|
71
71
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
72
72
|
with:
|
|
@@ -106,7 +106,7 @@ jobs:
|
|
|
106
106
|
contents: write
|
|
107
107
|
uses: ./.github/workflows/production.yml
|
|
108
108
|
with:
|
|
109
|
-
tag: ${{ github.
|
|
109
|
+
tag: ${{ github.ref_name }}
|
|
110
110
|
|
|
111
111
|
create-release:
|
|
112
112
|
needs: [publish-github, publish-npm, update-production]
|
|
@@ -118,7 +118,6 @@ jobs:
|
|
|
118
118
|
steps:
|
|
119
119
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
120
120
|
with:
|
|
121
|
-
ref: ${{ github.event.client_payload.tag }}
|
|
122
121
|
fetch-depth: 0
|
|
123
122
|
|
|
124
123
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
@@ -143,4 +142,4 @@ jobs:
|
|
|
143
142
|
run: pnpm exec tsx scripts/release/github-release-cli.ts
|
|
144
143
|
env:
|
|
145
144
|
GH_TOKEN: ${{ github.token }}
|
|
146
|
-
RELEASE_TAG: ${{ github.
|
|
145
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
load_environment_file,
|
|
10
10
|
notice_refusal_path,
|
|
11
11
|
refusal_path
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-GQ7KP45M.js";
|
|
13
|
+
import "./chunk-NJQOBE6L.js";
|
|
14
|
+
import "./chunk-WQ4OSBSS.js";
|
|
15
|
+
import "./chunk-7VPDUBRN.js";
|
|
16
16
|
import "./chunk-WPALEIRX.js";
|
|
17
17
|
import "./chunk-RKUAD3I6.js";
|
|
18
18
|
export {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
git_gh_exec,
|
|
22
22
|
has_any_label,
|
|
23
23
|
has_stderr_field,
|
|
24
|
+
issue_cite,
|
|
24
25
|
lane_background,
|
|
25
26
|
lane_park,
|
|
26
27
|
lane_registry,
|
|
@@ -31,37 +32,38 @@ import {
|
|
|
31
32
|
repo_party,
|
|
32
33
|
run_carry,
|
|
33
34
|
run_cut,
|
|
35
|
+
run_headless,
|
|
34
36
|
run_hold,
|
|
35
37
|
run_progress_clock,
|
|
36
38
|
run_tail,
|
|
37
39
|
run_watcher_guard,
|
|
38
40
|
shell_segments,
|
|
39
41
|
tail_commands
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-RLDX5SOC.js";
|
|
41
43
|
import {
|
|
42
44
|
find_local_bin_upwards
|
|
43
45
|
} from "./chunk-3V37IID6.js";
|
|
44
46
|
import {
|
|
45
47
|
batch_outcome
|
|
46
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-GQ7KP45M.js";
|
|
47
49
|
import {
|
|
48
50
|
investigation_refusal
|
|
49
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-DMQN45FN.js";
|
|
50
52
|
import {
|
|
51
53
|
duplicate_read_outcome
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-HJIISEAC.js";
|
|
53
55
|
import {
|
|
54
56
|
investigation_reads
|
|
55
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-EJDX2TOB.js";
|
|
56
58
|
import {
|
|
57
59
|
lane_guard_policy,
|
|
58
60
|
time_batch_guard
|
|
59
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-NJQOBE6L.js";
|
|
60
62
|
import {
|
|
61
63
|
hook_decision,
|
|
62
64
|
lane_child_marker,
|
|
63
65
|
run_issue_number
|
|
64
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-WQ4OSBSS.js";
|
|
65
67
|
import {
|
|
66
68
|
PLATFORM_TEMP_ROOT,
|
|
67
69
|
cost_attribute,
|
|
@@ -73,11 +75,7 @@ import {
|
|
|
73
75
|
lane_paths,
|
|
74
76
|
stamp_file,
|
|
75
77
|
time_shell
|
|
76
|
-
} from "./chunk-
|
|
77
|
-
|
|
78
|
-
// scripts/hooks/pretool-guard.ts
|
|
79
|
-
import { text } from "node:stream/consumers";
|
|
80
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
78
|
+
} from "./chunk-7VPDUBRN.js";
|
|
81
79
|
|
|
82
80
|
// scripts/rules/direct-filing.ts
|
|
83
81
|
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
|
|
@@ -138,17 +136,19 @@ function next_state(previous, key, now_ms) {
|
|
|
138
136
|
if (previous?.key === key) return previous;
|
|
139
137
|
return { key, unchanged_since_ms: now_ms };
|
|
140
138
|
}
|
|
141
|
-
function format_child(child) {
|
|
139
|
+
function format_child(child, cite) {
|
|
142
140
|
const labels = child.labels.length > 0 ? child.labels.join(",") : "(no labels)";
|
|
143
|
-
return
|
|
141
|
+
return `${cite(child.issue)} ${labels} PR:${child.pr_state}`;
|
|
144
142
|
}
|
|
145
|
-
function format_children(children) {
|
|
143
|
+
function format_children(children, repo) {
|
|
146
144
|
if (children.length === 0) return NO_CHILD_YET;
|
|
147
|
-
|
|
145
|
+
const cite = issue_cite.citer(repo, new Map(children.map((child) => [child.issue, child.title])));
|
|
146
|
+
return children.map((child) => format_child(child, cite)).join(" \xB7 ");
|
|
148
147
|
}
|
|
149
|
-
function format_lanes(lanes) {
|
|
148
|
+
function format_lanes(lanes, repo) {
|
|
150
149
|
if (lanes.length === 0) return NO_LANES;
|
|
151
|
-
|
|
150
|
+
const cite = issue_cite.citer(repo, /* @__PURE__ */ new Map());
|
|
151
|
+
return lanes.map((lane) => `${cite(lane.issue)}:${lane.state}`).join(" ");
|
|
152
152
|
}
|
|
153
153
|
function format_record(record_age_ms) {
|
|
154
154
|
return record_age_ms === void 0 ? "unread" : `+${format_minutes(record_age_ms)}`;
|
|
@@ -173,8 +173,8 @@ function format_line(observations, timing) {
|
|
|
173
173
|
observed_at: format_observed_at(timing.now_ms),
|
|
174
174
|
quiet: format_minutes(timing.now_ms - timing.quiet_since_ms),
|
|
175
175
|
unchanged: format_minutes(timing.now_ms - timing.unchanged_since_ms),
|
|
176
|
-
children: format_children(observations.children),
|
|
177
|
-
lanes: format_lanes(observations.lanes),
|
|
176
|
+
children: format_children(observations.children, observations.repo),
|
|
177
|
+
lanes: format_lanes(observations.lanes, observations.repo),
|
|
178
178
|
load: observations.load_average.toFixed(1),
|
|
179
179
|
record: format_record(observations.record_age_ms),
|
|
180
180
|
next: format_next_report(timing.now_ms, timing.interval_ms)
|
|
@@ -1182,8 +1182,8 @@ function labels_of(question) {
|
|
|
1182
1182
|
}
|
|
1183
1183
|
function render_question(question) {
|
|
1184
1184
|
const labels = labels_of(question);
|
|
1185
|
-
const
|
|
1186
|
-
return labels.length === 0 ?
|
|
1185
|
+
const text = question.question ?? UNKNOWN_QUESTION;
|
|
1186
|
+
return labels.length === 0 ? text : `${text} [${labels.join(OPTION_SEPARATOR)}]`;
|
|
1187
1187
|
}
|
|
1188
1188
|
function interactive_input(raw) {
|
|
1189
1189
|
const denial = DENIAL_SCHEMA.safeParse(raw);
|
|
@@ -1860,8 +1860,8 @@ function is_rule_document(file_path) {
|
|
|
1860
1860
|
}
|
|
1861
1861
|
var LINK_TARGET = /\]\([^)]*\)/gu;
|
|
1862
1862
|
var BARE_URL = /https?:\/\/\S+/gu;
|
|
1863
|
-
function prose_of(
|
|
1864
|
-
return
|
|
1863
|
+
function prose_of(text) {
|
|
1864
|
+
return text.replaceAll(LINK_TARGET, "]()").replaceAll(BARE_URL, "");
|
|
1865
1865
|
}
|
|
1866
1866
|
var MIN_APPENDED_PROSE = 120;
|
|
1867
1867
|
function adds_rule_prose(old_text, new_text) {
|
|
@@ -4719,7 +4719,7 @@ function usable_text(value) {
|
|
|
4719
4719
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
4720
4720
|
}
|
|
4721
4721
|
function claude_progress(content) {
|
|
4722
|
-
return content.map((block) => TEXT_BLOCK_SCHEMA.safeParse(block)).filter((parsed) => parsed.success).map((parsed) => parsed.data.text).findLast((
|
|
4722
|
+
return content.map((block) => TEXT_BLOCK_SCHEMA.safeParse(block)).filter((parsed) => parsed.success).map((parsed) => parsed.data.text).findLast((text) => text.trim() !== "");
|
|
4723
4723
|
}
|
|
4724
4724
|
function claude_result(value, state) {
|
|
4725
4725
|
const result = claude_result_event.decode(value);
|
|
@@ -5041,9 +5041,10 @@ function event_column(lane) {
|
|
|
5041
5041
|
const state = run_liveness.describe_agent_state(lane.output);
|
|
5042
5042
|
return state ?? "agent -";
|
|
5043
5043
|
}
|
|
5044
|
-
|
|
5044
|
+
var PLAIN_CITE = issue_cite.citer(void 0, /* @__PURE__ */ new Map());
|
|
5045
|
+
function describe_lane(lane, cite = PLAIN_CITE) {
|
|
5045
5046
|
return [
|
|
5046
|
-
|
|
5047
|
+
cite(lane.issue),
|
|
5047
5048
|
`seat ${value_or_unknown(lane.seat)}`,
|
|
5048
5049
|
`dev ${value_or_unknown(lane.development_port)}`,
|
|
5049
5050
|
`preview ${value_or_unknown(lane.preview_port)}`,
|
|
@@ -5055,9 +5056,9 @@ function describe_lane(lane) {
|
|
|
5055
5056
|
event_column(lane)
|
|
5056
5057
|
].join(COLUMN_SEPARATOR);
|
|
5057
5058
|
}
|
|
5058
|
-
function describe_lanes(lanes) {
|
|
5059
|
+
function describe_lanes(lanes, cite = PLAIN_CITE) {
|
|
5059
5060
|
if (lanes.length === 0) return NO_LANES2;
|
|
5060
|
-
return lanes.map((lane) => describe_lane(lane)).join("\n");
|
|
5061
|
+
return lanes.map((lane) => describe_lane(lane, cite)).join("\n");
|
|
5061
5062
|
}
|
|
5062
5063
|
function describe_ports(lane) {
|
|
5063
5064
|
if (lane.seat === void 0) return "no lane seat could be read";
|
|
@@ -5304,7 +5305,7 @@ function to_labels(issue) {
|
|
|
5304
5305
|
async function to_child(issue) {
|
|
5305
5306
|
const number = String(issue.number);
|
|
5306
5307
|
const child = await run_preflight.read_child_state(number);
|
|
5307
|
-
return { issue: number, labels: to_labels(issue), pr_state: child.pr_state };
|
|
5308
|
+
return { issue: number, title: issue.title, labels: to_labels(issue), pr_state: child.pr_state };
|
|
5308
5309
|
}
|
|
5309
5310
|
async function read_children(repo) {
|
|
5310
5311
|
const read2 = await epic_busy.read_repository(repo);
|
|
@@ -5349,6 +5350,7 @@ async function read_observations(request) {
|
|
|
5349
5350
|
if (children === void 0) return UNREADABLE_READ;
|
|
5350
5351
|
if (children.length === 0 && !await has_run_started(lanes)) return IDLE_READ;
|
|
5351
5352
|
const observations = {
|
|
5353
|
+
repo: request.repo,
|
|
5352
5354
|
children,
|
|
5353
5355
|
lanes,
|
|
5354
5356
|
load_average: loadavg()[FIRST_LOAD_AVERAGE] ?? 0,
|
|
@@ -5403,7 +5405,58 @@ async function watcher_hook_reason(raw_payload, now_ms = Date.now()) {
|
|
|
5403
5405
|
if (transcript === void 0) return void 0;
|
|
5404
5406
|
return await stale_reason(transcript, now_ms);
|
|
5405
5407
|
}
|
|
5406
|
-
var run_watcher_hook = { SWITCH_ENV_KEY: SWITCH_ENV_KEY2, STAMP_PREFIX: STAMP_PREFIX2, watcher_hook_reason };
|
|
5408
|
+
var run_watcher_hook = { SWITCH_ENV_KEY: SWITCH_ENV_KEY2, STAMP_PREFIX: STAMP_PREFIX2, transcript_of, watcher_hook_reason };
|
|
5409
|
+
|
|
5410
|
+
// scripts/run/run-parent-cut-hook.ts
|
|
5411
|
+
var SWITCH_ENV_KEY3 = "JOSH_PARENT_CUT_GUARD";
|
|
5412
|
+
var STAMP_PREFIX3 = "josh-parent-cut-guard-";
|
|
5413
|
+
var QUIET_WINDOW_MS = 6e5;
|
|
5414
|
+
function current_state3() {
|
|
5415
|
+
return {
|
|
5416
|
+
carry: async () => await run_headless.driving_carry(),
|
|
5417
|
+
verdict: () => implementation_cut_verdict.reused_verdict(() => cost_cli.session_verdict())
|
|
5418
|
+
};
|
|
5419
|
+
}
|
|
5420
|
+
var THRESHOLD_TEXT2 = `${cost_format.format_tokens(CONTEXT_CUT_THRESHOLD)}-token`;
|
|
5421
|
+
var PARENT_CUT_REASON = `\u26D4 backlogrun parent hand-off: this session drives a \`backlogrun\` (it owns the live carry record) and its recent-context cost has crossed the shared ${THRESHOLD_TEXT2} threshold between merges, so every later request re-reads the accumulated context (joshuafolkken/kit#2947). Take the hand-off now rather than at the next merge: follow \`.claude/skills/workflow-commands/backlogrun-progress.md\` \u2192 "The hand-off" from its \`over\` branch \u2014 open no new lane and take no new child, read \`pnpm josh lane:list\`, make sure every lane in flight records an output path, then run \`pnpm josh run:carry --cut --owner "$PPID"\` and, unless it answers \`capped\`, \`pnpm josh run:wake\`, post the progress comment and end the turn. A lane nobody can poll means the cut does not happen; carry on and this asks again later. Reissue this call when the hand-off is done or cannot be taken \u2014 the guard stays quiet for a while after it refuses, so the hand-off calls themselves are not refused.`;
|
|
5422
|
+
async function is_due2(state) {
|
|
5423
|
+
const carry = await state.carry();
|
|
5424
|
+
if (carry === void 0 || run_carry.is_at_cut_cap(carry)) return false;
|
|
5425
|
+
return state.verdict() === cost_verdict.OVER_VERDICT;
|
|
5426
|
+
}
|
|
5427
|
+
function is_quiet(target, now_ms) {
|
|
5428
|
+
const last_ms = hook_decision.create_refusal_stamp(STAMP_PREFIX3).last_ms(target);
|
|
5429
|
+
return last_ms !== hook_decision.NEVER_MS && now_ms - last_ms < QUIET_WINDOW_MS;
|
|
5430
|
+
}
|
|
5431
|
+
async function refusal_for(transcript, now_ms, state) {
|
|
5432
|
+
const stamp = hook_decision.create_refusal_stamp(STAMP_PREFIX3);
|
|
5433
|
+
const target = stamp.path(transcript);
|
|
5434
|
+
if (is_quiet(target, now_ms) || !await is_due2(state)) return void 0;
|
|
5435
|
+
return stamp.record(target, now_ms) ? PARENT_CUT_REASON : void 0;
|
|
5436
|
+
}
|
|
5437
|
+
function is_unit_call(raw_payload) {
|
|
5438
|
+
try {
|
|
5439
|
+
return (hook_decision.parse_hook_payload(raw_payload)?.agent_id ?? "") !== "";
|
|
5440
|
+
} catch {
|
|
5441
|
+
return false;
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
function is_exempt2(raw_payload) {
|
|
5445
|
+
return !hook_decision.is_switch_enabled(SWITCH_ENV_KEY3) || is_unit_call(raw_payload);
|
|
5446
|
+
}
|
|
5447
|
+
async function parent_cut_reason(raw_payload, now_ms = Date.now(), state = current_state3()) {
|
|
5448
|
+
if (is_exempt2(raw_payload)) return void 0;
|
|
5449
|
+
const transcript = run_watcher_hook.transcript_of(raw_payload);
|
|
5450
|
+
if (transcript === void 0) return void 0;
|
|
5451
|
+
return await refusal_for(transcript, now_ms, state);
|
|
5452
|
+
}
|
|
5453
|
+
var run_parent_cut_hook = {
|
|
5454
|
+
PARENT_CUT_REASON,
|
|
5455
|
+
QUIET_WINDOW_MS,
|
|
5456
|
+
STAMP_PREFIX: STAMP_PREFIX3,
|
|
5457
|
+
SWITCH_ENV_KEY: SWITCH_ENV_KEY3,
|
|
5458
|
+
parent_cut_reason
|
|
5459
|
+
};
|
|
5407
5460
|
|
|
5408
5461
|
// scripts/hooks/pretool-guard.ts
|
|
5409
5462
|
function combine_outcomes(batch, extra_reason) {
|
|
@@ -5429,24 +5482,19 @@ function pretool_outcome(raw_payload) {
|
|
|
5429
5482
|
const combined = combine_outcomes(batch, investigation ?? duplicate_refusal ?? rule);
|
|
5430
5483
|
return with_duplicate_notice(combined, duplicate.notice);
|
|
5431
5484
|
}
|
|
5485
|
+
async function async_reason(raw_payload) {
|
|
5486
|
+
const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload);
|
|
5487
|
+
return watcher ?? await run_parent_cut_hook.parent_cut_reason(raw_payload);
|
|
5488
|
+
}
|
|
5432
5489
|
async function pretool_outcome_async(raw_payload) {
|
|
5433
5490
|
const base = pretool_outcome(raw_payload);
|
|
5434
5491
|
if (!is_clear(base)) return base;
|
|
5435
|
-
const reason = await
|
|
5492
|
+
const reason = await async_reason(raw_payload);
|
|
5436
5493
|
return reason === void 0 ? base : { reason, notice: void 0, fault: void 0 };
|
|
5437
5494
|
}
|
|
5438
5495
|
var pretool_guard = { combine_outcomes, pretool_outcome, pretool_outcome_async };
|
|
5439
|
-
if (process.argv[1] === fileURLToPath3(import.meta.url)) {
|
|
5440
|
-
if (process.stdin.isTTY) hook_decision.report_no_payload("pretool:guard");
|
|
5441
|
-
else {
|
|
5442
|
-
const raw_payload = await text(process.stdin);
|
|
5443
|
-
hook_decision.load_environment_file();
|
|
5444
|
-
hook_decision.emit_outcome(await pretool_outcome_async(raw_payload));
|
|
5445
|
-
}
|
|
5446
|
-
}
|
|
5447
5496
|
|
|
5448
5497
|
export {
|
|
5449
|
-
pretool_outcome,
|
|
5450
5498
|
pretool_outcome_async,
|
|
5451
5499
|
pretool_guard
|
|
5452
5500
|
};
|