Skip to content

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

Merged
JacobStephens2 merged 17 commits into
mainfrom
issue-237
Oct 1, 2026
Merged

JacobStephens2 merged 17 commits into
mainfrom
issue-237

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Closes #237

Summary

An Architect run that is safe to start from a timer, and the cron recipe for it. thirdshift is not a scheduler: the operating system runs the same command a person would type.

 thirdshift architect [base <branch>] [<focus>] [--plan-only]
   arguments, User config, the Run notification's checks
-  preflight: HEAD not detached, the checked-out branch is the Base branch
+  preflight: Base branch = base <branch>, else the checked-out branch
+             (a detached HEAD is fine with base)
+  lock for owner/repo held by another process?
+    -> skipped: "an Architect run is already running on <owner>/<repo>"
+  an open issue labelled architect-plan?
+    -> skipped: each Architect plan's URL on stdout, "pick it up with thirdshift <plan URL>"
   launch pull (only when the Base branch is the one checked out)
   Architecture review in <repo>-architect, at origin/<Base branch>
-  mark the plan ready: needs-triage -> ready-for-agent
+  mark the plan ready: needs-triage -> ready-for-agent, + architect-plan
+                       (label created if the repository lacks it)
-  dispatch the plan from the checked-out branch
+  dispatch the plan on the Architect run's Base branch

Skipped is an outcome, not a failure: exit 0, one progress line with the reason, and the one Run notification (subject ... Architect run: skipped, body Skipped: <reason>) when one was asked for. A skipped run starts no agent session, does no launch pull, and creates no worktree, issue or label.

src/
├── args.rs          # base <branch> / --base on architect only, once, also with --plan-only
├── architect.rs     # Outcome { Skipped(AlreadyRunning | OpenPlans), Reviewed(..) }, the lock, the open-plan check, the label
├── run.rs           # StartedBy { Command, ArchitectRun { base }, Child }: where a Run's Base branch comes from
├── branch.rs        # a given Base branch stands in for the checked-out one
├── github.rs        # ensure_labels, open_issues_labelled by repository
├── notification.rs  # the skipped outcome and its Skipped: line
├── worktree.rs      # a leftover <repo>-architect worktree from a killed run is removed
└── main.rs          # help: base, the two skips, a pointer to the README's schedule section
README.md            # "On a schedule": the crontab entry, PATH, the log file, logins, one line per repository, the cautious variants

The lock is an OS advisory lock on ~/.thirdshift/architect-locks/<owner>/<repo>.lock, held by the Architect run's process until it exits, so it covers the Spec run or Run it dispatched and is released however the process ends. It is tried before the open-plan check, so a running Architect run's own Architect plan is reported as already running, never as an unfinished plan.

Seams under test: the thirdshift binary, through the shared scenario support with the fake claude, gh and Resend (tests/architect_run.rs, tests/command_surface.rs), and the argument parser's unit tests (src/args.rs).

Evidence

  • Before: on main, thirdshift architect base main is an argument error, a second Architect run on a repository starts a second Architecture review, and one starts every night whatever the last one left open.
    After: cargo test on 75f2baf: every suite passes, architect_run with 74 tests and command_surface with 23; cargo clippy --all-targets -- -D warnings and cargo fmt --check are clean.
base_starts_the_review_at_the_named_branchs_origin_head_whatever_is_checked_out            ok
a_second_architect_run_on_the_repository_is_skipped_while_the_first_is_mid_review          ok
a_second_architect_run_is_skipped_while_the_firsts_dispatched_run_or_spec_run_is_still_going  ok
once_the_first_architect_run_is_killed_a_new_one_runs_with_nothing_to_clean_up             ok
an_open_architect_plan_skips_the_architect_run_before_any_review_whatever_its_flags        ok
a_skipped_architect_run_sends_one_notification_with_skipped_in_its_subject_and_why_in_its_body  ok
base_skips_an_architect_run_from_a_clone_on_another_branch_or_a_detached_head_as_from_any  ok   (added in this review)
  • The cron recipe is not run in CI, and this branch holds no record of the hand check on a real machine that the Spec asks for.

Merge Danger

Door: two-way

Reverting restores the old command. What stays behind is harmless: the architect-plan label on the repository and on plans already published, and empty lock files under ~/.thirdshift/architect-locks.

Blast Radius: Architect runs

A hand-typed thirdshift architect now exits 0 as skipped where it used to start a review: while another Architect run on the repository is running on the machine, or while an issue labelled architect-plan is open. Runs and Spec runs started on an Issue URL are unchanged, apart from the progress line a Continuation prints when its pull request's base replaces another branch.

Unaddressed findings

