Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ Free-form markdown. Strong conventions:
Status: draft
Consequence: glance # notify | glance | judge — how much review it needs
Witness: ids bit-identical, 62 -> 9.7 ms # what makes it checkable in minutes
Lane: any # any | local-dev | ral | gpu | wsl-data; the last three imply local-dev
Review-minutes: 3 # a seed, not a measurement
Unattended: ready # ready | needs-slicing | never
Filed: 2026-07-09 # optional; the day the prompt was written
Expand Down Expand Up @@ -496,6 +497,14 @@ faculty at conception and all overridable by declaring them:
**compaction rule** — a task that would need context compaction to finish is
too big to run unattended — so a single-repo `large` task is still `ready`
while a `large` one across four repos is not.
- **`Lane:`** `any` | `local-dev` | `ral` | `gpu` | `wsl-data` — not a cost but
a resource: where the work can run at all. `any` runs anywhere, a cloud
session included; `local-dev` needs the laptop; `ral`, `gpu` and `wsl-data`
each **imply `local-dev`** and refine it (RAL test runs, the laptop GPU, data
under `/mnt/c`). Write `any`, never `none`. A `ral` task never submits on its
own: the Science orchestrator is the single RAL gateway, and a dev worker
hands it a **RAL test packet** (what to run, at which commit, the pass/fail
rule) — planned for phase 5 of the autonomous-orchestrators epic.

Measured over the 153 backlog prompts the day this shipped: **151 grade `judge`,
because three carry a witness.** Given one, the same backlog grades 33 `notify` /
Expand Down
111 changes: 111 additions & 0 deletions batches/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,114 @@ wrote here at dispatch. A run classifying a new reason as its sibling's doing on
its own authority is an autonomous acknowledgement, which the hard invariants
forbid (`AUTONOMY.md`, "Leg 4 under a batch launch", 2026-08-31 amendment). No
line, no match, the run parks.

## Shift brief (2026-11-03)

A **shift brief** is a batch record with a `brief:` block. It is the same file in
the same folder, under the same ledger-merge rules; every key above keeps its
meaning, `heart-ack:` and `expected-effects:` included. What changes is who
picks the work: the human writes the brief, and **writing it with `review-at:` is
the dispatch** (`PyAutoBrain/AUTONOMY.md`, "Shift briefs and unattended
`glance`" — 2026-11-03). The grant covers the named tasks plus the brief's
backlog policy, until `review-at:`. The deterministic watcher that starts
sessions under it is the one exemption to `policy/end_at_deliverable.md`, and
only while a brief is open (`policy/shift_watcher.md`).

```markdown
# Batch 2026-11-04 am
- dispatched: 2026-11-04T08:30Z
- review-at: 2026-11-04T11:30Z # stated by the human; the grant expires here
- shift: morning
- issue: https://github.com/PyAutoLabs/PyAutoMind/issues/<n> # the digest is posted here once
- packet: yes # optional; the HTML packet is opt-in under a brief
- heart-ack:
- <reason line>
- expected-effects:
- <member>: <what it may legitimately change>
- brief:
- goals: # in order; the first outranks the rest
- 1. land the next cluster-strong-lensing slice
- 2. clear review-ready glance PRs on PyAutoArray
- tasks: # named prompt paths, done first, in this order
- draft/feature/autolens/<name>.md
- draft/bug/autoarray/<name>.md
- backlog: scoped # none | fill-idle | scoped
- scoped: # only with backlog: scoped
- epics: cluster-strong-lensing, autonomous-orchestrators
- tags: jax-gradient
- tiers: notify, glance
- exclude:
- draft/feature/autofit/<name>.md
- epics: euclid-dr1
- review-minutes: 45
- lanes: any local-dev ral # any subset of: any local-dev ral gpu wsl-data
- merge-cap: 3 # unattended glance merges this shift, at most
- token-budget: <n> # tokens the shift may spend, all sessions
- ral-allowance: # only when lanes includes ral; phase 5 consumes it
- packets: 2
- partition: ral
- cpu-hours: 64
```

`backlog:` decides what happens once the named tasks are done or blocked:
`none` — nothing more; `fill-idle` — the planner's own ranking, under every
batch constraint; `scoped` — only prompts matching the `scoped:` epics, tags or
tiers. `exclude:` removes prompt paths or epics from either dip. Backlog the
brief does not cover is **never** dipped.

**Calibration rule.** A new brief is not accepted while the previous record
lacks `review-minutes-actual:`. The planned figure is a seed; the 2026-08-31
record never closed it, and an estimate nobody measures never improves.

`batches/examples/` (phase 2) holds validator fixtures. It is **not** a record:
nothing in it was dispatched, and `lifecycle.py` does not read it as one.

### Digest

Appended to the record at collect, under a `## Digest` heading, and **posted
once** as a comment on the shift's `issue:`. The record plus that comment is
the review surface; the HTML packet is written only when `packet: yes`.

```markdown
## Digest

### Brief work
- <slug> — PyAutoLens#<n> — HEALTHY — glance, auto-merged (1/3)
- <slug> — parked: awaiting-input (question on the issue)

### Backlog picks
- <slug> — PyAutoArray#<n> — HEALTHY — why: scoped epic cluster-strong-lensing, next phase, notify
- <slug> — draft filed — why: decomposed from goal 2; needs bulk approval

### Proof packets
<one proof packet per task, in the order above>

### Counters
- merges: 2
- glance-auto-merges: 1
- reverts: 0
- pushes: 0
- drafts-filed: 1
- packets-sent: 0
- workers-crashed: 0
- tokens: <n> of <token-budget>
- review-minutes-planned: 45
```

Brief work and backlog picks stay separate, and **every backlog pick carries a
one-line reason** — the brief's rule it was picked under. A pick the human
cannot trace to the brief is a finding.

### Proof packet

One screen per task — if it does not fit, the task was too big:

```markdown
#### <slug> — <PR url>
- claim: <one sentence — what is now true>
- before/after: <numbers, or a figure path>
- witness: `<the Witness: command>`
<its output, trimmed to the lines that decide it>
- not checked: <what this packet does not show>
- revert: `<one command>`
```
30 changes: 30 additions & 0 deletions policy/shift_watcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## The shift watcher — the one exemption to "sessions end at their deliverable" (2026-11-03)

`policy/end_at_deliverable.md` stands as written for every Claude session,
orchestrator and worker alike, and its hook keeps blocking them. This page
names the single thing that is allowed to keep time.

**The watcher is a deterministic script, not a session** (built in phase 3 of
the autonomous-orchestrators epic; a systemd user timer or cron entry). It polls
`gh pr checks`, the project `hpc/sync jobs` verb (never raw `squeue`) and Mind
state, and starts a Claude session (`claude --bg`) only when there is work to
do. No tokens are spent watching.

What keeps the exemption narrow:

- It **calls no Claude tool** and **arms nothing inside a session**. A session
it starts is an ordinary session: it ends at its deliverable, and
`send_later`, `ScheduleWakeup`, `CronCreate`, `RemoteTrigger` and `/loop`
stay blocked for it.
- It **dispatches nothing without an open shift brief** — a
`PyAutoMind/batches/<slot>.md` record carrying a `brief:` block whose
`review-at:` has not passed (`PyAutoBrain/AUTONOMY.md`, "Shift briefs and
unattended `glance`"). A tick with no open brief does nothing.
- **Kill switch:** it checks for a `STOP` file in its slot directory **first**,
before reading anything else, and does nothing while that file exists.

Why it is this narrow: the timer rule was written after five batch members armed
hourly check-ins on 2026-08-31, and a mobile `/prm` re-armed a 60-minute
`send_later` hourly all night on 2026-09-03 with no task active, draining usage.
Both were sessions keeping time. The watcher keeps time so that no session has
to; anything wider than this page reopens those two nights.
Loading