Skip to content

Nightly Architect runs: a cron recipe, and an Architect run that is safe to schedule #237

Closed
4 of 4 issues completed

Description

@JacobStephens2

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-only and not started.
  • A scheduler has no terminal and a bare environment, so the command isn't even found unless PATH is 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.

  1. base <branch> names the Base branch, so the branch checked out in the Launch directory stops mattering.
  2. 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.
  3. 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

  1. As a thirdshift user, I want a documented way to run thirdshift architect every night, so that architecture upkeep happens without me typing a command.
  2. As a thirdshift user, I want the nightly pass to be the ordinary thirdshift architect command run by my operating system's scheduler, so that there is no daemon to keep alive and nothing new to learn.
  3. 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.
  4. As a thirdshift user, I want the recipe to set PATH, so that cron, which doesn't read my shell profile, finds thirdshift, claude, gh, git and my build tools.
  5. 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.
  6. As a thirdshift user, I want the README to say that claude and gh must already be logged in for the user the timer runs as, so that the first night doesn't fail on authentication.
  7. 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.
  8. As a thirdshift user, I want the README to show no-merge and --plan-only as the cautious variants of the nightly line, so that I can choose how much the factory does unattended.
  9. 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.
  10. 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.
  11. 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.
  12. As a thirdshift user, I want the Architecture review's worktree to start at the origin head of the named branch, so that the plan is based on the code the work will branch from.
  13. 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.
  14. As a thirdshift user, I want base <branch> to stop the run before any work when the branch doesn't exist on origin, or when my local copy of it is ahead of origin, so that the existing Base branch checks still hold.
  15. 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.
  16. As a thirdshift user, I want thirdshift architect without base to take the checked-out branch as it does today, so that a hand-typed run is unchanged.
  17. As a thirdshift user, I want base accepted together with --plan-only, so that a plan-only pass can also be pinned to a branch.
  18. As a thirdshift user, I want base with no branch after it, or given twice, to be an argument error, so that a typo doesn't silently pick a branch.
  19. 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.
  20. 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.
  21. As a thirdshift user, I want that rule to apply to a hand-typed thirdshift architect too, so that there is one rule and no scheduled mode.
  22. 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.
  23. 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.
  24. 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.
  25. As a thirdshift user, I want thirdshift to label the Architect plan architect-plan in the same step that marks it ready-for-agent, including under --plan-only, so that the mark is made by thirdshift and not by the agent.
  26. As a thirdshift user, I want thirdshift to create the architect-plan label when the repository doesn't have it, so that the first Architect run on a repository needs no setup.
  27. 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.
  28. 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.
  29. 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.
  30. 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.
  31. 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.
  32. As a thirdshift user, I want the check to apply to --plan-only as well, so that a plan-only schedule doesn't queue a new plan each night from a codebase the earlier ones haven't changed.
  33. 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.
  34. 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-plan label, so that I can release the schedule by hand.
  35. As a thirdshift user, I want idea issues, the needs-triage ones 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.
  36. As a thirdshift user, I want a plan that a failed review left half-published, still needs-triage and without the architect-plan label, not to block the next pass, so that one crashed review doesn't stall the schedule.
  37. 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.
  38. As a thirdshift user, I want the skipped notification's subject to carry the outcome skipped, so that I can filter those messages.
  39. 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.
  40. 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.
  41. As a thirdshift user, I want a skipped run to touch nothing: no launch pull, no worktree, no issue, no label.
  42. 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.
  43. As a thirdshift user, I want thirdshift help and the README to document base <branch>, the one-at-a-time rule and the open-plan rule, so that I know why a run was skipped.
  44. 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 schedule command, 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 on architect only, with a --base form, 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 when base is given, since the checkout no longer picks the branch; the two Base branch checks (exists on origin, 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 base is the common case.
  • base and --plan-only: allowed together, unlike merge, parallel and base-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/repo from origin), 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-plan label: 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 swap needs-triage for ready-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-only included, 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 skipped in 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 command thirdshift <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 architect documentation: the recipe above with placeholders for the user's paths, and the sentences user stories 4 to 9 ask for. thirdshift help gains base <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 gh and git calls it makes, whether claude was 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_run end-to-end tests Architect run: thirdshift finds and implements a Strong deepening opportunity unattended #27 introduces, which drive the thirdshift binary through the shared scenario support with the fake claude and gh. 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's origin head and the dispatched Run's pull request targets it; the same for a dispatched Spec run's Spec PR; a branch missing on origin and a local copy ahead of origin stop the run; launch pull is skipped unless that branch is checked out; base with --plan-only is 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 fake claude is never started; with that issue closed, or the label removed, it runs; an open needs-triage idea issue doesn't skip it; --plan-only is skipped too.
    • Label: a published plan gets architect-plan together with ready-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 fake gh reports it missing; a review that fails leaves the plan without it.
    • Notification: each skip sends exactly one, with skipped in the subject and the reason in the body.
    • The fake gh gains what these need, such as listing issues by label and creating a label.
  • Argument parser: unit tests for base <branch> and --base, base with nothing after it, base twice, and base with --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

Further Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions