@sleepy_panda_srl/portulan 0.2.0 → 0.4.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/README.md +15 -4
- package/cli/README.md +61 -6
- package/cli/advisory.mjs +167 -53
- package/cli/cache-risk.mjs +69 -0
- package/cli/comments.mjs +8 -5
- package/cli/compile.mjs +781 -130
- package/cli/context.mjs +204 -43
- package/cli/doctor.mjs +248 -31
- package/cli/drills.mjs +56 -9
- package/cli/finish.md +34 -0
- package/cli/finish.mjs +8 -67
- package/cli/form.mjs +34 -18
- package/cli/frontmatter.mjs +39 -0
- package/cli/gate.mjs +14 -12
- package/cli/guidance-files.mjs +335 -0
- package/cli/handoff-policy.mjs +76 -0
- package/cli/html-entities.mjs +2130 -0
- package/cli/import-files.mjs +15 -0
- package/cli/index.mjs +77 -24
- package/cli/init-preview.mjs +553 -0
- package/cli/init.mjs +261 -46
- package/cli/instructions-journal.mjs +1185 -0
- package/cli/instructions.mjs +332 -57
- package/cli/ledger.mjs +230 -40
- package/cli/librarian.mjs +103 -19
- package/cli/persona-routing.mjs +209 -0
- package/cli/plugin-lint.mjs +77 -56
- package/cli/portulan.mjs +1 -1
- package/cli/recipe-output.mjs +91 -0
- package/cli/recipe-runner.md +68 -0
- package/cli/recipe-runner.mjs +131 -0
- package/cli/recipe-set.mjs +12 -3
- package/cli/review-meter.mjs +14 -9
- package/cli/ritual-contexts.mjs +103 -0
- package/cli/sessions.mjs +2 -1
- package/cli/spend-analysis.mjs +111 -0
- package/cli/spend-offer.md +53 -0
- package/cli/spend-offer.mjs +93 -0
- package/cli/stop-diagnostics.mjs +114 -0
- package/cli/stop-gate.mjs +133 -90
- package/cli/stop-policy.mjs +14 -0
- package/cli/symbols.mjs +55 -8
- package/cli/telemetry.mjs +66 -17
- package/cli/upgrade.mjs +62 -44
- package/cli/vendor.mjs +207 -125
- package/core/operating/context.md +401 -18
- package/core/operating/evolution.md +3 -2
- package/core/operating/loop.md +9 -4
- package/core/operating/sessions.md +6 -15
- package/core/personas/README.md +75 -0
- package/core/personas/implementer.md +3 -0
- package/core/personas/librarian.md +3 -0
- package/core/personas/reviewer.md +3 -0
- package/core/skills/clarify/SKILL.md +1 -1
- package/core/skills/consolidate/SKILL.md +1 -1
- package/core/templates/handoff.md +6 -2
- package/evals/README.md +23 -7
- package/evals/ab/personas/README.md +111 -0
- package/evals/ab/personas/acquisition.json +330 -0
- package/evals/ab/personas/bindings/implementer.txt +43 -0
- package/evals/ab/personas/bindings/librarian.txt +40 -0
- package/evals/ab/personas/bindings/reviewer.txt +41 -0
- package/evals/ab/personas/corpus.json +69 -0
- package/evals/ab/personas/grader.mjs +171 -0
- package/evals/ab/personas/implementer.json +774 -0
- package/evals/ab/personas/implementer.md +22 -0
- package/evals/ab/personas/librarian.json +758 -0
- package/evals/ab/personas/librarian.md +22 -0
- package/evals/ab/personas/protocol/continuation.mjs.txt +284 -0
- package/evals/ab/personas/protocol/first-outcome.mjs.txt +227 -0
- package/evals/ab/personas/protocol/first-probe.mjs.txt +172 -0
- package/evals/ab/personas/protocol/publisher.mjs.txt +126 -0
- package/evals/ab/personas/protocol/second-probe.mjs.txt +193 -0
- package/evals/ab/personas/repairs/first-control-answer.txt +23 -0
- package/evals/ab/personas/repairs/grader-before-decode-repair.mjs.txt +143 -0
- package/evals/ab/personas/reviewer.json +758 -0
- package/evals/ab/personas/reviewer.md +22 -0
- package/evals/releases/0.3.0.json +150 -0
- package/evals/releases/0.3.0.md +68 -0
- package/evals/releases/0.4.0.json +154 -0
- package/evals/releases/0.4.0.md +69 -0
- package/package.json +7 -2
- package/packs/.claude-plugin/plugin.json +2 -1
- package/packs/README.md +7 -4
- package/packs/rituals/checkpoints/README.md +35 -0
- package/packs/rituals/checkpoints/pack.json +3 -2
- package/packs/tools/README.md +8 -8
- package/packs/tools/local-llm/README.md +176 -0
- package/packs/tools/local-llm/pack.json +32 -0
- package/packs/tools/local-llm/personas/local-worker.md +44 -0
- package/packs/tools/local-llm/runner/local-llm.mjs +1779 -0
- package/packs/tools/local-llm/skills/delegate-local/SKILL.md +88 -0
- package/packs/tools/local-llm/verify/README.md +18 -0
- package/spec/README.md +37 -3
- package/spec/migrations/0009-instruction-sections-on-read.mjs +60 -11
- package/spec/migrations/0011-persona-bindings-compiled.mjs +23 -0
- package/spec/migrations/README.md +6 -0
- package/spec/pack.schema.json +12 -3
- package/spec/slots.md +272 -123
- package/spec/workspace.schema.json +67 -11
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
> loads into every context is paid for again by every fresh context: each session, subagent, checkpoint
|
|
5
5
|
> and persona. So guidance sits in load tiers, the always tier is budgeted like memory, and a demotion
|
|
6
6
|
> is measured before and after. `loop.md` manages the window while a task runs; this page governs what
|
|
7
|
-
> the host loads into it, and when. _(
|
|
8
|
-
> [`0036`](../../.portulan/proposals/0036-what-a-host-loads-into-every-context-is-budgeted.md)
|
|
7
|
+
> the host loads into it, and when, then [what a change spends](#what-a-change-spends). _(Proposals
|
|
8
|
+
> [`0036`](../../.portulan/proposals/0036-what-a-host-loads-into-every-context-is-budgeted.md) and
|
|
9
|
+
> [`0038`](../../.portulan/proposals/0038-what-a-change-spends-is-measured.md).)_
|
|
9
10
|
|
|
10
11
|
## Four load tiers
|
|
11
12
|
|
|
@@ -48,11 +49,37 @@ raising the budget in the change that breached it. The shape is memory's on purp
|
|
|
48
49
|
a budget is a rail, not an aim, and the no-raise half is a rule no checker establishes, for the reason
|
|
49
50
|
[`memory.md`](memory.md) gives.
|
|
50
51
|
|
|
51
|
-
**Bytes per token are declared too**, as `context.ratio
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
**Bytes per token are declared too**, as `context.ratio`, with the compile host whose measurement
|
|
53
|
+
domain the ratio describes. Calibration is deliberate, never repeated inside a recipe: no verify recipe
|
|
54
|
+
may make a network call, and a ratio that moved between runs would make one tree red on one run and green
|
|
55
|
+
on the next. The keys are argued in [`../../spec/slots.md`](../../spec/slots.md).
|
|
56
|
+
|
|
57
|
+
**On-demand provider count.** `portulan doctor --count-tokens MODEL_ID .portulan` sends the measured
|
|
58
|
+
Claude Code project always corpus to Anthropic using `ANTHROPIC_API_KEY` from the environment. Replace `MODEL_ID`
|
|
59
|
+
with a supported model; none is defaulted. The direct diagnostic also accepts
|
|
60
|
+
`node cli/context.mjs --workspace .portulan --count-tokens MODEL_ID`. Both send whole instructions,
|
|
61
|
+
imports and unscoped rules, but only extracted project skill, command and agent descriptions;
|
|
62
|
+
later-tier bodies and the measurement's personal/global/system/tool/plugin exclusions stay excluded.
|
|
63
|
+
Incomplete imports or invalid UTF-8 refuse before transport. Doctor validates the workspace and both
|
|
64
|
+
supported project-format budgets first; the request contains only the Claude Code corpus. Boot's
|
|
65
|
+
`--brief` line and the standalone context diagnostic also use that Claude Code domain. Count mode
|
|
66
|
+
summarizes validation failures without source values, and a separate `doctor --static` run
|
|
67
|
+
provides their details. The direct diagnostic validates its kind/tree/context domain, not the full schema.
|
|
68
|
+
|
|
69
|
+
The result is a **provider estimate**, including API framing and possibly system-added tokens;
|
|
70
|
+
actual host usage can differ. It reports provider/model, source bytes and an approximate effective
|
|
71
|
+
ratio, changes no manifest and does not override offline budget judgments. Assess the ratio manually
|
|
72
|
+
before declaring it for a compile host; `calibrated_by` remains that host's ID, never a provider/model
|
|
73
|
+
identifier. A ratio below the schema's minimum is reported as not declarable. An empty corpus makes
|
|
74
|
+
no request and supplies no ratio. Marius accepted “Provider count with stated limit” on 2026-11-09 for
|
|
75
|
+
[#506](https://github.com/sleepy-panda-srl/portulan/issues/506), qualifying proposal `0036`'s original
|
|
76
|
+
exact-host wording. The [provider guide](https://platform.claude.com/docs/en/build-with-claude/token-counting)
|
|
77
|
+
and [API contract](https://platform.claude.com/docs/en/api/messages/count_tokens) were checked that day.
|
|
78
|
+
This demonstrates a counting contract, not Claude Code's actual complete prompt.
|
|
79
|
+
|
|
80
|
+
Counting is unavailable with `--static` or in CI. The existing telemetry audit rejects literal
|
|
81
|
+
count-mode invocations in yielded recipe commands and their shell scripts; injected transports test
|
|
82
|
+
the contract offline. This is textual inspection of known modes, not arbitrary-shell prevention.
|
|
56
83
|
|
|
57
84
|
**A report by default, a rail by declaration.** A workspace is failed only against a budget it declared.
|
|
58
85
|
|
|
@@ -84,7 +111,7 @@ Prefer moving content to a later tier over deleting it, keep one source per fact
|
|
|
84
111
|
after. **A demotion states both figures.**
|
|
85
112
|
|
|
86
113
|
**What this does not ask for**: a defaulted workspace rail, lossy compression, a codebase vector index,
|
|
87
|
-
|
|
114
|
+
output-shortening tricks, or any change to a host. Portulan
|
|
88
115
|
compiles to what hosts load and does not change how they load it.
|
|
89
116
|
|
|
90
117
|
## Every request pays for what the session has read
|
|
@@ -148,14 +175,51 @@ each section's anchor and size, the same way. It prints from the file as it is,
|
|
|
148
175
|
go stale. Nothing checks that a session outlined first; what the rule saves is measured by rerunning a
|
|
149
176
|
task, never by a recipe, which may not read the host's usage records.
|
|
150
177
|
|
|
178
|
+
## What a handoff hands on
|
|
179
|
+
|
|
180
|
+
A handoff carries open state, decisions with their why, questions, the next action and what remains
|
|
181
|
+
unsafe. It links a fact's owning record instead of repeating the diff, measured figures or doctrine.
|
|
182
|
+
Its heading names the outcome; its filename already carries the date. Keep decisions and their why
|
|
183
|
+
when compressing: moving them and linking preserves them, dropping them does not.
|
|
184
|
+
|
|
185
|
+
A workspace may declare a per-handoff token cap and a cutoff at its explicit ratio. Only records dated
|
|
186
|
+
after that cutoff are bound; merged history is not rewritten. Compression or moving a fact to its
|
|
187
|
+
owning record repairs a breach, never splitting, retirement, a higher cap or a later cutoff in that
|
|
188
|
+
change. A correction counts toward the cap; if it would breach a merged record, put it in the correcting
|
|
189
|
+
session's handoff and name the corrected one. Review prevents backdating or moving a cutoff backward,
|
|
190
|
+
and advances it only with a tighter cap or lower ratio to exempt already merged history. The cutoff
|
|
191
|
+
day remains unbound because a date cannot separate morning merges from evening work.
|
|
192
|
+
|
|
193
|
+
The generated index may have its own token budget: a complete header and omission notice plus the
|
|
194
|
+
newest whole entry lines that fit. Omitted handoffs stay in the dated series, with no second index.
|
|
195
|
+
Both are on-read; neither belongs in the boot or always tier. The session-end gate checks existence
|
|
196
|
+
and a date, never length; the index recipe owns the upper cap. There is no aggregate series budget
|
|
197
|
+
and no minimum length. These are accepted proposal 0037's rules, implemented in Workspace Definition
|
|
198
|
+
2.15; the schema contract and history-dependent limits are in [`spec/slots.md`](../../spec/slots.md).
|
|
199
|
+
|
|
151
200
|
## What is machinery today, and what is not
|
|
152
201
|
|
|
153
202
|
As of Workspace Definition 2.9 a workspace can declare the budget and the ratio, and both are checked
|
|
154
203
|
for shape, by the schema and by `doctor`; the manifest key refuses a budget with no ratio to count it by.
|
|
155
|
-
**The always tier is measured and reported
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
204
|
+
**The always tier is measured and reported per project format.**
|
|
205
|
+
[`cli/context.mjs`](../../cli/context.mjs) measures Claude Code's project files and the root vendored
|
|
206
|
+
`AGENTS.md` in a workspace's `tree`. Doctor reports both without host-selection configuration. The
|
|
207
|
+
single declared cap is judged independently for each, never against their sum. A shared ratio is called
|
|
208
|
+
calibrated only where its declared calibration ID matches the reported format ID; a different or unknown
|
|
209
|
+
ID is explicitly extrapolated. The AGENTS measurement includes its
|
|
210
|
+
inline guidance and pointer text; pointer targets, at-sign expansion, personal, above-tree, nested
|
|
211
|
+
files and plugins are outside that domain. An absent optional root file is zero; a present root source
|
|
212
|
+
that is unreadable, nonregular, dangling or invalid UTF-8 is unmeasured, and an escaping AGENTS root
|
|
213
|
+
is unmeasured too. Without a budget that is a report; with one it cannot judge the declaration and
|
|
214
|
+
fails. Known excluded Claude imports and its ignored missing/depth-limited imports stay excluded.
|
|
215
|
+
Each host receives its verdict even when the other cannot be measured. These are project-file
|
|
216
|
+
estimates, not whole-host prompts. The boot closes with the Claude Code line. Split offers and priced
|
|
217
|
+
demotion levers describe that domain; the form report also recognizes a vendored AGENTS boot card and
|
|
218
|
+
requires its budget verdict to be resolved before applying an offered migration. Form reads check
|
|
219
|
+
canonical containment and regular-file status before opening project sources; its boot-unit source is
|
|
220
|
+
bounded to the workspace. Instruction-state advice checks ancestors too, while preserving valid local
|
|
221
|
+
links and supplied prospective readers. This repository's
|
|
222
|
+
`context` recipe holds the boot, description and owned prompt rails described below. **A boot can
|
|
159
223
|
read a card instead of the slots**: the `boot` unit of `slots.context` (Workspace Definition 2.10) is
|
|
160
224
|
compiled into every context, imports whole the files it needs, writes out the lead sentences of others,
|
|
161
225
|
and names the file behind each line, so the rest loads when a rule's path or the card sends a session to
|
|
@@ -169,15 +233,334 @@ a section is never moved for its size alone. Claude Code 2.1.281 drops a comment
|
|
|
169
233
|
before it loads an instruction file or rule (read in its program text on 2026-09-24), so the mark and the
|
|
170
234
|
marker left behind cost nothing. This repository's own card holds its boot under the 7,500-token line of
|
|
171
235
|
2026-09-23, railed at today's figure plus 2% by the same recipe. A pointer's repository is not measured,
|
|
172
|
-
because a pointer declares no tree. **
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
and under
|
|
176
|
-
|
|
177
|
-
|
|
236
|
+
because a pointer declares no tree. **A persona can declare what its context costs**: `model`, `effort`,
|
|
237
|
+
`omitClaudeMd` and `cacheLifetime`, refused when malformed and never defaulted, which `compile` writes into
|
|
238
|
+
the hand-written binding of a persona a composed pack ships, and the core personas when compiling this
|
|
239
|
+
engine's own declared tree, and byte-compares under `--check`, printing
|
|
240
|
+
each field a host cannot express, with the persona and the host, as its degradation report for personas.
|
|
241
|
+
The compiler and doctor select `agents/` in a plugin tree and `.claude/agents/` in an ordinary
|
|
242
|
+
project, using the same bounded marker classification. Existing bindings compile even without a gate
|
|
243
|
+
policy or guidance; missing adopter bindings remain unbound. Nonregular and unreadable inputs are
|
|
244
|
+
not treated as absence. The two composed product roles have real plugin bindings. The committed
|
|
245
|
+
four-tier fixture includes an on-invoke skill. An on-path unit can select a literal nested
|
|
246
|
+
`CLAUDE.md` instead of scoped rules. Its mark and portable ownership registry distinguish repairable
|
|
247
|
+
drift from human replacement and allow owned target retirement. The body refuses imports that could
|
|
248
|
+
change meaning at its destination. From a project-root Claude session, the nested file is loaded on
|
|
249
|
+
subtree access; starting within that subtree can load it at startup. An unimported nested file is
|
|
250
|
+
outside project-root budget accounting, while an explicit root import is included. The unit contract
|
|
251
|
+
and limits are in [the slot specification](../../spec/slots.md#slotscontext--the-guidance-a-host-loads-each-unit-in-its-tier).
|
|
252
|
+
The compile recipe checks the committed exemplars and their companion rules
|
|
253
|
+
without writing. These are file/discovery contracts, not evidence of host enablement, field honoring
|
|
254
|
+
or runtime savings. The acquired [native-child comparisons](../../evals/ab/personas/README.md)
|
|
255
|
+
observe the three named core roles, matching inherited parent/child models and
|
|
256
|
+
effective high/medium child effort. They do not isolate every compiled field or
|
|
257
|
+
host precedence rule. The core declarations choose
|
|
258
|
+
`model: inherit`, `omitClaudeMd: false` and `cacheLifetime: 5m`, subject to the host precedence described in
|
|
259
|
+
[`../personas/README.md`](../personas/README.md). **The librarian reports project always-tier headroom**
|
|
260
|
+
against a declared budget and puts conditional demotion drafts in its standing report
|
|
261
|
+
([#508](https://github.com/sleepy-panda-srl/portulan/issues/508)). It drafts only loaded,
|
|
262
|
+
matching, compiler-owned pure always units from the workspace, excluding boot
|
|
263
|
+
cards, at-sign text, imported sources, expansions, links and stale targets.
|
|
264
|
+
Compiler ownership needing regeneration is repaired before drafts are offered.
|
|
265
|
+
An ownership plan covering another tree or workspace directory is refused.
|
|
266
|
+
The human decides whether guidance can wait,
|
|
267
|
+
supplies its description, and recompiles and measures before/after when applying
|
|
268
|
+
the demotion. The pass changes no guidance, and its measurement keeps the same
|
|
269
|
+
Claude Code project domain and exclusions as `context`; other hosts and full-host
|
|
270
|
+
context remain outside it. **The tier matrix reports which tiers each host expresses.** The Stop-gate
|
|
271
|
+
now captures recipe failures while the command runs, so later success noise cannot displace an earlier
|
|
272
|
+
failure from the runner's tail. It keeps stdout/stderr line state separate, omits recognized passing,
|
|
273
|
+
skipped and expected-TODO reporter records, and relays recognized failures with
|
|
274
|
+
up to eight following indented detail lines per marker. An unrecognized diagnostic is explicitly
|
|
275
|
+
labeled as fallback; a nonzero exit with only passing lines says no failure diagnostic was identified.
|
|
276
|
+
The excerpt, executor diagnostics and clipping notices fit 4,096 bytes; the whole recipe problem fits
|
|
277
|
+
6,144 bytes, including bounded identity and error text. Full output requires an explicit verification
|
|
278
|
+
run. These are diagnostic excerpts, not a proof that arbitrary output was classified correctly.
|
|
279
|
+
The repository's `context` recipe also rails registered skill bodies (manual-only included), core and
|
|
280
|
+
composed persona charters and actual plugin bindings, deterministic native hook message profiles, and
|
|
281
|
+
vendored AGENTS.md profiles. The byte limits, inventories, fixed-input accounting and finite-profile
|
|
282
|
+
limits live in [the verify README](../../.portulan/verify/README.md#owned-prompt-profiles).
|
|
283
|
+
These checks cover repository growth, not arbitrary runtime payload size or whole-host context.
|
|
284
|
+
Row 12 of [`../../docs/plan.md`](../../docs/plan.md) remains in progress; remaining obligations are held
|
|
285
|
+
by the human gate rather than described as enforcement.
|
|
178
286
|
|
|
179
287
|
_(Provenance: proposal `0036`'s sealed incident, in which an adopting repository's always-loaded
|
|
180
288
|
instruction files measured 130k tokens per fresh context, cache writes were most of the spend, and every
|
|
181
289
|
subagent and checkpoint paid it again, because nothing measured, budgeted or demoted what the host
|
|
182
290
|
loads. Host facts from the Claude Code documentation on costs, prompt caching and memory, checked
|
|
183
291
|
2026-09-21.)_
|
|
292
|
+
|
|
293
|
+
## What a change spends
|
|
294
|
+
|
|
295
|
+
This is proposal [`0038`](../../.portulan/proposals/0038-what-a-change-spends-is-measured.md)'s second
|
|
296
|
+
part: the cost of re-reading and rebuilding what the first part loads. The rules below govern spend;
|
|
297
|
+
[`sessions.md`](sessions.md) applies them to session startup, cache lifetimes and host switches.
|
|
298
|
+
|
|
299
|
+
### Price a change from usage records
|
|
300
|
+
|
|
301
|
+
**A change is priced from the host's own usage records, numbers only**, attributed to its branch and
|
|
302
|
+
then its pull request: requests, tokens by class (uncached, written by lifetime, read and output), fresh
|
|
303
|
+
contexts opened, largest context, hit rate, cache rebuilds and their causes, and tokens per changed line.
|
|
304
|
+
Per-block duplicates count once. Where the host keeps totals of its own, the measurement states its
|
|
305
|
+
difference from them; unavailable records are not evidence of zero spend.
|
|
306
|
+
|
|
307
|
+
Token accounting uses usage fields, and local-worker accounting uses recognized receipt lines in
|
|
308
|
+
recorded Bash results; conversation text is not a pricing signal. The receipt-format limit and the
|
|
309
|
+
separate local figures belong to [the local-model rules](#a-local-model-runs-without-the-host-floor).
|
|
310
|
+
[`cli/ledger.mjs`](../../cli/ledger.mjs) reads locally, without a network call. A verify recipe never
|
|
311
|
+
opens host usage records: `doctor --static` validates without them, and ledger fixtures supply synthetic
|
|
312
|
+
records for verification.
|
|
313
|
+
|
|
314
|
+
Ordinary `doctor` reports the current branch and the latest five timed owned
|
|
315
|
+
parent sessions across branches and worktrees. A parent's latest valid main or
|
|
316
|
+
child request timestamp ranks it; session-ID order breaks ties. All owned requests
|
|
317
|
+
in a selected session contribute, including undated requests. Wholly undated
|
|
318
|
+
sessions remain in branch totals and are excluded from recency. Branch absence
|
|
319
|
+
does not hide recent-session evidence from other branches.
|
|
320
|
+
|
|
321
|
+
For valid complete observations, raw class totals remain exact integers. Missing
|
|
322
|
+
or invalid counters retain closed validity metadata through per-block deduplication;
|
|
323
|
+
affected totals are partial and class analysis remains unassessed. Missing fields
|
|
324
|
+
can be completed by another block; a provided invalid counter remains unassessed.
|
|
325
|
+
Cache-read and total cache-write counts are observations too: absence is unknown,
|
|
326
|
+
while an explicit zero is known. Without an aggregate, both explicitly observed
|
|
327
|
+
lifetime buckets may supply the write sum; one bucket alone may not. Duplicate
|
|
328
|
+
bucket maxima are combined before deriving that sum; a derived sum never becomes
|
|
329
|
+
an observed aggregate ceiling. This follows the [provider's write-total definition](https://platform.claude.com/docs/en/build-with-claude/prompt-caching),
|
|
330
|
+
checked 2026-11-11. Explicit partial live input remains unpriced; null or absent
|
|
331
|
+
live usage retains the transcript fallback. Output uncertainty remains separate
|
|
332
|
+
from valid input-only restart arithmetic.
|
|
333
|
+
Beside valid totals, a normalized class index
|
|
334
|
+
uses uncached input at 1×, declared or general read/write multipliers and output
|
|
335
|
+
at an explicitly assumed 5×. It assumes uniform input rates across models and is
|
|
336
|
+
an estimate in input-equivalents, not actual monetary dominance or a bill. The
|
|
337
|
+
output assumption follows proposal 0038 and the [Claude pricing table](https://platform.claude.com/docs/en/about-claude/pricing),
|
|
338
|
+
checked 2026-11-11. Writes with unstated lifetime span the minimum and maximum of
|
|
339
|
+
both write rates; differing endpoint leaders make dominance uncertain. Ties,
|
|
340
|
+
partial records and unsafe arithmetic are named.
|
|
341
|
+
|
|
342
|
+
| Dominant index class | Lever to review |
|
|
343
|
+
|---|---|
|
|
344
|
+
| Cache reads | Session length and tool output |
|
|
345
|
+
| Cache writes | Fresh contexts, TTL gaps and cache busts, with inferred rebuild causes |
|
|
346
|
+
| Output | Effort and model by recorded role, with A/B evidence before changing a route |
|
|
347
|
+
| Uncached input | Cache engagement, eligibility and uncached tails |
|
|
348
|
+
|
|
349
|
+
Role labels come only from recognized, optionally namespaced `attributionAgent`
|
|
350
|
+
metadata. Unknown roles, effort and model families remain unknown; UUIDs and
|
|
351
|
+
conversation text do not identify roles. Model/effort alternatives remain
|
|
352
|
+
unpriced without quality evidence. The existing biggest-lever comparison covers
|
|
353
|
+
quantified demotion/restart candidates only. With explicit horizon and resume
|
|
354
|
+
expense, the report compares continuation reads with fresh writes, resumed reads
|
|
355
|
+
and recovery expense. A later main request outside the branch's owned scope makes
|
|
356
|
+
that session's restart comparison unavailable. These are fixed-context
|
|
357
|
+
counterfactuals from caller-supplied estimates, not forecasts.
|
|
358
|
+
Invalid or missing input counters also suppress advisory comparisons, status and
|
|
359
|
+
Stop holds. Invalid fresh input stays unpriced until compaction; missing fields
|
|
360
|
+
can be completed by duplicate observations. Unusable latest input waits for a
|
|
361
|
+
valid next request or completed missing fields. Validity markers and numeric
|
|
362
|
+
input vectors persist in the incremental cache. Changed or contradictory tracked
|
|
363
|
+
observations replay deduplicated records through the last consumed newline,
|
|
364
|
+
including a retained historical crossing; the normal valid append path remains
|
|
365
|
+
incremental. Aggregate input sums are checked after deduplication and in live
|
|
366
|
+
status data. Warm calls cannot turn unknown inputs into known zeros. Recorded
|
|
367
|
+
output uncertainty does not invalidate otherwise usable input-only restart math.
|
|
368
|
+
|
|
369
|
+
### Restart at a computed threshold
|
|
370
|
+
|
|
371
|
+
**A restart comparison states its assumptions before advising a handoff.** Its
|
|
372
|
+
break-even context is `C* = F + (F × m_w + R) / (n × m_r)`: fresh context `F`,
|
|
373
|
+
write/read multipliers `m_w` and `m_r`, requests remaining `n`, and additional
|
|
374
|
+
resume expense `R` in uncached-input-token equivalents. The workspace declares
|
|
375
|
+
these in `spend`; undeclared multipliers and horizon use the stated general
|
|
376
|
+
figures. The horizon does not decrement automatically.
|
|
377
|
+
|
|
378
|
+
`spend.resume.tokens` includes handoff output, boot/state recovery and additional
|
|
379
|
+
rereads over that horizon, excluding the formula's existing fresh-context write
|
|
380
|
+
and baseline reads. These are weighted cost units: 2,000 output tokens at 5× cost
|
|
381
|
+
contribute 10,000 equivalents. The estimate is supplied, not predicted. An absent
|
|
382
|
+
estimate is unknown, distinct from an explicit zero. Its threshold is then only
|
|
383
|
+
a lower bound, and the report says to finish bounded work when that costs less.
|
|
384
|
+
|
|
385
|
+
[`cli/advisory.mjs`](../../cli/advisory.mjs) speaks once with a tool result or at
|
|
386
|
+
a prompt, again after a compaction, and shows the same assumptions on the status
|
|
387
|
+
line. A declared `restart: "block"` holds once only with an explicit horizon and
|
|
388
|
+
resume expense and finite, strictly positive estimated savings
|
|
389
|
+
`n × m_r × (C − F) − m_w × F − R`. Equality never holds. Existing block declarations
|
|
390
|
+
without both estimates become conditional reports without a hold. Nothing ends
|
|
391
|
+
the session automatically. [`spec/slots.md`](../../spec/slots.md#spend--the-figures-the-restart-threshold-is-computed-at)
|
|
392
|
+
gives the declaration contract. This refinement follows the bounded-run evidence
|
|
393
|
+
in [#503](https://github.com/sleepy-panda-srl/portulan/issues/503); proposal 0038 is
|
|
394
|
+
preserved as its dated decision record.
|
|
395
|
+
|
|
396
|
+
A delayed transcript batch retains its first usable crossing in the current
|
|
397
|
+
compaction generation, including that request's context, fresh context and price
|
|
398
|
+
assumptions. If later counts fall below threshold, the line identifies that
|
|
399
|
+
historical crossing and says the latest estimate does not favor restarting.
|
|
400
|
+
Status remains current; Stop uses only the latest comparison. Changed declared
|
|
401
|
+
prices, horizon or resume expense replay the transcript before selecting a
|
|
402
|
+
crossing under those assumptions. A controlled Claude Code trace and delayed
|
|
403
|
+
batch checks live in [`cli/fixtures/advisory-crossing/`](../../cli/fixtures/advisory-crossing/);
|
|
404
|
+
they do not promise a universal host transcript latency bound.
|
|
405
|
+
|
|
406
|
+
Message IDs retain their first-seen order and parent/child ownership across the
|
|
407
|
+
transcript. Repeated observations merge counters and validity into that request;
|
|
408
|
+
only a new distinct parent request consumes a pending parent compaction. The
|
|
409
|
+
public ledger figures and hook replay use the canonical request fold. Hooks keep
|
|
410
|
+
16 hot identities and a fixed membership bitmap. Possible older IDs, including
|
|
411
|
+
bitmap collisions, trigger exact completed-prefix replay before a decision.
|
|
412
|
+
Saturation can increase replay work and transient memory with transcript size;
|
|
413
|
+
the bitmap bounds retained identity metadata, not total replay work.
|
|
414
|
+
|
|
415
|
+
Full compilation emits Claude prompt/tool restart advice and a status line for every authored
|
|
416
|
+
repository, demo or portfolio workspace, independently of a gate policy. Declared session switches
|
|
417
|
+
and ritual caps compose into that settings artifact; only explicit `spend.restart: "block"` adds
|
|
418
|
+
advisory Stop. Permissions and verification Stop still require a gate policy. Other adapters do not
|
|
419
|
+
compile this advice. A missing manifest or pointer does not adopt native settings.
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
### Keep the prefix stable
|
|
423
|
+
|
|
424
|
+
`doctor` reports potential Claude Code cache risks from the declared tree's
|
|
425
|
+
`.claude/settings.json`, `.claude/settings.local.json` and `.mcp.json`. It checks
|
|
426
|
+
`opusplan` aliases and conditional Haiku plan upgrades, explicit project
|
|
427
|
+
`ENABLE_TOOL_SEARCH=false`, and `ToolSearch` permission denial. Local scalar/env
|
|
428
|
+
values override shared values; rejection and denial lists accumulate. MCP counts
|
|
429
|
+
distinguish eligible configured candidates from project-file approvals. Neither count
|
|
430
|
+
establishes trust, live activation or connectivity. User, managed, CLI and broader
|
|
431
|
+
model/MCP policies, the full MCP schema, experimental-beta disabling and other
|
|
432
|
+
selectors remain unassessed. Every assessment names its selectors and these
|
|
433
|
+
limits, including when no recognized risk is found. Unreadable or invalid project sources produce sanitized notes rather
|
|
434
|
+
than a cache-safe assurance. These reports change no validation verdict or settings.
|
|
435
|
+
|
|
436
|
+
The inspected behavior follows Claude Code's [model configuration](https://code.claude.com/docs/en/model-config#opusplan-model-setting),
|
|
437
|
+
[tool search](https://code.claude.com/docs/en/mcp#configure-tool-search) and
|
|
438
|
+
[project MCP rejection](https://code.claude.com/docs/en/settings-reference#disabledmcpjsonservers)
|
|
439
|
+
references, checked 2026-11-11. A detected setting is a potential risk, not a measured
|
|
440
|
+
cache miss or bill.
|
|
441
|
+
|
|
442
|
+
**Model and effort are chosen when a context is created**, and tool definitions are deferred wherever
|
|
443
|
+
the host can defer them. Content that varies per session — a git snapshot, a date, a fresh working
|
|
444
|
+
directory, a session ID — stays out of Portulan's own always tier and hook-injected text. It may leave
|
|
445
|
+
one session's cache intact while forfeiting the prefix that sessions in one directory share.
|
|
446
|
+
|
|
447
|
+
Portulan's repository makes changes to these static carriers reviewable through
|
|
448
|
+
its `cache-stability` recipe: loaded files, hook sources and commands, and gate
|
|
449
|
+
explanation inputs are compared with a committed full-content golden. Its lexical
|
|
450
|
+
inventory identifies suspicious literal forms; unchanged digests establish only
|
|
451
|
+
unchanged repository text. Runtime session values, arbitrary helper outputs and
|
|
452
|
+
the host's prefix remain outside that capture. The repository's
|
|
453
|
+
[recipe instructions](../../.portulan/verify/README.md#the-recipes) name the explicit
|
|
454
|
+
refresh command and the forced-red drill. This is its own rail, not an adopter's
|
|
455
|
+
automatically installed policy.
|
|
456
|
+
|
|
457
|
+
A hosted session whose system prompt names the session itself starts cold whatever a workspace compiles;
|
|
458
|
+
that is inferred from such a prompt, not documented. The [session-start rules](sessions.md#a-session-starts-warm-or-it-pays-the-prefix-again)
|
|
459
|
+
describe the host's warm-start conditions; a workspace cannot repair the host's prefix by compiling one
|
|
460
|
+
of its own.
|
|
461
|
+
|
|
462
|
+
### Routing is declared and earned
|
|
463
|
+
|
|
464
|
+
**The spawner states effort and how many contexts it may open, alongside what it loads**, extending
|
|
465
|
+
[the fresh-context declaration](#a-fresh-context-is-priced-where-it-is-created). Ritual packs can declare
|
|
466
|
+
`contributes.contexts.max_concurrent_subagents` under Pack Definition 1.1. Counts
|
|
467
|
+
compose by minimum and compile to Claude's project concurrency env setting even
|
|
468
|
+
without a gate policy. This controls ordinary fresh native Agent admission per
|
|
469
|
+
session, excluding the parent; resumes/forks, independent agent systems, host
|
|
470
|
+
overrides and experimental paths qualify it. It is no lifetime context, request
|
|
471
|
+
or token budget. Other adapters report it as not compiled, and the ledger counts
|
|
472
|
+
actual contexts either way. See [the pack contract](../../packs/rituals/checkpoints/README.md#context-admission)
|
|
473
|
+
and [the dated native trace](../../cli/fixtures/ritual-cap/README.md).
|
|
474
|
+
|
|
475
|
+
**A cheaper model or a lower effort for a role is earned by an A/B run before it is shipped.** A host
|
|
476
|
+
switch that changes the prefix meets the same evidence gate. The comparison must show reduced spend
|
|
477
|
+
without lost quality, and the shipped routing must be the routing its record supports. A cheaper
|
|
478
|
+
context that cannot do its role costs rework; a reviewer keeps the conventions it grades against.
|
|
479
|
+
Session switches retain their [specific guards](sessions.md#a-switch-that-changes-the-prefix-is-earned),
|
|
480
|
+
and local routing retains its screen-versus-comparison distinction below.
|
|
481
|
+
The three [core-persona records](../../evals/ab/personas/README.md) compare shipped
|
|
482
|
+
inheritance with a native medium-effort override on one concrete model. Both arms
|
|
483
|
+
pass the nine frozen cases; the shipped policy retains inheritance and undeclared
|
|
484
|
+
effort. Other parent models remain unmeasured. Plugin updates carry the baked
|
|
485
|
+
bindings; `upgrade` refreshes existing eligible bindings through the compiler's
|
|
486
|
+
ownership planner without creating foreign core adapters or changing settings.
|
|
487
|
+
|
|
488
|
+
### Budget what returns to the model
|
|
489
|
+
|
|
490
|
+
**What a tool, recipe or hook hands back to the model is budgeted: failures in full, passes as a count,
|
|
491
|
+
and nothing echoed on every turn.** This is [the loop's compact-error rule](loop.md#context-is-the-budget)
|
|
492
|
+
applied to every output the model reads, with failure evidence retained so the saving cannot lower
|
|
493
|
+
quality. The reading rules above govern how a session asks for bounded output.
|
|
494
|
+
|
|
495
|
+
`doctor --agent` returns one aggregate count line on green. `recipe-set --agent`
|
|
496
|
+
wraps every yielded workspace and pack command in a shared executor: one counted
|
|
497
|
+
green line, complete stdout and stderr on failure. Direct commands retain human
|
|
498
|
+
output. Required tests and the existing CI job exercise that interface. Prefix
|
|
499
|
+
stability is a separate claim: a text baseline cannot establish runtime cache
|
|
500
|
+
behavior. The remaining demonstrations stay in [row 13](../../docs/plan.md#milestones).
|
|
501
|
+
|
|
502
|
+
### A local model runs without the host floor
|
|
503
|
+
|
|
504
|
+
A model served on the machine itself costs nothing per token, so `0038`'s dollar arithmetic gives it no
|
|
505
|
+
restart threshold. What it does cost is time to prefill, memory for each context it holds, and recall that
|
|
506
|
+
still sags in a long context. And every turn it takes is a turn the parent session does not send again on
|
|
507
|
+
each request after. So a local model is called as a context firewall, and four rules hold it:
|
|
508
|
+
|
|
509
|
+
1. **A local worker runs without the host floor (`omitClaudeMd`) and under a hard input-byte ceiling.** It
|
|
510
|
+
is given one bounded input: a symbol span from [`cli/symbols.mjs`](../../cli/symbols.mjs), a file under
|
|
511
|
+
300 lines, a diff, or a log to compress, and never the parent session's instruction files. A prompt over
|
|
512
|
+
the ceiling is refused before a socket opens. _Why: a host floor of 44k to 85k tokens is prefilled into
|
|
513
|
+
a local model's cache on every cold context, where `0039` measured prompts of about 400 to 470 tokens
|
|
514
|
+
prefilling in 0.85 to 1.88 seconds._
|
|
515
|
+
2. **Thinking effort is set explicitly at invocation**: low or off for a mechanical transform, bounded for
|
|
516
|
+
prose or review, never left to the chat template's default. _Why: one template `0039` read sets
|
|
517
|
+
reasoning effort to `xhigh` unless thinking is switched off, and under a 1,500-token cap that model
|
|
518
|
+
returned no answer on any of three tasks, every token spent thinking; at low effort it passed both
|
|
519
|
+
tasks it was rerun on._
|
|
520
|
+
3. **A local edit is kept only on its verify command's exit `0`.** The runner applies the edit and runs the
|
|
521
|
+
command. On exit `0` one receipt line is all that enters the parent context (`0038` rule 5). On exit `1`
|
|
522
|
+
after its retries, or at once on any other exit, which is no verdict, it restores the file's original
|
|
523
|
+
bytes, and it does the same on SIGINT, SIGTERM and SIGHUP. **This is a rollback, not an atomic write**:
|
|
524
|
+
the verify needs the edited file in place, so any other signal that ends the runner, SIGKILL and SIGQUIT
|
|
525
|
+
among them, or a power loss between the edit and its verdict leaves the edit, and a transform with no
|
|
526
|
+
receipt is unverified. _Why not atomic: a scratch worktree would judge the last commit plus the edit
|
|
527
|
+
rather than the working copy plus the edit, would lack the untracked files a verify reads, and moving
|
|
528
|
+
the edit back after exit `0` would not be atomic either (`0039` ruling 4)._
|
|
529
|
+
4. **Loopback is a rail, and routing follows the [declared-and-earned gate](#routing-is-declared-and-earned).**
|
|
530
|
+
A local runner refuses an endpoint outside loopback
|
|
531
|
+
(`127.0.0.1`, `localhost`, `[::1]`) unless leaving it is proposed and approved. A screen grades an
|
|
532
|
+
installed model once per task against Portulan's own predicates, from
|
|
533
|
+
[`cli/comments.mjs`](../../cli/comments.mjs), [`cli/doctor.mjs`](../../cli/doctor.mjs) and
|
|
534
|
+
[`cli/skill-goldens.mjs`](../../cli/skill-goldens.mjs), and a model that fails it is not routed. Passing
|
|
535
|
+
it earns nothing. For the local comparison, use the local model as routed against the host's model on the
|
|
536
|
+
same tasks, `k` runs per cell, captured once and railed to its register in `evals/ab/`. _Why: an endpoint off the machine sends workspace context away under a local
|
|
537
|
+
tool's name, and the screen takes one sample per task with no baseline arm, so a screen is not routing
|
|
538
|
+
evidence (`0039` ruling 5)._
|
|
539
|
+
|
|
540
|
+
**What the runner holds today.** The [`tools/local-llm`](../../packs/tools/local-llm/README.md) pack's
|
|
541
|
+
runner holds rules 1 to 3 and rule 4's loopback rail for the calls it makes. It sends the model its own
|
|
542
|
+
short instructions and the input it was given, with the end of a red verify's output on a retry, and
|
|
543
|
+
nothing else. It refuses a prompt over its ceiling before a socket opens, names the effort on every chat
|
|
544
|
+
request, low unless set, requires a verify command for an edit and rolls back as rule 3 says, and refuses
|
|
545
|
+
a host off loopback and a redirect away from it. Which inputs it is given is the caller's: only their
|
|
546
|
+
bytes are checked. `--allow-remote` lifts the loopback rail on the caller's word, and the pack's
|
|
547
|
+
`local-llm-off-loopback` gate has no matcher, so approval to leave loopback is the human's. Its `eval`
|
|
548
|
+
subcommand is the screen. Its `runner-rails` recipe holds the loopback and ceiling refusals and the
|
|
549
|
+
rollback on a red or no-verdict verify offline, with no network call; the rollback on a signal is held by
|
|
550
|
+
the runner's tests. This repository composes the pack and registers its skill; the ledger reports local
|
|
551
|
+
receipts beside the host's tokens. `doctor` includes that branch-owned usage report by default,
|
|
552
|
+
with unavailable data reported as unmeasured; `--static` omits usage-record discovery for verification.
|
|
553
|
+
Receipts are recognized by their whole-line format in recorded Bash results: a matching echo can be
|
|
554
|
+
counted, and output omitted from the transcript is missed.
|
|
555
|
+
The [captured A/B](../../evals/ab/local-worker.md) supports code
|
|
556
|
+
at low effort (L5/H5), while shared prose is disabled because codify scored L0/H3, despite handoff's
|
|
557
|
+
L5/H5. Operational defaults and explicit roles require the measured model at low effort; explicit
|
|
558
|
+
model-only calls remain candidates. The record and register are regraded and byte-compared in CI,
|
|
559
|
+
with shipped routing rederived from their turns. The `local-worker` persona declares
|
|
560
|
+
`omitClaudeMd: true` and `effort: low`, and `compile` writes both into a workspace's binding against a
|
|
561
|
+
committed golden.
|
|
562
|
+
|
|
563
|
+
_(Provenance: proposal
|
|
564
|
+
[`0039`](../../.portulan/proposals/0039-a-local-model-runs-without-the-host-floor-and-under-a-thinking-cap.md)'s
|
|
565
|
+
measurement of two local models against three of this repository's tasks on 2026-10-27, and its rulings 4
|
|
566
|
+
and 5 of 2026-10-30.)_
|
|
@@ -68,8 +68,9 @@ moments**: the plan before building starts, the diff before it goes outward, and
|
|
|
68
68
|
the close — where done is *demonstrated, not asserted* (`verification.md`). Two conditions hold the
|
|
69
69
|
cycle up and neither is optional. The unit of work carries **hard exit criteria** fixed before it
|
|
70
70
|
begins, so the close has a standard to grade rather than an impression. And **every change writes its
|
|
71
|
-
record** (`loop.md`), its why in the commit and any open work
|
|
72
|
-
the next moment inherits the reasoning instead of re-deriving it — a graded
|
|
71
|
+
record** (`loop.md`), its why in the commit and any open work under the workspace's record policy, so
|
|
72
|
+
the context arriving at the next moment inherits the reasoning instead of re-deriving it — a graded
|
|
73
|
+
moment whose grader must first reconstruct what was decided is
|
|
73
74
|
grading its own reconstruction.
|
|
74
75
|
|
|
75
76
|
The altitudes stay separate, and the separation is the part that travels. Core states that the three
|
package/core/operating/loop.md
CHANGED
|
@@ -29,12 +29,17 @@ The loop runs inside a finite attention window, so context is managed, not hoard
|
|
|
29
29
|
- **A change's why goes in its commit; a handoff carries only the work still open.** A commit's subject
|
|
30
30
|
says what changed and its body says why, in a few lines: the reasoning the next agent cannot
|
|
31
31
|
reconstruct from the diff, kept beside the diff. A session (one bounded working stretch, however the
|
|
32
|
-
host names it) that
|
|
33
|
-
stand, the open questions and the single next action, and
|
|
32
|
+
host names it) that leaves work not committed and pushed writes a dated handoff when its workspace
|
|
33
|
+
declares `slots.handoffs`: where things stand, the open questions and the single next action, and
|
|
34
|
+
nothing the diff or a commit already says.
|
|
34
35
|
**Dated** means the filename leads with an ISO date, `YYYY-MM-DD-{slug}.md`, because a rule justified by
|
|
35
36
|
being checkable has to say what the checker looks at, and the series then sorts chronologically for
|
|
36
|
-
free. The session-end gate (milestone 4) checks
|
|
37
|
-
unpushed work, a handoff dated today exists, never its length or its shape.
|
|
37
|
+
free. The session-end gate (milestone 4) checks that declared series: while the tree holds uncommitted
|
|
38
|
+
or unpushed work, a handoff dated today exists, never its length or its shape. A workspace may cap new handoffs in its verify recipe (see `context.md`); no minimum
|
|
39
|
+
length applies. With no series declared,
|
|
40
|
+
it asks for the work to be committed and pushed, its why in the commit message, and never asks for a
|
|
41
|
+
handoff. The workspace owns that record policy ([#505](https://github.com/sleepy-panda-srl/portulan/issues/505));
|
|
42
|
+
the verify recipe and the gate's retry bounds still apply. The librarian (milestone 5)
|
|
38
43
|
reads the series: it indexes it, ages it from git, and mines it, since an incident nothing in the
|
|
39
44
|
curated layer points back at is a candidate for the `codify` skill.
|
|
40
45
|
_(Provenance: Cognition — handoffs record decisions and their rationale; Git's own SubmittingPatches —
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
> On Claude Code that prefix comes before any workspace file, and cache writes were most of what a task
|
|
6
6
|
> cost on this repository ([`../../evals/ab/warm.md`](../../evals/ab/warm.md) has the figures). So a task
|
|
7
7
|
> runs in as few fresh contexts as it can, each starts warm where it can, the cache lifetime fits the gap
|
|
8
|
-
> between uses
|
|
9
|
-
>
|
|
10
|
-
> around it. _(Proposal [`0038`](../../.portulan/proposals/0038-what-a-change-spends-is-measured.md), rules
|
|
8
|
+
> between uses. [The spend rules](context.md#what-a-change-spends) govern pricing, restart, prefix stability,
|
|
9
|
+
> routing and output budgets; this page applies them to session startup, cache lifetimes and host switches. _(Proposal [`0038`](../../.portulan/proposals/0038-what-a-change-spends-is-measured.md), rules
|
|
11
10
|
> 2 to 4, and row 12 of [`../../docs/plan.md`](../../docs/plan.md), whose compile targets carry the cache
|
|
12
11
|
> lifetime.)_
|
|
13
12
|
|
|
@@ -20,18 +19,11 @@ prefix at the read price; a cold one writes it again. Sessions running side by s
|
|
|
20
19
|
already share it. Sessions that follow one another share it only while nothing between them changed it,
|
|
21
20
|
and on a local checkout the host's startup git snapshot changes with every commit.
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
working directory made fresh for each run, a hook's per-session output, a session's own id. Rule 3 of
|
|
25
|
-
`0038` keeps these out of Portulan's own always tier and hook text. A hosted session whose system prompt
|
|
26
|
-
names the session itself starts cold whatever a workspace compiles; that is inferred from such a prompt,
|
|
27
|
-
not documented.
|
|
22
|
+
The [prefix-stability rule](context.md#keep-the-prefix-stable) governs what may appear ahead of the task.
|
|
28
23
|
|
|
29
24
|
## Fewer fresh contexts per task
|
|
30
25
|
|
|
31
|
-
- **
|
|
32
|
-
the fresh context and a horizon, which a workspace declares in `spend` (Workspace Definition 2.12) and which
|
|
33
|
-
are the general ones where it does not, and [`../../cli/advisory.mjs`](../../cli/advisory.mjs) says it once.
|
|
34
|
-
A restart that finds its prefix still cached costs less than the threshold assumes.
|
|
26
|
+
- **Restart policy:** follow [the computed threshold and handoff rule](context.md#restart-at-a-computed-threshold).
|
|
35
27
|
- **Resume a finished worker within its cache lifetime** rather than start a new one: its context is
|
|
36
28
|
read again rather than written again.
|
|
37
29
|
- **A subagent is a fresh context**, on a five-minute cache unless set. Fan out when the reads it keeps
|
|
@@ -57,9 +49,8 @@ Claude Code has three switches that change what a session writes: the git instru
|
|
|
57
49
|
snapshot together with the commit and pull-request instructions), the cache lifetime, and, for a run
|
|
58
50
|
started headless, moving the per-machine sections (working directory, platform, memory paths, the
|
|
59
51
|
snapshot) out of the system prompt and into the first message, so runs in different directories or on
|
|
60
|
-
different machines share the system prompt. **Each
|
|
61
|
-
|
|
62
|
-
removes keeps it**:
|
|
52
|
+
different machines share the system prompt. **Each follows the [declared-and-earned gate](context.md#routing-is-declared-and-earned)**,
|
|
53
|
+
and **a session that needs what a switch removes keeps it**:
|
|
63
54
|
|
|
64
55
|
- **The git instructions are dropped only for sessions that neither commit nor open a pull request**, or
|
|
65
56
|
where the workspace's own guidance carries its commit and pull-request workflow. A session that needs
|