riskrails 3.0.0 → 3.1.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.
Files changed (112) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/README.md +259 -812
  5. package/agents/ai-reviewer.md.tmpl +6 -4
  6. package/ai/policies/model-routing.md +7 -7
  7. package/ai/policies/production.md +3 -3
  8. package/ai/policies/release.md +2 -2
  9. package/ai/policies/review-economy.md +34 -32
  10. package/ai/using-riskrails.md +15 -0
  11. package/bin/claude-1m +15 -14
  12. package/bin/riskrails.js +113 -37
  13. package/codex/hooks.json +25 -2
  14. package/hooks/AGENTS.md +2 -2
  15. package/hooks/CLAUDE.md +2 -2
  16. package/hooks/ai-guard.py +29 -8
  17. package/hooks/ai-path-guard-defaults.json +3 -1
  18. package/hooks/codex-hooks.json +25 -2
  19. package/hooks/context-guard.py +252 -36
  20. package/hooks/dialog-gate.py +105 -0
  21. package/hooks/guard/common.py +7 -0
  22. package/hooks/guard/decision.py +123 -0
  23. package/hooks/guard/dialog.py +598 -0
  24. package/hooks/guard/git.py +49 -3
  25. package/hooks/guard/paths.py +89 -19
  26. package/hooks/hooks.json +37 -2
  27. package/hooks/memory-pre-push.py +28 -0
  28. package/hooks/project-scaffold.py +1 -5
  29. package/hooks/runtime-gate.py +65 -18
  30. package/hooks/update_notice.py +141 -0
  31. package/instructions/routing.md +4 -4
  32. package/instructions/stub.md +3 -3
  33. package/package.json +1 -1
  34. package/profiles/max.json +3 -1
  35. package/profiles/pro.json +3 -1
  36. package/scripts/merge-codex-config.py +134 -0
  37. package/scripts/resolve-profile.py +15 -3
  38. package/settings.common.json +39 -2
  39. package/skills/AGENTS.md +3 -3
  40. package/skills/CLAUDE.md +3 -3
  41. package/skills/ai-init/templates/.ai/AGENTS.md +3 -3
  42. package/skills/ai-init/templates/.ai/VERSION +1 -1
  43. package/skills/ai-init/templates/.ai/policies/dialog.json +1 -0
  44. package/skills/ai-init/templates/.ai/policies/safety.md +22 -2
  45. package/skills/ai-init/templates/.ai/policies/security.md +7 -1
  46. package/skills/ai-init/templates/.ai/policies/testing.md +19 -6
  47. package/skills/ai-init/templates/.ai/policies/weight.json +1 -0
  48. package/skills/ai-init/templates/.ai/policies/weight.md +2 -2
  49. package/skills/ai-init/templates/.ai/state/README.md +9 -6
  50. package/skills/ai-init/templates/AGENTS.block.md +2 -2
  51. package/skills/ai-init/templates/CLAUDE.block.md +2 -2
  52. package/skills/ai-init/templates/GEMINI.block.md +2 -2
  53. package/skills/ai-init/templates/gitignore.snippet +1 -0
  54. package/skills/ai-init/templates/junie-guidelines.block.md +2 -2
  55. package/skills/ai-task/memory_compact.py +968 -0
  56. package/skills/ai-task/memory_format.py +775 -0
  57. package/skills/ai-task/memory_store.py +528 -0
  58. package/skills/ai-task/memory_sync.py +1571 -0
  59. package/skills/ai-task/sensors.py +25 -5
  60. package/skills/ai-task/state.py +999 -79
  61. package/skills/ai-task/weight.py +3 -3
  62. package/skills/project-init/templates/constitution.md +1 -1
  63. package/skills/project-init/templates/sdlc-README.md +20 -14
  64. package/skills/project-update/history/blobs/00289f98434fed230eebe768dfb3425f83d737866a3aebc97aa95167c4fa0c6e +131 -0
  65. package/skills/project-update/history/blobs/079a91f976ad69b5cf49ccb9a28849576e4735ddd9485fc26599e528a69fd2f4 +30 -0
  66. package/skills/project-update/history/blobs/07ff7fa6f93d471ff533f553b4e523e4105ccde1f3dab70132c5237728f68422 +13 -0
  67. package/skills/project-update/history/blobs/0ef4a360c76da290173dea0f8073949cb0f8113710a38df95acae5d96bdd1286 +123 -0
  68. package/skills/project-update/history/blobs/164cd34c182de88c33de7df35beb29200d792419e1c13a5f0b2104f944e57be7 +1 -0
  69. package/skills/project-update/history/blobs/18c7a2753ce9cb6f6d3d1e9d5d1125d75950b879d56b7325225b200869631b29 +15 -0
  70. package/skills/project-update/history/blobs/1d1d907449ec74594d0ee923ac142ae746b9aedf0a1eda9469f075a44d23e3d7 +13 -0
  71. package/skills/project-update/history/blobs/23a42febbacaa7b52a802d428033a99c0694391c81d51fe3b6e1dff608a641d5 +67 -0
  72. package/skills/project-update/history/blobs/25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e +1 -0
  73. package/skills/project-update/history/blobs/48d9950f16e31d55475f566a80dc95f64b35f1c804d2f51729e313a4f29bac8d +13 -0
  74. package/skills/project-update/history/blobs/5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 +49 -0
  75. package/skills/project-update/history/blobs/5a93f78b4ed4d416c171139b2ce610a117f9c1dd46fbb56ca86729cb4c53d10c +6 -0
  76. package/skills/project-update/history/blobs/5f65bc7ab3fc3c4e75ae05d49eaf4331fafc398c661abe33c69f7380a720344c +32 -0
  77. package/skills/project-update/history/blobs/6a3be0e624e5ad91e44d08857b7183ee53b7903e1e8d56ff196dbc9dde271e4a +75 -0
  78. package/skills/project-update/history/blobs/7170553410bba904ffb7edb4b231e3b3579b85542d84de717299980938f82c9d +13 -0
  79. package/skills/project-update/history/blobs/774b1e3a04ef0d49d33c44a2cfecd92eb6f6b738a12b6a681103ba7fc13e7b86 +67 -0
  80. package/skills/project-update/history/blobs/7be527cd648b27cca6e267de30f8e33a2ce17d5631a82694f23ae5c0623efe1f +15 -0
  81. package/skills/project-update/history/blobs/7ee99edafe7cec45ff577376c0d0178fdb2690134d4656488f46e129aba3cc5a +67 -0
  82. package/skills/project-update/history/blobs/7f063e79195a5c85c37b7260dd20c184d3b9bea85e37202e0bbca73ad84bba49 +13 -0
  83. package/skills/project-update/history/blobs/8b3ef9586491a426767c01da5d01bdd6b0655c576c68c4cc6dfb093367817dc0 +67 -0
  84. package/skills/project-update/history/blobs/917df3320d778ddbaa5c5c7742bc4046bf803c36ed2b050f30844ed206783469 +1 -0
  85. package/skills/project-update/history/blobs/935af1019931058ec3a0b834fe230e127ec914f75edcc4a6c589cfe733b0b979 +13 -0
  86. package/skills/project-update/history/blobs/ae5ce131d6bae401199ff32d8c3dc4d8aec188010a9111624ac64506e2116ff4 +84 -0
  87. package/skills/project-update/history/blobs/c8573ca1f810ce6c4ac49456a4c1725b93e0d3b74c54ce95d46d8747777895f0 +13 -0
  88. package/skills/project-update/history/blobs/ca4499d829268c8766fb3b825a515e88dc55c237e9d202816b50959b4dac031f +13 -0
  89. package/skills/project-update/history/blobs/d6e5b34d475e7187d2a8cb2aac7e7f1a427142baf6461bda387d70ceb4d8a8c1 +30 -0
  90. package/skills/project-update/history/blobs/e120ff4e32b2e950fc94ada4e787d57dc9c790da37223e5a202d74f7e3aadf80 +13 -0
  91. package/skills/project-update/history/blobs/e9adb7a9905eb719ac78ab96671f055da53e5001810f5ad6d6c94306b0323fe7 +74 -0
  92. package/skills/project-update/history/blobs/fae3064e648cdc55be4aeb1177525b302a5e34ebd752e4a904f47dc0f4557b96 +71 -0
  93. package/skills/project-update/history/index.json +60 -20
  94. package/skills/project-update/migrations/0009_state_file.py +25 -0
  95. package/skills/project-update/migrations/0010_dialog_switch.py +30 -0
  96. package/skills/project-update/migrations/0011_retire_risk_tiers.py +37 -0
  97. package/skills/project-update/migrations/README.md +2 -1
  98. package/skills/project-update/migrations/__init__.py +6 -1
  99. package/skills/project-update/render_instructions.py +1 -1
  100. package/skills/project-update/update.py +385 -89
  101. package/skills/rr-build/SKILL.md +14 -5
  102. package/skills/rr-build-all/SKILL.md +190 -0
  103. package/skills/rr-plan/SKILL.md +16 -4
  104. package/skills/rr-review/SKILL.md +24 -9
  105. package/skills/rr-setup/SKILL.md +15 -2
  106. package/skills/rr-setup/setup.py +7 -1
  107. package/skills/rr-ship/SKILL.md +16 -8
  108. package/skills/rr-spec/SKILL.md +4 -2
  109. package/skills/rr-status/SKILL.md +2 -2
  110. package/skills/usage-report/usage-report.py +45 -10
  111. package/skills/ai-init/templates/.ai/policies/risk-tiers.json +0 -628
  112. package/skills/ai-init/templates/.ai/policies/risk-tiers.md +0 -180
