Skip to content

Releases: simonholliday/subroutine

Subroutine 0.10.0

Choose a tag to compare

@github-actions github-actions released this 05 Oct 09:04

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 subroutine plugin passes its token field as SUBROUTINE_PLUGIN_TOKEN alone. It
    passed it as SUBROUTINE_TOKEN too, 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
    in local_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 copy into 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.
  • A workspace given to -w by 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 --repeat without
      --repeat-from - turned a series counted from completion into one counted from the schedule.
      Leaving it out now leaves it as it was.
  • 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 connecting and 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
    run subroutine 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.
  • ...
Read more

Subroutine 0.9.13

Choose a tag to compare

@github-actions github-actions released this 30 Sep 15:05
  • 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_enabled in the
    configuration decides now: unset, an instance sends where only its own machine can reach it, and
    sends nothing where it has a public_url or listens beyond loopback. Set osc_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 copy moves 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 for comment:write in 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, show and uncomment see 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=1 answered 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 names uv tool install 'subroutine[postgres]' as well as pip, and
    user create says is now a member of. init where the system cannot say who is running it
    asks for --username; doc edit says so when the editor quits without saving, is not
    installed or has an unbalanced quote; and a token_command with an unbalanced quote is refused
    naming the setting. Each of those ended in a crash report. document create without --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 as importance.gt=40000; a change-feed since or before
    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 Bearer in front
    is no longer quoted back whole in the refusal, which put a live secret into a body an agent
    keeps. /v1/me calls a credential narrowed only in the projects it may write narrowed in
    every workspace, as the rest of its answer and /v1/tokens already 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_user naming 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/me no 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, and db backup
    and db backups name what could not be counted. A pg_dump, pg_restore or psql still
    running after ten minutes is stopped and reported in words, where it went on behind a crash
    report. POST /v1/admin/backups refuses a keep below 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...
Read more

Subroutine 0.9.12

Choose a tag to compare

@github-actions github-actions released this 29 Sep 08:05
  • 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 .subroutine file 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 --here now 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=, naming web beside alpha/web filed 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, unlink and move act on the item the address names. Given
    an address on another connection, delete and restore acted 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. delete and restore go 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:read and task:write can file in a project. Naming the project it
    files in, moves to or narrows a listing by needed project:read too, so an agent made as the
    hosting guide recommends could file nothing in one. Asking for the list of projects still
    needs project: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 said subroutine use work, which is refused; the connection's own
    SUBROUTINE_TOKEN_WORK where they offered a bare SUBROUTINE_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 mcp naming 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 --store and connections add --url check 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.
  • serve on 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 --workspace of its own takes -w as well, before the command or after
    it.
    token create, agent create, calendar create, user create and user list ignored
    a -w given 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 use or a checkout's marker, because each binds what it makes or shows to the
    workspace - and the refusal quotes the command back with -w in place. So
    subroutine -w projects token create pins the token to projects, where it used to make one
    for every workspace. user add, user role and user remove take -w after the command
    too, and a -w and a --workspace naming two different workspaces are refused.
  • Each entry of subroutine://conventions names 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
    sends Origin or Sec-Fetch-Site with 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 with subroutine use --here --project web, subroutine add filed into
    web while the agent's subroutine_add filed 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 .subroutine file 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_show says 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_update says 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 in plan --until alike, 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 ...
Read more

Subroutine 0.9.11

Choose a tag to compare

@github-actions github-actions released this 28 Sep 12:19
  • Subroutine now needs SQLAlchemy 2.1.1 or later, and upgrading brings it. Two things read
    differently under it. A database_url of plain postgresql://…, which names no driver, now
    uses psycopg 3 - the driver subroutine[postgres] installs - where it used to look for
    psycopg2. And in a database_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_list and subroutine_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,archived lists every one - and the browser's
    everything shows them. GET /v1/documents still lists every document unless it is asked
    for open=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, changes and whoami exited 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, and list --json printed [], 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 failed update does, and journal, changes and whoami under
    --json report 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; --strict stops 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 role and
    user remove crashed, 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 --here says 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 --store where 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_update and the browser's edit form now defer the one in front of
    you without asking. defer --from-now-on and --just-this-one are still accepted, and say
    they made no difference; applies_to sent with a deferral alone is reported the same way
    rather than refused. Given the repeat itself, whose number show prints 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; skip names 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 .subroutine file
    and never found the caller's: a task or document written without naming a project landed in
    the workspace's Inbox. The subroutine plugin now sends its checkout's project with every
    request, and the instance files by it. subroutine-remote and 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. A database_url
    can hold one, such as a PostgreSQL password with an @ in it, which a URL writes as %40,
    and init, db migrate and db upgrade crashed on it, as did db restore bringing 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 named josé, so this is also what lets init set 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...
Read more

Subroutine 0.9.10

Choose a tag to compare

@github-actions github-actions released this 26 Sep 16:06
  • subroutine_whoami says beforehand when a sign-out would change who the tools are. Where
    the subroutine plugin'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, from agent 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

Choose a tag to compare

