@joshuafolkken/kit 1.1455.0 → 1.1471.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/dependency-update/SKILL.md +26 -55
- package/.claude/skills/epic-commands/SKILL.md +87 -148
- package/.claude/skills/epic-commands/epic-bundle.md +45 -77
- package/.claude/skills/epic-commands/execution-waves.md +44 -81
- package/.claude/skills/verify-ui/SKILL.md +25 -50
- package/.claude/skills/workflow-commands/background-commands.md +36 -47
- package/.claude/skills/workflow-commands/backlogrun-child.md +72 -85
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +91 -121
- package/.claude/skills/workflow-commands/backlogrun-park.md +73 -117
- package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -9
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +11 -20
- package/.claude/skills/workflow-commands/backlogrun-steps.md +43 -52
- package/.claude/skills/workflow-commands/chain-rule.md +24 -34
- package/.claude/skills/workflow-commands/followup.md +57 -76
- package/.claude/skills/workflow-commands/issue-comments.md +43 -60
- package/.claude/skills/workflow-commands/kickoff.md +65 -74
- package/.claude/skills/workflow-commands/observation-filing.md +62 -107
- package/.claude/skills/workflow-commands/observation-ledger.md +56 -111
- package/.claude/skills/workflow-commands/pre-gate-cut.md +89 -138
- package/.claude/skills/workflow-commands/prerequisite.md +10 -15
- package/.claude/skills/workflow-commands/progress-watcher.md +21 -26
- package/.claude/skills/workflow-commands/retrospective.md +31 -50
- package/.claude/skills/workflow-commands/split-assessment.md +34 -58
- package/.claude/skills/workflow-commands/working-tree-hold.md +54 -71
- package/CLAUDE.md +6 -5
- package/dist/CLAUDE.md +6 -5
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-7WQBPLUN.js → chunk-43ZHT22P.js} +31 -15
- package/dist/hooks/{chunk-LIAFCXL7.js → chunk-AS74OU3D.js} +23 -7
- package/dist/hooks/{chunk-3C4XRYG6.js → chunk-E7OIKX5Z.js} +17 -12
- package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-EUMACUK2.js} +18 -12
- package/dist/hooks/{chunk-WNLVGT7E.js → chunk-H5HVWYXK.js} +4 -4
- package/dist/hooks/{chunk-HHI6FO3R.js → chunk-IIGFYXKC.js} +345 -240
- package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-J47HU5H5.js} +14 -3
- package/dist/hooks/{chunk-OLMO72ON.js → chunk-K4EOONYM.js} +1 -1
- package/dist/hooks/{chunk-D5QBTOCZ.js → chunk-K4LMBQC2.js} +4 -4
- package/dist/hooks/{chunk-TBJFTSUY.js → chunk-KC5AK2GF.js} +8 -6
- package/dist/hooks/chunk-SHGPANFB.js +56 -0
- package/dist/hooks/{chunk-5H4SSDSD.js → chunk-YHBZAJSV.js} +3 -3
- package/dist/hooks/{chunk-HFGWYGUV.js → chunk-YYPI7SJZ.js} +5 -5
- package/dist/hooks/codex-hook-adapter.js +13 -13
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +3 -3
- package/dist/hooks/inputs.json +216 -214
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +12 -12
- package/dist/hooks/session-lang.js +6 -5
- package/dist/hooks/stop-guard.js +18 -14
- package/dist/josh.js +5 -6
- package/dist/managed-marker/index.js +1 -2
- package/dist/version/index.js +15 -12
- package/eslint/base.js +2 -1
- package/eslint/rules/unicorn.js +16 -0
- package/package.json +3 -3
- package/prompts/coding-standards.md +12 -22
- package/prompts/collaboration-workflow/file-edits.md +26 -44
- package/prompts/collaboration-workflow/gh-rest.md +20 -36
- package/prompts/collaboration-workflow/glossary.md +5 -7
- package/prompts/collaboration-workflow/issue-citation.md +7 -13
- package/prompts/collaboration-workflow/issue-template.md +29 -29
- package/prompts/collaboration-workflow/operating-rules.md +35 -44
- package/prompts/collaboration-workflow/output-bounds.md +15 -15
- package/prompts/collaboration-workflow/overview.md +14 -19
- package/prompts/collaboration-workflow/plan-comment.md +13 -13
- package/prompts/collaboration-workflow/principles.md +50 -53
- package/prompts/collaboration-workflow/proposal-request.md +1 -9
- package/prompts/collaboration-workflow/report-format.md +32 -49
- package/prompts/collaboration-workflow/residency.md +10 -10
- package/prompts/collaboration-workflow/rule-delivery.md +9 -9
- package/prompts/collaboration-workflow/shell-body.md +10 -20
- package/prompts/collaboration-workflow/turn-batching.md +8 -14
- package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
- package/prompts/collaboration-workflow/wip-cap.md +24 -30
- package/prompts/collaboration-workflow.md +24 -25
- package/prompts/refactoring.md +33 -43
- package/prompts/review-rubric.md +84 -129
- package/prompts/review.md +111 -141
- package/prompts/sonar-hotspot-handling.md +22 -50
- package/prompts/testing-guide.md +48 -99
- package/scripts/adopt/adopt-toolkits.ts +2 -4
- package/scripts/agent/agent-exit-record.ts +23 -1
- package/scripts/agent/codex-agent-argv.ts +7 -2
- package/scripts/backlog/backlog-budget-cli.ts +1 -2
- package/scripts/backlog/backlog-drive-carried.ts +26 -0
- package/scripts/backlog/backlog-drive-cli.ts +10 -5
- package/scripts/backlog/backlog-drive-named.ts +2 -4
- package/scripts/backlog/backlog-drive-restore.ts +19 -1
- package/scripts/backlog/backlog-drive-seats.ts +27 -0
- package/scripts/backlog/backlog-drive.ts +11 -6
- package/scripts/backlog/backlog-ready.ts +13 -6
- package/scripts/behavior/behavior-rules.ts +3 -2
- package/scripts/cost-runtime/codex-usage.ts +4 -2
- package/scripts/cost-runtime/cost-cli.ts +1 -2
- package/scripts/delegation/investigation-guard.ts +1 -1
- package/scripts/delegation/investigation-reads.ts +1 -1
- package/scripts/discovery/repo-discovery.ts +4 -2
- package/scripts/discovery/repo-origin.ts +2 -4
- package/scripts/doctor/doctor-logic.ts +6 -3
- package/scripts/document/document-byte-budget.ts +27 -27
- package/scripts/document/document-byte-check.ts +1 -2
- package/scripts/document/entry-read-budget.ts +2 -2
- package/scripts/epic/epic-audit-rationale.ts +8 -2
- package/scripts/epic/epic-bundle-cli.ts +1 -2
- package/scripts/epic/epic-chains.ts +6 -2
- package/scripts/epic/epic-close-comment.ts +1 -2
- package/scripts/gate/verification-gate.ts +7 -2
- package/scripts/gh/git-pr.ts +6 -2
- package/scripts/git/git-common-directory.ts +3 -3
- package/scripts/git/git-diff-reads.ts +16 -9
- package/scripts/git/git-execa-error.ts +1 -2
- package/scripts/hooks/format-edited-file.ts +10 -4
- package/scripts/hooks/pre-push-unit.ts +15 -4
- package/scripts/hooks/pretool-guard.ts +23 -29
- package/scripts/hooks/step-zero-notice.ts +3 -2
- package/scripts/init/hook-command-rewrite.ts +6 -2
- package/scripts/init/init-logic-deploy-vps.ts +4 -2
- package/scripts/issue/behavior-change-lint.ts +12 -6
- package/scripts/issue/issue-scout-cli.ts +3 -2
- package/scripts/josh/file-map-stamp.ts +1 -3
- package/scripts/josh/hook-decision.ts +17 -2
- package/scripts/lane/lane-await.ts +3 -2
- package/scripts/lane/lane-child-invocation.ts +14 -1
- package/scripts/lane/lane-guard-policy.ts +4 -4
- package/scripts/lane/lane-ledger.ts +9 -0
- package/scripts/lane/lane-load.ts +9 -2
- package/scripts/lane/lane-measure-cli.ts +2 -0
- package/scripts/lane/lane-parked.ts +18 -0
- package/scripts/lane/lane-process-load.ts +125 -0
- package/scripts/lane/lane-process-stats.ts +93 -0
- package/scripts/lane/lane-registry.ts +8 -3
- package/scripts/lane/lane-session-broker.ts +29 -5
- package/scripts/lane/lane-spent.ts +14 -3
- package/scripts/lane/lane-stats.ts +1 -0
- package/scripts/managed-marker/managed-marker-logic.ts +1 -2
- package/scripts/overrides/lockfile-overrides.ts +1 -3
- package/scripts/propagate/propagate.ts +1 -2
- package/scripts/release/release-progress.ts +3 -2
- package/scripts/report/report-lint.ts +7 -4
- package/scripts/review/review-record-cli.ts +6 -2
- package/scripts/rules/destructive-command.ts +6 -9
- package/scripts/rules/direct-filing.ts +3 -7
- package/scripts/rules/early-heartbeat.ts +7 -18
- package/scripts/rules/file-body.ts +6 -13
- package/scripts/rules/filing-cap.ts +3 -6
- package/scripts/rules/git-force.ts +5 -9
- package/scripts/rules/implementation-cut.ts +37 -35
- package/scripts/rules/index-guard.ts +4 -7
- package/scripts/rules/issue-comments.ts +5 -15
- package/scripts/rules/josh-git-bare.ts +3 -6
- package/scripts/rules/lane-background.ts +10 -21
- package/scripts/rules/lane-carry-conflict.ts +7 -17
- package/scripts/rules/lane-interactive-ask.ts +6 -11
- package/scripts/rules/lane-park.ts +5 -13
- package/scripts/rules/lane-split-park.ts +2 -7
- package/scripts/rules/lane-switch-main.ts +9 -12
- package/scripts/rules/oracle-consulted.ts +4 -5
- package/scripts/rules/piped-verification.ts +81 -31
- package/scripts/rules/poll-loop.ts +7 -17
- package/scripts/rules/pre-gate-cut.ts +7 -21
- package/scripts/rules/prior-comment-read.ts +7 -5
- package/scripts/rules/protected-files.ts +5 -9
- package/scripts/rules/raw-field-body.ts +3 -7
- package/scripts/rules/rule-body-guard.ts +4 -11
- package/scripts/rules/run-tail-rule.ts +6 -22
- package/scripts/rules/shell-body-trigger.ts +4 -6
- package/scripts/rules/shell-segments.ts +7 -0
- package/scripts/rules/stop-rules.ts +16 -29
- package/scripts/rules/test-declared-commit.ts +4 -10
- package/scripts/rules/third-party-write.ts +5 -10
- package/scripts/rules/worktree-guard.ts +5 -12
- package/scripts/run/board/run-board-github.ts +11 -3
- package/scripts/run/board/run-board-idle.ts +31 -0
- package/scripts/run/board/run-board-read.ts +9 -12
- package/scripts/run/board/run-board-reason.ts +7 -3
- package/scripts/run/board/run-board-state.ts +4 -0
- package/scripts/run/board/run-board-status.ts +18 -3
- package/scripts/run/board/run-board-thanks.ts +7 -2
- package/scripts/run/board/run-board-tick.ts +32 -7
- package/scripts/run/carry/run-carry-args.ts +1 -3
- package/scripts/run/carry/run-carry-conversation.ts +7 -2
- package/scripts/run/cut/run-cut-cli.ts +15 -20
- package/scripts/run/cut/run-cut-decline.ts +72 -0
- package/scripts/run/cut/run-cut-ready.ts +74 -0
- package/scripts/run/cut/run-cut-report.ts +13 -0
- package/scripts/run/cut/run-cut.ts +10 -1
- package/scripts/run/hold/run-hold-cli.ts +14 -3
- package/scripts/run/hold/run-hold.ts +6 -3
- package/scripts/run/merge/run-merge-cli.ts +30 -7
- package/scripts/run/merge/run-merge-ship.ts +59 -1
- package/scripts/run/merge/run-merge.ts +3 -2
- package/scripts/run/run-ending.ts +31 -3
- package/scripts/run/run-parent-cut-hook.ts +5 -10
- package/scripts/run/run-preflight.ts +1 -2
- package/scripts/run/run-stop-notify.ts +1 -2
- package/scripts/run/run-watcher-guard.ts +6 -3
- package/scripts/run/ship/run-ship-review-steps.ts +13 -4
- package/scripts/run/ship/run-ship-review.ts +15 -2
- package/scripts/run/tidy/run-tidy-cli.ts +3 -3
- package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
- package/scripts/safe-chain/project-config.ts +7 -2
- package/scripts/sync/sync-hook-safety.ts +8 -2
- package/scripts/test/unit-worker-share.ts +3 -2
- package/scripts/time-runtime/time-batch-guard.ts +3 -3
- package/scripts/time-runtime/time-hook-transcript.ts +3 -2
- package/scripts/time-runtime/time-shell.ts +10 -0
- package/scripts/version/effective-upstream.ts +3 -4
- package/scripts/version/kit-version-config.ts +8 -2
- package/scripts/version/latest-corepack.ts +1 -2
- package/scripts/version/release-hold.ts +11 -4
- package/scripts/version/version-commands.ts +12 -4
- package/dist/hooks/chunk-UT7YC3N2.js +0 -56
|
@@ -102,8 +102,7 @@ function overview_lines(summary: string): ReadonlyArray<string> {
|
|
|
102
102
|
function item_sentence(line: string): string {
|
|
103
103
|
const open = line.indexOf(BOLD_MARKER)
|
|
104
104
|
const close = open === -1 ? -1 : line.indexOf(BOLD_MARKER, open + BOLD_MARKER.length)
|
|
105
|
-
const raw =
|
|
106
|
-
close === -1 ? line.slice(ITEM_LINE_PREFIX.length) : line.slice(close + BOLD_MARKER.length)
|
|
105
|
+
const raw = line.slice(close === -1 ? ITEM_LINE_PREFIX.length : close + BOLD_MARKER.length)
|
|
107
106
|
|
|
108
107
|
return raw.replace(LEADING_LABEL_PUNCTUATION, '').trim()
|
|
109
108
|
}
|
|
@@ -199,8 +198,12 @@ function break_condition_count(lines: ReadonlyArray<string>): number {
|
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
function case_line_violations(summary: string): ReadonlyArray<string> {
|
|
202
|
-
if (
|
|
203
|
-
|
|
201
|
+
if (
|
|
202
|
+
!has_changes_section(summary) ||
|
|
203
|
+
changes_region_lines(summary).some((line) => line.includes(CASE_LABEL))
|
|
204
|
+
) {
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
204
207
|
|
|
205
208
|
return [`✖ no case tier (${CASE_LABEL}:) in ${CHANGES_LABEL}`]
|
|
206
209
|
}
|
|
@@ -58,8 +58,12 @@ interface Parsed {
|
|
|
58
58
|
// A finding spec is `<category>:<severity>:<file>`; the file field keeps any `:line` a citation
|
|
59
59
|
// carries, so only the first two colons separate fields.
|
|
60
60
|
function to_finding(category: string, severity: string, file: string): Finding | undefined {
|
|
61
|
-
if (
|
|
62
|
-
|
|
61
|
+
if (
|
|
62
|
+
!review_finding_ledger.is_category(category) ||
|
|
63
|
+
!review_finding_ledger.is_severity(severity)
|
|
64
|
+
) {
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
63
67
|
|
|
64
68
|
return review_finding_ledger.is_valid_file(file) ? { category, severity, file } : undefined
|
|
65
69
|
}
|
|
@@ -97,21 +97,18 @@ function is_destructive_command(command: string): boolean {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const DESTRUCTIVE_COMMAND_REASON =
|
|
100
|
-
'⛔ destructive command: a recursive forced `rm
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
'The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row ' +
|
|
105
|
-
'reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user ' +
|
|
106
|
-
'to run the command in their own terminal. `gh issue close` and removing a label from an Issue ' +
|
|
107
|
-
'(`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every ' +
|
|
108
|
-
'occurrence, not once per run.**'
|
|
100
|
+
'⛔ destructive command: a recursive forced `rm`, a `gh repo delete` / `archive` / visibility edit, ' +
|
|
101
|
+
'`gh pr close` or a `gh api -X DELETE` cannot be undone, which is Tier C. Remove one path at a ' +
|
|
102
|
+
'time without `-f`, or ask the user to run it. It fires on every occurrence ' +
|
|
103
|
+
'(`prompts/collaboration-workflow/operating-rules.md`).'
|
|
109
104
|
|
|
110
105
|
const ROW = {
|
|
111
106
|
id: 'destructive-command',
|
|
112
107
|
is_trigger: bash_triggers.on_bash_command(is_destructive_command),
|
|
113
108
|
reason: DESTRUCTIVE_COMMAND_REASON,
|
|
114
109
|
decide: (): boolean => true,
|
|
110
|
+
// The opening the reason carried before it was cut to the action and the next command.
|
|
111
|
+
former_reasons: ['⛔ destructive command: a recursive forced `rm` (`rm -rf`'],
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
const destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW, is_destructive_command }
|
|
@@ -9,13 +9,9 @@ import { bash_triggers } from './bash-triggers'
|
|
|
9
9
|
|
|
10
10
|
const DIRECT_FILING_REASON =
|
|
11
11
|
'⛔ direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> ' +
|
|
12
|
-
'--depth <0|1|2
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'into another repository, and `epic:bundle` after the create — so none of them is a step to remember. ' +
|
|
16
|
-
'When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,…>` ' +
|
|
17
|
-
'for the ones that are separate deliverables. Reference: `docs/josh-commands-backlog.md` → "`josh issue:file`". ' +
|
|
18
|
-
'**This rule fires on every occurrence, not once per run.**'
|
|
12
|
+
'--depth <0|1|2>`, never with `gh api …/issues` or `gh issue create` — it runs the scout, the lint, ' +
|
|
13
|
+
'the labels and `epic:bundle` itself. It fires on every occurrence (`docs/josh-commands-backlog.md` ' +
|
|
14
|
+
'→ "`josh issue:file`").'
|
|
19
15
|
|
|
20
16
|
function is_any_filing(command: string): boolean {
|
|
21
17
|
return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command)
|
|
@@ -178,25 +178,14 @@ function decide(call: GuardedCall, run: GuardRun, can_record: boolean): boolean
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// The instruction in the shape a refusal can carry: what the call is about to do, what already does it
|
|
181
|
-
// properly, and the one command an explicit ask is answered with. The observed failure is
|
|
182
|
-
//
|
|
181
|
+
// properly, and the one command an explicit ask is answered with. The observed failure behind it is in
|
|
182
|
+
// `docs/maintainers/rule-delivery-rationale.md`.
|
|
183
183
|
const EARLY_HEARTBEAT_REASON =
|
|
184
|
-
'⛔ early heartbeat: this call arms a wait timer of its own
|
|
185
|
-
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
|
|
189
|
-
'after it exits, which it does when newly runnable work arrives. Two timers armed at ' +
|
|
190
|
-
'once is what produced reports 3–5 minutes apart on a 15-minute setting ' +
|
|
191
|
-
'(joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a ' +
|
|
192
|
-
"child's completion woke the run, and `--mark` recorded each report without anything refusing the " +
|
|
193
|
-
'early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run ' +
|
|
194
|
-
'`pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says — ' +
|
|
195
|
-
'under `backlogrun`, `pnpm josh run:board --chat` instead. If you are ' +
|
|
196
|
-
'waiting on something rather than on a clock, wait on the thing itself — `pnpm josh followup` ' +
|
|
197
|
-
'waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` → "Progress while ' +
|
|
198
|
-
'the run is quiet". **This rule fires on every early arm, not once per run**, so reissuing the ' +
|
|
199
|
-
'same sleep will be refused again.'
|
|
184
|
+
'⛔ early heartbeat: this call arms a wait timer of its own. Start `pnpm josh run:progress --wait` ' +
|
|
185
|
+
"once in the background: it reports to the run's event stream on its own — relay nothing. When " +
|
|
186
|
+
'asked, `pnpm josh run:progress --once` — under `backlogrun`, `pnpm josh run:board --chat`; ' +
|
|
187
|
+
'for CI, `pnpm josh followup`. It fires on every occurrence ' +
|
|
188
|
+
'(`.claude/skills/workflow-commands/progress-watcher.md`).'
|
|
200
189
|
|
|
201
190
|
// **Keeping this rule is letting the watcher hold the clock** — `pnpm josh run:progress --wait` waits
|
|
202
191
|
// the interval out, and `--once` answers an explicit ask. `--mark` is neither: it records a report
|
|
@@ -171,20 +171,13 @@ function carries_a_file_body(
|
|
|
171
171
|
)
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
// The instruction in the shape a refusal can carry: what the command is doing
|
|
175
|
-
//
|
|
176
|
-
// surprising — the command body is billed on every later request of the run.
|
|
174
|
+
// The instruction in the shape a refusal can carry: what the command is doing and the safe tool. The
|
|
175
|
+
// measured cost behind it is in `file-edits.md`.
|
|
177
176
|
const FILE_BODY_REASON =
|
|
178
|
-
"⛔ file body in a shell command: this carries a file's new text inline
|
|
179
|
-
'
|
|
180
|
-
'
|
|
181
|
-
'
|
|
182
|
-
'is billed for the rest of it (`file-edits.md` measured one run at ~2m45s spent retyping existing ' +
|
|
183
|
-
'lines). Edit a region with the Edit tool (old / new); a change scattered so widely that the ' +
|
|
184
|
-
'partial edits would carry more than the file is rewritten whole with the Write tool, never a ' +
|
|
185
|
-
'heredoc. Pass a body by path (`--field body=@<path>`, `--body-file <path>`). A new-file creation (`cat > ' +
|
|
186
|
-
'<new-file> <<EOF`), a read-only heredoc (`cat <<EOF`), and a short `sed -i` stay allowed. **This ' +
|
|
187
|
-
'rule fires on every occurrence, not once per run.**'
|
|
177
|
+
"⛔ file body in a shell command: this carries a file's new text inline, which is re-read on every " +
|
|
178
|
+
'later request. Edit a region with the Edit tool, rewrite a whole file with the Write tool, and ' +
|
|
179
|
+
'pass a body by path (`--body-file <path>`); a new-file `cat > <new-file> <<EOF` stays allowed. ' +
|
|
180
|
+
'This rule fires on every occurrence (`prompts/collaboration-workflow/file-edits.md`).'
|
|
188
181
|
|
|
189
182
|
const ROW = {
|
|
190
183
|
id: 'file-body',
|
|
@@ -104,12 +104,9 @@ function decide(_call: unknown, run: GuardRun): boolean {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const FILING_CAP_REASON =
|
|
107
|
-
`⛔ filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling
|
|
108
|
-
|
|
109
|
-
'
|
|
110
|
-
"`prerequisite.md`, `observation-filing.md`). A further Issue that genuinely must exist is a person's call, not " +
|
|
111
|
-
"this run's. **This rule fires on every filing past the cap, not once per run**, so reissuing the " +
|
|
112
|
-
'same filing will be refused again.'
|
|
107
|
+
`⛔ filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling. Stop ` +
|
|
108
|
+
"filing and report; a further Issue is a person's call. It fires on every occurrence " +
|
|
109
|
+
'(`.claude/skills/workflow-commands/observation-filing.md`).'
|
|
113
110
|
|
|
114
111
|
// The enumeration row itself, so `delivered-rules.ts` spreads one entry rather than restating the
|
|
115
112
|
// trigger and reason it already single-sources here. It declares no `keeps`: staying under a cap is
|
|
@@ -69,19 +69,15 @@ function is_force_or_delete(command: string): boolean {
|
|
|
69
69
|
return shell_segments.segments_of(command).some((segment) => is_destructive_segment(segment))
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// The instruction in the shape a refusal can carry: what the command does
|
|
73
|
-
//
|
|
72
|
+
// The instruction in the shape a refusal can carry: what the command does and the one route left. The
|
|
73
|
+
// spellings the deny glob misses are pinned on `is_force_or_delete`, not repeated in the text. There is no "safe reissue" the way a backgrounded push is for
|
|
74
74
|
// `run-tail` — a force push and a branch delete are Tier C, so the honest next move is a person's, in
|
|
75
75
|
// their own terminal.
|
|
76
76
|
const GIT_FORCE_REASON =
|
|
77
77
|
'⛔ force push or branch delete: this rewrites or removes shared history, which is Tier C and needs ' +
|
|
78
|
-
'explicit current-turn user instruction
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'<path>` prefix, and the colon-form delete (`git push origin :branch`) all pass it. This row reads ' +
|
|
82
|
-
'the argv instead, so the spelling does not matter. For an ordinary push use `pnpm josh git`; a ' +
|
|
83
|
-
"force push, a `git push --delete` / `:branch`, or a `git branch -d` / `-D` is the user's to run in " +
|
|
84
|
-
'their own terminal. **This rule fires on every occurrence, not once per run.**'
|
|
78
|
+
'explicit current-turn user instruction. Push with `pnpm josh git`; leave a force push or branch ' +
|
|
79
|
+
"delete to the user's own terminal. This rule fires on every occurrence " +
|
|
80
|
+
'(`prompts/collaboration-workflow/operating-rules.md`).'
|
|
85
81
|
|
|
86
82
|
// The row itself, so `delivered-rules.ts` spreads one entry. `decide` returns true so it refuses every
|
|
87
83
|
// occurrence; it declares no `keeps` — not force-pushing is the absence of a call, not a call, so the
|
|
@@ -7,7 +7,9 @@ import type { GuardRun } from '#scripts/josh/hook-decision'
|
|
|
7
7
|
import { lane_child_marker, type MarkerSource } from '#scripts/lane/lane-child-marker'
|
|
8
8
|
import { lane_paths } from '#scripts/lane/lane-paths'
|
|
9
9
|
import { run_cut, type RunCut } from '#scripts/run/cut/run-cut'
|
|
10
|
+
import { run_cut_decline } from '#scripts/run/cut/run-cut-decline'
|
|
10
11
|
import { run_cut_handoff } from '#scripts/run/cut/run-cut-handoff'
|
|
12
|
+
import { run_cut_ready } from '#scripts/run/cut/run-cut-ready'
|
|
11
13
|
import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
|
|
12
14
|
import { bash_triggers } from './bash-triggers'
|
|
13
15
|
import { implementation_cut_verdict } from './implementation-cut-verdict'
|
|
@@ -62,6 +64,16 @@ interface LaneCostState {
|
|
|
62
64
|
// held outside a lane is told apart from a person's tree.
|
|
63
65
|
held_issue: () => string | undefined
|
|
64
66
|
verdict: () => CostVerdict
|
|
67
|
+
// Whether `run:cut --impl` could take the cut for this issue now — see `can_cut_sync`.
|
|
68
|
+
can_cut: (issue: string) => boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// **The guard asks for a cut only where `run:cut --impl` could take one** (joshuafolkken/kit#3881).
|
|
72
|
+
// A run held on the default branch, or one whose cut was already answered `not-a-lane` / `unready`
|
|
73
|
+
// under this hold, can only be refused again, so each refusal was a round trip that cut nothing. The
|
|
74
|
+
// decline record is a file read and comes first; the tree read spawns git and comes second.
|
|
75
|
+
function can_cut_sync(issue: string): boolean {
|
|
76
|
+
return !run_cut_decline.is_declined_sync(issue) && run_cut_ready.is_ready_sync()
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
function current_state(): LaneCostState {
|
|
@@ -75,6 +87,7 @@ function current_state(): LaneCostState {
|
|
|
75
87
|
// the window collapses one turn's burst of edits to a single whole-transcript price.
|
|
76
88
|
verdict: (): CostVerdict =>
|
|
77
89
|
implementation_cut_verdict.reused_verdict(() => cost_cli.session_verdict()),
|
|
90
|
+
can_cut: can_cut_sync,
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
93
|
|
|
@@ -140,14 +153,19 @@ function warrants_the_cut(state: LaneCostState): boolean {
|
|
|
140
153
|
// **The unmeasurable direction matches the pre-gate cut**. A session that
|
|
141
154
|
// cannot be priced reads as warranting the cut, the same safety-net direction `pre-gate-cut.ts`'s
|
|
142
155
|
// `warrants_the_cut` takes (`verdict !== UNDER_VERDICT`): the two rules read one statistic against one
|
|
143
|
-
// threshold, so they must not disagree about whether an unmeasurable session is due a cut.
|
|
156
|
+
// threshold, so they must not disagree about whether an unmeasurable session is due a cut. Whether the
|
|
157
|
+
// cut could be taken is asked only once it is warranted, so an under-threshold edit spawns no git.
|
|
144
158
|
function is_over_threshold_edit(
|
|
145
159
|
call: GuardedCall,
|
|
146
160
|
state: LaneCostState = current_state(),
|
|
147
161
|
): boolean {
|
|
148
|
-
if (!is_edit_tool(call.name)
|
|
162
|
+
if (!is_edit_tool(call.name)) return false
|
|
163
|
+
|
|
164
|
+
const issue = uncut_run_issue(state)
|
|
165
|
+
|
|
166
|
+
if (issue === undefined) return false
|
|
149
167
|
|
|
150
|
-
return warrants_the_cut(state)
|
|
168
|
+
return warrants_the_cut(state) && state.can_cut(issue)
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
// **The threshold in the refusal text is assembled from the constant, never spelled**.
|
|
@@ -155,36 +173,14 @@ function is_over_threshold_edit(
|
|
|
155
173
|
// building it from `CONTEXT_CUT_THRESHOLD` keeps the two from drifting. `cost_format.format_tokens` renders it the way every cost report does.
|
|
156
174
|
const THRESHOLD_TEXT = `${cost_format.format_tokens(CONTEXT_CUT_THRESHOLD)}-token`
|
|
157
175
|
|
|
158
|
-
// The instruction in the shape a refusal can carry:
|
|
159
|
-
// the
|
|
160
|
-
//
|
|
161
|
-
// read which of the others leave it implementing — the same reason `pre-gate-cut.ts` spells them out.
|
|
176
|
+
// The instruction in the shape a refusal can carry: the handoff in the format the parser reads, the
|
|
177
|
+
// cut, and the one verdict that ends the turn — `cut` is the only one, so a run told merely to "take
|
|
178
|
+
// the cut" is not left to look it up.
|
|
162
179
|
const IMPLEMENTATION_CUT_REASON =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
`
|
|
166
|
-
'
|
|
167
|
-
`First write a handoff file at \`${run_cut_handoff.HANDOFF_PATH}\` (ignored, so it is never committed) with the Write tool — the user’s instruction verbatim, what you have ` +
|
|
168
|
-
'completed, what remains, and what you deliberately did not touch, as ' +
|
|
169
|
-
`${run_cut_handoff.HANDOFF_FORMAT} (Markdown is refused) — and pass it as \`--handoff <path>\`, ` +
|
|
170
|
-
'so the fresh process resumes on the original instruction rather than the working tree alone ' +
|
|
171
|
-
'(joshuafolkken/kit#2354); a resume that finds no instruction is refused `incomplete` rather than ' +
|
|
172
|
-
'continuing blind. ' +
|
|
173
|
-
'`pnpm josh run:cut --impl <N> --handoff <path>` ends this process and relaunches a fresh one that resumes back into ' +
|
|
174
|
-
'implementation (`resume-impl`), dropping the accumulated context rather than carrying it — ' +
|
|
175
|
-
'joshuafolkken/kit#1933 built the boundary and joshuafolkken/kit#2310 measured it firing 0 times ' +
|
|
176
|
-
'because the verdict was only ever read at session entry, where the context has not yet grown. The ' +
|
|
177
|
-
"verdict is `pnpm josh cost --cut`'s exactly — the same per-request billed-input statistic against the " +
|
|
178
|
-
'same threshold, never a second measurement. Issue `pnpm josh run:cut --impl <N> --handoff <path>` now and read the ' +
|
|
179
|
-
'verdict: on `cut`, read what the command printed. Where it relaunched a fresh process, **end the turn ' +
|
|
180
|
-
'immediately** — that process owns the run and continues implementing, so it must not be waited for, ' +
|
|
181
|
-
'and **no Telegram is owed**: a cut that resumes itself leaves a person nothing to act on. Where it printed that nothing was relaunched, do what it prints, then end the turn; on `not-a-lane`, `unready`, `busy`, `failed` or ' +
|
|
182
|
-
'`unknown`, this process carries the run on and the edit is simply the next call. Never relaunch a ' +
|
|
183
|
-
'second process after `busy`. This fired because the dispatch mark names this lane or the run hold ' +
|
|
184
|
-
'names this issue; a person working here carries no mark and holds no run, so sees no refusal, so there is no human-or-child judgement left to make. The ' +
|
|
185
|
-
'procedure is `.claude/skills/workflow-commands/pre-gate-cut.md`. Reissue this edit once the cut has ' +
|
|
186
|
-
'answered — an edit reissued right after this refusal passes, so `busy` / `failed` cannot wedge the ' +
|
|
187
|
-
'run edit after edit; this fires again on the next threshold crossing rather than once per run.'
|
|
180
|
+
`⛔ implementation-phase cut: refused on every edit past the ${THRESHOLD_TEXT} line. ` +
|
|
181
|
+
`Write \`${run_cut_handoff.HANDOFF_PATH}\` as ${run_cut_handoff.HANDOFF_FORMAT}, run ` +
|
|
182
|
+
'`pnpm josh run:cut --impl <N> --handoff <path>`; `cut`: end the turn immediately, no Telegram ' +
|
|
183
|
+
'is owed; else reissue the edit (`.claude/skills/workflow-commands/pre-gate-cut.md`).'
|
|
188
184
|
|
|
189
185
|
// **A refusal younger than this window is this edit's own refusal being answered**.
|
|
190
186
|
// A `decide` row that refused every over-threshold edit would wedge a run whose cut came back `busy` /
|
|
@@ -201,7 +197,8 @@ function is_reissued_refusal(now_ms: number, delivered_at_ms: number): boolean {
|
|
|
201
197
|
// silenced the row for the rest of a process the moment its first refusal landed — and a `busy` /
|
|
202
198
|
// `failed` / `unready` verdict, or an edit reissued unchanged, left the context to grow unwatched to
|
|
203
199
|
// 282,747 tokens with the cut never taken. `decide` re-asks on every over-threshold edit; the reissue
|
|
204
|
-
// window
|
|
200
|
+
// window keeps that from wedging a run whose cut came back `busy` / `failed`, and a run that cannot
|
|
201
|
+
// cut at all never reaches here — `can_cut_sync` keeps the trigger silent for it. `delivered_at_ms` is this
|
|
205
202
|
// row's own last refusal, written by `fire_once` only when a refusal actually fired, so a passed
|
|
206
203
|
// reissue does not move it.
|
|
207
204
|
function decide(
|
|
@@ -213,8 +210,8 @@ function decide(
|
|
|
213
210
|
return !is_reissued_refusal(run.now_ms, delivered_at_ms)
|
|
214
211
|
}
|
|
215
212
|
|
|
216
|
-
// The row itself, so `delivered-rules.ts` spreads one entry. `decide` fires it per threshold crossing
|
|
217
|
-
// `keeps` is the implementation cut command, read from a `Bash` call the same
|
|
213
|
+
// The row itself, so `delivered-rules.ts` spreads one entry. `decide` fires it per threshold crossing
|
|
214
|
+
// in a run that can cut; `keeps` is the implementation cut command, read from a `Bash` call the same
|
|
218
215
|
// way `pre-gate-cut`'s is.
|
|
219
216
|
const ROW = {
|
|
220
217
|
id: 'implementation-cut',
|
|
@@ -222,6 +219,11 @@ const ROW = {
|
|
|
222
219
|
reason: IMPLEMENTATION_CUT_REASON,
|
|
223
220
|
decide,
|
|
224
221
|
keeps: bash_triggers.on_bash_command(takes_the_impl_cut),
|
|
222
|
+
// The opening the reason carried before it was cut to the action and the next command.
|
|
223
|
+
former_reasons: [
|
|
224
|
+
'⛔ implementation-phase cut: this checkout is a lane dispatched',
|
|
225
|
+
"⛔ implementation-phase cut: this run's context crossed the",
|
|
226
|
+
],
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
const implementation_cut = {
|
|
@@ -51,13 +51,10 @@ function is_index_mutation(command: string): boolean {
|
|
|
51
51
|
|
|
52
52
|
const INDEX_MUTATION_REASON =
|
|
53
53
|
'⛔ git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore ' +
|
|
54
|
-
"--staged` overwrite the user's staged snapshot
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user ' +
|
|
59
|
-
'instruction, run by the user in their own terminal. **This rule fires on every occurrence, not once ' +
|
|
60
|
-
'per run.**'
|
|
54
|
+
"--staged` overwrite the user's staged snapshot. Commit with `pnpm josh git -y`; any other staging " +
|
|
55
|
+
"is the user's, on explicit instruction. It fires on every occurrence " +
|
|
56
|
+
'(`prompts/collaboration-workflow/operating-rules.md` → ' +
|
|
57
|
+
'"no-self-staging").'
|
|
61
58
|
|
|
62
59
|
const ROW = {
|
|
63
60
|
id: 'index-mutation',
|
|
@@ -89,21 +89,11 @@ function reads_issue_comments(command: string): boolean {
|
|
|
89
89
|
// the fix. The label keeps `time_transcript_line.guard_from_refusal` naming this guard, not the command.
|
|
90
90
|
const ISSUE_COMMENTS_REASON =
|
|
91
91
|
'⛔ issue comments: pnpm josh issue:read <N>\n' +
|
|
92
|
-
"Run that in place of this read
|
|
93
|
-
'the
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
'diagnosis (joshuafolkken/kit#1537), a changed default and an added acceptance criterion ' +
|
|
98
|
-
'(joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) — and ' +
|
|
99
|
-
'nothing in the body says it was superseded, so a body-only reader builds the wrong thing and ' +
|
|
100
|
-
'sees no contradiction. ' +
|
|
101
|
-
'Then the later text is the agreement in force — a comment supersedes the body it contradicts — ' +
|
|
102
|
-
"except for two answers that are not the run's to make: work a comment reassigns to another " +
|
|
103
|
-
'Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a ' +
|
|
104
|
-
'reason to exist stops the run with a `confirmation` Telegram. The procedure is ' +
|
|
105
|
-
'`.claude/skills/workflow-commands/issue-comments.md`. ' +
|
|
106
|
-
'Every body-only read is refused until the comments are read.'
|
|
92
|
+
"Run that in place of this read — an Issue's comments are part of it, and a later comment " +
|
|
93
|
+
'supersedes the body it contradicts; refused until that command is on the transcript. Without ' +
|
|
94
|
+
'josh, read ' +
|
|
95
|
+
'`gh api repos/{owner}/{repo}/issues/<N>/comments` beside the body ' +
|
|
96
|
+
'(`.claude/skills/workflow-commands/issue-comments.md`).'
|
|
107
97
|
|
|
108
98
|
// The enumeration row itself, so `delivered-rules.ts` spreads one entry. `already_satisfied` stands it
|
|
109
99
|
// down once the comments are on the transcript tail, and until then it refuses every body-only read.
|
|
@@ -52,12 +52,9 @@ function is_bare_josh_git(command: string): boolean {
|
|
|
52
52
|
// sibling reasons do.
|
|
53
53
|
const JOSH_GIT_BARE_REASON =
|
|
54
54
|
'⛔ bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to ' +
|
|
55
|
-
'its cancel branch
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'`pnpm josh git -y "<title> #<N>"`. The recovery form that opens the PR for a push that already ' +
|
|
59
|
-
'landed is `pnpm josh git -y --skip-commit --skip-push`. This rule fires on every occurrence, not ' +
|
|
60
|
-
'once per run.'
|
|
55
|
+
'its cancel branch. Run `pnpm josh git -y "<title> #<N>"`, or ' +
|
|
56
|
+
'`pnpm josh git -y --skip-commit --skip-push` to open the PR for a push that already landed. It ' +
|
|
57
|
+
'fires on every occurrence.'
|
|
61
58
|
|
|
62
59
|
// The row itself, so `delivered-rules.ts` spreads one entry. `is_trigger` reads the input through
|
|
63
60
|
// `on_bash_command` — the `Bash` tool-name gate every command row shares — and `decide` returns true so
|
|
@@ -109,9 +109,9 @@ function foreground_input(
|
|
|
109
109
|
call: GuardedCall,
|
|
110
110
|
is_lane_child: () => boolean = () => lane_child_marker.is_child_of(process.cwd()),
|
|
111
111
|
): Record<string, unknown> | undefined {
|
|
112
|
-
if (!is_backgrounded_ship(call) || !json_value.is_record(call.input))
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
if (!is_backgrounded_ship(call) || !json_value.is_record(call.input) || !is_lane_child()) {
|
|
113
|
+
return undefined
|
|
114
|
+
}
|
|
115
115
|
|
|
116
116
|
return { ...call.input, [BACKGROUND_KEY]: false, [TIMEOUT_KEY]: FOREGROUND_TIMEOUT_MS }
|
|
117
117
|
}
|
|
@@ -119,7 +119,7 @@ function foreground_input(
|
|
|
119
119
|
const FOREGROUND_NOTE =
|
|
120
120
|
'lane background: this lane child backgrounded `josh ship`, which would die with the headless turn ' +
|
|
121
121
|
'before its supervisor exists — so it was run in the foreground instead, where it detaches itself ' +
|
|
122
|
-
'after the preflight. End the turn on `launched` / `busy
|
|
122
|
+
'after the preflight. End the turn on `launched` / `busy`.'
|
|
123
123
|
|
|
124
124
|
// The launches `launched_ids` reads off the transcript tail that never saw their finish. A launch is
|
|
125
125
|
// read off its own tool result and a finish off the harness notice, both by `time_transcript_line`.
|
|
@@ -149,26 +149,15 @@ function pending_agent_ids(tail: string): ReadonlyArray<string> {
|
|
|
149
149
|
|
|
150
150
|
const LANE_BACKGROUND_REASON =
|
|
151
151
|
'⛔ lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless ' +
|
|
152
|
-
'
|
|
153
|
-
'`
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'supervisor instead: `pnpm josh ship --detach --review "<title> #<N>"` (add `--cite <N>`) issued in ' +
|
|
157
|
-
'the foreground — its preflight runs in this process before the supervisor exists, so a backgrounded ' +
|
|
158
|
-
'`ship` dies with the turn (joshuafolkken/kit#3027; a backgrounded `ship` issued alone is moved to ' +
|
|
159
|
-
'the foreground for you, joshuafolkken/kit#3154) — then end the turn on `launched` / `busy`. ' +
|
|
160
|
-
'`.claude/skills/workflow-commands/chain-rule.md` step 0 is the single source. A ' +
|
|
161
|
-
'single check you must read before continuing runs in the foreground within the tool timeout; the ' +
|
|
162
|
-
'commit-push step does not — in a child it too belongs to the detached ship, in either spelling. This ' +
|
|
163
|
-
'rule fires on every occurrence, not once per run.'
|
|
152
|
+
'process whose background tasks die when the turn ends. Hand over to ' +
|
|
153
|
+
'`pnpm josh ship --detach --review "<title> #<N>"`, issued in the foreground, then end the turn on ' +
|
|
154
|
+
'`launched` / `busy`. It fires on every occurrence ' +
|
|
155
|
+
'(`.claude/skills/workflow-commands/chain-rule.md` step 0).'
|
|
164
156
|
|
|
165
157
|
const LANE_BACKGROUND_STOP_REASON =
|
|
166
158
|
'⛔ lane background stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a background task ' +
|
|
167
|
-
'it launched has not finished
|
|
168
|
-
'
|
|
169
|
-
'in the foreground and act on its result, or re-issue the work through `pnpm josh ship --detach`, ' +
|
|
170
|
-
'whose supervisor outlives the turn (`.claude/skills/workflow-commands/chain-rule.md` step 0). Do ' +
|
|
171
|
-
'not repeat your previous reply.'
|
|
159
|
+
'it launched has not finished, and ending this turn kills it. Wait for it in the foreground, or ' +
|
|
160
|
+
're-issue the work through `pnpm josh ship --detach` — do not notify, stop or repeat your reply.'
|
|
172
161
|
|
|
173
162
|
// The row itself, so `delivered-rules.ts` spreads one entry. It fires on every occurrence (`decide`
|
|
174
163
|
// returns true): a child must never background these, so the route is always the detached ship, never
|
|
@@ -89,25 +89,15 @@ function is_carry_conflict(
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// The instruction in the shape a refusal can carry: what the child cannot do, why the record is not a
|
|
92
|
-
// conflict, and the mechanical branch to take instead — continue, never park or ask.
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// avoided so the single-quoted literal needs no escaping, as the sibling reasons do.
|
|
92
|
+
// conflict, and the mechanical branch to take instead — continue, never park or ask. Its history is
|
|
93
|
+
// `docs/maintainers/rule-delivery-rationale.md` → "Why a refusal is the action and the next command".
|
|
94
|
+
// Apostrophes are avoided so the single-quoted literal needs no escaping, as the sibling reasons do.
|
|
96
95
|
const LANE_CARRY_CONFLICT_REASON =
|
|
97
96
|
'⛔ lane child carry conflict: this session is a dispatched lane child, and ' +
|
|
98
|
-
'`
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'
|
|
102
|
-
'parent (joshuafolkken/kit#2267, measured 2026-09-21 in backlogrun #2252 — a child read run:merge ' +
|
|
103
|
-
'`busy` as a competitor and parked #2258 unimplemented). Do not run this command, and do not treat ' +
|
|
104
|
-
'it as a conflict: no `needs-decision`, no `confirmation` Telegram, no question to a person — a ' +
|
|
105
|
-
'dispatched child cannot ask, and there is nothing to decide. Continue implementing this Issue; the ' +
|
|
106
|
-
'parent owns the budget and counts the outcome at the return. The procedure is ' +
|
|
107
|
-
'`.claude/skills/workflow-commands/pre-gate-cut.md` and `backlogrun-progress.md` → "The hand-off". ' +
|
|
108
|
-
'Live evidence of `run:carry` is taken with a fresh `JOSH_TEMP_ROOT` prefixed to the call, which this ' +
|
|
109
|
-
'rule lets through (joshuafolkken/kit#3458); `run:merge` is refused under any root, because it also ' +
|
|
110
|
-
'syncs main, closes lanes and writes to GitHub. This rule fires on every occurrence, not once per run.'
|
|
97
|
+
'`run:merge` / `run:carry` are the parent budget commands; the live record belongs to the parent, ' +
|
|
98
|
+
'not to a competing run. Continue implementing this Issue: no `needs-decision`, no Telegram. ' +
|
|
99
|
+
'Live `run:carry` evidence takes a fresh `JOSH_TEMP_ROOT`; `run:merge` is refused under any root, ' +
|
|
100
|
+
'on every occurrence.'
|
|
111
101
|
|
|
112
102
|
// The row itself, so `delivered-rules.ts` spreads one entry. `is_trigger` reads the input through
|
|
113
103
|
// `on_bash_command` — the `Bash` tool-name gate every command row shares — and `decide` returns true
|
|
@@ -41,17 +41,12 @@ function is_interactive_ask(
|
|
|
41
41
|
// `backlogrun-park.md` → "park and continue", and a second copy here would be the clone `CLAUDE.md`
|
|
42
42
|
// prohibits. The one command is spelled so the child is not left to reconstruct it from a doc read.
|
|
43
43
|
const LANE_INTERACTIVE_ASK_REASON =
|
|
44
|
-
'⛔ lane child interactive ask: this session is a dispatched lane child and cannot ask a
|
|
45
|
-
'
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
'`
|
|
49
|
-
|
|
50
|
-
'`pnpm josh issue:comment <N> --body-file <path>` — then send the ' +
|
|
51
|
-
'`confirmation` Telegram and stop. The procedure is ' +
|
|
52
|
-
'`.claude/skills/workflow-commands/pre-gate-cut.md` → "A lane child records its park before it stops", ' +
|
|
53
|
-
'and the park itself is `backlogrun-park.md` → "park and continue". This rule fires on every ' +
|
|
54
|
-
'occurrence, not once per run.'
|
|
44
|
+
'⛔ lane child interactive ask: this session is a dispatched lane child and cannot ask a ' +
|
|
45
|
+
'person, so park instead: ' +
|
|
46
|
+
"`gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=needs-decision'`, " +
|
|
47
|
+
'`pnpm josh issue:comment <N> --body-file <path>`, ' +
|
|
48
|
+
'the `confirmation` Telegram, stop. It fires on every occurrence ' +
|
|
49
|
+
'(`.claude/skills/workflow-commands/pre-gate-cut.md`, `backlogrun-park.md`).'
|
|
55
50
|
|
|
56
51
|
// The row itself, so `delivered-rules.ts` spreads one entry. `decide` returns true so it refuses every
|
|
57
52
|
// occurrence; it declares no `keeps` — not asking is the absence of a call, not a call, so the row is
|
|
@@ -108,19 +108,11 @@ function records_the_park(command: string): boolean {
|
|
|
108
108
|
// single source is `backlogrun-park.md` → "park and continue", and a second copy here would be the
|
|
109
109
|
// clone `CLAUDE.md` prohibits.
|
|
110
110
|
const LANE_PARK_REASON =
|
|
111
|
-
'⛔ lane child park: this session is a dispatched lane child,
|
|
112
|
-
|
|
113
|
-
'
|
|
114
|
-
'
|
|
115
|
-
'
|
|
116
|
-
'the parent to guess from a log. Before you notify and stop: apply `needs-decision` and post a ' +
|
|
117
|
-
'comment carrying the question, the options, and whether work was stashed — ' +
|
|
118
|
-
"`gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=needs-decision'` then " +
|
|
119
|
-
'`pnpm josh issue:comment <N> --body-file <path>`. If this stop is a ' +
|
|
120
|
-
'`needs-human-review` or `already-done` one, that label is already on the Issue and there is nothing ' +
|
|
121
|
-
'to add. Then reissue this notify — it fires once per run and cannot repeat on the call in hand. The ' +
|
|
122
|
-
'procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` → "A lane child records its park ' +
|
|
123
|
-
'before it stops", and the park itself is `backlogrun-park.md` → "park and continue".'
|
|
111
|
+
'⛔ lane child park: this session is a dispatched lane child, so a stop for a person parks first: ' +
|
|
112
|
+
"`gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=needs-decision'`; " +
|
|
113
|
+
'`pnpm josh issue:comment <N> --body-file <path>` (question, options, stash); a ' +
|
|
114
|
+
'`needs-human-review` / `already-done` stop already is. Reissue this notify, once per run ' +
|
|
115
|
+
'(`.claude/skills/workflow-commands/pre-gate-cut.md`).'
|
|
124
116
|
|
|
125
117
|
// The row itself, so `delivered-rules.ts` spreads one entry. **Once per run rather than `decide`, and
|
|
126
118
|
// no `already_satisfied`.** The stop ends the turn, so it is not the recurring act `decide` is for. It carries no `already_satisfied` because the park it asks for is a GitHub label a
|
|
@@ -101,13 +101,8 @@ function decide(_call: unknown, run: GuardRun): boolean {
|
|
|
101
101
|
|
|
102
102
|
const LANE_SPLIT_PARK_REASON =
|
|
103
103
|
'⛔ lane split park: this session is a dispatched lane child that promoted its own Issue to an epic ' +
|
|
104
|
-
'this run, and a split is not a park
|
|
105
|
-
'
|
|
106
|
-
'continues through its children; a question here has nobody to answer it (joshuafolkken/kit#3296, ' +
|
|
107
|
-
'measured 2026-10-06 when the child for #3255 parked its promoted epic). If what remains of the ' +
|
|
108
|
-
'original work needs a person, park that child issue instead, then end the turn. The procedure is ' +
|
|
109
|
-
'`.claude/skills/workflow-commands/backlogrun-park.md` → "Splitting a child mid-run". This rule fires ' +
|
|
110
|
-
'on every occurrence, not once per run.'
|
|
104
|
+
'this run, and a split is not a park: no `needs-decision` on the epic, no `confirmation` Telegram, ' +
|
|
105
|
+
'on every occurrence. Park a child issue if the rest needs a person, else end the turn (`.claude/skills/workflow-commands/backlogrun-park.md` → "Splitting a child mid-run").'
|
|
111
106
|
|
|
112
107
|
// It declares no `keeps`: not parking is the absence of a call, so the row is reported unmeasured
|
|
113
108
|
// rather than scored on an act that does not exist (`lane-carry-conflict.ts`).
|