@@ -47,12 +47,14 @@ does each named finding close against the real code, and what did the remediatio
47
47
  introduce? A re-review that re-reads the whole change is a first review wearing
48
48
  the wrong name, and costs the same.
49
49
 
50
- Verify by execution where execution is possible. A claim you ran beats a claim you
51
- reasoned to, and it is what makes your row in the ledger worth inheriting.
50
+ Read the diff; run only the tests the step names, through `step_test_command`; no
51
+ scripts, mutation or timing loops in a scratchpad or temp dir. About 40 tool calls
52
+ in light and guarded, about 80 in full, then FINDINGS and VERDICT, with
53
+ `not reached (budget): …` under EXAMINED AND CLEAN.
52
54
 
53
55
  ## Boundary
54
56
 
55
- The brief names the task's mode; `.ai/state/current.json` has it otherwise. In
57
+ The brief names the task's mode; `.ai/state/state.json` (or the legacy `current.json`) has it otherwise. In
56
58
  light and guarded mode your inputs are the task goal, the diff, the tests the
57
59
  step names, and the project's own fixture corpus and threat model:
58
60
  `.ai/policies/safety.md`, the guards' `*-defaults.json`, the test fixtures.
@@ -61,7 +63,7 @@ it — a shape no session produces, a size no fixture reaches — is reported as
61
63
  `[INFO]` with `in_scope: no`, with the severity it would carry inside the
