@joshuafolkken/kit 1.1136.0 → 1.1166.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +4 -4
- package/.claude/skills/epic-commands/SKILL.md +27 -239
- package/.claude/skills/epic-commands/epic-bundle.md +115 -0
- package/.claude/skills/epic-commands/execution-waves.md +126 -0
- package/.claude/skills/workflow-commands/SKILL.md +48 -194
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-child.md +12 -123
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +11 -60
- package/.claude/skills/workflow-commands/backlogrun-park.md +10 -8
- package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -173
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +155 -0
- package/.claude/skills/workflow-commands/backlogrun-steps.md +64 -144
- package/.claude/skills/workflow-commands/backlogrun.md +5 -4
- package/.claude/skills/workflow-commands/chain-rule.md +3 -2
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/entry-sequence.md +62 -0
- package/.claude/skills/workflow-commands/followup.md +104 -181
- package/.claude/skills/workflow-commands/fullrun-steps.md +8 -9
- package/.claude/skills/workflow-commands/fullrun.md +16 -73
- package/.claude/skills/workflow-commands/halfrun.md +10 -36
- package/.claude/skills/workflow-commands/into-target.md +4 -4
- package/.claude/skills/workflow-commands/issue-comments.md +7 -8
- package/.claude/skills/workflow-commands/issue-scout.md +6 -6
- package/.claude/skills/workflow-commands/kickoff.md +6 -6
- package/.claude/skills/workflow-commands/needs-human-review.md +6 -6
- package/.claude/skills/workflow-commands/observation-filing.md +21 -172
- package/.claude/skills/workflow-commands/observation-ledger.md +150 -0
- package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -93
- package/.claude/skills/workflow-commands/prerequisite.md +6 -6
- package/.claude/skills/workflow-commands/progress-watcher.md +90 -0
- package/.claude/skills/workflow-commands/prrun.md +5 -5
- package/.claude/skills/workflow-commands/retrospective.md +9 -9
- package/.claude/skills/workflow-commands/split-assessment.md +2 -2
- package/.claude/skills/workflow-commands/target-repository.md +2 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +7 -5
- package/.codex/hooks.json +4 -4
- package/AGENTS.md +1 -6
- package/CLAUDE.md +58 -120
- package/GEMINI.md +1 -6
- package/dist/CLAUDE.md +58 -120
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-NFZGFGOR.js → chunk-5KTCMCOT.js} +14 -1
- package/dist/hooks/{chunk-LJBLGHEH.js → chunk-A6P7PN7P.js} +4 -6
- package/dist/hooks/{chunk-FO2I7UT7.js → chunk-ANJ2WGFD.js} +1 -1
- package/dist/hooks/{chunk-GDU26K3J.js → chunk-DYAVAM6S.js} +563 -126
- package/dist/hooks/{chunk-RFWWUI5F.js → chunk-ENA7P5FB.js} +3 -3
- package/dist/hooks/{chunk-KBXZLMRH.js → chunk-GU5MATVE.js} +213 -65
- package/dist/hooks/chunk-GYNGP7HI.js +56 -0
- package/dist/hooks/{chunk-HBKZLITF.js → chunk-URJA6XY4.js} +4 -4
- package/dist/hooks/{chunk-KOKOAV6D.js → chunk-V2BTOZHT.js} +6 -4
- package/dist/hooks/{chunk-JKKTVDAG.js → chunk-ZDZ555YH.js} +100 -20
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +193 -192
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +11 -10
- package/dist/hooks/stop-guard.js +5 -5
- package/dist/josh.js +25 -2
- package/dist/version/index.js +5 -1
- package/package.json +2 -1
- package/prompts/collaboration-workflow/file-edits.md +11 -10
- package/prompts/collaboration-workflow/gh-rest.md +2 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-citation.md +5 -9
- package/prompts/collaboration-workflow/issue-template.md +40 -115
- package/prompts/collaboration-workflow/operating-rules.md +4 -5
- package/prompts/collaboration-workflow/output-bounds.md +12 -78
- package/prompts/collaboration-workflow/overview.md +7 -13
- package/prompts/collaboration-workflow/plan-comment.md +11 -39
- package/prompts/collaboration-workflow/principles.md +3 -4
- package/prompts/collaboration-workflow/report-format.md +28 -74
- package/prompts/collaboration-workflow/residency.md +12 -109
- package/prompts/collaboration-workflow/rule-delivery.md +98 -97
- package/prompts/collaboration-workflow/shell-body.md +3 -13
- package/prompts/collaboration-workflow/turn-batching.md +14 -88
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -14
- package/prompts/collaboration-workflow/wip-cap.md +6 -45
- package/prompts/collaboration-workflow.md +1 -13
- package/prompts/review-rubric.md +8 -71
- package/prompts/review.md +25 -55
- package/prompts/testing-guide.md +18 -67
- package/scripts/agent/agent-role-profile.ts +1 -1
- package/scripts/agent/codex-hooks.ts +3 -12
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +4 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +10 -1
- package/scripts/backlog/backlog-offer.ts +6 -6
- package/scripts/behavior/behavior-rules.ts +11 -4
- package/scripts/delegation/investigation-reads.ts +5 -5
- package/scripts/discovery/repo-discovery.ts +21 -7
- package/scripts/discovery/repo-origin.ts +26 -0
- package/scripts/discovery/repo-party.ts +42 -1
- package/scripts/document/backlogrun-parent-read-set.ts +14 -15
- package/scripts/document/document-byte-budget.ts +34 -20
- package/scripts/document/entry-read-budget.ts +21 -9
- package/scripts/document/lane-child-read-set.ts +12 -14
- package/scripts/document/read-set-cli.ts +4 -4
- package/scripts/document/read-set-point-of-use.ts +10 -2
- package/scripts/document/resident-budget.ts +47 -0
- package/scripts/epic/epic-shape.ts +10 -13
- package/scripts/epic/epic-solo.ts +1 -1
- package/scripts/exports/unused-members-cli.ts +7 -3
- package/scripts/gh/git-gh-api-path.ts +7 -0
- package/scripts/hooks/pretool-guard-cli.ts +2 -2
- package/scripts/hooks/pretool-guard.ts +45 -2
- package/scripts/hooks/run-hook.sh +41 -0
- package/scripts/init/hook-command-rewrite.ts +18 -35
- package/scripts/init/hook-launch.ts +21 -32
- package/scripts/issue/issue-auto-ok.ts +95 -0
- package/scripts/issue/issue-file-cli.ts +53 -7
- package/scripts/issue/issue-file.ts +28 -5
- package/scripts/issue/issue-labels.ts +12 -12
- package/scripts/issue/issue-rank.ts +1 -1
- package/scripts/issue/issue-read-cli.ts +3 -3
- package/scripts/issue/issue-read.ts +2 -2
- package/scripts/issue/issue-scout-cli.ts +2 -2
- package/scripts/issue/issue-state-cli.ts +1 -1
- package/scripts/issue/issue-state.ts +2 -2
- package/scripts/issue/issue-wip.ts +70 -0
- package/scripts/josh/hook-decision.ts +17 -0
- package/scripts/josh/josh-commands-development.ts +10 -0
- package/scripts/josh/josh-commands-issue.ts +2 -2
- package/scripts/lines/bytes-command.ts +29 -6
- package/scripts/observations/observation-ledger-line.ts +1 -1
- package/scripts/release/release-progress.ts +167 -0
- package/scripts/release/release-publish.ts +19 -8
- package/scripts/release/release-tag.ts +22 -8
- package/scripts/rules/decision-oracle-stage.ts +1 -1
- package/scripts/rules/decision-oracle.ts +6 -6
- package/scripts/rules/delivered-rules.ts +11 -11
- package/scripts/rules/direct-pr-create.ts +110 -0
- package/scripts/rules/file-body.ts +98 -42
- package/scripts/rules/filing-cap.ts +2 -2
- package/scripts/rules/filing-offer.ts +1 -1
- package/scripts/rules/gh-api.ts +18 -1
- package/scripts/rules/implementation-cut.ts +3 -1
- package/scripts/rules/lane-background.ts +67 -10
- package/scripts/rules/permission-guards.ts +10 -2
- package/scripts/rules/stop-rules.ts +4 -4
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +56 -12
- package/scripts/run/run-cut-handoff.ts +21 -1
- package/scripts/run/run-entry-cli.ts +11 -3
- package/scripts/run/run-hold-cli.ts +16 -2
- package/scripts/run/run-label.ts +50 -0
- package/scripts/run/run-merge-steps.ts +8 -3
- package/scripts/run/run-merge.ts +1 -1
- package/scripts/run/run-prep-cli.ts +6 -2
- package/scripts/run/run-prep.ts +17 -0
- package/scripts/run/run-ship-preflight.ts +34 -3
- package/scripts/run/run-ship-review-steps.ts +1 -1
- package/scripts/run/run-ship-review.ts +26 -5
- package/scripts/run/run-step-cli.ts +20 -7
- package/scripts/run/run-wake-driver.ts +24 -1
- package/scripts/run/run-wake-session.ts +2 -1
- package/scripts/test/test-declared-logic.ts +1 -1
- package/scripts/time-runtime/time-batch-guard.ts +52 -16
- package/scripts/time-runtime/time-density-hook.ts +20 -2
- package/scripts/time-runtime/time-density.ts +4 -3
- package/scripts/time-runtime/time-markers.ts +15 -4
- package/scripts/version/npm-registry.ts +11 -1
- package/templates/workflows/ci.yml +9 -1
- package/.claude/skills/workflow-commands/followup-reference.md +0 -118
- package/dist/hooks/chunk-HANSKHES.js +0 -464
|
@@ -33,22 +33,13 @@ const UNMATCHED_EVENTS: ReadonlySet<string> = new Set(['UserPromptSubmit'])
|
|
|
33
33
|
const JSON_INDENT = '\t'
|
|
34
34
|
|
|
35
35
|
function adapter_command(mode: string): string {
|
|
36
|
-
return hook_launch.
|
|
37
|
-
`codex-hook-adapter.js ${mode}`,
|
|
38
|
-
`pnpm exec tsx scripts/hooks/codex-hook-adapter.ts ${mode}`,
|
|
39
|
-
)
|
|
36
|
+
return hook_launch.hook_launch_command(`codex-hook-adapter ${mode}`)
|
|
40
37
|
}
|
|
41
38
|
|
|
42
39
|
// Each Claude-side guard command the adapter stands in for, keyed by that command verbatim.
|
|
43
40
|
const ADAPTED_COMMANDS: ReadonlyMap<string, string> = new Map([
|
|
44
|
-
[
|
|
45
|
-
|
|
46
|
-
adapter_command('pretool'),
|
|
47
|
-
],
|
|
48
|
-
[
|
|
49
|
-
hook_launch.hook_launch_command('format-edited.js', 'format:edited'),
|
|
50
|
-
adapter_command('posttool'),
|
|
51
|
-
],
|
|
41
|
+
[hook_launch.hook_launch_command('pretool-guard'), adapter_command('pretool')],
|
|
42
|
+
[hook_launch.hook_launch_command('format-edited'), adapter_command('posttool')],
|
|
52
43
|
])
|
|
53
44
|
|
|
54
45
|
function codex_handler(handler: HookHandler): HookHandler {
|
|
@@ -59,7 +59,7 @@ type BudgetVerdict =
|
|
|
59
59
|
typeof RUN_VERDICT | typeof WATCH_VERDICT | typeof STOP_VERDICT | typeof TRIAGE_VERDICT
|
|
60
60
|
|
|
61
61
|
// What `backlog:next` answered, in the words this decision needs. The mapping is mechanical and is
|
|
62
|
-
// written once, in `
|
|
62
|
+
// written once, in `docs/josh-commands-automation.md` → "`josh backlog:offer`": numbers are `candidates`, `wait` is `blocked` while
|
|
63
63
|
// this run has children in flight and `exhausted` when it does not, `none` is `exhausted`, `stop` is
|
|
64
64
|
// `parked`, `triage` is `untriaged`, and `error` or a failed listing is `unreadable`.
|
|
65
65
|
const ANSWERS = ['blocked', 'candidates', 'exhausted', 'parked', 'unreadable', 'untriaged'] as const
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { CONTEXT_CUT_THRESHOLD } from '#scripts/cost-runtime/context-cut-threshold'
|
|
5
4
|
import { hook_decision } from '#scripts/josh/hook-decision'
|
|
6
5
|
import { josh_command } from '#scripts/josh/josh-run'
|
|
7
6
|
import { lane_await, type AwaitState } from '#scripts/lane/lane-await'
|
|
@@ -217,7 +216,8 @@ function merge_token(out: string, code: number): string {
|
|
|
217
216
|
}
|
|
218
217
|
|
|
219
218
|
// The child's transcript, where its lane recorded one, so `run:merge` can tell an API outage from a
|
|
220
|
-
// failure exactly as it does for the parent that passes `--output` by hand.
|
|
219
|
+
// failure exactly as it does for the parent that passes `--output` by hand. No hand-off threshold: the
|
|
220
|
+
// supervisor runs this loop, and a process has no context to cut (joshuafolkken/kit#3156).
|
|
221
221
|
async function merge(issue: string, owner: string): Promise<MergeResult> {
|
|
222
222
|
await backlog_drive_owner.assert_current(owner)
|
|
223
223
|
const lane = await lane_registry.find_open_lane(issue)
|
|
@@ -225,7 +225,7 @@ async function merge(issue: string, owner: string): Promise<MergeResult> {
|
|
|
225
225
|
child: issue,
|
|
226
226
|
epic: undefined,
|
|
227
227
|
repo: undefined,
|
|
228
|
-
over:
|
|
228
|
+
over: undefined,
|
|
229
229
|
owner: run_carry.owner_of(Number(owner)),
|
|
230
230
|
output: lane?.output,
|
|
231
231
|
})
|
|
@@ -381,6 +381,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
|
381
381
|
const backlog_drive_cli = {
|
|
382
382
|
end_line,
|
|
383
383
|
finish: backlog_drive_finish.finish,
|
|
384
|
+
merge,
|
|
384
385
|
merge_token,
|
|
385
386
|
offer_argv: backlog_drive_offer_argv.offer_argv,
|
|
386
387
|
parse,
|
|
@@ -3,11 +3,20 @@ import type { DriveState } from './backlog-drive'
|
|
|
3
3
|
|
|
4
4
|
const LIST_SEPARATOR = ','
|
|
5
5
|
|
|
6
|
+
// **A child in flight is never offered** (joshuafolkken/kit#3156). A lane child merges its own PR and
|
|
7
|
+
// drops `in-progress` before GitHub closes the issue and before the loop collects it, so `backlog:next`
|
|
8
|
+
// can offer it again; launching it then is refused by its still-open lane and handed to an AI session.
|
|
9
|
+
function excluded(state: DriveState): ReadonlyArray<string> {
|
|
10
|
+
return [...new Set([...state.exclude, ...state.in_flight])]
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
function offer_argv(
|
|
7
14
|
state: DriveState,
|
|
8
15
|
carry: RunCarry,
|
|
9
16
|
forwarded: ReadonlyArray<string>,
|
|
10
17
|
): ReadonlyArray<string> {
|
|
18
|
+
const exclude = excluded(state)
|
|
19
|
+
|
|
11
20
|
return [
|
|
12
21
|
'backlog:offer',
|
|
13
22
|
'--json',
|
|
@@ -21,7 +30,7 @@ function offer_argv(
|
|
|
21
30
|
String(state.in_flight.length),
|
|
22
31
|
'--retries',
|
|
23
32
|
String(state.retries),
|
|
24
|
-
...(
|
|
33
|
+
...(exclude.length === 0 ? [] : ['--exclude', exclude.join(LIST_SEPARATOR)]),
|
|
25
34
|
...forwarded,
|
|
26
35
|
]
|
|
27
36
|
}
|
|
@@ -5,15 +5,15 @@ import { backlog_next } from './backlog-next'
|
|
|
5
5
|
// answered — its stdout tokens and its exit code — into the one word `backlog:budget` is then asked
|
|
6
6
|
// with, plus the issue numbers to start and the consecutive-retry count carried to the next ask.
|
|
7
7
|
//
|
|
8
|
-
// **The mapping is `
|
|
9
|
-
//
|
|
10
|
-
// branches an agent
|
|
11
|
-
// children in flight and `exhausted` when it has none, and `retry` is `blocked` until the third
|
|
8
|
+
// **The mapping is `docs/josh-commands-automation.md` → "`josh backlog:offer`", held here so it cannot
|
|
9
|
+
// drift.** It fixes which `backlog:next` answer becomes which `backlog:budget` word, and it carries two
|
|
10
|
+
// context branches an agent once applied by hand every iteration: `wait` is `blocked` while this run
|
|
11
|
+
// has children in flight and `exhausted` when it has none, and `retry` is `blocked` until the third
|
|
12
12
|
// consecutive one, which is `unreadable`. Both are decided from a count here rather than from a
|
|
13
|
-
// judgement.
|
|
13
|
+
// judgement (joshuafolkken/kit#3175 dropped the prose copy from `backlogrun-steps.md`).
|
|
14
14
|
|
|
15
15
|
// Three consecutive `retry` answers end the run: below that a transport hiccup is re-asked, at it the
|
|
16
|
-
// outage is not a hiccup. `
|
|
16
|
+
// outage is not a hiccup. "`josh backlog:offer`" is the single source of the count.
|
|
17
17
|
const RETRY_LIMIT = 3
|
|
18
18
|
const FAILURE_EXIT_CODE = 1
|
|
19
19
|
const NO_RETRIES = 0
|
|
@@ -13,7 +13,13 @@ const TOOL_USE = 'tool_use'
|
|
|
13
13
|
const COMMAND_KEY = 'command'
|
|
14
14
|
const FIRST_POSITION_OFFSET = 1
|
|
15
15
|
const TOOL_RESULT = 'tool_result'
|
|
16
|
-
|
|
16
|
+
// The openings of the two results that mean a Bash call never ran: the permission layer's refusal,
|
|
17
|
+
// and a PreToolUse hook's block (joshuafolkken/kit#3197). A PostToolUse hook error is deliberately
|
|
18
|
+
// absent — that hook fires after the command has already run.
|
|
19
|
+
const DENIED_BASH_PREFIXES = [
|
|
20
|
+
'Permission to use Bash with command ',
|
|
21
|
+
'PreToolUse:Bash hook error: ',
|
|
22
|
+
]
|
|
17
23
|
|
|
18
24
|
// The git subcommands that stage or rewrite the index — the ones `CLAUDE.md` reserves for `pnpm josh
|
|
19
25
|
// git` under "Never stage or mutate the git index on your own". An agent that runs one directly has
|
|
@@ -71,14 +77,15 @@ function is_index_mutation(command: string): boolean {
|
|
|
71
77
|
.some((segment) => INDEX_MUTATION_HEAD.test(segment) && !is_dry_run(segment))
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
// An errored result alone may mean a command ran and failed. Only
|
|
75
|
-
// identifies a Bash call that never ran; the parser keeps
|
|
80
|
+
// An errored result alone may mean a command ran and failed. Only a refusal before execution — the
|
|
81
|
+
// permission layer's or a PreToolUse hook's — identifies a Bash call that never ran; the parser keeps
|
|
82
|
+
// the opening of that result's text.
|
|
76
83
|
function is_denied_result(block: Block): boolean {
|
|
77
84
|
return (
|
|
78
85
|
block.type === TOOL_RESULT &&
|
|
79
86
|
block.result_id !== '' &&
|
|
80
87
|
block.is_error === true &&
|
|
81
|
-
block.error_text.startsWith(
|
|
88
|
+
DENIED_BASH_PREFIXES.some((prefix) => block.error_text.startsWith(prefix))
|
|
82
89
|
)
|
|
83
90
|
}
|
|
84
91
|
|
|
@@ -71,7 +71,7 @@ const INVESTIGATOR_AGENT = 'investigator'
|
|
|
71
71
|
// What the refusal tells the model. It has to name the count, the command and the return shape,
|
|
72
72
|
// because the deny reason is the only text that reaches the model — a run that is refused and told
|
|
73
73
|
// nothing reads it as a broken tool.
|
|
74
|
-
const REASON = `⛔ investigation: ${String(delegation_policy.INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(delegation_policy.INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit, so the reading from here goes to a unit of its own (a successful write also starts the search count over). Ask \`pnpm josh delegate investigation\`, then dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer) with a brief of what the main line has already concluded and what is left to find out; it returns the conclusion plus its \`file:line\` citations, never the file text. Keep a read in the main line only where this run will edit that file — an \`Edit\` cannot be issued against text you do not hold. This run's own instructions and the harness's own session files — a backgrounded call's output, a persisted tool result, this session's scratchpad — never count toward the files, so they are not what took the file count here; every read-only search turn counts, whatever it names. The rule is \`.claude/skills/workflow-commands/
|
|
74
|
+
const REASON = `⛔ investigation: ${String(delegation_policy.INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(delegation_policy.INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit, so the reading from here goes to a unit of its own (a successful write also starts the search count over). Ask \`pnpm josh delegate investigation\`, then dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer) with a brief of what the main line has already concluded and what is left to find out; it returns the conclusion plus its \`file:line\` citations, never the file text. Keep a read in the main line only where this run will edit that file — an \`Edit\` cannot be issued against text you do not hold. This run's own instructions and the harness's own session files — a backgrounded call's output, a persisted tool result, this session's scratchpad — never count toward the files, so they are not what took the file count here; every read-only search turn counts, whatever it names. The rule is \`.claude/skills/workflow-commands/delegation.md\` → "The pre-implementation reading".`
|
|
75
75
|
|
|
76
76
|
interface ReadTally {
|
|
77
77
|
// The files read and not since edited, resolved so the two ways a target reaches here compare.
|
|
@@ -92,13 +92,13 @@ interface ReadTally {
|
|
|
92
92
|
searches: number
|
|
93
93
|
searches_since: number
|
|
94
94
|
// The files this run has successfully edited within the window, resolved (joshuafolkken/kit#1840).
|
|
95
|
-
// A re-read of one of them is a read of a file the run edited, which
|
|
95
|
+
// A re-read of one of them is a read of a file the run edited, which `delegation.md` keeps in the main line — so
|
|
96
96
|
// it is excluded from the count and from a call's projected growth rather than counted as fresh
|
|
97
97
|
// investigation.
|
|
98
98
|
edited: ReadonlySet<string>
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
// **The run's own instructions are not the Issue's subject, so reading them is not what
|
|
101
|
+
// **The run's own instructions are not the Issue's subject, so reading them is not what `delegation.md` delegates.**
|
|
102
102
|
// #1426's rule is about reading to find out how the subject works; `CLAUDE.md`, a `prompts/` topic and
|
|
103
103
|
// a skill file are read to find out what to *do*, they are read by nearly every run, and a unit cannot
|
|
104
104
|
// be sent to read them on the main line's behalf. #1441's own measurement excluded 5 of them by hand
|
|
@@ -264,7 +264,7 @@ function is_failed_outcome(span: Span): boolean {
|
|
|
264
264
|
|
|
265
265
|
// **Every file the run successfully wrote within the window, resolved** (joshuafolkken/kit#1840). It
|
|
266
266
|
// is the one source of "this run edited that file", shared by the live tally and the replay: a read of
|
|
267
|
-
// a file the run edits is reading
|
|
267
|
+
// a file the run edits is reading `delegation.md` keeps in the main line, so it is never counted — whether the read
|
|
268
268
|
// came before the edit or after it. A failed edit wrote nothing and so exempts nothing, and a file
|
|
269
269
|
// edited outside the window is unknown rather than assumed: the set is only ever as wide as the tail
|
|
270
270
|
// the hook read, which is the bound joshuafolkken/kit#1764 already holds the accumulation to.
|
|
@@ -444,7 +444,7 @@ function is_at_search_threshold(search_count: number): boolean {
|
|
|
444
444
|
//
|
|
445
445
|
// **A delegated unit is not re-armed here because it is not refused at all** (joshuafolkken/kit#1840).
|
|
446
446
|
// The hook counts a unit's reading against the unit (joshuafolkken/kit#1424), and the refusal asks for
|
|
447
|
-
// a dispatch — but a unit is already where
|
|
447
|
+
// a dispatch — but a unit is already where `delegation.md` sends the reading, and a read-only one (`Explore`,
|
|
448
448
|
// `Plan`) has no `Agent` tool to dispatch with. It was formerly degraded to a single first refusal,
|
|
449
449
|
// which still asked for an action the unit could not take; now the unit short circuit in `should_block`
|
|
450
450
|
// turns it off entirely, so this function is the parent's alone and needs no delegation condition.
|
|
@@ -59,22 +59,35 @@ function resolve_git_directory(repository_path: string): string | undefined {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// The
|
|
63
|
-
|
|
64
|
-
// fail a command whose contract is to report what it could find.
|
|
65
|
-
function read_origin_url(repository_path: string): string | undefined {
|
|
62
|
+
// The work tree's git config text, or nothing when it is unreadable or not a work tree.
|
|
63
|
+
function read_config(repository_path: string): string | undefined {
|
|
66
64
|
const git_directory = resolve_git_directory(repository_path)
|
|
67
65
|
if (git_directory === undefined) return undefined
|
|
68
66
|
|
|
69
67
|
try {
|
|
70
|
-
return
|
|
71
|
-
readFileSync(path.join(git_directory, GIT_CONFIG), 'utf8'),
|
|
72
|
-
)
|
|
68
|
+
return readFileSync(path.join(git_directory, GIT_CONFIG), 'utf8')
|
|
73
69
|
} catch {
|
|
74
70
|
return undefined
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
73
|
|
|
74
|
+
// The `origin` URL a work tree declares, or nothing when it has no `origin`, no readable config, or
|
|
75
|
+
// is not a work tree at all. Every failure reads as "no remote" so one unreadable sibling cannot
|
|
76
|
+
// fail a command whose contract is to report what it could find.
|
|
77
|
+
function read_origin_url(repository_path: string): string | undefined {
|
|
78
|
+
const config = read_config(repository_path)
|
|
79
|
+
|
|
80
|
+
return config === undefined ? undefined : repo_origin.parse_origin_from_config(config)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// The `url` of every remote the work tree declares, with the same "unreadable reads as none" contract
|
|
84
|
+
// as `read_origin_url`.
|
|
85
|
+
function read_remote_urls(repository_path: string): Array<string> {
|
|
86
|
+
const config = read_config(repository_path)
|
|
87
|
+
|
|
88
|
+
return config === undefined ? [] : repo_origin.parse_remote_urls_from_config(config)
|
|
89
|
+
}
|
|
90
|
+
|
|
78
91
|
// Every immediate child directory of `parent`, in name order. An unreadable parent yields nothing
|
|
79
92
|
// rather than throwing: `josh doctor` reports what it could find, and a permission error on the
|
|
80
93
|
// parent is not a broken install.
|
|
@@ -158,6 +171,7 @@ const repo_discovery = {
|
|
|
158
171
|
OVERRIDE_ENV_KEY,
|
|
159
172
|
main_worktree,
|
|
160
173
|
read_origin_url,
|
|
174
|
+
read_remote_urls,
|
|
161
175
|
resolve_current_owner,
|
|
162
176
|
scan_siblings,
|
|
163
177
|
discover_repositories,
|
|
@@ -26,6 +26,7 @@ const REPO_PATH_SEGMENTS = 2
|
|
|
26
26
|
const NOT_FOUND = -1
|
|
27
27
|
const SECTION_PREFIX = '['
|
|
28
28
|
const ORIGIN_SECTION = /^\[remote\s+"origin"\]$/u
|
|
29
|
+
const REMOTE_SECTION = /^\[remote\s+"[^"]+"\]$/u
|
|
29
30
|
const URL_KEY = 'url'
|
|
30
31
|
|
|
31
32
|
// Whether a URL's hostname is GitHub. Exact, with no alias spelling accepted: an alias is a name
|
|
@@ -132,6 +133,30 @@ function parse_origin_from_config(content: string): string | undefined {
|
|
|
132
133
|
.find((url) => url !== undefined)
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
// The config's lines grouped by section, each group led by its header. Lines before the first header
|
|
137
|
+
// belong to no section and are dropped.
|
|
138
|
+
function section_groups(lines: ReadonlyArray<string>): Array<Array<string>> {
|
|
139
|
+
const groups: Array<Array<string>> = []
|
|
140
|
+
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
if (is_section_header(line)) groups.push([line])
|
|
143
|
+
else groups.at(-1)?.push(line)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return groups
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// The `url` of every `[remote "…"]` section, in config order — the candidates `gh` picks its base
|
|
150
|
+
// repository from when it expands the `{owner}` placeholder (joshuafolkken/kit#3188).
|
|
151
|
+
function parse_remote_urls_from_config(content: string): Array<string> {
|
|
152
|
+
const lines = content.split('\n').map((line) => line.trim())
|
|
153
|
+
|
|
154
|
+
return section_groups(lines)
|
|
155
|
+
.filter((group) => REMOTE_SECTION.test(group[0] ?? ''))
|
|
156
|
+
.flatMap((group) => group.map((line) => entry_value(line, URL_KEY)))
|
|
157
|
+
.filter((url) => url !== undefined)
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
// The `owner/repo` key a discovery map is keyed by. Lowercased, because GitHub resolves owner and
|
|
136
161
|
// repository names case-insensitively: two remotes spelling the same repository differently are the
|
|
137
162
|
// same repository, and keying by the spelling would put both in the map — where an override, which
|
|
@@ -146,6 +171,7 @@ const repo_origin = {
|
|
|
146
171
|
is_github_ssh_host,
|
|
147
172
|
parse_origin_from_config,
|
|
148
173
|
parse_origin_url,
|
|
174
|
+
parse_remote_urls_from_config,
|
|
149
175
|
format_identity,
|
|
150
176
|
}
|
|
151
177
|
|
|
@@ -16,6 +16,8 @@ type Party = 'first-party' | 'third-party' | 'unknown'
|
|
|
16
16
|
const FIRST_PARTY: Party = 'first-party'
|
|
17
17
|
const THIRD_PARTY: Party = 'third-party'
|
|
18
18
|
const UNKNOWN: Party = 'unknown'
|
|
19
|
+
const GH_REPO_ENV_KEY = 'GH_REPO'
|
|
20
|
+
const GH_REPO_PATH_SEGMENTS = 2
|
|
19
21
|
|
|
20
22
|
// `unknown` is not `third-party`. An owner that cannot be read — no `origin`, an unreadable config —
|
|
21
23
|
// is a "cannot decide", and reading it as third-party would refuse first-party writes whenever the
|
|
@@ -40,7 +42,46 @@ function current_owner(repository_path: string = process.cwd()): string | undefi
|
|
|
40
42
|
return repo_discovery.resolve_current_owner(repository_path)
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
// The owner `GH_REPO` names, read from its last two segments so an optional host prefix is skipped.
|
|
46
|
+
function gh_repo_owners(gh_repo: string): Array<string | undefined> {
|
|
47
|
+
return [target_owner(gh_repo.split('/').slice(-GH_REPO_PATH_SEGMENTS).join('/'))]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The owner of every remote the work tree declares. A remote whose owner cannot be read stays in the
|
|
51
|
+
// set as `undefined` rather than being dropped: an SSH host alias (`git@github-work:o/r.git`) is one
|
|
52
|
+
// `gh` translates to github.com and may pick, so dropping it would hide the owner `gh` writes to.
|
|
53
|
+
function remote_owners(repository_path: string): Array<string | undefined> {
|
|
54
|
+
return repo_discovery
|
|
55
|
+
.read_remote_urls(repository_path)
|
|
56
|
+
.map((url) => repo_origin.parse_origin_url(url)?.owner)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Every owner `gh` could expand the `{owner}` placeholder to (joshuafolkken/kit#3188). `GH_REPO`
|
|
60
|
+
// (`[HOST/]OWNER/REPO`) overrides the remotes outright; otherwise `gh` picks its base repository among
|
|
61
|
+
// the GitHub remotes — `upstream` ahead of `origin`, or a `gh repo set-default` choice — so in a fork
|
|
62
|
+
// checkout it can expand to the upstream owner. Answering the whole candidate set rather than
|
|
63
|
+
// re-implementing that pick keeps the caller safe whichever remote `gh` chooses. An `undefined` entry
|
|
64
|
+
// is a candidate whose owner could not be read, which the caller must not assume is first-party.
|
|
65
|
+
function placeholder_owners(
|
|
66
|
+
repository_path: string = process.cwd(),
|
|
67
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
68
|
+
): Array<string | undefined> {
|
|
69
|
+
const gh_repo = environment[GH_REPO_ENV_KEY]
|
|
70
|
+
|
|
71
|
+
return gh_repo === undefined || gh_repo === ''
|
|
72
|
+
? remote_owners(repository_path)
|
|
73
|
+
: gh_repo_owners(gh_repo)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const repo_party = {
|
|
77
|
+
FIRST_PARTY,
|
|
78
|
+
THIRD_PARTY,
|
|
79
|
+
UNKNOWN,
|
|
80
|
+
classify,
|
|
81
|
+
current_owner,
|
|
82
|
+
placeholder_owners,
|
|
83
|
+
target_owner,
|
|
84
|
+
}
|
|
44
85
|
|
|
45
86
|
export type { Party }
|
|
46
87
|
export { repo_party }
|
|
@@ -8,25 +8,19 @@
|
|
|
8
8
|
// (`lane-child-read-set.ts`) — through the shared `read-set-trim`.
|
|
9
9
|
//
|
|
10
10
|
// **This is a different set from the worker's.** The parent keeps §0 (its session-cut / resume
|
|
11
|
-
// paragraph is the parent's own),
|
|
12
|
-
//
|
|
13
|
-
// parent files prerequisites and observations for the batch) — all of which the worker drops. What the
|
|
14
|
-
// parent drops is the implementer-only set below. **The parent owns every point-of-use document**, so
|
|
15
|
-
// unlike the worker it drops none of them: the trim is the `SKILL.md` sections alone.
|
|
11
|
+
// paragraph is the parent's own), which the worker drops. **The parent owns every point-of-use
|
|
12
|
+
// document**, so unlike the worker it drops none of them: the trim is the `SKILL.md` sections alone.
|
|
16
13
|
|
|
17
14
|
import type { ReadSetCost } from './entry-read-set'
|
|
18
15
|
import { read_set_trim } from './read-set-trim'
|
|
19
16
|
|
|
20
17
|
const BACKLOGRUN = 'backlogrun'
|
|
21
18
|
|
|
22
|
-
// **The `SKILL.md` sections the `backlogrun` parent never uses.**
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
19
|
+
// **The `SKILL.md` sections the `backlogrun` parent never uses.** It edits no rule mid-run (§3, read
|
|
20
|
+
// only on an editing turn). The implementer-only rules it used to skip — the `into` target, the
|
|
21
|
+
// working-tree hold, the Issue's comments — are single rows of §2's table since joshuafolkken/kit#3174,
|
|
22
|
+
// so there is no section of them left to trim.
|
|
26
23
|
const UNUSED_SKILL_SECTIONS: ReadonlyArray<string> = [
|
|
27
|
-
'2a. The `into <target>` suffix — where the new Issue lands',
|
|
28
|
-
'2f. The working-tree hold — one run per tree',
|
|
29
|
-
"2g. An Issue's comments are part of the Issue",
|
|
30
24
|
'3. What stays resident, and what is read from here',
|
|
31
25
|
]
|
|
32
26
|
|
|
@@ -34,10 +28,15 @@ const UNUSED_SKILL_SECTIONS: ReadonlyArray<string> = [
|
|
|
34
28
|
// the park — so it drops none. Named empty so the shared trim reads the same shape as the worker's.
|
|
35
29
|
const SKIPPED_POINT_OF_USE: ReadonlySet<string> = new Set<string>()
|
|
36
30
|
|
|
37
|
-
// **The parent runs the end-of-run retrospective** (`
|
|
31
|
+
// **The parent runs the end-of-run retrospective** (`retrospective.md`) when `run:step` prints it, but its
|
|
38
32
|
// manifest names `retrospective.md` only through `backlogrun-steps.md` — one hop further than the
|
|
39
|
-
// derivation follows — so the parent's reach names it (joshuafolkken/kit#3078).
|
|
40
|
-
|
|
33
|
+
// derivation follows — so the parent's reach names it (joshuafolkken/kit#3078). **It polls a
|
|
34
|
+
// delegated unit that went silent** through `backlogrun-recovery.md`, named only from
|
|
35
|
+
// `backlogrun-child.md`, so the reach names that too (joshuafolkken/kit#3175).
|
|
36
|
+
const REACHED_POINT_OF_USE: ReadonlySet<string> = new Set([
|
|
37
|
+
'retrospective.md',
|
|
38
|
+
'backlogrun-recovery.md',
|
|
39
|
+
])
|
|
41
40
|
|
|
42
41
|
function costed(root: string): ReadSetCost {
|
|
43
42
|
return read_set_trim.costed(root, {
|
|
@@ -65,15 +65,19 @@ function block_ceiling(size: number): number {
|
|
|
65
65
|
// unreached file, so the definition cannot rot as documents move on or off the execution path.
|
|
66
66
|
const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
67
67
|
{ path: '.claude/skills/dependency-update/SKILL.md', bytes: 8192 },
|
|
68
|
-
|
|
68
|
+
// joshuafolkken/kit#3173 split the waves and the `epic:bundle` detail into lazily read references.
|
|
69
|
+
{ path: '.claude/skills/epic-commands/SKILL.md', bytes: 20_480 },
|
|
70
|
+
{ path: '.claude/skills/epic-commands/epic-bundle.md', bytes: 12_288 },
|
|
71
|
+
{ path: '.claude/skills/epic-commands/execution-waves.md', bytes: 12_288 },
|
|
69
72
|
{ path: '.claude/skills/verify-ui/SKILL.md', bytes: 8192 },
|
|
70
|
-
{ path: '.claude/skills/workflow-commands/followup-reference.md', bytes: 16_384 },
|
|
71
73
|
{ path: '.claude/skills/workflow-commands/fullrun-steps.md', bytes: 8192 },
|
|
72
74
|
{ path: '.claude/skills/workflow-commands/into-target.md', bytes: 4096 },
|
|
73
75
|
{ path: '.claude/skills/workflow-commands/issue-comments.md', bytes: 8192 },
|
|
74
76
|
{ path: '.claude/skills/workflow-commands/issue-fold-existing.md', bytes: 4096 },
|
|
75
77
|
{ path: '.claude/skills/workflow-commands/needs-human-review.md', bytes: 8192 },
|
|
76
|
-
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes:
|
|
78
|
+
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes: 12_288 },
|
|
79
|
+
// joshuafolkken/kit#3176 moved the ledger out of `observation-filing.md`, read only when it appends.
|
|
80
|
+
{ path: '.claude/skills/workflow-commands/observation-ledger.md', bytes: 12_288 },
|
|
77
81
|
// `pre-gate-cut.md` left this per-document budget in joshuafolkken/kit#2289: it became a point-of-use
|
|
78
82
|
// document, so its bytes are now held by every entry's total read (`entry-read-budget.ts`) and the
|
|
79
83
|
// two budgets must not hold it twice — the reachability line moved it from `unreached` to
|
|
@@ -82,34 +86,44 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
|
82
86
|
{ path: '.claude/skills/workflow-commands/target-repository.md', bytes: 8192 },
|
|
83
87
|
{ path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
|
|
84
88
|
// joshuafolkken/kit#2998 split the reference: the developer commands stay, the rest moved out.
|
|
85
|
-
|
|
89
|
+
// joshuafolkken/kit#3175 raised it: the supervisor, provider-table and answer-to-budget prose moved
|
|
90
|
+
// here out of `backlogrun-steps.md`, so a run reads it only when it asks about those commands.
|
|
91
|
+
{ path: 'docs/josh-commands-automation.md', bytes: 159_744 },
|
|
86
92
|
{ path: 'docs/josh-commands.md', bytes: 36_864 },
|
|
87
93
|
{ path: 'prompts/coding-standards.md', bytes: 8192 },
|
|
88
94
|
{ path: 'prompts/collaboration-workflow.md', bytes: 8192 },
|
|
89
95
|
{ path: 'prompts/collaboration-workflow/file-edits.md', bytes: 12_288 },
|
|
90
96
|
{ path: 'prompts/collaboration-workflow/gh-rest.md', bytes: 8192 },
|
|
91
97
|
{ path: 'prompts/collaboration-workflow/glossary.md', bytes: 4096 },
|
|
92
|
-
{ path: 'prompts/collaboration-workflow/issue-citation.md', bytes:
|
|
93
|
-
// #
|
|
94
|
-
{ path: 'prompts/collaboration-workflow/issue-template.md', bytes:
|
|
95
|
-
{ path: 'prompts/collaboration-workflow/operating-rules.md', bytes:
|
|
96
|
-
|
|
97
|
-
{ path: 'prompts/collaboration-workflow/
|
|
98
|
-
{ path: 'prompts/collaboration-workflow/
|
|
98
|
+
{ path: 'prompts/collaboration-workflow/issue-citation.md', bytes: 4096 },
|
|
99
|
+
// #3178 left the template and the judgement tables; the epic commands moved to `epic-commands`, the by-hand fallback stayed.
|
|
100
|
+
{ path: 'prompts/collaboration-workflow/issue-template.md', bytes: 8192 },
|
|
101
|
+
{ path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 12_288 },
|
|
102
|
+
// #3177 moved the rationale to `docs/maintainers/output-bounds-rationale.md`.
|
|
103
|
+
{ path: 'prompts/collaboration-workflow/output-bounds.md', bytes: 4096 },
|
|
104
|
+
{ path: 'prompts/collaboration-workflow/overview.md', bytes: 4096 },
|
|
105
|
+
// #3178 replaced the review-chain restatement with a pointer to `chain-rule.md`.
|
|
106
|
+
{ path: 'prompts/collaboration-workflow/plan-comment.md', bytes: 8192 },
|
|
99
107
|
{ path: 'prompts/collaboration-workflow/principles.md', bytes: 12_288 },
|
|
100
108
|
{ path: 'prompts/collaboration-workflow/proposal-request.md', bytes: 4096 },
|
|
101
|
-
|
|
102
|
-
{ path: 'prompts/collaboration-workflow/
|
|
103
|
-
|
|
104
|
-
{ path: 'prompts/collaboration-workflow/
|
|
105
|
-
|
|
109
|
+
// #3178 dropped the restatements `report:lint` and `chain-rule.md` already carry.
|
|
110
|
+
{ path: 'prompts/collaboration-workflow/report-format.md', bytes: 16_384 },
|
|
111
|
+
// #3177 cut it to the four questions; the history and lists moved to `docs/maintainers/residency-rationale.md`.
|
|
112
|
+
{ path: 'prompts/collaboration-workflow/residency.md', bytes: 8192 },
|
|
113
|
+
// #3186 moved the hook wiring and history to `docs/maintainers/rule-delivery-rationale.md`;
|
|
114
|
+
// #3179 adds the `index-mutation` / `destructive-command` / `protected-file` entries.
|
|
115
|
+
{ path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 20_480 },
|
|
116
|
+
{ path: 'prompts/collaboration-workflow/shell-body.md', bytes: 8192 },
|
|
117
|
+
// #3177 moved the measurements and rejected mechanisms to `docs/maintainers/turn-batching-rationale.md`.
|
|
118
|
+
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 8192 },
|
|
106
119
|
{ path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 12_288 },
|
|
107
|
-
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes:
|
|
120
|
+
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 12_288 },
|
|
108
121
|
{ path: 'prompts/refactoring.md', bytes: 8192 },
|
|
109
|
-
|
|
110
|
-
{ path: 'prompts/review.md', bytes:
|
|
122
|
+
// #3180 moved the history and the lint-settled proofs to `docs/maintainers/`.
|
|
123
|
+
{ path: 'prompts/review-rubric.md', bytes: 16_384 },
|
|
124
|
+
{ path: 'prompts/review.md', bytes: 16_384 },
|
|
111
125
|
{ path: 'prompts/sonar-hotspot-handling.md', bytes: 8192 },
|
|
112
|
-
{ path: 'prompts/testing-guide.md', bytes:
|
|
126
|
+
{ path: 'prompts/testing-guide.md', bytes: 20_480 },
|
|
113
127
|
]
|
|
114
128
|
|
|
115
129
|
// The recorded ceiling for one path, or undefined when it carries no budget entry.
|
|
@@ -57,7 +57,7 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
57
57
|
// block in joshuafolkken/kit#2317 when `backlogrun-child.md`'s outage section recorded the
|
|
58
58
|
// session-resume re-dispatch and the outage-fold window.
|
|
59
59
|
// Raised in joshuafolkken/kit#2328 when `retrospective.md` joined the point-of-use set and `SKILL.md`
|
|
60
|
-
// grew by
|
|
60
|
+
// grew by `retrospective.md`: every entry that reads `SKILL.md` discovers the retrospective document, so its ~5KB
|
|
61
61
|
// and the new section crossed a block for each. The lane child is exempt — it never runs the
|
|
62
62
|
// retrospective (`run:step` answers `stop` for a child at the stop position), so it skips both.
|
|
63
63
|
// joshuafolkken/kit#2335 recorded the drain-time firing with the operational detail in
|
|
@@ -70,8 +70,8 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
70
70
|
// list, so its ~48KB read crossed a block downward and the recorded ceiling drops to the block multiple
|
|
71
71
|
// the stale-ratchet message named. The other entries still read it, so their rows hold.
|
|
72
72
|
// joshuafolkken/kit#2345 added the `implementation-unit` delegation row (full fan-out procedure in
|
|
73
|
-
// `docs/josh-commands-automation.md` → "`josh fanout`"); `
|
|
74
|
-
// pointer, folded into the existing enumeration and offset by tightening
|
|
73
|
+
// `docs/josh-commands-automation.md` → "`josh fanout`"); `delegation.md` gained only a one-clause mention and a
|
|
74
|
+
// pointer, folded into the existing enumeration and offset by tightening `delegation.md` prose, so every entry
|
|
75
75
|
// stayed within its block (kickoff has no block of headroom — its pre-#2294 ceiling is the next
|
|
76
76
|
// multiple, which the downward ratchet holds shut).
|
|
77
77
|
// joshuafolkken/kit#2353 wired the watcher-guard note into `backlogrun-progress.md` (read by the three
|
|
@@ -92,12 +92,24 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
92
92
|
// `backlogrun-*` phase documents it never opens. Each total now counts only what its path reaches.
|
|
93
93
|
// joshuafolkken/kit#3077 trimmed non-resident rule bodies from `CLAUDE.md`, lowering fullrun, prrun
|
|
94
94
|
// and backlogrun one block each.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
// Lowered in joshuafolkken/kit#3172: the heartbeat left `backlogrun-progress.md` for its own
|
|
96
|
+
// `progress-watcher.md`, and `pre-gate-cut.md` became the lane child's alone, so fullrun, halfrun
|
|
97
|
+
// and prrun stop paying for ~50KB of batch and lane procedure; backlogrun lost the moved prose.
|
|
98
|
+
// Lowered in joshuafolkken/kit#3174: `SKILL.md` was cut to a manifest (~21KB → ~11KB) and the entry
|
|
99
|
+
// sequence `fullrun`, `halfrun` and `prrun` shared moved into one `entry-sequence.md`.
|
|
100
|
+
// Lowered in joshuafolkken/kit#3175: the loop became the driver's hand-backs, the supervisor and
|
|
101
|
+
// provider prose moved to `docs/josh-commands-automation.md`, and the failure-only sections moved to
|
|
102
|
+
// `backlogrun-recovery.md`, which the parent's reach still charges whole.
|
|
103
|
+
// joshuafolkken/kit#3171 cut `CLAUDE.md` to triggers and pointers (~12 KB), lowering every entry.
|
|
104
|
+
{ entry: 'kickoff', bytes: 45_056 },
|
|
105
|
+
// Raised in joshuafolkken/kit#3177: #3173, #3181 and #3183 merged in parallel and together tipped
|
|
106
|
+
// fullrun 12 bytes over its block; none of #3177's documents are in its read set.
|
|
107
|
+
// Lowered back in joshuafolkken/kit#3180: the history and lint-settled proofs left its read set.
|
|
108
|
+
{ entry: 'fullrun', bytes: 57_344 },
|
|
109
|
+
{ entry: 'halfrun', bytes: 57_344 },
|
|
110
|
+
{ entry: 'prrun', bytes: 61_440 },
|
|
111
|
+
{ entry: 'backlogrun', bytes: 176_128 },
|
|
112
|
+
{ entry: LANE_CHILD, bytes: 57_344 },
|
|
101
113
|
]
|
|
102
114
|
|
|
103
115
|
function byte_size(root: string, relative_path: string): number {
|
|
@@ -19,19 +19,12 @@ const LANE_CHILD = 'lane-child'
|
|
|
19
19
|
const FULLRUN = 'fullrun'
|
|
20
20
|
|
|
21
21
|
// **The `SKILL.md` sections a dispatched lane child never uses**, read at the section level rather
|
|
22
|
-
// than in full. A child
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// invocation, so §0 cannot decide anything for it either. Review delegation remains in chain-rule.md.
|
|
22
|
+
// than in full. A child never edits these documents (§3), and its dispatch is already an explicit
|
|
23
|
+
// workflow invocation, so §0 cannot decide anything for it either. The other rules it never uses — the
|
|
24
|
+
// `into` target, delegation, the repository prefix, the scout, observation filing, the retrospective —
|
|
25
|
+
// are single rows of §2's table since joshuafolkken/kit#3174, so no section of them is left to trim.
|
|
27
26
|
const UNUSED_SKILL_SECTIONS: ReadonlyArray<string> = [
|
|
28
27
|
'0. The rule that fires before any of them — explicit invocation',
|
|
29
|
-
'2a. The `into <target>` suffix — where the new Issue lands',
|
|
30
|
-
'2b. Delegating a step to a cheaper tier',
|
|
31
|
-
'2c. The `owner/repo#` prefix — which repository the run acts on',
|
|
32
|
-
'2e. Before filing a new Issue — `pnpm josh issue:scout`',
|
|
33
|
-
'2i. An observation worth filing is filed without asking',
|
|
34
|
-
'2j. The end-of-run retrospective — read when `run:step` prints it',
|
|
35
28
|
'3. What stays resident, and what is read from here',
|
|
36
29
|
]
|
|
37
30
|
|
|
@@ -49,10 +42,12 @@ const UNUSED_SKILL_SECTIONS: ReadonlyArray<string> = [
|
|
|
49
42
|
// list — what one invocation approves, the named-issue order, the session-cut record, the loop and the
|
|
50
43
|
// once-per-session tail — none of which a leaf child performs. Every reference to it lives in a
|
|
51
44
|
// document the child never reads (`backlogrun.md`, `backlogrun-progress.md`, `retrospective.md`) or in a
|
|
52
|
-
// `SKILL.md` section the child trims (§0's session-cut note
|
|
45
|
+
// `SKILL.md` section the child trims (§0's session-cut note), so the child has no path
|
|
53
46
|
// that opens it. It was the child's single largest read — ~16,000 tokens read whole — charged for a
|
|
54
47
|
// document it never reaches, so dropping it is a correction of an over-count, not a loss of any rule the
|
|
55
48
|
// child needs. The parent still reads it in full.
|
|
49
|
+
// **`progress-watcher.md` is the parent's too** (joshuafolkken/kit#3172): a dispatched child is refused
|
|
50
|
+
// a watcher by its `JOSH_LANE_CHILD` mark, so it never opens the watcher's document.
|
|
56
51
|
const SKIPPED_POINT_OF_USE: ReadonlySet<string> = new Set([
|
|
57
52
|
'backlogrun-child.md',
|
|
58
53
|
'backlogrun-lanes.md',
|
|
@@ -61,13 +56,16 @@ const SKIPPED_POINT_OF_USE: ReadonlySet<string> = new Set([
|
|
|
61
56
|
'delegation.md',
|
|
62
57
|
'issue-scout.md',
|
|
63
58
|
'latest-gate.md',
|
|
59
|
+
'progress-watcher.md',
|
|
64
60
|
'retrospective.md',
|
|
65
61
|
])
|
|
66
62
|
|
|
67
63
|
// **A child that parks reads `backlogrun-park.md`** (`SKILL.md` §1, "A lane child that parks"), a
|
|
68
64
|
// document a standalone `fullrun`'s path never names — so the child's reach adds it to its base
|
|
69
|
-
// entry's (joshuafolkken/kit#3078).
|
|
70
|
-
|
|
65
|
+
// entry's (joshuafolkken/kit#3078). **`pre-gate-cut.md` is the child's alone** (joshuafolkken/kit#3172):
|
|
66
|
+
// only a lane child resumes or takes the pre-gate cut, so `fullrun.md` stopped naming it and the
|
|
67
|
+
// child's reach carries it instead.
|
|
68
|
+
const REACHED_POINT_OF_USE: ReadonlySet<string> = new Set(['backlogrun-park.md', 'pre-gate-cut.md'])
|
|
71
69
|
|
|
72
70
|
function costed(root: string): ReadSetCost {
|
|
73
71
|
return read_set_trim.costed(root, {
|