Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ce26cea
feat(sweeps): judgments can name their card; sweep:status reports wha…
dshipper Sep 13, 2026
0f3fd2a
fix(sweeps): reserve exactly named cards across the whole batch
dshipper Sep 13, 2026
13eaaec
fix(sweeps): list every gap at once, honor work state, aggregate rout…
dshipper Sep 13, 2026
f83cdcd
fix(sweeps): say when the owner cannot complete, expose group coverag…
dshipper Sep 13, 2026
be8abd4
fix(sweeps): grouped cards do not present review judgments; batches c…
dshipper Sep 13, 2026
0ab84ef
fix(sweeps): no double counting through card-backed group items; sett…
dshipper Sep 13, 2026
4ec1878
fix(sweeps): a run recorded after the batch blocks completion until b…
dshipper Sep 13, 2026
fb794f7
fix(sweeps): a counted card presents one judgment per sweep; re-claim…
dshipper Sep 13, 2026
8ed68a6
fix(sweeps): bind acted-on checks to the card version; proposals only…
dshipper Sep 13, 2026
c803e74
fix(routine): a revived group proposal dates from its re-proposal
dshipper Sep 13, 2026
24147c7
fix(sweeps): routine group coverage requires a group created since th…
dshipper Sep 13, 2026
1268d41
fix(routine): every explicit group upsert starts a new proposal cycle
dshipper Sep 13, 2026
dfcd603
fix(sweeps): bind acted-on checks to the event ledger instead of a ti…
dshipper Sep 13, 2026
6f3dd03
fix(sweeps): every user disposition counts as reviewing the card
dshipper Sep 13, 2026
5643c5a
fix(sweeps): key routine coverage on the batch; count voice-queued in…
dshipper Sep 13, 2026
89dd68e
fix(sweeps): order routine group proposals against the batch through …
dshipper Sep 13, 2026
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
2 changes: 1 addition & 1 deletion CAPABILITY_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
| Recover parked or stuck Claude work | Parked notice → `Reassign to Codex` | Queued/parked items use `work:assign --feed ... --work ... --agent codex`; claimed/stuck items use claim replay by the Claude lane or `work:release --feed ... --work ... --token ...` to re-queue and rotate the token |
| Schedule refresh and drain | Approve proposed cadence | `feed:heartbeat:propose`, then host `automation_update`, then `feed:heartbeat:installed` |
| Verify an approved external action or cleanup | Choose the exact visible mutation, cleanup, or routine group | `action:verify` rereads the selected card-action ID, current artifact, cleanup, or batch digest immediately before mutation and records verification durably; Inbox replies additionally require `--mailbox <authenticated-gmail-email>` to match the source message's received-at mailbox |
| Collect evidence | Render the resulting cards | `source:record-run` or `source:import-json-file` writes immutable snapshots and checkpoints; `sweep:record-batch` records the judged batch separately |
| Collect evidence | Render the resulting cards | `source:record-run` (judgments may name their `cardId`) writes immutable snapshots and, for claimed recollection work, holds the checkpoint on the run; `sweep:record-batch` records the judged batch; `sweep:status` lists what is still unpresented; `work:complete` commits the held checkpoints once every judgment is presented |
| Rejudge sweep feedback | Submit dock feedback to `This sweep` | `sweep:rejudge` writes an explicit kept order and removed-card set before recollection is offered |
| Render a judged item | Review its validated structured blocks, including compact two-series charts for comparative metrics | `card:upsert --feed ... --card-file ...` |
| Compound learning | Answer yes when the feed thread offers its end-of-sweep learning pass; review and edit the resulting full-screen policy proposal before applying it | An idle `work:list` or `work:claim` reminds Codex to offer the pass after meaningful sweep work; `learning:request --feed ...` queues one `compound_learnings` job; Codex reviews durable evidence and uses `revision:propose --source compound`; the browser applies only the user-reviewed Markdown |
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ a promise of ongoing maintenance.

## Unreleased