62
64
  boundary in `why it matters`. It goes to the risk register, never to a
63
65
  remediation, and it does not move your verdict. In full mode there is no
64
- boundary: the list below applies unbounded.
66
+ boundary: the list below applies unbounded, but the call cap holds.
65
67
 
66
68
  ## Look for
67
69
 
@@ -19,16 +19,16 @@ not settle — never for fact collection.
19
19
 
20
20
  Which model a tier is depends on the plan and the runtime: the installed profile
21
21
  decides it, `state.py profile --tier <TIER>` prints it for the runtime you are in,
22
- and `/ai-status` shows all four. This file names tiers only, so it is true on
22
+ and `/rr-status` shows the plan's budget. This file names tiers only, so it is true on
23
23
  every plan.
24
24
 
25
25
  The main session runs the model the installed profile sets and does the
26
26
  implementation itself. Planning, the weight, the test run and the release are
27
27
  its work too, not an agent's: `/rr-plan`, `state.py weight`, `state.py verify`,
28
28
  `/rr-ship`. Which stages spawn an agent at all
29
- is decided by `pipeline_profile` in `risk-tiers.json`: in `solo`, T0–T4 run in
30
- direct mode — nothing on STRONG below T4, a BALANCED review at T2/T3 only when
31
- a sensor is red, one STRONG review at T4 — and tests run once
29
+ is decided by the mode `state.py weight` gives: light runs direct — no review
30
+ unless `close` reports a red fact — guarded has one BALANCED review, full one
31
+ STRONG review, and nothing runs on STRONG below full — and tests run once
32
32
  after the last step, to the end, with every failure fixed as one batch.
