@sleepy_panda_srl/portulan 0.1.3 → 0.3.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 +7 -6
- package/cli/README.md +145 -358
- package/cli/advisory.mjs +354 -0
- package/cli/comments.mjs +932 -0
- package/cli/compile.mjs +1719 -1699
- package/cli/context.mjs +883 -0
- package/cli/control-chars.mjs +12 -301
- package/cli/discover.mjs +17 -574
- package/cli/doctor.mjs +269 -1080
- package/cli/drills.mjs +92 -504
- package/cli/feedback.mjs +16 -237
- package/cli/finish.mjs +437 -0
- package/cli/form.mjs +644 -0
- package/cli/fuzz-shell.mjs +20 -305
- package/cli/gate.mjs +7 -199
- package/cli/goldens.mjs +26 -184
- package/cli/index.mjs +305 -655
- package/cli/init.mjs +243 -566
- package/cli/inside.mjs +3 -28
- package/cli/instructions.mjs +829 -0
- package/cli/ledger.mjs +882 -0
- package/cli/librarian.mjs +135 -599
- package/cli/manifest.mjs +2 -39
- package/cli/mutants.mjs +9 -170
- package/cli/new.mjs +12 -243
- package/cli/pack-identity.mjs +5 -74
- package/cli/pack-version.mjs +13 -255
- package/cli/plugin-lint.mjs +42 -658
- package/cli/portulan.mjs +6 -125
- package/cli/recipe-set.mjs +15 -245
- package/cli/release-eval.mjs +28 -348
- package/cli/review-meter.mjs +29 -265
- package/cli/rule-carriers.mjs +12 -175
- package/cli/sessions.mjs +82 -0
- package/cli/skill-goldens.mjs +10 -176
- package/cli/skills-set.mjs +12 -414
- package/cli/stop-gate.mjs +83 -514
- package/cli/symbols.mjs +1132 -0
- package/cli/telemetry.mjs +82 -505
- package/cli/upgrade.mjs +171 -314
- package/cli/vendor.mjs +146 -476
- package/cli/version-carriers.mjs +8 -86
- package/core/README.md +1 -1
- package/core/engine.md +3 -9
- package/core/operating/autonomy.md +1 -1
- package/core/operating/context.md +258 -0
- package/core/operating/evolution.md +3 -3
- package/core/operating/loop.md +13 -20
- package/core/operating/memory.md +12 -3
- package/core/operating/sessions.md +100 -0
- package/core/personas/README.md +33 -0
- package/core/personas/librarian.md +2 -2
- package/core/skills/clarify/SKILL.md +1 -1
- package/core/skills/consolidate/SKILL.md +5 -3
- package/core/templates/handoff.md +28 -15
- package/core/templates/memory-entry.md +1 -0
- package/evals/README.md +271 -845
- package/evals/releases/0.2.0.json +146 -0
- package/evals/releases/0.2.0.md +67 -0
- package/evals/releases/0.3.0.json +150 -0
- package/evals/releases/0.3.0.md +68 -0
- package/evals/releases/README.md +3 -3
- package/package.json +5 -2
- package/packs/.claude-plugin/plugin.json +2 -1
- package/packs/README.md +7 -4
- package/packs/rituals/checkpoints/README.md +1 -1
- package/packs/tools/README.md +8 -8
- package/packs/tools/github/verify/actions-pinned.sh +10 -55
- package/packs/tools/local-llm/README.md +136 -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 +1665 -0
- package/packs/tools/local-llm/skills/delegate-local/SKILL.md +64 -0
- package/packs/tools/local-llm/verify/README.md +18 -0
- package/spec/README.md +113 -6
- package/spec/migrations/0001-repository-declares-its-tree.mjs +1 -26
- package/spec/migrations/0002-bundle-fallback-path.mjs +13 -74
- package/spec/migrations/0003-handoff-index-not-kept.mjs +66 -0
- package/spec/migrations/0004-changelog-fragments.mjs +53 -0
- package/spec/migrations/0005-session-log-retired.mjs +69 -0
- package/spec/migrations/0006-boot-card.mjs +77 -0
- package/spec/migrations/0007-guidance-compiled.mjs +31 -0
- package/spec/migrations/0008-card-reading.mjs +55 -0
- package/spec/migrations/0009-instruction-sections-on-read.mjs +60 -0
- package/spec/migrations/0010-comment-rail.mjs +57 -0
- package/spec/migrations/README.md +45 -10
- package/spec/pack.schema.json +1 -1
- package/spec/slots.md +343 -27
- package/spec/workspace.schema.json +145 -4
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Sessions
|
|
2
|
+
|
|
3
|
+
> Core doctrine — on-read. Every fresh context pays for the host's own system prompt and tools, and for
|
|
4
|
+
> the always tier, at the cache's write price, unless an earlier session left that prefix in the cache.
|
|
5
|
+
> On Claude Code that prefix comes before any workspace file, and cache writes were most of what a task
|
|
6
|
+
> cost on this repository ([`../../evals/ab/warm.md`](../../evals/ab/warm.md) has the figures). So a task
|
|
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, and a host switch that changes the prefix is earned by an A/B run.
|
|
9
|
+
> [`context.md`](context.md) governs what the host loads; this page governs how sessions start and end
|
|
10
|
+
> around it. _(Proposal [`0038`](../../.portulan/proposals/0038-what-a-change-spends-is-measured.md), rules
|
|
11
|
+
> 2 to 4, and row 12 of [`../../docs/plan.md`](../../docs/plan.md), whose compile targets carry the cache
|
|
12
|
+
> lifetime.)_
|
|
13
|
+
|
|
14
|
+
## A session starts warm, or it pays the prefix again
|
|
15
|
+
|
|
16
|
+
**A fresh session starts warm when an earlier one left the same prefix in the cache**: the same host
|
|
17
|
+
version, tools, model and effort, the same instruction files and rules, the same working directory, and
|
|
18
|
+
nothing that varies per session ahead of the task, within the cache lifetime. A warm start reads the
|
|
19
|
+
prefix at the read price; a cold one writes it again. Sessions running side by side in one checkout
|
|
20
|
+
already share it. Sessions that follow one another share it only while nothing between them changed it,
|
|
21
|
+
and on a local checkout the host's startup git snapshot changes with every commit.
|
|
22
|
+
|
|
23
|
+
What moves the break earlier is anything per session in front of the task: a git snapshot, a date, a
|
|
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.
|
|
28
|
+
|
|
29
|
+
## Fewer fresh contexts per task
|
|
30
|
+
|
|
31
|
+
- **Continue until the restart threshold, then restart.** `0038`'s threshold is computed from the multipliers,
|
|
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.
|
|
35
|
+
- **Resume a finished worker within its cache lifetime** rather than start a new one: its context is
|
|
36
|
+
read again rather than written again.
|
|
37
|
+
- **A subagent is a fresh context**, on a five-minute cache unless set. Fan out when the reads it keeps
|
|
38
|
+
out of the parent cost more than the prefix it writes ([`loop.md`](loop.md)'s firewall), not for one
|
|
39
|
+
read.
|
|
40
|
+
- A checkpoint, persona or ritual states what it loads ([`context.md`](context.md)), and the host's
|
|
41
|
+
prefix is part of that price.
|
|
42
|
+
|
|
43
|
+
## The cache lifetime fits the gap
|
|
44
|
+
|
|
45
|
+
**Five minutes where the next use of the prefix comes within five minutes**: bursts, and headless runs that
|
|
46
|
+
follow one another. **An hour where sessions idle or the next one starts later.** A five-minute write costs
|
|
47
|
+
1.25 times an uncached input token and an hour's 2, and every read renews either; a gap past the lifetime
|
|
48
|
+
writes the whole context again. On this repository's own tasks, run straight through, five-minute writes cut a
|
|
49
|
+
boot's cost by 22 to 30% and an edit's by about 18% against an hour's
|
|
50
|
+
([`../../evals/ab/warm.md`](../../evals/ab/warm.md), row 2 and the measurement beside it); in a long session,
|
|
51
|
+
one pause past five minutes, while a person reads or a review or CI runs, can cost more than every write the
|
|
52
|
+
shorter lifetime saved. A subagent's lifetime is set apart from the session's.
|
|
53
|
+
|
|
54
|
+
## A switch that changes the prefix is earned
|
|
55
|
+
|
|
56
|
+
Claude Code has three switches that change what a session writes: the git instructions (the startup
|
|
57
|
+
snapshot together with the commit and pull-request instructions), the cache lifetime, and, for a run
|
|
58
|
+
started headless, moving the per-machine sections (working directory, platform, memory paths, the
|
|
59
|
+
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 stays off until an A/B run shows it cuts spend with no
|
|
61
|
+
loss**, which is `0038` rule 4's gate applied to sessions, and **a session that needs what a switch
|
|
62
|
+
removes keeps it**:
|
|
63
|
+
|
|
64
|
+
- **The git instructions are dropped only for sessions that neither commit nor open a pull request**, or
|
|
65
|
+
where the workspace's own guidance carries its commit and pull-request workflow. A session that needs
|
|
66
|
+
them is started with `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=0`, which outranks the compiled setting, and
|
|
67
|
+
a runner sets that per run.
|
|
68
|
+
- **The per-machine sections are moved, never dropped**: the exclusion puts them in the first message. It
|
|
69
|
+
applies to headless runs only, because only whatever starts a session can ask for it.
|
|
70
|
+
- **The cache lifetime removes nothing.** It trades the write price against the gap.
|
|
71
|
+
|
|
72
|
+
## What a workspace declares
|
|
73
|
+
|
|
74
|
+
**`sessions` in the manifest**, since Workspace Definition 2.11
|
|
75
|
+
([`../../spec/slots.md`](../../spec/slots.md)): `git_instructions` and `cache_lifetime` compile into the
|
|
76
|
+
host's project settings and reach every session in the repository; `headless` is what Portulan's own runners
|
|
77
|
+
apply to the sessions they start. Nothing is defaulted: an undeclared switch is the host's own default. This
|
|
78
|
+
repository declares one switch, five-minute writes for its headless runs, which row 2 of
|
|
79
|
+
[`../../evals/ab/warm.md`](../../evals/ab/warm.md) earned. Its interactive sessions keep the host's default,
|
|
80
|
+
because they commit and idle on reviews and CI; the git instructions wait on that page's row 3, and the
|
|
81
|
+
per-machine sections on its row 4.
|
|
82
|
+
|
|
83
|
+
## What is machinery today, and what is not
|
|
84
|
+
|
|
85
|
+
`compile` emits the two interactive keys and says so on every run, and writes `spend`'s figures onto the
|
|
86
|
+
restart advisory's commands; `doctor` checks the shape and version of `sessions` and `spend`, and reports
|
|
87
|
+
every switch's state in one line; `init` offers an adopter's repository the five-minute lifetime with its
|
|
88
|
+
reason and its trade-off, and writes the key on a yes for `compile` to carry, and `upgrade` prints the same
|
|
89
|
+
offer and writes nothing. [`../../cli/warm.mjs`](../../cli/warm.mjs) runs fresh headless sessions in sequence
|
|
90
|
+
and prices each from the host's own usage records, warm against cold, and
|
|
91
|
+
[`../../evals/ab/warm.md`](../../evals/ab/warm.md) is its specification and its record. **Still to land**: the
|
|
92
|
+
interactive A/B, which needs real local sessions and so runs on a maintainer's device; a report of how often
|
|
93
|
+
sessions start warm, from the ledger's first request per context; an offer of the other switches, once their
|
|
94
|
+
rows have run; and carrying the key through `vendor`. Until each lands, the rule it would enforce is held by
|
|
95
|
+
the human gate.
|
|
96
|
+
|
|
97
|
+
_(Provenance: the maintainer's five-run measurement of 2026-09-24, whose figures the record in
|
|
98
|
+
[`../../evals/ab/warm.md`](../../evals/ab/warm.md) carries as its before, and the cache test of the same day,
|
|
99
|
+
which ran its rows 1 and 2; the techniques survey before it, from Claude Code's documentation on prompt
|
|
100
|
+
caching, settings and the CLI; and each switch read in Claude Code 2.1.281's program text on 2026-09-24.)_
|
package/core/personas/README.md
CHANGED
|
@@ -21,6 +21,39 @@ context firewalls; Cognition — read-parallel / write-isolated.)_
|
|
|
21
21
|
- **Read / write posture** — most personas read in parallel; writes are isolated to one place so two
|
|
22
22
|
agents never clobber the same file.
|
|
23
23
|
|
|
24
|
+
## What a persona may declare about its context
|
|
25
|
+
|
|
26
|
+
Four optional frontmatter fields say what a fresh context for the role should cost, per
|
|
27
|
+
[`../operating/context.md`](../operating/context.md): `model`, one model name or alias as a single
|
|
28
|
+
token; `effort`, one of `low`, `medium`, `high`, `xhigh` or `max`; `omitClaudeMd`, `true` or `false`
|
|
29
|
+
unquoted, whether the role runs without the project's instruction files; and `cacheLifetime`, `5m` or
|
|
30
|
+
`1h`. The effort levels and the name `omitClaudeMd` are adopted from Claude Code's vocabulary, the first
|
|
31
|
+
host to express them. `effort` sets the host subagent's reasoning effort, and is not the thinking cap the
|
|
32
|
+
`tools/local-llm` runner's `--effort` sends with each request it makes. `doctor` fails a
|
|
33
|
+
malformed value and `compile` refuses one, and neither supplies a value the persona did not write. For a
|
|
34
|
+
persona a composed pack ships, `compile` writes the declared fields into the frontmatter of the role's
|
|
35
|
+
hand-written host binding, `agents/<name>.md` in the workspace's tree, and leaves every other line of
|
|
36
|
+
that file as its author wrote it; `--check` byte-compares the result, and a line inside the compiled
|
|
37
|
+
block that `compile` did not write is refused rather than removed. One it cannot catch: a line added inside
|
|
38
|
+
the block in exactly the spelling and order `compile` writes reads as compiled, and is dropped once the
|
|
39
|
+
persona no longer declares that field, since the two cannot be told apart. And once a persona declares any
|
|
40
|
+
field, `compile` owns every top-level key it could write, so a hand-written `experimental:` is refused
|
|
41
|
+
even for a key other than `cacheTtl`. A field a host cannot express is
|
|
42
|
+
named, with the persona and the host, in the degradation report `compile` prints. Claude Code takes all
|
|
43
|
+
four on an agent (`cacheLifetime` as `experimental.cacheTtl`), and a vendored `AGENTS.md` takes none.
|
|
44
|
+
**Claude Code loads `agents/` only from a plugin root**, and a repository that is not a plugin loads its
|
|
45
|
+
agents from `.claude/agents/`, which `compile` does not write. So outside a plugin the compiled block is
|
|
46
|
+
loaded by nothing, and the degradation report says so for each persona it applies to. Where the tree is
|
|
47
|
+
a plugin root the report is silent, which is not proof of loading: a plugin's agents load only while it
|
|
48
|
+
is installed and enabled, and `compile` cannot see that. _(Provenance:
|
|
49
|
+
proposal `0036`'s order of work, item 7, and proposal `0039`'s first two rules. The host's keys, values
|
|
50
|
+
and agent locations were read in Claude Code 2.1.317's program text on 2026-11-03.)_
|
|
51
|
+
|
|
52
|
+
A binding has one persona owner across composed packs, including an unbound persona or one declaring
|
|
53
|
+
no fields; duplicate destinations are refused before any artifact is written. The binding must be a
|
|
54
|
+
regular file whose real path stays in the real declared tree, including its parent directories.
|
|
55
|
+
`omitClaudeMd` accepts only unquoted `true` or `false`, in both declarations and compiled blocks.
|
|
56
|
+
|
|
24
57
|
## Status
|
|
25
58
|
|
|
26
59
|
**Milestone 3 — contract, exemplars, and one host binding.** This README fixes the persona contract; the
|
|
@@ -49,8 +49,8 @@ Re-expressed from public practice (Letta — consolidation; compounding engineer
|
|
|
49
49
|
— curated beats generated). Invocable on demand, and **schedulable as of milestone 5**: `cli/librarian.mjs`
|
|
50
50
|
runs reindex over both generated indexes, staleness (the sealed-stamp re-validation nag, and record age
|
|
51
51
|
from git), proposal nagging, demotion drafts, **mining** and **consolidation**, and renders what it
|
|
52
|
-
found as a
|
|
53
|
-
executes.
|
|
52
|
+
found as a report, which a workspace's scheduler files with the pull request the pass opens when it
|
|
53
|
+
changed the tree — this persona is the contract it executes.
|
|
54
54
|
|
|
55
55
|
**Every subject in the charter above is now reached on a cadence, and one of them is reached less
|
|
56
56
|
deeply than the charter describes — which is the distinction between this persona and that tool.** A
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clarify
|
|
3
|
-
description: Run a bounded clarification pass before planning a full-lane task
|
|
3
|
+
description: Run a bounded clarification pass before planning a full-lane task. Use when the ask is underspecified, the constraints conflict, or the criteria can't be written as observable checks — surface the smallest set of decision-questions, get human answers, and fold them into EARS-shaped acceptance criteria before any code is planned.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Skill — Clarify
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: consolidate
|
|
3
|
-
description: Bring a memory store back under its budget by merging, compressing, splitting, and retiring records
|
|
3
|
+
description: Bring a memory store back under its budget by merging, compressing, splitting, and retiring records. Use when the index verify recipe reports a breach, or on the librarian's scheduled pass. The output is drafts through the human gate; raising the budget is not one of the moves.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Skill — Consolidate
|
|
@@ -18,8 +18,10 @@ description: Bring a memory store back under its budget by merging, compressing,
|
|
|
18
18
|
decides which move applies**, and the finding names it: the index's `lines` (too many records —
|
|
19
19
|
merge or retire; compressing removes no line and splitting adds one), the store's `kilobytes` (too
|
|
20
20
|
much in total), or a record's `record_kilobytes` (one record too large — split, compress, or demote;
|
|
21
|
-
step 2 and step 4)
|
|
22
|
-
|
|
21
|
+
step 2 and step 4), which binds only the records dated after a declared `cutoff`: one dated on or
|
|
22
|
+
before it is reported, never red, and meets the cap when its text next changes. A fourth red, the
|
|
23
|
+
index's `columns`, is **not** this pass's work: one over-long line is repaired by renaming the record,
|
|
24
|
+
and nothing is consolidated.
|
|
23
25
|
- Two records say the same thing, or say opposite things.
|
|
24
26
|
- A record's `Retire when:` condition has fired.
|
|
25
27
|
- The librarian's scheduled pass runs.
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
# Template — Handoff
|
|
2
2
|
|
|
3
|
-
> A **handoff**
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
3
|
+
> A **handoff** carries the work still open, so the next agent — or the next context window — starts
|
|
4
|
+
> from where things stand rather than from the transcript. Write one when a session ends with work not
|
|
5
|
+
> committed and pushed, when compacting, when hitting an iteration cap, or when passing work across a
|
|
6
|
+
> persona boundary. Work that is committed carries its own why, in its commit message, so a handoff
|
|
7
|
+
> says nothing the diff or a commit already says. **Drop any section with nothing to say**; five lines
|
|
8
|
+
> is a valid handoff. The date leads the **filename**, in ISO form — `YYYY-MM-DD-{slug}.md` — because
|
|
9
|
+
> that is what a gate checks and what sorts the series. _(Provenance: Cognition — handoffs record
|
|
10
|
+
> decisions and their rationale. When one is owed, and why the commit carries the rest:
|
|
11
|
+
> `../operating/loop.md`.)_
|
|
12
|
+
>
|
|
13
|
+
> **The commit a change lands in:** a subject that says what changed, in the present tense and under 72
|
|
14
|
+
> characters; a blank line; a body of a few lines that says why, and what was rejected when that matters.
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
17
18
|
# Handoff — {task / thread}
|
|
18
19
|
|
|
19
|
-
**State.** {where things
|
|
20
|
-
|
|
21
|
-
**Decisions + why.** {each decision taken and the reason it was taken; the reasons are the payload.}
|
|
22
|
-
- {decision} — because {why}; alternatives considered: {…}.
|
|
20
|
+
**State.** {where things stand: what is done and committed, what is in progress and uncommitted.}
|
|
23
21
|
|
|
24
22
|
**Open questions.** {what is undecided, and who decides it — flag the human-owned ones.}
|
|
25
23
|
|
|
@@ -27,3 +25,18 @@
|
|
|
27
25
|
|
|
28
26
|
**Recoverability.** {anything left in a partial state, and how to make it safe; see
|
|
29
27
|
`../operating/safety.md`.}
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
_A filled one, for a session that stopped mid-change:_
|
|
32
|
+
|
|
33
|
+
# Handoff — retry the flaky upload test
|
|
34
|
+
|
|
35
|
+
**State.** The retry wrapper is written and its unit test passes; it is stashed, not committed, because
|
|
36
|
+
the integration suite still fails once in about ten runs.
|
|
37
|
+
|
|
38
|
+
**Open questions.** Whether three attempts is the right cap — the owner of the upload service decides.
|
|
39
|
+
|
|
40
|
+
**Next action.** `git stash pop`, run the integration suite twenty times, and read the one failure's log.
|
|
41
|
+
|
|
42
|
+
**Recoverability.** Nothing was pushed; dropping the stash loses only the wrapper.
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
**type:** rule | decision | reference | glossary
|
|
11
|
+
**dated:** {YYYY-MM-DD — the day this record's text last changed; an edit re-dates it. The per-record cap binds by it where the workspace declares a cutoff}
|
|
11
12
|
**scope:** {the persona / repo / workspace this belongs to — memory is per-agent, not global}
|
|
12
13
|
**provenance:** {one of the two forms below — a rule carrying neither is taste, and `doctor` rejects it}
|
|
13
14
|
|