- Let sweep judgments name the card that will present them (`cardId`), so `work:complete` and the
new `tend cli sweep:status --feed <feed>` check each judged item against its exact card and report
precisely what is still missing; judgments without a `cardId` keep the count-based check. Re-claimed
recollection work carries the same list as `operatorGuidance.pendingPresentation`, and the judge
prompt, agent contract, skill, and runbook describe the convention. Advances the CLI contract to
`0.9`.
- Hold source checkpoints recorded for claimed recollection work until `work:complete` succeeds,
and refuse completion until every `review` judgment has a card and every `routine_action`
judgment has a card or a proposed routine action group, so a sweep
Expand Down
17 changes: 17 additions & 0 deletions RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ treat broad natural-language dock input as a literal prompt edit.

## Collect

### Finish an interrupted sweep

A sweep recorded for claimed recollection work keeps its source checkpoints on the run records until
`work:complete` succeeds, so an agent that dies after judging but before presenting loses nothing:
the next sweep starts from the previous checkpoint. To finish the interrupted one instead, re-claim
the work (the claim output carries `operatorGuidance.pendingPresentation`) or ask directly:

```bash
tend cli sweep:status --feed <feed-id>
```

It reports `ready` plus every judgment that is not presented yet, with the run, the judgment's
position, its `cardId` when it named one, and the exact reason. Upsert the listed cards with
`sourceRunIds` including the run (or propose the routine action group), re-run `sweep:status`, then
`work:complete`. Judgments that name a `cardId` are checked against that exact card; judgments
without one are matched by count.

### Compose meeting cards with independent readers

