Repository navigation
Description
Problem Statement
thirdshift architect (#27) lets the factory find and implement its own architecture work, but someone still has to type the command. I want it to run every night without me, and today nothing says how, and an Architect run is not safe to start from a timer:
- It takes its Base branch from whatever branch is checked out in the Launch directory. On a night my clone is on a pushed feature branch, the run would review that branch and merge a refactor into it.
- Nothing stops a second Architect run starting while last night's is still going. Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 only documents "run one at a time", which a timer can't read.
- Nothing stops a new refactor being planned and built every night while the last one is still waiting: its pull request open for review, its run failed, or its plan published with
--plan-onlyand not started. - A scheduler has no terminal and a bare environment, so the command isn't even found unless
PATHis set, and the only way I hear what happened is the Run notification.
Solution
thirdshift does not become a scheduler. The operating system's scheduler runs the same command a person would type, and the README gives a tested cron recipe for it:
PATH=/home/you/.local/bin:/home/you/.cargo/bin:/usr/local/bin:/usr/bin:/bin
0 2 * * * cd ~/repos/thirdshift && thirdshift architect base main >> ~/.thirdshift/logs/architect-cron.log 2>&1
Three changes make an Architect run safe to schedule. None of them is a "scheduled mode": each applies to every thirdshift architect, however it was started.
base <branch>names the Base branch, so the branch checked out in the Launch directory stops mattering.- One Architect run per repository at a time on a machine. One started while another is still running, its dispatched Spec run or Run included, is skipped.
- An open Architect plan skips the next pass. thirdshift labels the Architect plan
architect-plan. An Architect run that finds an open issue with that label is skipped before any agent session starts, and names the plan.
Skipped is not a failure: the run exits 0. It still sends its Run notification when one was asked for, so each night produces exactly one email, and a quiet week can't be mistaken for a broken timer.
What a pass does with its plan is not decided here. The command's flags and the User config decide, as for a hand-typed run: with merge.always it merges, with no-merge it leaves a pull request, with --plan-only it stops at the plan.
User Stories
- As a thirdshift user, I want a documented way to run
thirdshift architectevery night, so that architecture upkeep happens without me typing a command. - As a thirdshift user, I want the nightly pass to be the ordinary
thirdshift architectcommand run by my operating system's scheduler, so that there is no daemon to keep alive and nothing new to learn. - As a thirdshift user, I want the README to give a complete crontab entry I can paste and edit, so that I don't have to work out what cron needs.
- As a thirdshift user, I want the recipe to set
PATH, so that cron, which doesn't read my shell profile, findsthirdshift,claude,gh,gitand my build tools. - As a thirdshift user, I want the recipe to send the command's output to a log file, so that a failure before the notification checks (a broken User config, no Resend key, a bad argument) is recorded somewhere.
- As a thirdshift user, I want the README to say that
claudeandghmust already be logged in for the user the timer runs as, so that the first night doesn't fail on authentication. - As a thirdshift user with several repositories, I want one crontab line per repository at different hours, so that thirdshift needs no list of repositories and the passes don't compete for the machine.
- As a thirdshift user, I want the README to show
no-mergeand--plan-onlyas the cautious variants of the nightly line, so that I can choose how much the factory does unattended. - As a thirdshift user, I want the README to say in one sentence that any scheduler that runs the command works, and that a systemd user timer needs lingering on to fire while I'm logged out, so that I can adapt the recipe.
- As a thirdshift user, I want
thirdshift architect base <branch>to name the Base branch, so that the run doesn't depend on which branch my clone happens to be on. - As a thirdshift user, I want
base <branch>to work from a clone that is on another branch, on a detached HEAD, or has uncommitted changes, so that I can schedule the pass from my working clone without keeping a second copy of the codebase. - As a thirdshift user, I want the Architecture review's worktree to start at the
originhead of the named branch, so that the plan is based on the code the work will branch from. - As a thirdshift user, I want the named branch carried to the Spec run or Run the Architect run dispatches, so that the work branches off it and its pull request targets it.
- As a thirdshift user, I want
base <branch>to stop the run before any work when the branch doesn't exist onorigin, or when my local copy of it is ahead oforigin, so that the existing Base branch checks still hold. - As a thirdshift user with launch pull on, I want it to update my checkout only when the named branch is the one checked out, so that the setting never touches a branch I'm working on.
- As a thirdshift user, I want
thirdshift architectwithoutbaseto take the checked-out branch as it does today, so that a hand-typed run is unchanged. - As a thirdshift user, I want
baseaccepted together with--plan-only, so that a plan-only pass can also be pinned to a branch. - As a thirdshift user, I want
basewith no branch after it, or given twice, to be an argument error, so that a typo doesn't silently pick a branch. - As a thirdshift user, I want a second Architect run on a repository to be skipped while another is still running on the same machine, so that two passes never plan the same refactor.
- As a thirdshift user, I want "still running" to cover the Spec run or Run the Architect run dispatched, so that a long Spec run from last night stops tonight's pass.
- As a thirdshift user, I want that rule to apply to a hand-typed
thirdshift architecttoo, so that there is one rule and no scheduled mode. - As a thirdshift user, I want an Architect run that was killed, or a machine that rebooted, to leave nothing behind that blocks the next night, so that I never have to clear a stale lock.
- As a thirdshift user, I want Architect runs on different repositories to run at the same time, so that one repository's pass doesn't hold up another's.
- As a thirdshift user, I want the skipped run to say
an Architect run is already running on <owner>/<repo>and exit 0, so that cron doesn't treat it as an error. - As a thirdshift user, I want thirdshift to label the Architect plan
architect-planin the same step that marks itready-for-agent, including under--plan-only, so that the mark is made by thirdshift and not by the agent. - As a thirdshift user, I want thirdshift to create the
architect-planlabel when the repository doesn't have it, so that the first Architect run on a repository needs no setup. - As a thirdshift user, I want an Architect run to be skipped when an open issue is labelled
architect-plan, so that a new refactor isn't planned while the last one is unfinished. - As a thirdshift user, I want that check made by thirdshift before the Architecture review starts, so that a skipped night costs no agent session.
- As a thirdshift user, I want the skipped run to name each open Architect plan and print its URL, so that I know what is in the way.
- As a thirdshift user, I want the message to give the command that picks the plan up,
thirdshift <plan URL>, so that I can continue a failed run in one step. - As a thirdshift user, I want an Architect plan whose dispatched run failed to stay open and never be retried by a later Architect run, so that a plan that fails for a real reason doesn't burn a session every night.
- As a thirdshift user, I want the check to apply to
--plan-onlyas well, so that a plan-only schedule doesn't queue a new plan each night from a codebase the earlier ones haven't changed. - As a thirdshift user, I want no way to override the check from the command line, so that the rule is simple: finish or close the plan, or remove its label.
- As a thirdshift user, I want an Architect plan to stop counting once its issue is closed, by a merge or by me, or once I remove the
architect-planlabel, so that I can release the schedule by hand. - As a thirdshift user, I want idea issues, the
needs-triageones a review files when nothing is Strong, never to count as Architect plans, so that an idea waiting for me doesn't stop the schedule. - As a thirdshift user, I want a plan that a failed review left half-published, still
needs-triageand without thearchitect-planlabel, not to block the next pass, so that one crashed review doesn't stall the schedule. - As a thirdshift user who asked for a Run notification, I want exactly one for every Architect run that passes the email checks, skipped ones included, so that each night I get one message and silence means something is wrong.
- As a thirdshift user, I want the skipped notification's subject to carry the outcome
skipped, so that I can filter those messages. - As a thirdshift user, I want the skipped notification's body to say why: another Architect run is running, or which Architect plans are open and how to pick them up.
- As a thirdshift user, I want a skipped run to print a progress line saying why it was skipped, so that the cron log shows it.
- As a thirdshift user, I want a skipped run to touch nothing: no launch pull, no worktree, no issue, no label.
- As a thirdshift user, I want an Architect run started with no terminal to behave as it does from one, apart from never offering Setup, so that the cron line and the typed command give the same result.
- As a thirdshift user, I want
thirdshift helpand the README to documentbase <branch>, the one-at-a-time rule and the open-plan rule, so that I know why a run was skipped. - As a thirdshift user, I want the README's "run only one Architect run per repository at a time" advice from Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 replaced by a description of what thirdshift now does, so that the docs match the behaviour.
Implementation Decisions
- No scheduler in thirdshift. No
schedulecommand, no--every, no daemon, no GitHub Actions workflow. The deliverable for scheduling is the README recipe; everything else here is what makes the command safe to run from one. base <branch>: a new word flag onarchitectonly, with a--baseform, taking the next argument as the branch, at most once. It is not added to Run parsing. With it, the Architect run's Base branch is that branch; without it, the checked-out branch as Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 defines. The detached-HEAD preflight check is skipped whenbaseis given, since the checkout no longer picks the branch; the two Base branch checks (exists onorigin, local copy not ahead) run against the named branch.- Carrying the Base branch to the dispatched run: the Spec run or Run the Architect run dispatches takes the Architect run's Base branch, not the Launch directory's checked-out branch. A Spec run's Spec branch is branched off it and its Spec PR targets it; a Run's Issue branch is branched off it and its pull request targets it. This is an internal parameter of the dispatch, not a user-facing flag on
thirdshift <Issue URL>. - Launch pull: unchanged rule. It is skipped when the checked-out branch isn't the Base branch, which with
baseis the common case. baseand--plan-only: allowed together, unlikemerge,parallelandbase-fix, because the Architecture review itself needs the Base branch.- One at a time: an OS advisory lock on a file in thirdshift's per-user directory, keyed by the repository (
owner/repofromorigin), taken by the Architect run's process and held until it exits, so it covers the dispatched Spec run or Run. The operating system releases it when the process ends for any reason; thirdshift never deletes or times out a lock. The lock is tried without waiting: if it is held, the run is skipped. Runs and Spec runs started by hand on an Issue URL don't take it. architect-planlabel: added to the Architect plan, the Spec or the standalone Ticket, never a Spec's Tickets, by thirdshift in the same deterministic step Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 uses to swapneeds-triageforready-for-agent, after the review session ends cleanly and the plan passes its checks. If the repository has no such label, thirdshift creates it first. The agent never applies it, and the Factory skills don't mention it.- Open-plan check: one listing of the repository's open issues labelled
architect-plan. Any result skips the run. It needs no agent session and applies to every Architect run,--plan-onlyincluded, with no override flag. - Order: arguments, User config, the Run notification's checks, the preflight checks, the lock, the open-plan check, then launch pull, the worktree and the Architecture review. The notification checks come first so that a skip is notified; the lock comes before the open-plan check so that a running Architect run's own plan is never reported as an unfinished one.
- Skipped outcome: a new outcome of an Architect run next to those Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 defines. Exit 0. stderr gets one progress line with the reason. For an open plan, stdout gets each plan's URL, as the "idea already filed" outcome prints the issue that covers it; for "already running", stdout gets nothing.
- Run notification: One Run notification per Architect run #233's Architect run notification gains the outcome
skippedin its subject, and a body that gives the reason:an Architect run is already running on <owner>/<repo>, or the open Architect plans with the commandthirdshift <plan URL>for each. One notification per invocation, as for every other outcome. - No retry: an Architect run never dispatches an existing Architect plan. A failed dispatched run leaves the plan open, and picking it up is
thirdshift <plan URL>by a person, which is a Continuation. - Supersedes Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27: Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27's "Concurrency: no lock … documented rather than prevented", its Out of Scope line on a lock, and the README sentence from its story 34 are replaced by the one-at-a-time rule here. Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 itself is not edited.
- README: a section on running an Architect run on a schedule, under the
architectdocumentation: the recipe above with placeholders for the user's paths, and the sentences user stories 4 to 9 ask for.thirdshift helpgainsbase <branch>and a line on skipping. - Glossary: Architect plan is added to CONTEXT.md; Architect run says when it is skipped; Architecture review publishes the Architect plan; Base branch says an Architect run's command can name it. These are written already (see Further Notes).
Testing Decisions
- Tests check external behaviour only: what the binary prints, its exit status, the
ghandgitcalls it makes, whetherclaudewas started, and the notification it sends. They don't test internals such as the lock file. - End-to-end seam: the existing one. Cases are added to the
architect_runend-to-end tests Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 introduces, which drive thethirdshiftbinary through the shared scenario support with the fakeclaudeandgh. No new seam is needed.base: from a Launch directory on another branch and from a detached HEAD, the review's worktree starts at the named branch'soriginhead and the dispatched Run's pull request targets it; the same for a dispatched Spec run's Spec PR; a branch missing onoriginand a local copy ahead oforiginstop the run; launch pull is skipped unless that branch is checked out;basewith--plan-onlyis accepted.- One at a time: with a first Architect run held mid-review by the fake agent, a second on the same repository exits 0, prints the skip line and starts no session; after the first ends, or is killed, a new one runs; a second on a different repository is not skipped.
- Open plan: with an open issue labelled
architect-plan, the run exits 0, prints the plan's URL, and the fakeclaudeis never started; with that issue closed, or the label removed, it runs; an openneeds-triageidea issue doesn't skip it;--plan-onlyis skipped too. - Label: a published plan gets
architect-plantogether withready-for-agent, for a Spec and for a standalone Ticket, and the Spec's Tickets don't get it; the label is created when the fakeghreports it missing; a review that fails leaves the plan without it. - Notification: each skip sends exactly one, with
skippedin the subject and the reason in the body. - The fake
ghgains what these need, such as listing issues by label and creating a label.
- Argument parser: unit tests for
base <branch>and--base,basewith nothing after it,basetwice, andbasewith--plan-only, in the style of the existing parser tests. - No terminal: the end-to-end harness already runs the binary without a terminal, so no extra case is needed beyond those above passing.
- The recipe is not tested in CI, since nothing there can run cron. It is checked by hand once on a real machine before the README Ticket is closed out, and the README says which environment it was written for.
- Prior art: the Spec run, Merge run, Run notification, preflight and launch pull end-to-end tests.
Out of Scope
- thirdshift owning a schedule: a
schedulecommand that writes crontab or timer entries, a long-running loop, or a GitHub Actions workflow. - thirdshift picking up
ready-for-agentissues on its own (Pickup runs: thirdshift takes ready-for-agent issues on its own #212) and a loop that resolves conflicted pull requests (Make a way to resolve merge conflicts - a command focused on that - if already handled though, then focus on daemon or loop part which can check repo's PR's for conflicts and resolve them #41). The recipe works for any thirdshift command, but those keep their own designs. base <branch>onthirdshift <Issue URL>.- Any nightly-specific policy, such as forcing
no-mergefor scheduled runs. - Skipping a pass because the machine is busy with other Runs.
- Seeing Architect runs on other machines: the one-at-a-time rule is per machine. The open-plan check, which reads GitHub, is what holds across machines.
- Retrying or continuing an open Architect plan automatically.
- Systemd timer and launchd recipes beyond the one sentence in the README.
- A mode that sends no notification for skipped runs.
Further Notes
- Decided in a grilling session on 2026-10-01. The glossary changes are in CONTEXT.md on the
issue-237-architect-nightly-grillbranch. - Everything here builds on
thirdshift architect, which doesn't exist until Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27's Spec PR is merged intomain. Architect run dispatches its plan as a Spec run or a Run #232 (dispatch) and One Run notification per Architect run #233 (the Architect run's Run notification) are the parts it depends on most directly, but they close when they merge into Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27's Spec branch, not intomain. Work on this Spec should start only once Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 itself is closed. - Suggested Tickets: (1)
base <branch>; (2) one Architect run at a time, with the skipped outcome and its notification; (3) thearchitect-planlabel and the open-plan check; (4) the README recipe and help text, blocked by the other three. Tickets 2 and 3 both add a skip, so whichever lands second reuses the first one's skipped outcome. - No ADR: none of these decisions is hard to reverse, and the glossary carries the rules.
Activity
[-]make a way to run `thirdshift architect` nightly[/-][+]Nightly Architect runs: a cron recipe, and an Architect run that is safe to schedule[/+]