Skip to content
2 changes: 1 addition & 1 deletion GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ _Avoid_: cron log, output log, run log (a **Ticket run** is one kind of command)
The retained Command logs and Session traces available to list on a machine, each naming an exact file under the User config's log folder. A Command's referenced Session traces may include files outside that inventory.

**Activity log**:
One running record per repository of what the factory did there: a line when a **Ticket run**, **Spec run**, **Architect run**, **Pickup run**, **Security run**, **Triage run** or **Custom process** starts work, naming its **Command log**, a line when it is given a **Stop** or a **Dismissal**, or a Dismissal is taken back, naming who asked and whether from the CLI or the **Console**, and a line when it ends, with its outcome. A Custom process's lines name it as Process `<name>`. A skipped Architect run, Pickup run, Security run or Triage Pass writes a line only when its reason differs from the last one recorded for its kind, so a repository that sits idle shows one line, not one per pass. A skipped Custom process started with no issue writes `Process <name> skipped: <reason>` when that is not already the last Process line. A warning about the **User config**, such as a setting it ignores, is written once per change. It is what thirdshift writes about a scheduled pass, in place of output to the scheduler.
One running record per repository of what the factory did there: a line when a **Ticket run**, **Spec run**, **Architect run**, **Pickup run**, **Security run**, **Triage run** or **Custom process** starts work, naming its **Command log**, a line when it is given a **Stop** or a **Dismissal**, or a Dismissal is taken back, naming who asked and whether from the CLI or the **Console**, and a line when it ends, with its outcome. A Custom process's lines name it as Process `<name>`. A skipped Architect run, Pickup run, Security run or Triage Pass writes a line only when its reason differs from the last one recorded for its kind, so a repository that sits idle shows one line, not one per pass. A skipped Custom process started with no issue writes `Process <name> skipped: <reason>` when that is not already the last Process line. During a **Quota wait**, each named Custom process compares its own last reason instead, so alternating processes do not repeat an unchanged quota skip. A warning about the **User config**, such as a setting it ignores, is written once per change. It is what thirdshift writes about a scheduled pass, in place of output to the scheduler.
_Avoid_: cron log, pass log, history