33
33
 
34
34
  **Reading by difficulty.** A lookup — where X is, one route, service id, config
@@ -65,12 +65,12 @@ depends on a local model; if one becomes available it slots in at the FAST tier.
65
65
 
66
66
  ## EXPERT triggers
67
67
 
68
- 1. The task is T6: migration, infrastructure, production architecture.
68
+ 1. The task is full and irreversible: migration, infrastructure, production architecture.
69
69
  2. STRONG could not settle it: `confidence: uncertain`, or two STRONG results
70
70
  contradict each other.
71
71
  3. An irreversible design with several viable options — core data model, public
72
72
  API or event contract, service split.
73
- 4. The final check of a T6 plan or of a production-incident root cause before a
73
+ 4. The final check of a full-and-irreversible plan or of a production-incident root cause before a
74
74
  human acts on it.
75
75
 
76
76
  ## Escalation
@@ -104,7 +104,7 @@ the model `state.py profile --tier STRONG` printed — because a tier alone does
104
104
  cost or how much weight to give it. Report the model that **ran**, not the one
105
105
  that was requested: a gate may have sent an EXPERT agent down one tier during a
106
106
  rate limit, and a Codex agent file may have overridden the spawn-time model. Both
107
- runtimes expose the current state through `/ai-status`.
107
+ runtimes expose the current state through `/rr-status`.
108
108
 
109
109
  ## What never gets delegated
110
110
 
@@ -7,8 +7,8 @@ is reached by a real customer until you have evidence it is not.
7
7
 
8
8
  1. Find the callers. `grep`/`rg` for the method, the service id, the route, the
9
9
  event name, the template.
10
- 2. Find the tests that pin the current behaviour. If none exist and the tier is
11
- T3 or above, write a characterization test **first** — one that passes against
10
+ 2. Find the tests that pin the current behaviour. If none exist and the mode is
11
+ guarded or full, write a characterization test **first** — one that passes against
12
12
  the code as it is today.
13
13
  3. Check the history for why it looks like this: `git log -L`, `git log -S`,
14
14
  `git blame`. A workaround with a ticket number in the message is a constraint,
@@ -37,6 +37,6 @@ named in the release report. "It worked locally" is not observability.
37
37
 
38
38
  ## Rollback
39
39
 
40
- Every T3+ change states how to undo it. If undoing it is impossible — a data
40
+ Every guarded or full change states how to undo it. If undoing it is impossible — a data
41
41
  migration, an external system call, a fiscal record — say so plainly in the
42
42
  release report, because that changes who has to approve it.
@@ -29,7 +29,7 @@ It exists so that the human approving the change can see, in one page, what
29
29
  changed, what deliberately did not, what was tested, what was reviewed, what
30
30
  could go wrong, and how to undo it.
31
31
 
32
- A report that says "no known risks" for a T4 change is a report that was not
32
+ A report that says "no known risks" for a guarded or full change is a report that was not
33
33
  written carefully.
34
34
 
35
35
  ## What blocks a release
@@ -38,7 +38,7 @@ written carefully.
38
38
  `focus: security` pass;
39
39
  - a new regression in the last `state.py verify` run;
40
40
  - a destructive database operation without its own named approval;
41
- - a T3+ change with no rollback section;
41
+ - a guarded or full change with no rollback section;
42
42
  - an unanswered blocking question in the plan.
43
43
 
44
44
  ## Project specifics
@@ -72,8 +72,8 @@ The ledger is what replaces agent memory. Without it, every pass is pass one.
72
72
  ## 3. Findings are fixed as one batch, and re-review is scoped, not repeated
