gizmoduck: rows 270 and 271 packets: the 264c count and 264d clear contracts pinned for row 272 (Huey), and the Pi 0.85.1 compaction source contract
This commit is contained in:
1 parent
0cbe2e9b35
commit
8365e4e5b4
2 files changed
+121
No files matched your search
@@ -0,0 +1,49 @@
|
|||||||
|
# Row 270 build notes (Gizmoduck)
|
||||||
|
|
||||||
|
264c, T1 item 7: the compaction count, exact for Pi. Gitea #1775. Brief:
|
||||||
|
docs/plans/2026-10-21_t1-sessions.md section 4 (D6), with RULINGS Q6 (Pi exact, Claude `unknown` with
|
||||||
|
the Vikunja 466 reason). Reviewer Duckworth, gate owner Sage. Branch gizmoduck/row270 from
|
||||||
|
gizmoduck/row269 2d0d407ba (sage-2797, Q220). Row 269 lands first, and this branch rebases with it.
|
||||||
|
|
||||||
|
## Contract for 264e (pinned for Huey, row 272)
|
||||||
|
|
||||||
|
The surface reads the count from `session.list`. Each row gains one key, `compactions`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": "...", "at": "...", "name": "...", "predecessor": null, "default": true,
|
||||||
|
"compactions": { "count": 3, "known": true, "reason": null, "at": "2026-10-22T16:40:00.000Z" } }
|
||||||
|
```
|
||||||
|
|
||||||
|
- `known: true`: `count` is the exact number of completed compactions over the session's lifetime,
|
||||||
|
an integer of 0 or more. `reason` is null.
|
||||||
|
- `known: false`: `count` is a lower bound (an integer) or null when there is no bound. Show
|
||||||
|
`unknown` with the reason, never a bare 0. `reason` is one of:
|
||||||
|
- `harness-unsupported`: Claude Code, until Vikunja 466 pins its format.
|
||||||
|
- `not-collected`: no collection has run for this session yet.
|
||||||
|
- `store-unreadable`: a store file couldn't be read, or isn't a Pi session.
|
||||||
|
- `store-incomplete`: a stream file got shorter, was replaced, or has unreadable lines, so
|
||||||
|
entries may be missing.
|
||||||
|
- `at`: ISO time of the last collection, or null if none has run.
|
||||||
|
- A clear successor (264d) starts at `known: true, count: 0` once its first collection runs. The
|
||||||
|
retired session keeps its row and its count.
|
||||||
|
- On a base without 264c, the key is absent. The surface shows the count as unavailable, which is
|
||||||
|
what Huey's plan does (Sage Q1).
|
||||||
|
|
||||||
|
No new verb and no new input key. Access is `session.list`'s: the thread owner, or the tester.
|
||||||
|
|
||||||
|
## Pi source contract (pinned offline at 0.85.1)
|
||||||
|
|
||||||
|
`@earendil-works/pi-coding-agent` 0.85.1 (package.json pin), read in node_modules:
|
||||||
|
|
||||||
|
- One line per completed compaction: `{"type":"compaction","id":...,"parentId":...,"timestamp":...,
|
||||||
|
"summary":...,"firstKeptEntryId":...,"tokensBefore":...}` from `appendCompaction`
|
||||||
|
(dist/core/session-manager.js:818-833). The two callers write it after the summary exists
|
||||||
|
(dist/core/agent-session.js:1539, :1826). A start or an aborted compaction writes no entry.
|
||||||
|
- What doesn't count: a `message` entry with role `compactionSummary` (a rendered summary,
|
||||||
|
docs/session-format.md:154), any message inside a compaction's `retainedTail`, and
|
||||||
|
`branch_summary` entries.
|
||||||
|
- Identity: the stream is the `*.jsonl` file name under the session's `session/` directory, and the
|
||||||
|
event is the entry `id`. Pi's ids are unique within a file (`generateId(this.byId)`). An entry
|
||||||
|
id repeated in one file counts once.
|
||||||
|
- Partial trailing line: wait. Only lines that end with a newline are read, as
|
||||||
|
packages/conversation/src/pi.mjs:35-38 does.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Row 271 build notes (Gizmoduck)
|
||||||
|
|
||||||
|
264d, T1 item 7: clear context through row 194's lifecycle stop. Gitea #1776. Brief:
|
||||||
|
docs/plans/2026-10-21_t1-sessions.md section 5 (D7, D8), with RULINGS Q4 (rebase onto 194's
|
||||||
|
landing, no 194 code copied). Reviewer Duckworth, gate owner Sage. Built after row 270 (sage-2797).
|
||||||
|
|
||||||
|
## Contract for 264e (pinned for Huey, row 272)
|
||||||
|
|
||||||
|
Proposed, pending Duckworth's review. Any change goes to Huey before it's pushed.
|
||||||
|
|
||||||
|
### session.clear
|
||||||
|
|
||||||
|
Input, every key required, with no other key (`keys()` as `session.create` does):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "thread": "<thread id>", "session": "<session id>", "revision": 0, "confirm": true, "op": "<op key>" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- `revision`: the integer from the session's `session.list` row (below). It names the state the
|
||||||
|
human confirmed.
|
||||||
|
- `confirm`: the literal `true`. Anything else refuses `confirm-required`.
|
||||||
|
- `op`: the caller's operation key, an identifier, unique per thread. It is also the idempotency
|
||||||
|
key.
|
||||||
|
|
||||||
|
Answer, the operation envelope:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "op": "...", "thread": "...", "session": "<old id>", "successor": "<new id>",
|
||||||
|
"state": "clearing", "start": null, "refusal": null, "at": "..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
- `successor` is bound when the request is accepted, before any asynchronous work.
|
||||||
|
- `state`:
|
||||||
|
- `clearing`: accepted. Sends and launches to the old session are fenced, and the drain or stop
|
||||||
|
is in progress.
|
||||||
|
- `uncertain`: stop proof isn't available. Capacity stays reserved and nothing is forced. It
|
||||||
|
resumes when proof arrives.
|
||||||
|
- `retired`: the old session is retired and the successor exists, in one bus transaction.
|
||||||
|
- `failed`: no retirement happened, and `refusal` names why. The old session stays as it was.
|
||||||
|
- `start` is null until `state` is `retired`. After that it is `held` (waiting for capacity),
|
||||||
|
`started`, or `refused` with `refusal` set.
|
||||||
|
- `at`: the time of the latest state change.
|
||||||
|
|
||||||
|
Repeats and conflicts:
|
||||||
|
|
||||||
|
- The same `op` with the same payload returns the current envelope and starts nothing new.
|
||||||
|
- The same `op` with any other payload refuses `session-op-conflict`.
|
||||||
|
- A new `op` on a session with an open clear refuses `session-clearing`.
|
||||||
|
- A `revision` that isn't current refuses `session-revision-stale`.
|
||||||
|
- Other refusals: `session-not-found`, `session-retired`, `processing-halted` (D8: a halt refuses
|
||||||
|
and changes nothing), and the owner refusals `session.create` already gives.
|
||||||
|
|
||||||
|
### session.operation
|
||||||
|
|
||||||
|
Input `{ "thread": "...", "op": "..." }`. Answer: the same envelope. Unknown op refuses
|
||||||
|
`operation-not-found`. A CLI that waits 30 s and then reports pending uses this to poll. A retry
|
||||||
|
queries the op here and never resends the clear.
|
||||||
|
|
||||||
|
### session.list additions
|
||||||
|
|
||||||
|
Each row gains:
|
||||||
|
|
||||||
|
- `state`: `active`, `clearing`, `uncertain` or `retired`.
|
||||||
|
- `revision`: an integer. 0 at creation, plus 1 for each lifecycle event on that session.
|
||||||
|
- `successor`: the successor's id once retired, otherwise null.
|
||||||
|
|
||||||
|
A successor row has `predecessor` set (264a's column). When the cleared session was the thread's
|
||||||
|
default, `default` moves to the successor at retirement. Clearing any other session leaves the
|
||||||
|
default where it is.
|
||||||
|
|
||||||
|
On a base without 264d, `session.clear` refuses `unknown-verb`, and the rows have no `state`,
|
||||||
|
`revision` or `successor`.
|
||||||
Reference in new issue
Block a user