**Command record**:
Expand Down
6 changes: 4 additions & 2 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ For each repository and kind of Pass whose latest Activity log line is a skip, s
Architect run idle on acme/widgets: Ready issue #7 "Make widgets" goes first: https://github.com/acme/widgets/issues/7
```

`status` and the Console Inbox also show `Pickup last fired <age> ago on <owner>/<repo>` (and the equivalent for Architect and Security), even when repeated Passes skip for the same reason. Every Pass saves its start once its repository is known, before launch checks and the Command limit, in `<logs.dir>/<owner>/<repo>/<pickup|architect|secure|review|fix|triage>.last-fired`. This replaces the previous time without adding Activity log lines. A repository with no saved start has no last-fired entry; older Activity log times are not used as evidence that the scheduler recently fired.
`status` and the Console Inbox also show `Pickup last fired <age> ago on <owner>/<repo>` (and the equivalent for Architect and Security), even when repeated Passes skip for the same reason. Beside those lines, `GitHub Quota wait until <time>` appears while this machine has a Quota wait and disappears after its reset. Every Pass saves its start once its repository is known, before launch checks and the Command limit, in `<logs.dir>/<owner>/<repo>/<pickup|architect|secure|review|fix|triage>.last-fired`. This replaces the previous time without adding Activity log lines. A repository with no saved start has no last-fired entry; older Activity log times are not used as evidence that the scheduler recently fired.

When no Commands are running, each bullet in the Inbox's **Last recorded skipped Passes** and **Last fired Passes** starts with its recorded date and time. A skipped reason uses its Activity log time, which repeated skips do not advance; a last-fired entry uses the saved Pass start and keeps its relative age. An unreadable skip time shows **Time unknown**.

Expand Down Expand Up @@ -3625,6 +3625,7 @@ The `PATH` line, the `cd` and `base main` and the logins are as in [On a schedul

Most passes are skipped, and that is what paces frequent Architect runs. A pass is skipped while:

- **GitHub is in a Quota wait or below the reserve**: every Pass checks the machine's Quota wait record, then `gh api rate_limit`, before any other GitHub work or taking a Command-limit slot. Either fewer than 500 REST requests or fewer than 500 GraphQL points records a Quota wait until that resource resets. This covers Pickup, Architect, Security, Triage without a Report or issue, Review with `--author`, Fix without a pull request, and a Custom process without an issue, whether started by cron or by a person. A quota check that fails for a reason other than exhaustion lets the Pass proceed. A quota skip exits `0`, prints nothing on stdout and sends no Run notification. Its Activity log reason, `skipped: GitHub Quota wait until <time> (<resource>: <n> left)`, is recorded only when it changes for that kind;
- **a run is still going**: the repository is at its Command limit, a Pass's Spec run or Ticket run counted, or another Architect run is running there ([The Command limit](#the-command-limit));
- **an Architect plan is open**: one whose pull request waits for review, one whose run failed, or one published with `--plan-only` and not yet built ([One Architect plan at a time](#one-architect-plan-at-a-time));
- **an Architect idea waits for triage**, since the factory has run out of Strong ideas ([An Architect idea waits for triage](#an-architect-idea-waits-for-triage));
Expand Down Expand Up @@ -3848,9 +3849,10 @@ PATH=/home/you/.local/bin:/home/you/.cargo/bin:/usr/local/bin:/usr/bin:/bin

The `PATH` line, the `cd` and `base main`, the log file's directory, the logins the Harness's CLI and `gh` need, and schedulers other than cron are as for an Architect run: see its [On a schedule](#on-a-schedule). What differs for a Pickup run:

- **Budget per GitHub user across hosts.** Every host authenticated as the same GitHub user shares one quota across repositories. Budget Pass frequency per user across all those hosts, rather than giving each host or repository a separate quota budget. A machine's Quota wait record is local; the shared GitHub quota still applies everywhere.
- **The interval** sets how soon a Ready issue is taken and how much work is started: a pass takes one issue, so `*/30` starts at most one every half hour. A pass lasts as long as the run it dispatched, and the passes that fire meanwhile are skipped, so at the default [Command limit](#the-command-limit) of 1 the schedule builds one issue of a repository at a time on a machine, and the first pass after it ends takes the next, unless the repository is by then at its [Failure limit](#the-failure-limit): at 3 failures left for you, or the `pickup.failure_limit` you set, the passes take nothing until you have dealt with one. A pull request left for review never holds them back. An issue you have just labelled also waits five minutes by default, or the `pickup.wait_minutes` you set, until it is settled. A Ticket run you start by hand on an Issue URL never waits for a pass, but it fills a slot, so at the limit it makes one skip. Raise `pass.command_limit` to let a repository's passes overlap.
- **One line per repository.** thirdshift keeps no list of repositories. Each Pass on one repository skips at its [Command limit](#the-command-limit), 1 by default, so both lines can go in one crontab and, by default, two Passes never build that repository at once. Passes on different repositories do run at the same time: give their lines different minutes, such as `15,45`, if they would compete for the machine.
- **A pass is skipped** when the repository is at its [Command limit](#the-command-limit), when the repository has no Ready issue, and when it is at its Failure limit. A skip exits `0`, so the scheduler sees no failure, and sends no email, even with Run notifications on. The repository's [Activity log](#logs) records a skip when its reason changes. Unless `activity.quiet_skips` is set, the log file holds each skip's line too, and before the line of a pass that found no Ready issue, as before the line of one that took an issue, [why each issue was passed over](#why-an-issue-was-passed-over). A pass skipped for the Command limit or the Failure limit looks at no issue, so it has no such lines.
- **A pass is skipped** during a GitHub Quota wait or below the 500-unit reserve, when the repository is at its [Command limit](#the-command-limit), when the repository has no Ready issue, and when it is at its Failure limit. A skip exits `0`, so the scheduler sees no failure, and sends no email, even with Run notifications on. The repository's [Activity log](#logs) records a skip when its reason changes. Unless `activity.quiet_skips` is set, the log file holds each skip's line too, and before the line of a pass that found no Ready issue, as before the line of one that took an issue, [why each issue was passed over](#why-an-issue-was-passed-over). A pass skipped for quota, the Command limit or the Failure limit looks at no issue, so it has no such lines.
- **A Run notification**, by `email.always = true` or `email.unless_merged = true` in the [User config](#user-config), or `email` or `email-unless-merged` on the line, is sent [only for an issue taken](#one-run-notification-for-the-issue-taken). So a day without email doesn't say the passes are running: a pass that fails before it takes an issue, on a broken User config, a missing Resend key or a failed check, shows up only in the log file.

A whole crontab with both frequent Architect runs and a Pickup run on one repository looks like this, with `activity.quiet_skips = true` in the User config, every line sending its output to the one log file, which catches only what went wrong, so `tail ~/.thirdshift/logs/acme/widgets/activity.log` shows what the factory has been doing on the repository lately:
Expand Down
1 change: 1 addition & 0 deletions site/architecture/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ <h1>Architecture</h1>
<section class="sec arch-sec" id="passes" aria-labelledby="passes-title">
<div class="wrap">
<div class="sec-head"><p class="eyebrow">Figure 5 · <code>thirdshift pickup</code>, <code>architect</code>, <code>secure</code></p><h2 id="passes-title">The Passes: the factory finds its own work</h2></div>
<p>Quota admission precedes every Pass's other GitHub work and Command-limit slot: consult the machine's Quota wait record, then <code>gh api rate_limit</code>. Either a current wait or fewer than 500 REST requests or GraphQL points skips the Pass; a low resource records a wait until reset. Non-quota probe failures let work proceed. This applies to Pickup, Architect, Security, Triage without input, Review with <code>--author</code>, Fix without a pull request and issue-less Custom processes, from cron or by hand. Skips exit zero without stdout or notifications and deduplicate the Activity log reason per kind. The Inbox and <code>status</code> show the wait beside last-fired Passes until reset. Frequency is a budget per GitHub user across hosts and repositories sharing that quota.</p>
<p>Before selecting a Ready issue, Pickup immediately requests one re-run of failed jobs in the Base branch's latest push CI. Green on re-run files a Flaky-test Ticket with the recorded failure as evidence; its Ticket run goes straight to implementation. Red again files an ordinary bug Ticket after 30 minutes from that attempt's completion. That ticket's title, body and comments leave job names and log lines out, and it waits until you write the task and apply <code>ready-for-agent</code>. A CI-red Ticket in <code>needs-info</code> does not block new reports; another open CI-red Ticket receives new failing commits as comments, once per commit. Concurrent hosts reserve evidence reads, re-running, filing and commenting on origin. CI read failures warn and leave Pickup running; this check does not hold Self-merges.</p>
<p>Once a Pickup run, an Architect run or a Security run knows its repository, it replaces that kind's last-fired time under the repository's log folder, before launch checks or the Command limit. Repeated skips update the time without adding Activity log lines. <code>thirdshift status</code>, <code>thirdshift cronjobs</code> and their Console views read the same per-repository times; no reader starts work. A Custom process started with no issue, and neither an Issue-branch nor a Spec-branch worktree, counts toward the Command limit, runs beside other Passes up to it, takes no lock of a kind, and does not record a last-fired time. A file that declares either branch worktree is refused when no issue is given, before the Command limit.</p>
<p class="lede">A <strong>Pass</strong> takes no Issue URL, so your operating system's scheduler can start it; thirdshift never schedules itself. A Pass is skipped while its repository is at its Command limit, <code>[pass] command_limit</code>, 1 by default: every live Command on the repository on the machine counts, a Ticket run started on an Issue URL included, but only a Pass is skipped. Passes of different kinds share a repository up to the limit, but never two Architect runs or two Security runs at once: each of those holds a lock of its kind and a review worktree of its own, and reserves the plan or fix it dispatches on <code>origin</code>, as a Pickup run reserves its Ready issue. A Pass with nothing to do is skipped, not failed. Architect and Security runs wait while a Ready issue is open, so work you shaped goes first. A Custom process started with no issue is a Pass for that rule and does not wait on a Ready issue or an Architect idea, unless the file declares either branch worktree: that is refused before any session, rather than skipped. One started with an issue is not a Pass. Your scheduler can start <a href="/docs/#architect-runs-every-few-minutes">Architect runs every few minutes</a> and <a href="/docs/#security-runs-on-a-schedule-with-fixing-allowed">Security runs on a schedule, with fixing allowed</a>.</p>
Expand Down
Loading