Standards

  • "launch repository" in src/worktree.rs comments, where CONTEXT.md has Launch directory: the file uses it throughout for the repository the Launch directory is in, which is a different thing from the directory; the new sentence was shortened, the rest left as the file has it.
  • plan as a bare name in code (Skipped::OpenPlans, open_plans, PlanReady { plan }): CONTEXT.md's own Run notification entry says "the plan", and the identifiers sit inside the Architect module where no other plan exists. The prose that said the label "makes it the Architect plan" is fixed.
  • Base branch resolved in two places (architect.rs and branch.rs), symbolic-ref read twice: an Architect run has no Issue branch selection or Continuation to consult, and the two detached-HEAD messages differ on purpose, so a shared helper would take a flag for each difference.
  • open_issues_labelled and ensure_labels take an owner/repo string; Repo::slug beside IssueUrl::repo_slug; the (IssueUrl, String) pair in Skipped::OpenPlans: IssueUrl holds no Repo, so a string is what both callers have; giving it one is a wider change than this Spec.
  • ended in main.rs and notification.rs beside the type run::Ended; Outcome::name() beside Reviewed::review(): judgement calls on names an earlier Ticket review settled; no clearer pair came up.
  • base passed through conclude only to reach Reviewed::PlanReady: it keeps the dispatch from reading the Base branch a second way; one extra parameter.
  • try_run_lock repeats the shape of Git::lock, and its caller must mem::forget the file: the two differ in every line that matters (try or wait, home directory or git directory, held for the process or a scope), and the forget is one commented line next to the only call.

Spec

  • No record of the recipe's hand check on a real machine: it can't be done from this headless session; it is for the person merging to do or confirm.
  • A leftover <repo>-architect worktree is force-removed, which the Implementation Decisions don't list: story 22 needs it, a killed run otherwise blocks the next night, and it happens only once the lock is held.
  • base rejects a branch that starts with a dash: a flag taken as a branch is the typo story 18 is about.
  • A skipped run still runs git fetch origin <base> and creates the lock file: the Spec's own order puts the preflight checks and the lock before the skip, and none of what story 41 lists happens.
  • A failure of the lock or of the open-plan listing is notified as review failed: the count is right, one notification, and the wording is One Run notification per Architect run #233's for any failure before a plan, preflight failures included; a new outcome name is not in this Spec.
  • architect base merge takes merge as the branch: the Spec says base takes the next argument, and the run stops at preflight with base branch merge does not exist on origin.

Tickets

🤖 Generated with Claude Code

JacobStephens2 and others added 7 commits October 1, 2026 09:08
…ranch

base <branch> (or --base <branch>) on architect names the Base branch,
whatever the Launch directory has checked out: the Architecture review's
worktree starts at its origin head, and the Spec run or Run the plan is
dispatched as takes it as its Base branch, as an internal parameter of the
dispatch. The detached-HEAD check is skipped when it is given, and launch
pull only updates the checkout when the named branch is the one checked out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Architect run takes, without waiting, an OS advisory lock keyed by the
repository once its preflight checks pass, and holds it until its process
exits, the run it dispatches included. One started while the lock is held
is skipped: it says so on stderr, prints nothing on stdout, does nothing
else and exits 0, and still sends its Run notification, with the outcome
skipped and the reason. A review worktree a killed Architect run left is
removed by the next one, so a kill leaves nothing to clean up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d for how an Architect run ended, the worktree says only what it knows

The kill case now starts the next Architect run while the killed one's
session is still going, so it would notice a lock a child process kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h its PR replaces was given or checked out, exact errors for base on a Run, the test module's doc in order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One Architect run per repository at a time, with a skipped outcome
The plan an Architecture review marked ready carries the Architect run's
Base branch inside the Reviewed outcome, next to the skipped one, and a
run that names its Base branch takes the repository's lock once the
checks on that branch pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thirdshift architect base <branch>: an Architect run names its Base branch
JacobStephens2 and others added 9 commits October 1, 2026 12:22
…tect plan is open

thirdshift labels the plan it marks ready architect-plan, in the request
that swaps needs-triage for ready-for-agent, creating the label if the
repository lacks it. Once the one-at-a-time lock is its own, an Architect
run lists the repository's open issues with that label and is skipped if
there are any: each plan's URL on stdout, one progress line naming each
with the command that picks it up, exit 0, and the skipped Run
notification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… carries, the label's name and description apart, each open plan in the skipped notification

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The architect-plan label, and skipping an Architect run while an Architect plan is open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rchitect plan by their names, a two-line help pointer, one helper for the unwrapped help

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g to fix on the branch)

2026-10-01T12:58:12Z, host thirdshift. Uncommitted work at the time of failure is included in this commit.
…chitect plan, type for finding the commands, an even help pointer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: running an Architect run on a schedule
…rchitect plan as what the review published, doc comments wrapped

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 marked this pull request as ready for review October 1, 2026 16:59
…eside the Inherited failure's advice

Conflicts, each resolved to keep both sides:
- src/main.rs: the dispatched plan runs as StartedBy::ArchitectRun { base }, with main's retry command for its Base fix offer.
- src/notification.rs: both imports; the skipped body test gets main's advice field.
- tests/support/mod.rs: main's spawn_until_started now returns this branch's HeldRun, so run_until and run_copy_replaced_midway share it.
- CONTEXT.md, README.md: the Run notification's skipped outcome beside the Inherited failure's lines and the Pickup run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 9b65352 into main Oct 1, 2026
14 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-237 branch October 1, 2026 17:18
@JacobStephens2 JacobStephens2 mentioned this pull request Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant