Repository navigation
superself 0.10.0: reuse what repeats, and take back what was a mistake
Superself 0.10.0 lets a project record the things it does more than once — a procedure with stages, a command it keeps retyping, a document that is too long to be a rule — and reuse them from any session. It also gives a person a way to take a record back when it was simply wrong, instead of dressing the correction up as a replacement. Around those, the store learns to say how large it is and to give bytes back, and a report learns to say what differed from expectation.
Register a procedure the team repeats, and run it
A procedure is a list of stages a project works through, such as plan, draft, publish. Register it once and start a run of it whenever the work comes round again.
self runbook add "content loop" --stage plan --stage draft --stage publish
self runbook start <id|name> --instance E001
self runbook advance E001 --why "the assets are made"self runbook lists what is registered here, self runbook show <id|name> prints a procedure with its stages and its runs, and self runbook revise <id> --stage … --why "<what changed>" proposes a new edition that a person lands with self state confirm. Stages can be read from a markdown list with --file <path> instead of one --stage per step.
self context gains one section, so a session that has just started reads the resume point without being told:
## Runbooks
- E001 · content loop v1 (the definition is on v2) · 2/3 draft · next: publish · `self runbook show e-8mwfx --project 'demo'`
- E003 · content loop v2 · 1/4 plan · next: draft · `self runbook show e-8mwfx --project 'demo'`
A run copies the stages it started under, so a later edition never changes what a run already in flight means. Where the run and the current edition differ, context says which edition the run is following and the run keeps going.
Registering a procedure schedules nothing and starts nothing. No verb calls another, none advances a stage on its own, and there is no timer.
Park a run on a person, stop it, resume it, and name its work
A run can wait for someone to look at it before it goes on.
self runbook hold E001 --why "the final cut needs a look"
self runbook approve E001 --by <person>
self runbook stop E001 --why "the story was dropped"
self runbook resume E001
self runbook link E001 --work <work-id>A held run reaches the part of context that lists what is waiting for the person reading it:
## Waiting on you
- E001 E001 — content loop waits on your approval: the final cut needs a look (approve with `self runbook approve E001`)
The approval is a person's call at their own terminal, and the challenge is the run's key, so typing it back states which run is being approved. --by records who approved and gates nothing. Giving a run up goes through the same confirmation every other withdrawal goes through.
Register a command or recipe once and reuse it
Some know-how is neither a rule nor a procedure: the exact line that deploys, the flags that run one test file against the right environment, the recipe for a task that comes round every few weeks.
self skill add "<name>" --command "<line>" --purpose "<what it is for>" [--workspace]
self skill add "<name>" --file <path> --purpose "<what it is for>"
self skill show <id|name> [--project <slug>]
self skill drop <id|name> --why "<why it no longer helps>"self context gains a section carrying each one's name, its one-line purpose, and the command that prints it. The command line itself never enters context, so a session discovers what exists and asks for the body when it needs it:
## Skills
- deploy — push the built image to staging · `self skill show e-xxxxx --project 'demo'`
--workspace registers one for every project. A project's own entry of the same name takes the row and the workspace one takes none, and the listing, the page, and the receipt all say that it is standing in front of a shared one.
A registered line is printed, never run. self skill run is a refusal by name and appears on no help page: the store syncs between machines, so a line it holds could be appended anywhere and would then execute everywhere.
Registering a name that already stands proposes a replacement rather than displacing what is there; self state confirm lands it, and the earlier version stays in the list the page prints.
Take back a record that was a mistake
A record that is a plain mistake — the wrong id, the wrong text, the wrong link — is now taken back rather than replaced with ceremony.
self undo [<event-id>] [--supersession] [--why <text>]With no id it takes back the newest append, the one whose receipt was just printed. No reason is owed: "this was a mistake" is the whole statement, and the record of the undo already names the event it reversed.
$ self work link <w-id> --milestone <m-id>
entity.linked recorded [<event-id>]
<w-id> stage 1 lands the render gate and its pilot — verify; wrong? self undo <event-id>
$ self undo <event-id> --why the checkpoint was the wrong one
entity.annulled recorded [<event-id>]
<w-id> no longer carries the link — the linked event was taken back
Every command that records something now prints the record it resolved and the exact self undo line for it, as the receipt above shows. --meant "<what you meant>" optionally records the caller's own restatement beside a write; it is printed and never judged.
One append is one undo. Where a single command wrote several events about the same record, they come back together; where it wrote unrelated ones, they come back one at a time. --supersession narrows the undo of a record that replaced another, so the record stands and only its claim to replace the older one is taken back.
Nothing is rewritten. Both halves stay in self log, the row is marked · undone, and a record whose creation was undone still answers to self work show <id> as Status: undone with the date, so an id followed out of a commit message is answered rather than reported unknown.
A work unit is proposed by an agent and recorded by a person
Creating a confirmed work unit is now a person's own command. self work add, self work add --supersedes, and self work accept are refused where no person is at the terminal, and each refusal hands back the exact line to run instead.
self work propose "<plan>" [--supersedes <work-id> --why w]self work propose learns the correction spelling, so an agent can propose a replacement for an existing unit and the retirement lands with the acceptance, in one append under one confirmation.
A person at a terminal loses nothing: work add and work accept stay one command with no prompt to answer. The check is presence only — there is no challenge to type — and it reads the terminal alone, so piping a command's output changes nothing about who is running it.
Say what differed from expectation in a report
A report can now state the friction it met as a field rather than burying it in prose.
self report <work-id> "<summary>" --friction "<what differed>"The flag is free text and can be given more than once. The sentences appear under the report they belong to in self work show and the folded work page, on the report block of self handoff, on the report's one-line summary in self log, and as one line in context when a project's recent reports carry none:
health: no friction on 1 of this project's 1 report in the last 30 days — self report … --friction "<what differed>"
It is optional. Reports already in a store are not retroactively incomplete, and self work done --report still records without it.
Group the recorded friction into proposals
self sweep reads the friction sentences recorded lately across the workspace's active projects, groups the ones that say the same thing, and proposes a change for each group that recurs.
self sweep [--since <window>] [--record]Without --record it prints and records nothing. With --record each group becomes a proposal that reaches the person through the part of context that lists what is waiting for them. The window defaults to the last 30 days and is written as a whole number of days or weeks. There is no timer and no background process; an unattended run is an external scheduler calling self sweep --record.
A group needs at least three sentences before it is proposed. A proposal carries no count in its text and is built from the group's oldest sentence, so a group that keeps growing does not keep re-proposing itself under new wording; the events it was built from are recorded as its evidence. A sentence that says only that nothing differed is read and counted but never proposed, so an honest report is not punished.
Store a file with no report behind it, and point a rule at it
Guidance longer than one line had nowhere to live inside the store: a file was stored only as evidence attached to a work report.
self artifact add <path> [--entry <file>] [--why <text>]
self convention add "<text>" --artifact <id|path>
self state add "<text>" --artifact <id|path>artifact add stores a file or a directory with no report behind it. It is not evidence: registering a file never satisfies self work done.
A record that points at one renders a pointer to self artifact open <id> after its text, in context, in the handoff packet, in the terminal render, in self search, and on self state show. The retention cap charges the pointer and never the document behind it, so the length of the guidance no longer competes with the rules for room in context. self status reports a live record whose file is missing or altered.
Given a path rather than an id, the file is stored first and pointed at second, so a failure between the two leaves a file nothing points at rather than a record naming bytes that were never stored.
Remove a stored file's bytes and keep the record that names them
self artifact prune <id> --why "<reason>" [--project slug]<a-id> <name> pruned — the record is kept and its bytes are not
A done claim resting on that evidence stays auditable, because the completion check reads a unit's reports and never the files they name. Afterwards every reader answers pruned from the log: the row stays in artifact list and artifact search marked as such, the work unit's folded section and its report row carry the label, and artifact open refuses by name.
It is a person's call, disclosed and then confirmed by typing the artifact's own id back.
See how large the store is, and store the same bytes once
self store size [--json]
self store compactstore 182.2 KB — working tree 146.3 KB, .git 35.9 KB
artifacts: 2 recorded, 1 distinct contents, 11 B in 1 stored file
largest projects: demo 11 B
largest files: artifacts/demo/a-d1zbw-a.md 11 B
orphan bytes: none — every stored file is named by a record
git objects: 49 loose (196.0 KB), pack 0 B — run `self store compact`
compacting packs the history; it never rewrites it, so bytes already in the history stay there
store compact packs the store's git history once. It never rewrites history — the store syncs between machines by rebase, so a rewrite would break every other clone — and both the receipt and store size say so. self sync says one line when the loose objects have outgrown the pack, and nothing otherwise.
Bytes a project already stores are now stored once and referenced twice. A second attachment of the same content mints its own id and keeps its own name and entry, and shares the stored file. The digest recorded is always the digest of bytes the store holds.
Files a record no longer names are reported and never removed, since one cannot be told apart from a file another report is staging at that moment.
Record a goal or an objective for the whole workspace
A workspace holding many projects had nowhere to put the direction governing all of them, so it lived inside whichever project happened to hold it.
self goal add "<text>" --workspace
self objective add "<outcome>" --workspaceself objective and self objective show answer for another project's workspace objective, and self context at the workspace root states the direction once, above the project lines. At equal priority a workspace record reads above a project one. An existing objective is raised with self state place <id> --scope workspace.
The record stays in the store of the project it was recorded in. Archiving that project is refused while it still holds live workspace direction, and the refusal names both ways out.
A rule reaches the tracked instruction files only when it is recorded public
The block Superself writes into AGENTS.md and CLAUDE.md used to carry the full text of every rule the project had recorded, and every fold rewrote it — so an unrelated commit carried whatever had piled up since the last one.
self convention add "<text>" --publicWhether a rule reaches those two files is now a property of the record. Without --public a rule is internal and never reaches a tracked file. Rules recorded before this release carry no such mark, so they are all internal and leave the block on the next fold, with nothing to migrate.
Internal rules keep rendering everywhere they always did: self context, state.md, the HTML view, and the handoff packet. Visibility is stated when the rule is recorded; to change it, restate the rule with --supersedes.
Creating a work unit shows what it could contribute to
self work add now prints this project's open objectives and their checkpoints under the new id, each with the exact command that attaches the unit:
$ self work add "the flow works"
entity.confirmed recorded [01m0w90nk6e17darr4gw3ej8cr]
w-q68gp
o-83hnh unstarted reach preview
self work link w-q68gp --objective o-83hnh
m-xt8hf unstarted suite green
self work link w-q68gp --milestone m-xt8hf
1 open objective
With --supersedes it first names what the replaced unit was attached to, so a correction does not silently drop those links. Nothing here refuses; the unit is already recorded when this prints.
A report is evidenced by the checkout it ran in
A git worktree made inside a registered project carries no marker of its own, and directory resolution used to walk past it to the parent checkout. self report then attached the parent's commit and branch as evidence for work that exists only on the worktree's commit, and nothing warned.
A project's marker now governs a directory only when no other working tree of the same repository stands between them. A report made in a worktree is evidenced by that worktree's commit and branch:
report from the inside worktree -> {"commits":["cabc2c96b36a"],"branch":"feature"}
report from the outside worktree -> {"commits":["5196d6a4a051"],"branch":"feature2"}
A submodule and a nested unrelated repository are unaffected. self connect now writes the instruction block into the worktree it ran in.
A project whose checkout moved says its evidence cannot be judged
When a project's checkout has moved and was never linked again, no evidence verdict can be computed on this machine. self status, self context, and the workspace overview now say so instead of reporting success over the values frozen at the last successful reading, and self fold names the skip rather than answering that it refolded the project.
The notice names linking the project again wherever the checkout is now, and says that until then the verdicts stay as they are.
Every git the CLI runs is bounded
A self command could wait forever. Anything git starts — a hook, a credential helper, an ssh control master — inherits git's output pipes, so a hook that backgrounds a process and returns left the CLI waiting on a process it never started.
Every git the CLI runs now carries a deadline: 30 seconds locally, 300 seconds for one that talks to a remote or repacks the whole store. A git that had to be stopped becomes a refusal by name, stating what was run, in which directory, and for how long. A git that exited on its own while something else held the pipes is answered on, not refused.
Where nobody is at a keyboard, git is told not to prompt for credentials or passphrases, so a command run unattended fails instead of waiting. SUPERSELF_GIT_TIMEOUT_MS can lower the deadlines but never raise them.
Recording also stops reporting a write it did not make: a failed add, status, or commit used to be discarded, and each now refuses by name and shows git's own message.
Signing in sends the field the device approval expects
self login posted the label under a short key the device-grant contract does not name, so a service built to the contract rejected the request and the sign-in failed before the user code was shown. The request now carries device_label, which is the name the contract uses and the name this CLI already stored the value under.
Work units recorded before the cutover no longer appear
Work carried two state models: everything else read its older events as records, and work kept a separate projection beside them. That exception is gone, and the fold no longer reads the older work.* events.
A store that holds work units created before the cutover will show fewer of them after upgrading, and their derived pages are removed. Nothing is rewritten and no event is deleted: every one of them stays in the log and self log still prints them. A unit that is still wanted is set up again with self work add. Two machines on different versions will fold one store differently until both have upgraded.
Bounds and refusals
- A registered command line is printed, never run.
self skill runis a refusal, and there is no flag that fills a placeholder. self sweeprecords nothing unless--recordis passed, and it records into the project it runs in.- Taking a record back is refused, with the 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. A short list of kinds is refused by name, each naming the verb that does the job instead: a person's ruling on a design report, a registered artifact, a prune, a project archive or restore, process telemetry, and an undo itself.
- Pruning bytes is refused while a live record points at them, and for the evidence of a work unit that is neither done nor retired, so no ordinary prune can produce a false missing-file report. What an artifact left in the store's git history stays there.
- Files nothing names are reported by
self store sizeand never removed. - A single file attached to a report is bounded like a directory: over 100 MB is refused, over 10 MB attaches after one notice.
- Mixed versions and corrections: a store written by 0.10.0 folds correctly on an older CLI, but an older CLI cannot accept a proposed correction together with its replacement: it accepts the plan alone and leaves the unit it was meant to replace open. Propose a correction on the version that will accept it, or record the retirement by hand if a mixed-version acceptance has already happened.
Upgrade after publication
After the v0.10.0 release workflow finishes, install the published package:
npm install -g superself@0.10.0