@github-actions github-actions released this 26 Sep 13:03

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 plan and the agent tools
    read a start and an end the same way, so plan 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 --until does. 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 a T is read with its offset. With a space in place of the T, 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
    T to 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,
    through includes and 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.
    Moving blocks to counting made every task that blocked something read as including work, and
    moving includes to 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 includes under 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/blog when acme is renamed.

  • subroutine view run with --connection reads 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:me or answers_to:me is marked as drawing each reader their
    own work
    , as one saying assignee:me always 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.gte was told to use ref.gte, ref.ne that a ref is
    stored to the microsecond, and created_at.is that created_at takes is. A range is now
    suggested only for a field that takes one, and is on a field that always has a value says so.

  • agent create --here works in a directory whose name a .gitignore line 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 --here says when the plugin's tools will not follow. It names the variable
    after the connection a write goes to, and the subroutine plugin 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 --here no 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 to done, skip, plan, defer or update, and user create with
    --browser or --terminal are 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 --json with --browser or --terminal is 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. --json goes with neither now: a secret is shown once, to a person,
    and not to a script.

  • The subroutine plugin passes its token field under a name of its own,
    SUBROUTINE_PLUGIN_TOKEN.
    The field travelled as SUBROUTINE_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 as SUBROUTINE_TOKEN as 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 ...

Read more

Subroutine 0.9.8

Choose a tag to compare

@github-actions github-actions released this 25 Sep 14:50

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

Choose a tag to compare

@github-actions github-actions released this 25 Sep 09:04

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, and precedes, 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_total is new on the agenda response and is optional, so an older client
    is unaffected.

  • A milestone counts what it includes: an includes link from the milestone to its work,
    as in subroutine link 12 includes 43. Its show reads Links (3 of 5 included done) on
    the terminal, in the browser and to an agent, and show --tree walks 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_count and included_done_count
    are new on a task and are optional, so an older client is unaffected.

  • subroutine explain milestones says how milestones work: features, milestones, and the
    difference between includes, blocks and precedes. The link help's example is
    includes now, 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> --here gives 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.json under the variable for your connection, makes the repository
    ignore that file - adding the line to .gitignore if 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-remote it names the shell only
    : that plugin's tools present the one token it
    was given, in every project. agent create and subroutine_whoami both say so, and name the
    two claude plugin ... --scope local commands that switch one project to the subroutine
    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, and subroutine_whoami names the command. The offer is
    in the plugin's skill, so refresh the plugin to get it: claude plugin marketplace update subroutine, then claude plugin update subroutine@subroutine.

  • The subroutine plugin'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_whoami and 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 create says 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 create without 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 connecting say 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 run no longer offers --merged and --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-remote sends 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.toml or config.toml empty.
    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 subroutine plugin'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 whoever credentials.toml held 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, with 2028 left 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 2028 or
    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 written 0930 or 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 as 05: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....

Read more

Subroutine 0.9.0

Choose a tag to compare

@github-actions github-actions released this 22 Sep 13:05

0.9.0 is the release candidate for the first ready version of Subroutine. Everything
planned for it is in.

If you use the subroutine plugin 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, then claude plugin update subroutine@subroutine, and start a new session. subroutine-remote takes 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 add failed, 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 without web refused, and its refusal threw away the rows the other returned -
    so the search said nothing matched. subroutine list --project web had worked for a long
    time; the tolerance was keyed on the flag rather than on the project. The same was true of
    parent:12 when 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 timezone named 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/me publishes it as reader_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, unset or me. 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, where doc create, doc edit,
    add and update read the pipe. --description on project create, project update,
    workspace create and workspace update did the same, and so did --because on done,
    skip, plan, defer and update. 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 is created - 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 say deleted beside an item that
    still exists.

  • The capture shorthand's !3 can 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 capture offered as its one worked example, was either refused as
      event not found or 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 !, and explain capture says why.
  • 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 as database is locked after 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/meta no longer publishes sortable and selectable. They were renamed order
    and fields in 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 - and done, todo or cancelled, or a document's
    draft, current, superseded and archived - 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 saying include_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, PATCH and
    DELETE on /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...

Read more

Subroutine 0.8.29

Choose a tag to compare

@github-actions github-actions released this 21 Sep 14:11

Added

  • A board can be arranged by who the work is assigned to. ?view=board&group_by=assignee
    in the browser, group_by=assignee on GET /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 show on a milestone,
    and its page in the browser, now carry a line like 12h beneath this, over 9 of 14 under
    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} gains beneath; 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 #7 and 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 an inherited_from.
  • An agent can set a deadline. subroutine_update takes due - 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 as 2pm-3pm, at 9am to 5pm, from Monday 9am til 5pm or 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 at https://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 with subroutine 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_whoami says 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 create makes 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, as subroutine start does, 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_whoami says 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 - init stored the machine's - so changing the
    instance's zone never reached one, although subroutine workspace create --help said it
    would. Workspaces you already have keep the zone they hold: subroutine workspace update <name> --timezone '' makes one follow the instance.
  • subroutine mcp --help says which connection it uses. --connection defaults to the
    one subroutine connections marks default, not the one in use, and the help said the
    opposite - so somebody who ran subroutine use work expecting their agent to follow got
    their own database.
  • The local plugin's Agent token setting recommends subroutine agent create <name> --store, as its skill and agent create --help do. 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 update does. A read that spans your connections printed a refusal's first line alone, so
    subroutine list --status waiting said 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 a Domain. Where
    instances share a domain - acme.example.com and beta.example.com - a page on one could
    otherwise write a session cookie for example.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 with Secure.