73
73
 
74
74
  A review returns all its findings at once, and they are fixed at once: one
75
- `state.py remediate` step whose scope is the finished steps plus what the
76
- findings name, every BLOCKER and HIGH addressed in it, then the verification
75
+ fix batch (`state.py start --amend` widens the task to the finished steps plus
76
+ what the findings name), every BLOCKER and HIGH addressed in it, then the verification
77
77
  command once. Never one fix, one run, one re-review per finding — that is the
78
78
  serial loop the measurements below describe, and the remediation of one finding
79
79
  is where the next regression comes from.
@@ -91,16 +91,19 @@ A task gets **two passes**: one full review and one scoped re-review, at every
91
91
  tier. That holds whoever asks — a re-review the human requests is one of the
92
92
  two, a cross-vendor review is one, and the parallel dimensions of §4 together
93
93
  are one. When both are spent, a third pass is not the next move: the human
94
- decides the verdict. The reviewer's verdict says which closing rule applies:
94
+ decides the verdict. One exception, chosen per project or per task: a **batch**
95
+ task (`/rr-build-all`, `weight.json` `batch.*`) runs a whole plan as one task,
96
+ and its passes are full ones — `batch.review_rounds_max` of them (three as
97
+ shipped), each after a fix batch — before the same stop: the pass past that
98
+ number is the human's. The reviewer's verdict says which closing rule applies:
95
99
  `blockers_open` (a BLOCKER) takes the remediation and the scoped re-review;
96
100
  `pass_with_findings` (a HIGH, no BLOCKER) takes the remediation and a green
97
- verification run, no re-review; `pass` closes. `state.py` has no value of its
98
- own for `pass_with_findings` yet: it is recorded as `blockers_open`, stays so
99
- after the green run, and the close summary names the labels that run closed.
101
+ verification run, no re-review; `pass` closes. `state.py review --verdict`
102
+ takes all three values.
100
103
 
101
- ## 3a. The boundary of a direct-mode review
104
+ ## 3a. The boundary of a light or guarded review
102
105
 
103
- Below T5 the review is bounded. Its inputs are the task goal, the diff, the
106
+ In light and guarded the review is bounded. Its inputs are the task goal, the diff, the
104
107
  tests the step names, and the project's own fixture corpus and threat model —
105
108
  `.ai/policies/safety.md`, the guards' `*-defaults.json`, the test fixtures.
106
109
  The reviewer works its whole list inside that corpus; what it may not do is
@@ -113,8 +116,8 @@ it only towards `yes`.
113
116
 
114
117
  A guard whose input is any shell line is a bottomless well for an unbounded
115
118
  reviewer: every pass finds a new input nobody will type, and every fix for one
116
- is the next pass's target. The boundary is what lets a review end. At T5 and
117
- T6 there is none — the reviewer's list applies unbounded, as the cost of being
119
+ is the next pass's target. The boundary is what lets a review end. In full
120
+ mode there is none — the reviewer's list applies unbounded, as the cost of being
118
121
  wrong there warrants.
119
122
 
120
123
  ## 4. Split one pass into parallel dimensions, not serial rounds
@@ -170,40 +173,39 @@ script, or run it yourself:
170
173
  A review pass spent on a self-contradictory risk register is a pass not spent on
171
174
  the code. When a task closes a gap that a register entry calls open, the entry is
172
175
  corrected **in the same step as the fix**, not later. When the plan gains a
173
- remediation, the plan gains a section — at T4 and above the artefact a human
176
+ remediation, the plan gains a section — in guarded and full the artefact a human
174
177
  approves must describe the change that exists, and a diff wider than the plan is
175
178
  invisible to the path guard, because the file set never changed.
176
179
 
177
180
  ## 8. The one thing the sensors may replace
178
181
 
