Skip to content

Superself 0.11.0: consent given in chat reaches the store

Choose a tag to compare

@tonite31 tonite31 released this 28 Aug 06:51
· 24 commits to main since this release
21fc698

Superself 0.11.0 lets an agent session record the decisions a person already made in the conversation it is having with them. Seven verbs used to refuse and print "a person runs this in their own terminal"; the refusal did not withhold consent, it withheld the record of consent already given, and the command came back for the person to paste — which is where a long --supersedes … --why "…" line drifts from what was agreed, or never gets run at all. The rule that replaces it is one sentence: a verb whose record self undo can take back needs no person at a keyboard; it records who wrote it instead. One verb keeps its gate, because nothing takes back a deletion.

An undoable verb runs from the session that made the decision

work add, work confirm, decide --supersedes, the retracting and retiring family, report confirm, and runbook approve now record from an agent session. Each one writes an event self undo reverses, which is the whole justification: the gate was written before self undo existed, and it bought nothing a person cannot buy with one undo.

self work add "<required outcome>" [--supersedes <work-id> --why w]
self decide retract <event-id> --why "<reason>"
self work retire <id> --why w
$ self work add "the render gate holds"
entity.confirmed recorded [01m13hffrd1xwgebkk1kqg44zj]
  w-372yh the render gate holds — verify; wrong? self undo 01m13hffrd1xwgebkk1kqg44zj
w-372yh

What replaces the gate is attribution. Every event these verbs write carries one new payload field:

"by": { "kind": "person" | "agent", "session": "a1b2c3d4", "name": "rayim" }

kind reads the same marker that already answered "is anybody there". session is the session token, written for an agent only — a person's call has no session to name. name is what runbook approve --by already recorded as a bare string, folded into the same field, so who approved and what kind of process recorded it are one statement.

<verb> show <id> --history is where a reader sees it:

$ self state show e-3ryjc --history
e-3ryjc  live  3 events
2026-08-28T06:39:17.476Z  entity.proposed  [01m13hg6z49sgvzesvp4wqavxt]  every render is checked against the golden · by agent (session a1b2c3d4)
2026-08-28T06:39:17.605Z  entity.placed  [01m13hg735m7fge8pcvmkty29p]   · by agent (session a1b2c3d4)
2026-08-28T06:39:17.730Z  entity.confirmed  [01m13hg77266vpd58heb3qmzx0]   · by agent (session a1b2c3d4)

self log does not carry the column. It is the workspace timeline and a machine contract, and a column every reader of it has to skip is one it does not owe; the writer is read from the record's own history page instead. An event carrying no by — every record written before this release — prints its line and says nothing about a writer, rather than being read as a person.

The disclosure survived the gate. Every retiring verb still prints what it destroys — the record's own words, its age, what still points at it, the reason the call gives — before it writes. Only the challenge prompt at the end is gone:

$ self work add "the render gate holds for piped output too" --supersedes w-3vhw2 --why "the original outcome was too narrow"
this retires a confirmed work unit — `self undo` takes it back

  w-3vhw2  work  confirmed 2026-08-28T06:41:01.027Z  (just now)
  the render gate holds
  referenced by: nothing
  replaced by this new work: the render gate holds for piped output too

entity.confirmed recorded [01m13hkc7bqt77bzshbnp6h0xc]
  w-ck5gg the render gate holds for piped output too — verify; wrong? self undo 01m13hkc7bqt77bzshbnp6h0xc
entity.retired recorded [01m13hkc7aq45sdpb3ssrpazk7]
  w-ck5gg the render gate holds for piped output too — verify; wrong? self undo 01m13hkc7aq45sdpb3ssrpazk7
w-ck5gg

A person at a terminal reads the same disclosure and answers nothing: no prompt is printed and no answer is read. self apply still collects a reviewed set, still refuses a repeat, and still lands the whole set as one append.

work accept is now work confirm, and work propose means review

The verb is renamed to match decide confirm, state confirm and objective confirm.

self work propose "<plan>" [--supersedes <work-id> --why w]
self work confirm|decline <proposal-id> [--why w]
$ self work --help
usage: self work [--project <slug>] [--pretty|--plain]
       …
       self work propose "<plan>" [--supersedes <work-id> --why w] [--milestone m --value v]
       self work confirm|decline <proposal-id> [--why w]

accept stays as a hidden alias dispatching the identical handler — same event type, same payload shape, same by — so a script or a document written against it keeps working. It appears on no help page, because a page naming it would advertise the spelling this retires, and the unknown-verb refusal lists the new one:

$ self work bogus
error: unknown work subcommand "bogus" — use add|show|start|started|exited|block|unblock|done|retire|link|unlink|propose|revise|confirm|decline

After the rename, work propose means "I want this reviewed", not "I am an agent". Both forms are open to a session: propose a plan when it wants review, and work add it when it does not, exactly as decide "<text>" is to decide --proposed.

A proposal can be placed before it is confirmed

Placing a record that is still proposed used to be refused outright. A session that proposes a workspace-wide rule can now say where it belongs in the same breath, and the confirm lands it there.

self state place <id> --scope workspace
self state confirm <id>
$ self state place e-3ryjc --scope workspace
entity.placed recorded [01m13hg735m7fge8pcvmkty29p]
  e-3ryjc every render is checked against the golden — verify; wrong? self undo 01m13hg735m7fge8pcvmkty29p

$ self state confirm e-3ryjc
entity.confirmed recorded [01m13hg77266vpd58heb3qmzx0]
  every render is checked against the golden — verify; wrong? self undo 01m13hg77266vpd58heb3qmzx0