The optional meeting-reading workflow uses ordinary feeds, source runs and cards. Follow
Expand Down
30 changes: 21 additions & 9 deletions docs/AGENT_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,26 @@ For `recollect_sources` work:
["INBOX"])`. Its checkpoint's `inboxEnumeration.messages` must map every authoritative
`messageId` to its direct-read `threadId`; every resulting conversation must appear exactly once
in `readThreadIds` or `carriedForwardThreadIds` before recording the batch.
- Record the resulting sweep with `tend cli sweep:record-batch --work <work>`.
- Complete the work only after the source run and sweep batch are written back.
- Checkpoints recorded with `--work` are held on the run until `work:complete` succeeds, and
completion is refused until every `review` judgment in a run has its own card whose
`sourceRunIds` lists that run and that is visible for review in the current pass (or was acted
on since the run), and every `routine_action` judgment is presented by such a card or by an
item of a routine action group proposed since the sweep. If the work never completes,
the next sweep starts from the previous checkpoint and re-reads those items; a held checkpoint is
not written over one that changed after the run was recorded.
- Give every `review` or `routine_action` judgment a stable, file-safe `cardId` and reuse that
exact id in `card:upsert`. A judgment looks like
`{"decision":"review","cardId":"gmail-18c2f0a1","reason":"..."}`; `suppress` judgments need no
card. Two judgments may share one `cardId` when one card deliberately presents both. Judgments
without a `cardId` still work and are matched by count instead of by id; a card counts once per
sweep unless the judgments it presents name it.
- Record the resulting sweep with `tend cli sweep:record-batch --work <work>`. The batch must include
every run recorded for the work (or a newer run for the same source that supersedes it).
- Upsert one card per judgment with `sourceRunIds` including its run, then run
`tend cli sweep:status --feed <feed>`; it reports `ready` and lists every judgment that is not
presented yet with the exact reason (no such card, card does not list the run, card hidden).
- Complete the work only when `sweep:status` reports `ready`. Checkpoints recorded with `--work`
are held on the run until `work:complete` succeeds, and completion is refused with the same list
otherwise. A card presents a `review` judgment only if it is individually reviewable (not part of a
routine action group) in the current pass, or was acted on since the run; `routine_action`
judgments without a card may also be covered by items of a routine action group proposed since
the sweep. If the work never completes, the next sweep
starts from the previous checkpoint and re-reads those items; a held checkpoint is not written
over one that changed after the run was recorded. Re-claiming such work returns
`operatorGuidance.pendingPresentation` with the same list.

## Core Commands

Expand All @@ -103,6 +114,7 @@ Run `tend cli help` for the full command surface. Core feed-runner commands are:
| Remove source | `tend cli source:remove --feed <feed> --source <source>` |
| Record source run | `tend cli source:record-run --feed <feed> --source <source> (--snapshots <json> \| --snapshots-file <path>) (--judgments <json> \| --judgments-file <path>) (--checkpoint <json> \| --checkpoint-file <path>) [--context-use-file <path>]` |
| Record sweep batch | `tend cli sweep:record-batch --feed <feed> --runs <json-array> [--context <mind-update-id>]` |
| Check what the current sweep still needs before completing | `tend cli sweep:status --feed <feed>` |
| Record sweep rejudgment | `tend cli sweep:rejudge --feed <feed> --feedback <id> --ordered-cards <json-array> --removed-cards <json-array>` |
| Upsert card | `tend cli card:upsert --feed <feed> --card <json>` |
| Run configured readers on frozen input | `tend cli readers:run --feed <feed> --run <run> --packet-file <path> --readers-file <path>` |
Expand Down
5 changes: 3 additions & 2 deletions docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ waking this same thread and saying `go deal with the feed`.
5. Use local connectors only for the claimed item.
6. Write results back through the relevant `tend cli` command.
7. For `sweep_rejudge`, run `sweep:rejudge` against the returned `operatorGuidance.visibleCardIds` before completing the work.
8. For source recollection, record source runs and a sweep batch with the claimed `--work` id, then upsert a card for every kept judgment before completing the work.
Checkpoints recorded with `--work` only advance when `work:complete` succeeds, and completion is refused until every `review` judgment has its own card and every `routine_action` judgment has a card or a proposed routine action group.
8. For source recollection, record source runs and a sweep batch with the claimed `--work` id, then upsert a card for every `review` or `routine_action` judgment before completing the work.
Give each such judgment a stable `cardId` (for example `gmail-<threadId>`) and reuse it as the card id; run `sweep:status --feed <feed-id>` and complete only when it reports `ready`.
Checkpoints recorded with `--work` only advance when `work:complete` succeeds; it is refused with the list of unpresented judgments otherwise, and a re-claim returns that list as `operatorGuidance.pendingPresentation`.
If context influenced collection, include a file-backed `contextUse` on the relevant source run
and pin the same update id to the sweep batch.
A full Gmail sweep must begin with paginated `gmail_search_email_ids(query: "", label_ids:
Expand Down
1 change: 1 addition & 0 deletions server/cli/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const CLI_COMMANDS = [
"source:remove --feed <id> --source <id>",
"source:record-run --feed <id> --source <id> (--snapshots <json> | --snapshots-file <path>) (--judgments <json> | --judgments-file <path>) (--checkpoint <json> | --checkpoint-file <path>) [--work <recollection-work-id>] [--context-use <json> | --context-use-file <path>]",
"sweep:record-batch --feed <id> --runs <json-array> [--work <recollection-work-id>] [--context <mind-update-id>]",
"sweep:status --feed <id>",
"sweep:rejudge --feed <id> --feedback <id> --ordered-cards <json-array> --removed-cards <json-array>",
"source:import-json-file --feed <id> --source <id> --path <local-json-file>",
"source:import-file --feed <id> --source <id> --path <local-text-or-jsonl-file>",
Expand Down
8 changes: 8 additions & 0 deletions server/cli/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ export async function runOperatorCli(rawArgs: string[]): Promise<void> {
value("context"),
);
break;
case "sweep:status":
output = await domain.sweepPresentationStatus(required("feed"));
break;
case "sweep:rejudge":
output = await domain.recordSweepRejudgment(
required("feed"),
Expand Down Expand Up @@ -312,11 +315,16 @@ export async function runOperatorCli(rawArgs: string[]): Promise<void> {
claimed?.kind === "execute_approved_action"
? await store.readConfig(feedId)
: undefined;
const sweepPresentation =
claimed?.intent === "recollect_sources"
? await domain.sweepPresentationStatus(feedId)
: undefined;
output = formatWorkClaimOutput(feedId, work, {
card,
feedConfig,
routineActionGroup,
sweepFeedback,
sweepPresentation,
});
}
break;
Expand Down
Loading