179
- At T3 and below, when every sensor in `risk-tiers.json` `required_for_skip` is
180
- green or explicitly not applicable **on the tree as it is now**, the model
181
- review is replaced by the sensor set and `state.py review-gate` records
182
- `review_status: skipped_green`. This is the intent's decision 6, and it is an
183
- exception to the rule below, written here so the two do not contradict each
184
- other.
182
+ In light mode, when the two facts `close` measures are green **on the tree as
183
+ it is now** — a green whole-suite `verify` and every changed file inside the
184
+ task's files — the model review is replaced by the sensor set and the task
185
+ closes without one. This is the intent's decision 6, and it is an exception to
186
+ the rule below, written here so the two do not contradict each other.
185
187
 
186
188
  It is narrow on purpose. The skip requires that the suite passed on this exact
187
- tree, that the tests the plan named **fail without the change** (`bite` — a
188
- green suite that never reaches the diff is not evidence), that the diff is
189
- inside its budget — the budget is advisory for the tier, never for the skip —
190
- and that the tier re-scored from the real diff is still T3 or lower. A missing
191
- linter is not green: `unavailable` keeps the review, and only a human writing
192
- `lint_command: none` says the project has no linter. No
193
- sensor writes `skipped_green` by asserting it — `review-gate` reaches it from
194
- measurements, and `state.py set review_status skipped_green` is refused.
195
-
196
- From T4 the review always runs. What the sensors change there is where it
197
- starts: their rows are in the ledger, CONFIRMED, before the reviewer plans its
198
- work.
189
+ tree, and that the tests the plan named **fail without the change** (`bite` — a
190
+ green suite that never reaches the diff is not evidence). The mode is the one
191
+ re-scored from the real diff: a diff that raises it to guarded or full keeps the
192
+ review. A missing linter is not green: `unavailable` keeps the review, and only
193
+ a human writing `lint_command: none` says the project has no linter. No
194
+ sensor skips the review by asserting it — `close` reaches it from
195
+ measurements.
196
+
197
+ In guarded and full the review always runs. What the sensors change there is
198
+ where it starts: their rows are in the ledger, CONFIRMED, before the reviewer
199
+ plans its work.
199
200
 
200
201
  ## What this policy will not trade away
201
202
 
202
- - No review stage the tier requires is skipped, shortened, or merged — except
203
- the T2/T3 model review, and only under §8's measured conditions.
203
+ - No review stage the mode requires is skipped, shortened, or merged — except
204
+ the light-mode model review, and only under §8's measured conditions.
204
205
  - No reviewer is moved to a cheaper model to go faster. Narrow the surface, never
205
206
  the mind.
206
207
  - A blocker still returns to implementation, however late it arrives. Past the
207
- two passes of §3, a human — not a third pass — decides that it closed.
208
+ two passes of §3 — `batch.review_rounds_max` passes in a batch task — a human,
209
+ not another pass, decides that it closed.
208
210
  - The probe is the manager's own work and never replaces the adversarial pass. It
209
211
  exists so the pass starts further in, not so it happens less.
@@ -13,6 +13,7 @@ the steps; `policies/` holds the rules; this says which to open.
13
13
  | turn an idea into intent, requirements and design | `/rr-spec <idea>` |
14
14
  | plan a spec into tasks, or one task into a mode and a start | `/rr-plan <spec or task>` |
15
15
  | do the work, or resume it | `/rr-build` (no task in flight: it runs `/rr-plan` first) |
16
+ | run a whole plan as one task — parallel implementers and fixers, tests and the review again until it passes | `/rr-build-all <plan>` |
16
17
  | review the change | `/rr-review` — guarded and full, or when `close` asks |
17
18
  | close the task, get the human's approval | `/rr-ship` |
18
19
  | see where the task stands, or what it cost | `/rr-status`, `/rr-status --cost` |
@@ -38,6 +39,20 @@ domain at 2 is full whatever W is.
38
39
  The mode only rises during a task (`verify` and `close` re-score reversibility
39
40
  and domain from the diff); lowering it is the human's.
40
41
 