Until the confirm the proposal renders only in the waiting band, never as a placed workspace record, and a proposal placed past the retention cap still takes its seat at the confirm, which is where the cap is judged. --priority is allowed for the same reason: priority hides nothing.

A placement that would demote a proposal is still refused, because there is nothing to demote from:

$ self state place e-yzqe2 --exposure search --why "not for context"
error: e-yzqe2 is still proposed, so it renders nowhere to be demoted from — confirm it with `self state confirm e-yzqe2` and demote it then, or propose it at search instead

A design approval is recorded by a session, and taken back like anything else

report confirm binds an approval to the design artifact's hash. Its old refusal read "a person's ruling on a design report", which stops being a distinguishing fact the moment a session can record the same ruling.

self report <work-id> "<summary>" --design --implements <id> --artifact <path>
self report confirm <report-id>
$ self report confirm 01m13hhke7qpgp21vptmzy1mcw
report.confirmed recorded [01m13hhkphsa0ft8enf4c3330v]
  w-x1tkm design 01m13hhke7qpgp21vptmzy1mcw approved — verify; wrong? self undo 01m13hhkphsa0ft8enf4c3330v
01m13hhke7qpgp21vptmzy1mcw approved for w-x1tkm, bound to artifact 12e54cc862cd

report.confirmed leaves the list of types self undo refuses by name. With nothing built on it, taking it back leaves the design unapproved again, and the dispatch gate closes behind it:

$ self undo 01m13hhkphsa0ft8enf4c3330v
entity.annulled recorded [01m13hhkx89s4zkahxwz4fdfja]
w-x1tkm is open again — its report.confirmed was taken back

$ self work start w-x1tkm
error: w-x1tkm cannot be picked up yet — its design 01m13hhke7qpgp21vptmzy1mcw is waiting on an approval: `self report confirm 01m13hhke7qpgp21vptmzy1mcw`

What guards the approval instead of a gate is the dependent chain. Once a unit has started on it, the undo is refused with the list, so a dispatched unit is never left with a design nobody approved:

$ self undo 01m13hhm74xvjqxnkz6zrxb1jc
error: 01m13hhm74xvjqxnkz6zrxb1jc was built on — 1 event names it
    01m13hhmcx3dw8h8mzcaj6ypk1  entity.started   w-x1tkm
  take them back first, newest first:
    self undo 01m13hhmcx3dw8h8mzcaj6ypk1

runbook approve moves the same way. A held run is released from a session, and --by records a name beside the kind and the session — it authorizes nothing, and nothing here checks a name:

self runbook approve <key> [--by <person>]
$ self runbook approve E001 --by "rayim"
entity.unblocked recorded [01m13hk0j1trt47f43a6z1vppk]
  E001 — content loop — verify; wrong? self undo 01m13hk0j1trt47f43a6z1vppk
E001 is approved and moving again

artifact prune is the one verb that keeps its gate

artifact.pruned removes bytes from the working tree, and nothing takes back a deletion. That is the whole reason, and the refusal now states it in one line:

self artifact prune <id> --why "<reason>" [--project slug]
$ self artifact prune a-gj1v9 --why "superseded by the new guide"
error: removing stored bytes cannot be taken back by `self undo`, so a person types the id — this process has no terminal to type it at, and nothing was removed

  a-gj1v9  guide.md  registered  attached 2026-08-28
  the render guide
  stored at artifacts/demo/a-gj1v9-guide.md (8 bytes)
  those bytes are removed from the working tree
  the record itself is kept — what was attached stays auditable, and a done claim resting on it still holds
  history is never rewritten, so the copy this artifact left in .git stays there

  a person runs this in their own terminal:
    self artifact prune a-gj1v9 --why "…"

self help artifact is the one help page allowed to keep the gate's sentences, and it states why it is the only one. Every other self help <topic> and self help <command> page is held to the new rule, so the change cannot rot into a paragraph two files away that keeps promising the old refusal.

Bounds and refusals

  • Attribution answers cooperating callers, exactly as the gate did. A process that unsets the session marker and allocates a terminal records person. That was as true of the refusal this replaces; the trust model is unchanged, and nothing here is a signature, an allowlist, or an identity check.
  • by.session and origin.session hold the same string and answer different questions. A person typing into an agent harness's shell has an origin.session and no by.session, so the two fields are deliberately not merged.
  • runbook approve --by records a name and gates nothing. No verb reads it to decide anything.
  • Taking a record back is still refused, with the dependent list, when something was built on it — never a cascade, because a cascade computed on one clone would take back a set another clone would not. The list of kinds refused by name is one shorter: a registered artifact, a prune, a project archive or restore, process telemetry, and an undo itself. A design approval has left it.
  • work propose is not gated and never was. Proposing is what a session does when the plan wants review, not what it does because it is a session.
  • Pruning bytes stays refused while a live record points at them, and for the evidence of a work unit that is neither done nor retired. What an artifact left in the store's git history stays there.
  • Mixed versions: payload.by is a new optional field, and an older CLI's fold ignores it. A store written by 0.11.0 folds correctly on 0.10.0 — verified by folding one — and every reader there answers normally; it simply cannot say who wrote the record. In the other direction, a record written before this carries no by, and every surface reads that as unstated rather than as a person. No migration, no new event type, no new refs key. The one spelling that does not travel backwards is the rename: an older CLI answers unknown work subcommand "confirm", so a script that has to run on both should keep using work accept, which both accept.

Upgrade after publication

After the v0.11.0 release workflow finishes, install the published package:

npm install -g superself@0.11.0

Evidence

  • PR #401 lets consent given in chat reach the store: an undoable verb runs from an agent session and records who wrote it, work accept becomes work confirm, and artifact prune keeps the one gate that still holds. Case table.
  • Release policy defines the version, tag, and publication path.