Repository navigation
Releases: simonholliday/subroutine
Release list
Subroutine 0.10.0
This release changes the database schema, to
6c708db1582c.Install it, then run
subroutine db upgrade. That reports both versions, takes a
verified backup, migrates and checks the result - in that order. Stop the service
first if you are running one; expect it to be down for the length of the migration.
- The
subroutineplugin passes its token field asSUBROUTINE_PLUGIN_TOKENalone. It
passed it asSUBROUTINE_TOKENtoo, for programs older than 0.9.9, and its pin admits none of
them from this release. - An agent's API tool cannot make a calendar feed or give one a new URL, either of which
answered with a feed's address - a bearer URL with its owner's whole sight - into the agent's
context. And a route refused because it answers with a credential says so, where every refusal
said the act could not be undone and the command line would ask first. - Half a character in a link type, a tag name, a role, an account name, a scope or a search is
refused, naming the field, where it was a 500 over HTTP, a codec error to an agent and a crash
report in the terminal. Wherever else one reaches the database it is refused rather than
reported as a fault. - A link whose other end you cannot see is not yours to remove. A member could remove a
link to an item moved into a private project they were not in, and its owner's item lost
what blocked it. It is refused as a link that is not there, as their list of links already
leaves it out. - An agent acting as the terminal's account cannot sign anybody else out everywhere. Named
inlocal_user, it could sign a superuser out, which the same agent presenting a credential
is refused. A person at the terminal still can. - The conventions an agent reads give a filter that does list every document of a kind,
whatever its status: the one they gave listed open documents only, so a superseded design was
left out. And the agents' project list says its summaries were left out, rather than cut,
when none of them would fit. - Leaving an item while a change to it is on its way stays left. Completing or saving an
item and going elsewhere before it answered brought the item back. Back, Forward or a reload
onto an item keeps the arrangement it was opened from, so All items returns to the board or
the list rather than the agenda, and the list behind an item shows a save made on its page
straight away rather than at the next check for changes. - Two backups taken in the same second both succeed, under names a second apart. One of
the pair failed. - A PostgreSQL address setting a server option backs up and restores. One carrying a
space in its query -options=-c%20jit%3Doff, how libpq sets a setting per connection - served
every ordinary command, and every backup, upgrade and restore failed on it. - Crash reports are owner-only and carry no value a database statement was given, which
could include the hash a credential is looked up by. And a configuration file being rewritten
is made owner-only before the new text goes in, not after. db copyinto SQLite no longer writes through files others can read. The new database
was owner-only, but its write-ahead log and shared memory were made before it was, at the
default mode, and every copied row went through them.- Leaving an item while it loads leaves it behind. Going home, to another workspace or
project, or Back while an item was still loading drew it over the page you went to, and Back
then wrote its address over the one you had stepped to. And choosing a project from an open
item closes the item, where it stayed drawn with no list behind it. - The browser no longer draws the project you just left. A list or an agenda refreshed by
something that started before you moved - the background check for changes, or adding an item- could draw the old project's rows or agenda under the new one's address, and on a quiet
workspace they stayed until the next change.
- could draw the old project's rows or agenda under the new one's address, and on a quiet
- A workspace given to
-wby its id takes the checkout's file naming it by name. The two
were compared as written, so the file was ignored and the work went to the Inbox. - The agent tools name a project by its key, not its id. With a credential that may not list
projects, the conventions said what they were narrowed to by the project's id, and a checkout
whose file carries only ids said a write had gone in that id; both name the project now. - A repeat counted from when it is done keeps its start's time beside a whole-day
deadline. A weekly one starting Monday at 09:00 and due Friday, finished at 11:17, started
next on Sunday at 20:17; it starts on Monday at 09:00 again. - Capture reads a repeat in brackets, and says when it leaves one on several days. Water
the plants (every day) kept the repeat in its title with nothing said, and every day.) was
refused as a phrase it did not know; both repeat daily now. A repeat on several days is said
to be left as written wherever it is in the line, where words after it kept it quiet. - A repeat is refused in words that fit what is wrong. A phrase naming the 32nd of the month
was told to send a rule that is refused for the 32nd; a repeat holding a C1 control character
or a line separator was read as though it held a space and stored as sent; a frequency or a
weekday written in other letters was told how a number is written; and a rule with a space
after a;was answered in the words of the library that reads it - it is read now. Refusals
also put an before a word that needs one, and no longer write A A view's name. - A repeat's new rule is asked about the occurrence already made. A rule ending before that
occurrence was accepted when any date between came after now, and the series ended there
without a word; a series counted from when it is done was checked along a grid it does not
follow; and a date sent with a new rule was checked on the date it replaced. Each is refused
now, and a rule naming no date at all says so rather than that its dates have passed. - Clearing a repeat's deadline from now on asks whether the series is left a date. An
occurrence given a start of its own, then cleared of its deadline for every one from now on,
left the series with neither, and every completion after it was refused. It is refused instead. - A repeat started again is given its next occurrence when it has none open. One stopped
after its last occurrence was finished, then given a new rule, said it repeated and nothing ever
came round. A rule with no date left to give is refused instead. - Changing how often something repeats keeps what it is counted from. A new rule sent
without saying whether the series counts from its schedule or from when the last one was done- by the agent tools, which have no way to say it, or by
update --repeatwithout
--repeat-from- turned a series counted from completion into one counted from the schedule.
Leaving it out now leaves it as it was.
- by the agent tools, which have no way to say it, or by
- What privacy cannot hide about a name is written down. A project's key is one among the
projects beside it and a saved view's name one per workspace, so making either with a name
already taken is refused, naming it, even when what holds it is private and out of sight.
Privacy keeps such a name off every list but cannot make it a secret, and the API's own
descriptions,subroutine explain views,explain connectingand the connecting guide now
say so. - Opening a sign-in link asks before it signs a browser in. A browser signed in as nobody
now gets the page a browser signed in as somebody else already got: it names the account the
link is for, and only its button spends the link. So a link handed to somebody no longer puts
them in that account unseen, and a chat app's preview or a mail scanner that fetches the
address leaves it usable. A browser already signed in as that account is signed in again at
once. - A lost database is not made again, empty, by the next command. With the database file
gone and its folder still there, every command made an empty, world-readable file and said to
runsubroutine init; it now says no instance has been set up here. A restore over a lost
database restores without asking, where it made the file itself, refused to back it up and
asked whether to go on. - The change feed refuses a period only for events you could have seen. A reader who
cannot see a private project was refused for a period in which only that project's events had
moved to the archive, a refusal they could never satisfy, telling them something hidden had
happened then. - Walking the change feed back reads every event it still holds, and then ends. Once
anything had been archived, every page after the first was refused, though what it would
answer was all still there, and a client fetching several pages was refused where one reading
the database directly was answered. Asking past the end is refused, naming the journal, newest
first or not; an oldest-first walk past it answered nothing, as though nothing had happened. - Bringing a rule that binds the whole workspace into force takes what marking it takes, as
taking one out of force already did: setting it back to a status in force, making it a type
that binds, restoring it from the trash, and restoring, making public or moving out from under
a private project the project that holds it. Any contributor could put a retired rule back,
rewritten, into every agent's conventions. - On the night the clocks go forward, a repeating item's dates stay in order. Where the
date it repeats on fell in the skipped hour, the hour was carried into every other date, so a
start could land after its deadline and an event end before it started. A date whose time does
not exist that night now keeps its distance from the date the item repeats on. - ...
Subroutine 0.9.13
- An instance other people can reach stops sending OSC at this upgrade, until whoever runs it
turns it on. A workspace's administrator could have the server send a message to any machine
and port on every event, and nothing let the operator refuse.osc_enabledin the
configuration decides now: unset, an instance sends where only its own machine can reach it, and
sends nothing where it has apublic_urlor listens beyond loopback. Setosc_enabled = true
and restart to go on sending. Each workspace keeps where it sends, and its settings page says so
while nothing is sent. Whatever the setting says, nothing goes to an address in
169.254.0.0/16, where a cloud server answers questions about itself: a destination there is
refused, and one already set sends nothing. - OSC is sent once a change is saved, and never for one that was not. Each workspace's
messages went out while the change was still being saved, so one whose saving then failed was
heard all the same. And a music computer that could not be found held up every other
workspace's messages while it was looked for again, once a message; it is looked for once a
minute now. db copymoves an instance where a project has been prioritised. It failed in both
directions with a raw foreign key error, since a workspace's prioritised project names a table
copied after it; the reference is now filled in once everything it can name is there.- A credential that may not comment cannot edit or delete its owner's comments either. Adding
a comment asked forcomment:writein the item's project, and editing and deleting asked only
who wrote it, so a token scoped to reading, or one that may write only in another project,
rewrote and deleted its owner's comments. - A repeat naming two days, such as the 1st and 15th of the month, works. Every write of
such a rule answered 500, since reading it back as a sentence took the list for one number. It
reads every month, on the 1st and 15th now. - Giving an existing task a repeat keeps its end time, reminder and assigner. Each later
occurrence of a meeting made to repeat afterwards lost all three, where the same series filed in
one go kept them. A repeating sub-task's series is filed under the same parent as the task, too. - The repeat preview answers somebody who can reach two workspaces. It asked which workspace,
which nothing could say, so the browser showed that refusal under the Repeats box. It takes
?workspace_id=now, like every other request that can name one, and with several and none
named it reads days in the instance's zone. The browser names the workspace, and on an open
item it sends that item's own workspace and zone. - A repeat's coming dates fall where the series does, whoever reads them. They were worked
out in the reader's time zone, so a London series on Mondays at 00:30, read from New York,
listed Tuesdays and lost its first Monday. The reader's zone still decides what until
August means to them. - Over a remote connection,
showanduncommentsee all of an item's comments. They read
the first fifty, so an item commented on all day showed an old comment as its latest, and one
past the fiftieth could not be withdrawn. - The change feed no longer says events were pruned on an instance that has pruned nothing.
since=1answered 410 once a second workspace existed, and on PostgreSQL whenever the
sequence had a gap, which one rolled-back write leaves. Nothing prunes yet, so nothing is
refused as expired; the refusal returns with the pruning, measured against what it pruned. - An all-day repeat moved from now on across a clock change stays on its day. It was moved by
the hours in the move rather than the days, so a London series on Mondays moved from October
into November landed on the Sunday, and the occurrence after the moved one came round on that
same Monday again. - The terminal's first-run messages say what happened, and three crash reports and a hang are
gone.init --workspace <name>on an instance that already has its first workspace says the
name was not used and how to make another workspace, where it said nothing. Failing to open
anything says Nothing could be opened., since a write meets it as well as a read. The
missing-driver hint namesuv tool install 'subroutine[postgres]'as well as pip, and
user createsays is now a member of.initwhere the system cannot say who is running it
asks for--username;doc editsays so when the editor quits without saving, is not
installed or has an unbalanced quote; and atoken_commandwith an unbalanced quote is refused
naming the setting. Each of those ended in a crash report.document createwithout--body
reads standard input only when something is piped, so it no longer waits for ever under an
agent's shell. - Odd values are refused by name, where each answered 500 or was stored on SQLite only. An
agenda looking millions of days ahead, or reaching 31 December 9999 west of UTC; a rank filter
past what a rank can hold, such asimportance.gt=40000; a change-feedsinceorbefore
past the largest sequence number; a zone name longer than a filename; an estimate of²; a
status position past two billion; and a cursor holding a character that is not ASCII each
answered 500 on one backend or both. A NUL in a listing's filter or in an address, or in a
task's assignee, project, type or status, was a 500 on PostgreSQL, and an agent's tools
reported it as the database failing, with advice to check that it could be reached. A NUL in a
tag's description or in a verification's summary or excerpt, and an instance name longer than
255 characters, were stored on SQLite and a 500 on PostgreSQL. Each is refused now, saying
what was wrong. An agenda can no longer be built for the calendar's last day, which no clock
east of UTC can show. - Quick capture leaves a tag it cannot make in the title, and says why.
Ship it #ops,web
was refused whole, because a tag cannot hold a comma; it is filed as written now, with a
note carrying the tag rule's own advice. A tag longer than 128 characters is left the same
way. - Seven smaller gaps in who may do what are closed. A token sent without
Bearerin front
is no longer quoted back whole in the refusal, which put a live secret into a body an agent
keeps./v1/mecalls a credential narrowed only in the projects it may write narrowed in
every workspace, as the rest of its answer and/v1/tokensalready did. A milestone refused
a new type no longer says how many items it includes, a count of work the reader may not see
and of work in the trash.local_usernaming an agent whose person has left is refused, as
the agent's own token is. A workspace's short name written in capitals reaches its members
and its deletion, as it already reached its record. An account cannot be made with a time
zone nothing knows. And a project's owner must be a member of its workspace, when the project
is made and when it is handed over: an id naming nobody was a 500, and an account outside the
workspace was taken, so a private project made that way was hidden from whoever made it. - A credential narrowed to some projects administers nothing beyond them. An owner's or
administrator's token narrowed to one project could change the whole workspace: rename it,
change its settings - pointing its OSC anywhere included - add and remove its members and
administrators, curate its statuses, tags and link types, moderate other people's comments and
shared views, and move it to the trash. A superuser's could make accounts, list and revoke
other people's credentials, sign them out, and list every workspace on the instance. All of it
is refused now, saying why, and/v1/meno longer offers it to such a credential. Work in its
projects is unchanged, and so is each project's own administration. An agent that curates
the vocabulary or moderates needs a credential that is not narrowed to projects. - Only an owner makes, demotes or removes an owner. An administrator could make anybody
owner, themselves included, and then delete the workspace, or remove the owner who founded it,
though deleting the workspace is the one thing an owner may do and an administrator may not.
An administrator still grants roles up to administrator and removes anyone but an owner, and
whoever runs the instance is not bound by roles. - A backup whose counts could not be taken no longer reads as empty. Counting what a backup
holds stopped at the first table it could not read, and the listing then described the copy
as an empty instance, not your work. Each kind is counted on its own now, anddb backup
anddb backupsname what could not be counted. Apg_dump,pg_restoreorpsqlstill
running after ten minutes is stopped and reported in words, where it went on behind a crash
report.POST /v1/admin/backupsrefuses akeepbelow 1 before taking the backup rather than
after, and restoring a backup from the other engine says a PostgreSQL backup ends in.dump. - Three smaller faults in the browser are put right. A list whose project went away between
a page and its Show more fell back to the whole workspace part way down, skipping its first
rows until a reload; it starts from the top now. The note saying a project has gone stayed up
after moving to a live project or another page, and goes now. And an Until time earlier than
the start's, given no day of its own, ends the next day - 21:00 until 01:00 - where it took
the start's day and was refused as finishing before it starts. - A series moved from now on across a clock change keeps its waiting occurrence on its
grid. A weekly 09:00 series moved from October into November, past the clocks going back,
left the occurrence it had alread...
Subroutine 0.9.12
- A deferral given with a new repeat defers its first occurrence. An item made with a repeat
and a deferral in one request showed at once, since the deferral went on the repeat itself,
where nothing reads it. It goes on the first occurrence now, the one the request answers with. - A narrowed credential cannot act on its owner's account. A token given only the read
scopes, or narrowed to some projects or pinned to one workspace, could revoke its owner's
calendar feed, sign its owner out of every browser, and revoke its owner's other tokens,
including the unrestricted one. All three are refused now, as resetting and listing feeds
already were, and a narrowed token may still revoke itself. - A checkout's
.subroutinefile is read one way by the terminal and by the agent's tools.
A file naming its workspace as it is shown, Projects, or with an id gone stale beside a name
that matches, or naming the connection by a teammate's name for it, filed the agent's work in
the Inbox while the terminal filed it in the project; and an agent whose session named no
workspace was refused its first capture there.use --herenow writes the workspace as it is
stored, with the id of the connection it names. - A project named by its bare key goes to that project or nowhere. A checkout, or an
address's?project=, namingwebbesidealpha/webfiled into whichever was made first,
and a key two projects share filed into one of them in silence. The whole address is matched
first, a bare key only where one project has it, and a key several share is ignored, naming
them. And the terminal takes a checkout's project only in the workspace the checkout names. delete,restore,link,unlinkandmoveact on the item the address names. Given
an address on another connection,deleteandrestoreacted on this machine's item with
the same number, and said they had acted on the one named; and an item named in another
workspace or connection was read, as the second end of a link or the parent of a move, as
whatever wore its number beside the first.deleteandrestorego where the address points
now, and a link or a move across two workspaces is refused by name, since each joins two
items in one.- Continuing as another account from a sign-in link works in the browser. The confirmation
page told the browser to send its form with no origin, which the instance refuses, so
Continue as … ended in raw JSON. It sends the origin now, and still never the link. - An agent given
task:readandtask:writecan file in a project. Naming the project it
files in, moves to or narrows a listing by neededproject:readtoo, so an agent made as the
hosting guide recommends could file nothing in one. Asking for the list of projects still
needsproject:read, and a project named wrongly is refused without listing the others. - The guides no longer send a reader the wrong way:
subroutine use work/<workspace>
where they saidsubroutine use work, which is refused; the connection's own
SUBROUTINE_TOKEN_WORKwhere they offered a bareSUBROUTINE_TOKEN; the desktop and web
apps named as needing a sign-in flow that is not built yet; and pipx named as serving the
terminal, since the plugin needs uv either way. - An agent's tools say why their connection cannot be reached.
subroutine mcpnaming a
connection that does not exist, or one with no token, exited before the handshake with its
reason where no client shows it, so the tools were simply missing. It now answers every
message, the first included, with that reason. token create --storeandconnections add --urlcheck what they write. A name no
connection could have, such as My Laptop, and an address whose port is not a number up to
65535, each broke every command after them. Both are refused now, before anything is minted
or stored, and a capital in a name is lower-cased as it is everywhere else.serveon a port already in use says so and starts nothing, where it announced its
address and then printed uvicorn's own error.- A credential narrowed to some projects, or pinned to one workspace, can no longer reset its
owner's calendar feeds or list them. A reset hands back a working address that reads with
the owner's own sight, so such a credential could read everything its owner can see through
it. Minting one was refused already, and resetting and listing now are too. - A command with a
--workspaceof its own takes-was well, before the command or after
it.token create,agent create,calendar create,user createanduser listignored
a-wgiven before them, and the refusal for a missing workspace advised exactly that-w,
so following its advice was refused again. They take it now - and still never from
subroutine useor a checkout's marker, because each binds what it makes or shows to the
workspace - and the refusal quotes the command back with-win place. So
subroutine -w projects token createpins the token toprojects, where it used to make one
for every workspace.user add,user roleanduser removetake-wafter the command
too, and a-wand a--workspacenaming two different workspaces are refused. - Each entry of
subroutine://conventionsnames the project it belongs to, so an agent
reading what binds it can tell a rule for its own project from one for another. - A write made with a browser's session cookie has to say which page sent it. A browser
sendsOriginorSec-Fetch-Sitewith every write, so the browser pages are unaffected; a
write from a neighbouring site, or one carrying neither header, is refused and told to use an
API token, as agents and the terminal already do. It matters where several instances share
one domain, whose pages a browser treats as one site. - Two people acting at once can no longer remove the last of something between them.
Deleting the last two workspaces, removing or demoting a workspace's last two administrators,
or deactivating the instance's last two superusers could each succeed when done at the same
moment, because each counted the other as staying. The second now waits for the first and is
refused, as it would be one after the other. - On a local instance, the agent tools file into the project a checkout is marked for. In
a checkout marked withsubroutine use --here --project web,subroutine addfiled into
webwhile the agent'ssubroutine_addfiled into the Inbox, because the tools compared the
marker with the instance's own name rather than the connection's. The plugin now says where
it is standing on a local instance, as it already did on a served one. - A served instance no longer reads a
.subroutinefile in its own directory. Started
inside a marked checkout, it filed writes from everybody else's sessions by its own marker,
ahead of the?project=in their address. - A token holding a character no header can carry is refused rather than crashing. A token
pasted with a no-break space, or another character outside ASCII, crashed the plugin and the
terminal at start-up. Both now say that nothing was sent and ask for the token again, copied
whole, without quoting it. subroutine_showsays the status an item starts in - open, or draft - where it said
nothing, so the full record of an open item never said it was open.subroutine_updatesays what it saved when its dates are refused. It saves the other
fields first, so a title sent with an end before the start was saved while the answer read
as though nothing had been. Only a busy database said so before.- A time beside a date written without on sets nothing, and says so. Dentist 2 October
at 3pm was an appointment today at 15:00, with 2 October left in the title and nothing
said. The time now stays in the title, and the note names the day with it and says that
day was not read, since a date or a weekday is read after on, by or from; beside a
bare weekday the note says the same. Dentist on 2 October at 3pm is read as before. And a
time after at left as written for any other reason, such as a day the calendar has not or
a second time on the line, is no longer told that a time is read after at. - Four digits after a written date are always its year, and one outside the years read
leaves the date unread. File the return by 31 January 2024 was due on 31 January 2027,
with 2024 left in the title and called a time. A year from last year to fifty years ahead
is read; any other leaves the whole date, and any span it is part of, in the title with
nothing set, and the note says which years are read. A time or a quantity written as four
digits after a date loses the date too: Standup on 5 March 0930 sets nothing. - A timestamp no clock could have written is refused by name. One too near the calendar's
first or last day answered with a server error, and one with an offset outside -12:00 to
+14:00 was taken at its word, moving the moment to another day. A date field now refuses
both, naming the field, and a captured line leaves them in its title with a note - a date
written without a time included, so Deliver by 9999-12-31 is filed with the date in its
title. An offset is asked of only where it was written, so a date before a zone kept standard
time, such as 1800-06-01T12:00 in Anchorage, is not refused for its local mean time. - An end year from 2068 to 2076 is read as written, so from 2 October to 12 October 2070
is a span in 2070 in a captured line and inplan --untilalike, where one refused it and
the other stored 2026 to 2070. And a span refused with a weekday in it names the weekday
among what a span needs. - Projects after the fiftieth can be found. A lookup read one page of projects, so a
checkout marked for ...
Subroutine 0.9.11
- Subroutine now needs SQLAlchemy 2.1.1 or later, and upgrading brings it. Two things read
differently under it. Adatabase_urlof plainpostgresql://…, which names no driver, now
uses psycopg 3 - the driversubroutine[postgres]installs - where it used to look for
psycopg2. And in adatabase_url,%followed by two hex digits is read as an escape, as in
any URL, so a%meant as itself is written%25; the default database's URL escapes its
path already. - A retired document leaves the open list, as finished work does.
subroutine list,
subroutine search, the agent tools'subroutine_listandsubroutine_search, and the
browser's list now leave superseded and archived documents out, so retiring a document
shortens the list. Naming the category or a status in one, as a filter or in the search line,
asking about the trash, or naming one by its number still finds them -subroutine list --filter status_category.in=superseded,archivedlists every one - and the browser's
everything shows them.GET /v1/documentsstill lists every document unless it is asked
foropen=true. Reading an instance on an older release, the terminal lists every document
from it, as that release does, and says so under a list showing a retired one. - A read the instance refuses ends as a failure.
subroutine list,search,view,
agenda,journal,changesandwhoamiexited 0 when the instance refused what they asked,
such as a mistyped status, a project that is not there or a parameter an older instance does
not know, andlist --jsonprinted[], which is what an empty list prints, so a script took
the refusal for nothing to do. They now exit 1, with the refusal on standard error and nothing
on standard output, as a failedupdatedoes, andjournal,changesandwhoamiunder
--jsonreport the refusal where they said nothing. Where one connection refuses and another
answers, what answered is still printed, the one that refused is named, and the command exits
0;--strictstops instead. - The browser says what to do when an event is refused a deadline. Adding an event with a
deadline, or saving a task that has one as an event, said only that an event cannot have a
deadline; the note now adds the remedy - clear the deadline, and set Until to when it is
over. Other refusals on those forms still show only their own sentence, since many of their
hints name a terminal command. - A repeating event at a time of day keeps its hour when the clocks change. A calendar
subscribed to a feed showed a weekly 09:00 meeting at 08:00 once the clocks went back, because
each repeat was written at a fixed UTC time. A repeat at a time of day is now written on the
clock of the zone it was set in, with that zone's clock changes described beside it, so it
stays at 09:00 all year. Events that happen once, all-day ones, and repeats in a zone without
summer time were already right and read as before. - An account that belongs to no workspace is told so.
subroutine add,user roleand
user removecrashed, asking for the report to be sent in, when the account they ran as was
a member of no workspace - a server's own account taken out of the team's workspace, or
somebody who had removed themselves. They now say You are not a member of any workspace.
and how to be added to one. agent create --heresays what to do when a token cannot be kept private. On a drive
that does not keep file permissions, such as a network share mounted with fixed modes, it
said that anybody who could read the directory could read the token; it now names the
directory and the two ways out - a checkout on a local disk, or--storewhere one agent is
enough for the machine. Where the permissions hold, it says the file is readable only by
your account on this machine, since a folder that syncs elsewhere copies it regardless.- A deferral on a repeating item is never asked which occurrences it is for. It was, and
answering every one from now on wrote the date to the series, which clears it on each new
occurrence - so the answer meant just this one while saying otherwise.subroutine defer,
the agents'subroutine_updateand the browser's edit form now defer the one in front of
you without asking.defer --from-now-onand--just-this-oneare still accepted, and say
they made no difference;applies_tosent with a deferral alone is reported the same way
rather than refused. Given the repeat itself, whose numbershowprints after from
repeat, a deferral is refused by name, and the answer names the occurrence to defer instead,
where it said Deferred and changed nothing;skipnames the occurrence the same way. - An agent's write to a served instance is filed where its checkout says. The agents'
tools run on the instance, so they read the server's own directory for a.subroutinefile
and never found the caller's: a task or document written without naming a project landed in
the workspace's Inbox. Thesubroutineplugin now sends its checkout's project with every
request, and the instance files by it.subroutine-remoteand any other client can name a
default project in the address, as?project=, beside?workspace=. A project named in the
write still wins, the Inbox is still the answer when nothing says, and the write is checked
against what the credential may do wherever it lands. - The agenda's footer reads as a sentence. It ran its first count into a colon, as in
11 not shown here:4 dated further out, and a page holding one item said 1 items. - The browser uses a spaced hyphen where it used an em dash, as everything the program
prints already did: in the priority labels, the add box's example and the settings pages'
notes. An empty cell is a bare hyphen. - Agents are told that a decision is always a document. The instructions an instance gives
every connected agent now end: a decision is always a document, in the project it governs,
however it came up. A decision written only as a comment on the task at hand never reaches
the list of what is in force, which is what the next session reads. - A place's heading holds its own controls, and the saved views are a menu beside its
settings. A project's heading now carries Prioritise, and its settings button says
Project settings - Workspace settings on a workspace - while the menu under your name
keeps Settings. The saved views are a menu under a button naming the view you are on, or
Choose: each view in it is a link, so it opens in a new tab like any other, and None
leaves the one you are on. Each view you may forget has a Forget of its own, and Save this
view opens its form in a panel under its button. The heading's controls sit beside the
place's name when they fit and below it when the name is long. The bar above the rows appears only when a
tag, a person, a priority or the top-level collapse narrows them, and its Show everything
keeps you in the place. The Whose work selector and the masthead's place selector are
capped, so a long name no longer pushes the filters or the tabs onto another line. - Show everything drops a priority filter too. The bar named it as a narrowing and the link
kept it, so opening Show everything in a new tab still showed only the work it had ranked. - Choosing the board while the agenda is loading shows the board. When the agenda's answer
arrived after you had chosen board or list - as it can when the page refreshes the agenda
on its own, or when a server that has just restarted answers slowly - the agenda was drawn
under the other view's tab, one column wide, until you reloaded the page. A late answer is now
set aside, and the board or list you chose shows its own rows. - Claiming an item no longer changes its version. Taking a claim, renewing it or giving it
back moved the version, so an agent that read an item, claimed it and then saved with the
version it had read was refused every time, and a form open in the browser was refused
whenever somebody claimed the item under it. The version now moves only when what the item
says changes. The agent tools' replies to a claim and to an update also say the version they
leave the item at, so the next save needs no read first. - A
%in the database's address no longer stops setup or an upgrade. Adatabase_url
can hold one, such as a PostgreSQL password with an@in it, which a URL writes as%40,
andinit,db migrateanddb upgradecrashed on it, as diddb restorebringing an older
backup up to date. Under SQLAlchemy 2.1 the default database's address holds one too,
wherever its path has a letter outside ASCII or a character such as'or@, as in the home
of an account namedjosé, so this is also what letsinitset up there. - Saving an item in the browser no longer moves its dates when you are in another time
zone. The edit form shows an item's dates on the item's own clock and sends every one back
with each save, and the instance read them on the clock of whoever saved - so a colleague in
New York who fixed a typo in a London appointment moved it five hours, and every one after it
when the item repeated, while the page went on showing the same time. The form now says which
clock its dates are on, so they stay where they were, and a time you change lands where the
form showed it. - A repeat in a zone without summer time is on the right day in a subscribed calendar. A
timed repeat in such a zone - Tokyo, India, Arizona - was written in UTC, so one whose time
falls on another day in UTC landed a day out: every Monday at 08:00 in Tokyo arrived as every
Tuesday, and a month's 1st as its 2nd. It is written on its own zone's clock now, as a repeat
in a zone with summer time already was. A repeat...
Subroutine 0.9.10
subroutine_whoamisays beforehand when a sign-out would change who the tools are. Where
thesubroutineplugin's tools act with the token in its Agent token field, it adds a line
saying so: Claude Code deletes that field when you sign out of Claude Code, uninstall the
plugin or remove its marketplace, and the tools then act as whoever this machine's own
credentials name. An agent of the project's own, fromagent create --here, is out of reach of
all three. It takes a plugin that passes its field under a name of its own, as current ones
do; an older one cannot tell the field from a token set any other way, and says nothing.- What to check before signing out on purpose is in
docs/connecting.md, and
the skill tells an agent what the new line means and what to offer.
Subroutine 0.9.9
Fixed
-
A span with a year only on its end starts no more than eleven months before it. A start
with no year is counted back from an end that has one, so Trip from 3 November to 2026-10-30
was stored as a trip that began on 3 November 2025. Counted back further than eleven months,
a span is now left in the title and reported, and giving the start its own year stores it. A
weekday in front has to fall in that year too: from Friday 2 October to Tuesday 12 October
2027 is reported, since 2 October 2027 is a Saturday.subroutine planand the agent tools
read a start and an end the same way, soplan 1 "2 October" --until "12 October 2027"is ten
days in 2027 rather than a year and ten days. -
The agent tools read a start and an end given together as one span, as
subroutine plan --untildoes. Each was read on its own, counting from today, so friday until monday said
on a Saturday was refused as finishing before it started. -
An ISO time keeps its UTC offset. Workshop from 2026-10-02T09:00-05:00 was read as an
appointment from nine until five the next morning, and the deferral and the offset were both
lost. A time after aTis read with its offset. With a space in place of theT, the date
is read as any other date is, so on 2026-10-02 09:00-10:00 is an hour from nine; write the
Tto give an offset. -
A start written as an ISO time keeps its time. Workshop on 2026-10-02T09:00 was stored
as starting on 2 October with no time, and the time and any offset were dropped without a
word. A deadline and a deferral always kept theirs. -
A date that does not exist is reported rather than refusing the whole line. by 29
February 2027 and on 31 April stopped anything being added. They now stay in the title and
are reported, and the rest of the line is used. An ISO date that does not exist, such as
2026-02-30, is still refused by name, as it is when sent on its own. -
A line dated at the very end of the calendar is added rather than failing. from
9999-12-31 by tuesday and a few lines like it were an error from the server. -
Two times joined by an em dash are treated as two joined by a hyphen. Where the pair could
not be read as a range, the em dash form set a start at the first time and left the second in
the title without a word. It is now left whole and reported, as the hyphen's always was. -
An item's history and the change feed say what an unlink removed. Both said unlinked it
from something, in the terminal and in the agent tools, while the journal beside them named
the other item. -
A loop through two relations is refused, as a loop through one always was. X blocks Y
followed by Y precedes X was accepted, and so were two milestones that each counted the other,
throughincludesand a relation of a workspace's own in the same category. A loop that holds
no work up is now called a sequence that cannot come back to where it started, not work that
can never begin. -
A link type cannot move into or out of the gating or counting category under its links.
Movingblocksto counting made every task that blocked something read as including work, and
movingincludesto gating held every milestone's work off--ready. While links use a relation
such a move is now refused, saying how many; any other move still goes through. -
Downgrading the database below 0.9.7 finds the milestone type and
includesunder any name.
A workspace that had renamed either made the downgrade fail on a database constraint, rather than
complete or refuse in its own words. -
A milestone counts only the work you can see, and says when it includes more. Its row
counted work in projects you cannot see - how many pieces, and how many were done - while its
own page counted only what you could. Both now count what you can see, and add and more you
cannot see where there is more, never how much. Included done still means all of it, seen or
not, so finishing what you can see does not tell you the milestone is finished. -
A project's agenda can be saved as a view. On a project, whose page opens on its agenda,
Save was refused for narrowing an agenda by a search line you never typed. The view keeps
the project, and opening it draws that project's agenda. -
Renaming a project no longer stops on a saved view it cannot read. One shared view with a
malformed search line refused every rename in its workspace without naming the view. The rename
now names it in its question, and counts a view that names the project inside a path, such as
project:acme/blogwhenacmeis renamed. -
subroutine view runwith--connectionreads the view on that connection. It asked the
connection a write goes to, and said that one could not be reached. -
A view's name with no letter or digit in it is refused as you typed it. Over HTTP it was a
redirect the program could not read. -
A shared view saying
touched_by:meoranswers_to:meis marked as drawing each reader their
own work, as one sayingassignee:mealways was. -
A credential narrowed to some projects cannot change what a shared view says, as it could
already not share one. -
A filter refused for its operator says what that field takes. The advice written for a
timestamp was given to every field:ref.gtewas told to useref.gte,ref.nethat a ref is
stored to the microsecond, andcreated_at.isthatcreated_attakesis. A range is now
suggested only for a field that takes one, andison a field that always has a value says so. -
agent create --hereworks in a directory whose name a.gitignoreline would read as a
pattern, such as#notes,[draft]or!x. The line it adds is escaped: it was a comment, a
class or a negation, and the command refused, leaving the line behind. The command a refusal
suggests now names the file from where you are, in a form a shell runs as printed. -
agent create --heresays when the plugin's tools will not follow. It names the variable
after the connection a write goes to, and thesubroutineplugin reaches this machine's default
unless its settings name another, so where the two differ the tools go on as before. It said
they would name the agent. -
agent create --hereno longer leaves a new credential where git would take it, or where
nobody has it. The file is staged in a directory git ignores and is on the disk before it is
renamed into place. A settings file that is not UTF-8, or holds text UTF-8 cannot, is refused
before anything is minted, and any failure after minting shows the credential, where only a
failure of the disk did. A settings file that is a link out of the repository is refused by
saying where it leads. -
A search for an item's parent answers in the agent's tools and the browser.
parent:12
is asked of tasks and documents both, and each refused the other kind's number, so the search
failed whichever kind the number named - and on a project's page read as the project having
gone. The terminal already answered it. A number that names nothing is still refused. -
Moving a document and revising it in one call does both. The move put the document's
version up, and the revision was then refused as somebody else's change - the call's own - and
answered that nothing had changed. The browser's edit form did the same when its Parent box
changed. A version given with such a call is now checked on the move, before anything is
written. -
A refusal after part of an act says what had already been done. Several links made in one
call, a reason given todone,skip,plan,deferorupdate, anduser createwith
--browseror--terminalare each more than one request, and a busy database on a later one
said this request changed nothing while the earlier ones stood - so a reason was lost, or a
sign-in link made and never shown. The refusal now names what stands, and the command that
finishes the rest. -
user create --jsonwith--browseror--terminalis refused before anything is made.
It printed the account alone, so the sign-in link or credential it had just made was live and
had been shown to nobody.--jsongoes with neither now: a secret is shown once, to a person,
and not to a script. -
The
subroutineplugin passes its token field under a name of its own,
SUBROUTINE_PLUGIN_TOKEN. The field travelled asSUBROUTINE_TOKEN, which is also the
variable you may export in a shell for your default connection. An editor that left an empty
field's variable unset, rather than set and empty, would have let a token you exported be
taken for the field and sent to whichever instance the plugin names, and the tools could not
have said that the field had emptied. With this release and a refreshed plugin, neither can
happen. The plugin passes the field asSUBROUTINE_TOKENas well, so an older program goes on
reading it. -
An appointment inside one day names that day once: Mon 4 Oct, 14:00 to 15:00, where the
terminal and the browser said Mon 4 Oct at 14:00 to Mon 4 Oct at 15:00. A span across
midnight still names both days, because there the second day is the news. -
An event can be given an end in the browser. The form has Until beside Starts, so an
appointment from 11:00 to 13:00 is written there as it is at the terminal, and an end given a
time and no day ends on the day it starts. The item page shows a span as one fact. Due was
the only other date on the form, and an event refuses a deadline - which now says to set
until to when it is over, where it said only to give it a start. -
A time range written after its day is read. Meeting tomorrow from 11:00 to 13:00 and
Standup on monday from 9am to 5pm file appointments, where the first stayed in the title
and the ...
Subroutine 0.9.8
Added
- A workspace can send what happens in it to music software, over OSC - a short message each
time an item is filed, finished or commented on, or a milestone closed, which software such as
Subsequence can answer with a sound. Most people will never need it: nothing is sent until a
workspace's administrator says where to send, in the last section of the workspace's settings
page. Every event goes as it happens and nothing waits for it, titles go only if you turn them
on, and nothing goes from a private project. docs/osc.md has the addresses and
what each message carries.
Subroutine 0.9.7
This release changes the database schema, to
58c81c09d101.Install it, then run
subroutine db upgrade. That reports both versions, takes a
verified backup, migrates and checks the result - in that order. Stop the service
first if you are running one; expect it to be down for the length of the migration.
Added
-
Every workspace is given a milestone type and two new links:
includes, for what counts
toward a milestone, andprecedes, for an order that holds nothing up - Precedes at one
end, Follows at the other. A workspace that already has a type or a link under one of
those names keeps its own. -
A milestone is never offered as work, whether or not anything is in it yet:
--ready
and an agent's next work leave it out. The agenda shows one by its date - under Today,
Overdue or the look-ahead, where a missed one is late - and never under Next, nor as work
somebody else is holding up, and the terminal's closing tip never suggests ticking one off.
One with no date is counted on the line that says what the day is not showing.
undated_milestones_totalis new on the agenda response and is optional, so an older client
is unaffected. -
A milestone counts what it includes: an
includeslink from the milestone to its work,
as insubroutine link 12 includes 43. Itsshowreads Links (3 of 5 included done) on
the terminal, in the browser and to an agent, andshow --treewalks what it includes and
what that waits for. Its row on every listing says 3 of 5 included done too, and
included done once all of it is, so a roadmap reads down a list; completing it stays
yours. Only a milestone includes, and it includes only tasks - another milestone among
them, so a phase can sit inside a roadmap. A loop of them is refused, and so is retyping a
milestone that includes work.included_done,included_countandincluded_done_count
are new on a task and are optional, so an older client is unaffected. -
subroutine explain milestonessays how milestones work: features, milestones, and the
difference betweenincludes,blocksandprecedes. Thelinkhelp's example is
includesnow, where it used to teach a roadmap as things blocking one item. The plugin's
skill teaches the same, from this plugin update on. -
subroutine agent create <name> --heregives one project's Claude Code sessions an agent of
their own, in one command. Run in the project's directory, it writes the credential into
.claude/settings.local.jsonunder the variable for your connection, makes the repository
ignore that file - adding the line to.gitignoreif it needs one - and prints nothing
secret, so your agent can run it for you without ever seeing the credential. If it could not
finish, it refuses before a credential is made, leaving at most the line it added to
.gitignore, and if the settings file cannot be written afterwards it shows you the credential
rather than lose it. Reload the window straight afterwards: a session
already open may act as the new agent in its shell before its tools catch up. With
subroutine-remoteit names the shell only: that plugin's tools present the one token it
was given, in every project.agent createandsubroutine_whoamiboth say so, and name the
twoclaude plugin ... --scope localcommands that switch one project to thesubroutine
plugin, whose tools take the agent too. A different agent in each
project is built around it now. -
Your agent offers it. An agent that finds itself working as you in a project offers to
give the project an agent of its own, andsubroutine_whoaminames the command. The offer is
in the plugin's skill, so refresh the plugin to get it:claude plugin marketplace update subroutine, thenclaude plugin update subroutine@subroutine. -
The
subroutineplugin's tools say so when its token field has been emptied, rather than
going on as somebody else without a word. Claude Code empties that field when you sign out of
Claude Code, uninstall the plugin or remove its marketplace. The next time the tools start,
subroutine_whoamiand the answer to their first write say who they act as now, and how to
put the token back. A field that was always empty is never mentioned.
Changed
-
SQLAlchemy stays on 2.0 for now. Version 2.1 came out on 24 September and Subroutine is not
yet tested against it, so this release asks for a version below 2.1. An installation made since
then, which will have 2.1, is moved back to 2.0 when you upgrade. -
agent createsays whom the new agent answers to, which is whoever ran it. When that is
another agent - your own, giving a project its agent - it also prints the one command that
makes you the parent instead,subroutine user transfer <name> --to <you>, which only a
person can run. The plugin's skill says the same, and that mentioning the feature to your
agent is not a yes. -
agent createwithout a hand-over names the variable to put its credential under, for a
project on the machine it ran on, rather than leaving you to work it out. A name that is not
your connection's is ignored without a word. -
Every page that takes a token says where it is kept and what removes it. A token typed
into a Claude Code plugin's field is kept by Claude Code, which deletes it when you sign out of
Claude Code, uninstall the plugin or remove its marketplace; updating the plugin keeps it.
Where your token is kept
compares the seven places a token can live, and the plugins' own fields and
subroutine explain connectingsay the same.subroutine-remote's field no longer tells you
to ask for a new token when one is refused: enter it again first. -
Nothing the program prints carries an em dash. Command output, help, refusals, the API's
route and schema descriptions and the agent tools' descriptions use a spaced hyphen, and an
empty cell in a compact listing - no due date, no priority, a workspace with no timezone - is
a hyphen. -
Subroutine's mark is Lucide's waypoints, in the browser's tab, on a home screen and
beside the name at the top of every page, where it was kanban: points joined up, rather
than a board. -
view runno longer offers--mergedand--strict, which did nothing for a view: it runs
on the one connection it was saved on. Both are still accepted, so a script that passes either
goes on working. -
The journal is a tab beside Agenda, List and Board in the browser, on a workspace's pages
and on the journal itself, which now draws those tabs and the search too, so you can go from
a workspace's journal straight to its list or board. Choosing another workspace there opens
that workspace's journal rather than its agenda. An item's button reads Item journal.
Fixed
-
An instance says so when a request carries no token, rather than telling you your token may
be mistyped, revoked or expired.subroutine-remotesends an empty one once Claude Code has
emptied its token field - which signing out of Claude Code, uninstalling the plugin and
removing its marketplace all do - and the refusal now names that field and says to enter the
token again with/plugin. -
A refusal says what to do straight after what went wrong. Claude Code shows the first 500
characters of a refusal from an HTTP plugin, and the hint came last, so it was cut off. It now
follows the detail. -
Saving a token or a setting can no longer leave
credentials.tomlorconfig.tomlempty.
Both were cut to nothing and then written, so a full disk or a killed process part way
through lost what they held. Each is now written beside the old file and renamed over it, so
you find the old file or the new one, whole. One you keep elsewhere and link into place stays
linked. -
The
subroutineplugin's token field is used whichever instance the plugin names. Pointed
at a connection other than this machine's default, the plugin's token was skipped, and its
tools acted as whoevercredentials.tomlheld for that connection - usually you. A token you
export in a shell still goes only to your default connection. If you pointed the plugin at
another instance and left a token for your default one in its field, that token is now sent
to the instance the plugin names, which refuses it: put that instance's token in the field, or
empty it. -
A date written with its year is read in that year. by 31 March 2028 was due on 31 March
2027, the next one, with2028left in the title, and a start, a span and a date typed at the
command line did the same. The year is now read with the date it follows -14 March 2028or
March 14, 2028- and a year written once at the end of a span is both days', so from 2
October to 12 October 2027 is in 2027. A written date with no year is still the next one.
Four digits after a date count as its year from last year to fifty years ahead; anything else,
such as a time written0930or 1500 chairs, stays in the title and is reported. -
A range with am or pm on its start alone ends when you meant. Meet on friday at 9am-5:30
was stored as ending at 5:30 the next morning, and Party on friday at 11pm until 12:30 at noon
the next day. An end written without am or pm is now the first time after the start that a
clock shows it - half past five in the afternoon, and half past midnight - so 9am-9:00 runs
from nine to nine, and no range read this way is longer than twelve hours. An end on the 24-hour
clock, such as05:30, is read as written. -
After you add something in the browser, the note saying so links to it. It named the
new item's number and title in plain text, so going to it meant finding it in the list....
Subroutine 0.9.0
0.9.0 is the release candidate for the first ready version of Subroutine. Everything
planned for it is in.
If you use the
subroutineplugin in Claude Code, it stays on 0.8.x until you refresh
it. It runs the version its manifest names and never moves to a new minor on its own. Run
claude plugin marketplace update subroutine, thenclaude plugin update subroutine@subroutine, and start a new session.subroutine-remotetakes its tools from
your server, so it follows whatever version the server runs.
This release changes the database schema, to
cfeea5d4dd35.Install it, then run
subroutine db upgrade. That reports both versions, takes a
verified backup, migrates and checks the result — in that order. Stop the service
first if you are running one; expect it to be down for the length of the migration.
Fixed
-
An appointment with a deadline beside it is filed rather than refused. Workshop from
Monday 9am to 5pm by friday stopped the whole capture -subroutine addfailed, and the
API, the agent tools and the browser's capture box answered with an internal error - from
the release that learned to read an appointment's two ends. The deadline is counted from
the appointment's day now, as it is from any other start. -
A range of times is read the way it is written, or not at all. 0.8.29 began reading two
times as an appointment and read three kinds of line wrongly without saying so. Dinner on
friday at 7:30-9:30pm was stored as 07:30 to 21:30; a range beside a defer made an
appointment today; and a range in the middle of a sentence was read as one because a date
came later in the line. A meridiem written once now counts at both ends where it fits. A
range whose end reads earlier than its start, with nothing on the line to say which clock
it is - 12:30-1:30 - stays in the title and is reported, as every range was before. And a
time counts as written beside a date only when nothing stands between them. -
Two ISO spellings are read as the words are, and the calendar's edges no longer break
capture. from 2026-10-01 by friday is due on the Friday after the first rather than the
one before it. from 2026-10-02T09:00 to 17:00 is an appointment, where it was a hidden
defer with to 17:00 left in its title. A span in the first or last years the calendar
holds is read, where it was an internal error. -
A search narrowed to a project finds it in whichever workspace holds it. On an
instance with two workspaces,subroutine search "project:web"put the question to both,
the one withoutwebrefused, and its refusal threw away the rows the other returned -
so the search said nothing matched.subroutine list --project webhad worked for a long
time; the tolerance was keyed on the flag rather than on the project. The same was true of
parent:12when item 12 is a task and the documents were asked too. A project that is in
no workspace at all is still refused by name. -
A name of
.or..is refused rather than sent over HTTP. From a terminal on a
served instance,subroutine user remove ..removed the whole workspace and said a
member had left, because an address reads..as the level above;project unshare web ..
reached the project the same way. Both are refused before anything is sent now, and a
username or a tag cannot be.or.., so nothing is ever called one. The workspace went
to the trash, not away, so anything removed this way can be restored. -
Whose timezone an agent's days follow is named correctly.
whoami, the agent tools and
subroutine user timezonenamed an agent's account parent whenever it had one, whether or
not that account had set a zone - so an agent whose days came from further up, or from the
workspace or the instance, was told the wrong account. They name the account that set it
now, or say that nobody has./v1/mepublishes it asreader_timezone_set_by. -
A refused credential in the browser says why. Issuing or revoking one on the People page
and being refused drew an empty banner, because the notice was handed the error rather than
its words. -
A username cannot be
set,unsetorme. A search reads each of them as something
other than a person - assigned to anybody, to nobody, and to whoever is asking - so an
account called one could not be found by it, and a board drew a person called unset under
Nobody. -
-reads what is piped everywhere a command takes words.subroutine comment 42 -
stored a single hyphen and reported the comment as noted, wheredoc create,doc edit,
addandupdateread the pipe.--descriptiononproject create,project update,
workspace createandworkspace updatedid the same, and so did--becauseondone,
skip,plan,deferandupdate. All of them read the pipe now, and a-with nothing
piped is refused rather than stored. -
A link no longer reads as the item being created. A link is a row of its own, so the
event's action iscreated- and the journal, the change feed at a terminal and the journal
in the agent tools each printed that bare action. A linked item appeared twice, at the same
second, by the same person, with nothing saying which line was the link, and the obvious
inference is that something was filed twice. All three now say a link was made and name the
item at the far end and the kind of link, which is what the browser has said since it was
built. Undoing a link says so as well, where it used to saydeletedbeside an item that
still exists. -
The capture shorthand's
!3can be typed the way the help shows it. In an interactive
bash or zsh a!starts a reference to an earlier command, and double quotes do not stop it- so
subroutine add "Call the dentist before Sunday !3 ~15m #health", which is what
subroutine explain captureoffered as its one worked example, was either refused as
event not foundor had an earlier command's text pasted into the title with nothing said.
The task was created either way, so nothing looked wrong. Every example this program prints
is single-quoted where it carries a!, andexplain capturesays why.
- so
-
An agent's tool call no longer waits five seconds on itself. Reaching an instance over
the stdio MCP server runs the instance in process, and each tool call opened a second
database session while the request still held its own - two connections, and on SQLite one
waits the other out. It surfaced asdatabase is lockedafter exactly five seconds, from a
single process with nothing else running, and it needed only one call to one server. The
request now holds nothing while the tool works. -
A credential used only for reading is recorded as used. Resolving a credential writes
when it was last used, and the command line and the stdio MCP server wrote that into the
transaction doing the work and then threw it away - so a token in daily read-only use read
as never used, which is exactly the evidence somebody acts on when deciding whether to
revoke one. The served API has committed it separately since 0.6; now both do. It also
held a write lock for the whole of every read in order to record a timestamp, which on
SQLite makes every command a writer whatever it is called.
Removed
/v1/metano longer publishessortableandselectable. They were renamedorder
andfieldsin 0.8.1, both spellings were published since, and 0.8.1's notes said the old
pair would go in 0.9.0. A client from 0.8.1 on reads the new names already. A client from
this release reads only the new names, so it reads a 0.8.0 instance as offering nothing to
order or select by: upgrade that instance first.
Added
-
A search line can narrow by status category, so the board a team asks for first can be
saved.status_category:in_progress- anddone,todoorcancelled, or a document's
draft,current,supersededandarchived- compiles like any other term now: in the
search box, on the command line, and inside a saved view. A saved view's narrowing is a
search line, so until now what the team has in progress was the one view that could not be
kept. The words are the product's rather than your workspace's, so a status you renamed still
answers to the category it belongs to; a word that is no category at all is refused by
listing the four that are; and asking for a finished category reaches finished work without
also sayinginclude_completed, exactly as the older spellings do. -
A view can be saved under a name, and shared with the people you work with. A saved
view is a query and an arrangement: one search line for what is narrowed, written the
way you would type it into a search box, and a few named fields for how it is drawn - as an
agenda, a list or a board, in this order, grouped on this axis.GET,POST,PATCHand
DELETEon/v1/views, addressed by the name you gave it rather than by a number, because
a view exists to be typed, sent and clicked. A view is yours by default and shared on
purpose, so nobody's half-finished experiment appears in everybody's list; only the person
who saved one can change it, and sharing publishes a view rather than handing it over. Names
are one per workspace so that a link you send somebody draws what you were looking at. -
A view can be saved and reapplied from the browser, under the place it belongs to. What
is showing becomes a search line and an arrangement: the chips you set - a tag, a person,
an importance, who somebody answers to - are written as terms, and the two a search line
cannot carry are named before you save rather than dropped quietly. Coming back to one
expands it into the address rather than replacing it with a name, so what you send a
colleague is st...
Subroutine 0.8.29
Added
- A board can be arranged by who the work is assigned to.
?view=board&group_by=assignee
in the browser,group_by=assigneeonGET /v1/tasks. There is a column for each person
holding work in what you are looking at, and one headed Nobody for what nobody has been
given - which is drawn even when it is empty, because nothing here is unassigned is worth
being able to see. Columns follow the work rather than the roster, so a large team does not
produce a board of forty columns, and a person holding nothing here has no column. Dragging
is offered only on the status board, where a drop means something. - A parent says what the work beneath it adds up to.
subroutine showon a milestone,
and its page in the browser, now carry a line like12h beneath this, over 9 of 14under
the sub-task list - the whole subtree rather than just the children, so a middle layer's
own estimate is not lost. It says what it could add up: most backlogs have rows with no
estimate, and a bare total over those would report part of the work as if it were all of
it, so the coverage travels with the number. A parent whose work is entirely unsized says
so rather than reporting nothing.GET /v1/tasks/{ref}gainsbeneath; a listing does
not carry it. - Whoever picks up a leaf task is shown what governs the milestone above it. Read
first used to name a decision or a specification only on the item somebody had linked it
to, which is usually the parent - so the person reading the milestone, who often wrote it,
got the reading list, and the person actually doing the work got nothing and had to notice
part of #7and take another hop. A task or a document now inherits what binds anything it
is filed under, nearest first, each rule said once and labelled with the item it came from
so you can go and read the reasoning. Nothing changes on an item whose rules are its own.
In the terminal, in the browser, on the agent tools and at
GET /v1/tasks/{ref}/governing, which gains aninherited_from. - An agent can set a deadline.
subroutine_updatetakesdue- a date, a date and
time, or''to clear it - so an agent correcting one stays in its own name. Before this
there was no argument for a deadline on the curated tools, and an agent that met a wrong one
reached for a terminal instead, where the correction was recorded as whoever's credential
that terminal holds. - A captured line reads an appointment's two ends. Dentist on Monday at 2pm til 3pm
files a start at two and an end at three, where the end used to stay in the title. Write
them as2pm-3pm,at 9am to 5pm,from Monday 9am til 5pmor on the 24-hour clock, on
a day the line names or on today, and an end earlier than its start is the next morning -
so Party on Friday at 9pm til 1am ends on Saturday. Two times in prose are still left
alone, so Email Bob re: 3pm is untouched; a span whose two days each carry a time is
still held back and says so.
Changed
- The README is rewritten around what Subroutine makes possible, and is half its former
length. It opens with the thing that was hardest to find before: install it, tell your
coding agent "we use Subroutine now", and that sentence is the setup - the skill configures
the project, marks the checkout and writes a pointer into whichever file your agent reads at
startup, without interviewing you. Then what that makes possible, a standing instruction an
agent can work from unattended, and why an agent picks the API up unaided. What was reference
is still here, below a divider, and the guide athttps://subsystem.co/subroutine/carries
the walk-throughs. - The one-line description is now "A self-hosted, agent-native task and decision tracker for
people and coding agents working on complex projects", on every surface that carries one.
The old line said task management, which promised a to-do list - where a decision and a
task are the same kind of item here, sharing one numbering. Nothing about the software
changes; a search result and a plugin listing now describe it correctly. - An agent that has not said which timezone it is in reads days in its account
parent's, rather than in the workspace's. Nobody could set a zone for an agent they
made, since an account's zone is its own to say, so a time an agent wrote for somebody -
by Friday 16:30 - could be stored an hour or more from the one they both meant. An agent
that works somewhere else can still say so withsubroutine user timezone. - A time the agent tools print says which timezone it is in, as
2026-12-01T11:00 Europe/London. The value was always right and never said what it was
right in, so an agent in another zone read it as its own and corrected a deadline that was
not wrong. A whole day is unchanged: there is no o'clock to be wrong about. subroutine_whoamisays how an agent gets a name of its own, where the agent tools
are working on a person's credential: what they write is recorded as that person's, and
subroutine agent createmakes the account.
Fixed
- Claiming a finished item is refused. It took a lease that finishing had just given
back, so the item said somebody was holding work nobody could start.subroutine claim
now answers Already done, assubroutine startdoes, and the agent tools and the API
say it is finished and how to open it again. - The journal no longer shows a repeat's hidden twin. Giving an item a repeat makes a
second row to hold the rule, which no list shows - and the journal printed created #2 Water
the plants directly above the change that made it, so one act read as two and the second
named an item you could not find. The change itself is still there, against the item you can
see; the series' own history is still on its own page; and/v1/changes, the feed a client
polls, is untouched. - A deadline written beside a defer is counted from it. Chase the invoice from 15
September by 30 September, written on the 17th, filed something hidden until next
September that went overdue a fortnight later, and said nothing; from sunday by friday
was due before it could be seen at all. Each date was read from today on its own. A
deadline that would fall before the day an item appears is now read again from that day, as
one beside a start already was - and a deadline you wrote as a fixed date is left exactly
where you put it, because overdue work you have deferred is ordinary. - A time written beside a date stays on that date. In a captured line with a start and a
deadline, a time always went onto the deadline, whichever date it was written beside:
Dentist on monday at 2pm by friday made an all-day start on Monday and a deadline at
2pm, and the confirmation printed both, so it read as right. A time now goes on the date
it follows, or the one it comes before, and one beside no date is read as it was. subroutine_whoamisays which timezone an agent's days are read in, and names its
account parent when that is where the zone comes from. It named one only when it
differed from the machine's, and the agent tools see no machine wherever they run on the
server, so an agent was never told.- A new workspace follows the instance's timezone unless you give it one. Every way of
making a workspace stored UTC instead -initstored the machine's - so changing the
instance's zone never reached one, althoughsubroutine workspace create --helpsaid it
would. Workspaces you already have keep the zone they hold:subroutine workspace update <name> --timezone ''makes one follow the instance. subroutine mcp --helpsays which connection it uses.--connectiondefaults to the
onesubroutine connectionsmarks default, not the one in use, and the help said the
opposite - so somebody who ransubroutine use workexpecting their agent to follow got
their own database.- The local plugin's Agent token setting recommends
subroutine agent create <name> --store, as its skill andagent create --helpdo. It still said to set
SUBROUTINE_TOKEN_<CONNECTION>where your editor starts, which the skill tells an agent
never to offer. Its Workspace setting now asks what your credential reaches, rather than
how many workspaces the instance holds. - A listing that refuses a status or a type names the ones there are, as
subroutine updatedoes. A read that spans your connections printed a refusal's first line alone, so
subroutine list --status waitingsaid there was no such status and never which there are.
Security
- An instance served over HTTPS calls its session cookie
__Host-subroutine_session, and
a browser refuses to store a cookie of that name from anywhere that sets aDomain. Where
instances share a domain -acme.example.comandbeta.example.com- a page on one could
otherwise write a session cookie forexample.com, which the browser then sent to every
other instance, under the same name and impossible to tell from the reader's own. It takes
a script running on one of those pages first, so this narrows what such a script could
reach rather than closing a hole. You stay signed in: both names are read, and the
next request your browser makes hands your session back under the new one. An instance on
loopback or plain HTTP keeps the old name, because the prefix only works withSecure.