42
+ ## Batch — one task for a whole plan
43
+
44
+ `/rr-build-all docs/sdlc/plans/<slug>.md` runs every row of a programme plan as
45
+ one task: the ready rows go to parallel implementers on BALANCED (`after`
46
+ serializes the dependent ones), the suite runs once, every failure is fixed by
47
+ parallel fixers as one batch, one review at the mode's tier, its findings fixed
48
+ as one batch, then tests and the review again until it passes. Its budgets are
49
+ `weight.json` `batch.*`: three failed review passes (`batch.review_rounds_max`)
50
+ and eight suite runs stop at the human — `REVIEW_BUDGET` and `SUITE_BUDGET`, as
51
+ always. The mode, the guard, the two facts and the gate are unchanged.
52
+ `batch.default` makes it the project's default; `state.py batch off --by …
53
+ --why …` returns a task to the standard loop (anyone); `batch on` mid-task is
54
+ the human's, from their own terminal.
55
+
41
56
  ## The two facts
42
57
 
43
58
  `close` measures, never asks: (a) a green whole-suite `verify` on the tree as it
package/bin/claude-1m CHANGED
@@ -6,15 +6,15 @@
6
6
  # claude-1m Opus 5 [1m] as the main session
7
7
  # claude-1m fable Fable 5.1 [1m] as the main session
8
8
  # claude-1m fable -p '<brief>' one headless large-context run, e.g. called from another session
9
- # CLAUDE_1M_COMPACT_WINDOW=500000 claude-1m # compact earlier than 767k
9
+ # CLAUDE_1M_COMPACT_WINDOW=500000 claude-1m # compact earlier than the runtime would
10
10
  #
11
- # `autoCompactWindow` in settings.json is one value for every model, but Claude Code
12
- # caps it at the model's own window, so the 800 000 there already means near 167k on
13
- # `opus` and near 767k on `opus[1m]` and `fable[1m]`. This launcher is therefore about
14
- # the model, not the window: it pins a [1m] model for one session at launch.
15
- # CLAUDE_CODE_AUTO_COMPACT_WINDOW still outranks settings.json for this process only,
16
- # which is how CLAUDE_1M_COMPACT_WINDOW compacts *earlier* than 767k. context-guard
17
- # reads the same variable, so its warning and hold-back thresholds follow.
11
+ # This launcher is about the model, not the window: it pins a [1m] model for one
12
+ # session at launch, and by default leaves compaction to Claude Code (or to the
13
+ # `autoCompactWindow` of an install with `--context economy`, which Claude Code caps at
14
+ # the model's own window: 800 000 means near 167k on `opus` and near 767k on `opus[1m]`).
15
+ # Only CLAUDE_1M_COMPACT_WINDOW sets a window: exported as CLAUDE_CODE_AUTO_COMPACT_WINDOW,
16
+ # it outranks settings.json for this process only. context-guard reads the same
17
+ # variable, so its warning and hold-back thresholds follow.
18
18
  #
19
19
  # Subagents get the session's window, capped by Claude Code at each model's own: a 200k
20
20
  # agent compacts near 167k, and `architect` on fable[1m] gets the full window.
@@ -28,10 +28,11 @@ case "${1:-}" in
28
28
  fable) MODEL='fable[1m]'; shift;;
29
29
  esac
30
30
 
31
- WINDOW="${CLAUDE_1M_COMPACT_WINDOW:-800000}"
32
- case "$WINDOW" in
33
- ''|*[!0-9]*) echo "claude-1m: CLAUDE_1M_COMPACT_WINDOW must be a number of tokens (got '$WINDOW')" >&2; exit 2;;
34
- esac
35
-
36
- export CLAUDE_CODE_AUTO_COMPACT_WINDOW="$WINDOW"
31
+ if [ -n "${CLAUDE_1M_COMPACT_WINDOW:-}" ]; then
32
+ WINDOW="$CLAUDE_1M_COMPACT_WINDOW"
33
+ case "$WINDOW" in
34
+ ''|*[!0-9]*) echo "claude-1m: CLAUDE_1M_COMPACT_WINDOW must be a number of tokens (got '$WINDOW')" >&2; exit 2;;
35
+ esac
36
+ export CLAUDE_CODE_AUTO_COMPACT_WINDOW="$WINDOW"
37
+ fi
37
38
  exec "${CLAUDE_BIN:-claude}" --model "$MODEL" "$@"