|
1 | 1 | --- |
2 | 2 | id: "008" |
3 | 3 | title: Remove the Choices Agents Get Wrong |
4 | | -status: pending |
| 4 | +status: complete |
5 | 5 | amends: specs/001-agent-mail-cli.md, specs/006-inbox-watch.md |
6 | 6 | blocked_by: [] |
7 | 7 | blocks: [] |
@@ -124,51 +124,63 @@ They remain the standing contracts. This spec owns the change and its verificati |
124 | 124 |
|
125 | 125 | ## Implementation Tasks |
126 | 126 |
|
127 | | -- [ ] Remove the TTL constant, the expiry predicate, and the opportunistic purge from every command. |
128 | | -- [ ] Remove the expired-message filter from `watch`, which existed only to avoid waking on a row the purge would delete. |
129 | | -- [ ] Remove the per-message lifetime from `send` and `read` output and from the schema. |
130 | | -- [ ] Require `--older-than <days>` on `cleanup`, and have it sweep stale watcher rows under the same cutoff. |
131 | | -- [ ] Report the mailbox file size from `describe`. |
132 | | -- [ ] Replace `watch --timeout` with `--timeout-minutes` (default 90, range 5–240) and `--timeout 0` with `--once`. |
133 | | -- [ ] Fix the poll interval and remove `--poll`. |
134 | | -- [ ] Reject the removed flags with a message naming the replacement. |
135 | | -- [ ] Separate the blocking loop from argument validation so the deadline path is testable without waiting out the floor. |
136 | | -- [ ] Update the schema's invariants: messages persist, and the wait is in minutes. |
137 | | -- [ ] Add regression coverage for persistence, for the rejected values, and for an existing mailbox opening unchanged. |
138 | | -- [ ] Amend specs 001 and 006 with the new rules and acceptance criteria. |
139 | | -- [ ] Bump the package version to `0.2.0` and add the CHANGELOG entry, calling out both breaking changes. |
140 | | -- [ ] Release `v0.2.0` after JJ's approval and verify the published artifacts. |
| 127 | +- [x] Remove the TTL constant, the expiry predicate, and the opportunistic purge from every command. |
| 128 | +- [x] Remove the expired-message filter from `watch`, which existed only to avoid waking on a row the purge would delete. |
| 129 | +- [x] Remove the per-message lifetime from `send` and `read` output and from the schema. |
| 130 | +- [x] Require `--older-than <days>` on `cleanup`, and have it sweep stale watcher rows under the same cutoff. |
| 131 | +- [x] Report the mailbox file size from `describe`. |
| 132 | +- [x] Replace `watch --timeout` with `--timeout-minutes` (default 90, range 5–240) and `--timeout 0` with `--once`. |
| 133 | +- [x] Fix the poll interval and remove `--poll`. |
| 134 | +- [x] Reject the removed flags with a message naming the replacement. |
| 135 | +- [x] Separate the blocking loop from argument validation so the deadline path is testable without waiting out the floor. |
| 136 | +- [x] Update the schema's invariants: messages persist, and the wait is in minutes. |
| 137 | +- [x] Add regression coverage for persistence, for the rejected values, and for an existing mailbox opening unchanged. |
| 138 | +- [x] Amend specs 001 and 006 with the new rules and acceptance criteria. |
| 139 | +- [x] Bump the package version to `0.2.0` and add the CHANGELOG entry, calling out both breaking changes. |
| 140 | +- [x] Release `v0.2.0` after JJ's approval and verify the published artifacts. |
| 141 | + |
| 142 | + |
| 143 | +## Verification Record |
| 144 | + |
| 145 | +- [x] `ruff check src/ tests/` passed; `pytest` passed 80 tests, 0 skipped, with the PyInstaller binary and npm wrapper both available. |
| 146 | +- [x] Implementation defect caught by the new tests rather than by review: `read` issues `SELECT *`, so `ttl_hours` kept reaching callers after the schema stopped advertising it. Fixed by projecting to the documented `output_fields` by default, which closes the whole class rather than that one column. |
| 147 | +- [x] `python -m build` produced the `0.2.0` wheel and sdist; `twine check` passed on both. |
| 148 | +- [x] CI passed on `main` for Python 3.10-3.13 plus lint and build. |
| 149 | +- [x] Release workflow `30830448492` succeeded across all eight jobs; GitHub Release `v0.2.0` carries the wheel, the sdist, and the four platform binaries. PyPI and npm both serve `0.2.0`. |
| 150 | +- [x] `npm-smoke.yml` passed on Windows x64, Linux x64, macOS x64, and macOS arm64. |
| 151 | +- [x] AC15 verified against the globally installed `0.2.0` on JJ's machine, on the live second-brain mailbox: `--timeout-minutes 0.5` rejected with the range, `--timeout 30` rejected with the migration message, `--poll 0.1` rejected as fixed, `--once` returning the empty result, and `cleanup` with no `--older-than` refusing to delete. `describe` reported `messages: 21` and `size_bytes: 286720` against a mailbox carrying pre-upgrade rows. |
| 152 | +- [x] AC1 and AC12 are covered deterministically rather than by waiting: messages are aged by writing `created` (30 and 400 days), and the compatibility case builds an `0.1.8`-shaped mailbox by hand and opens it with the current code. |
141 | 153 |
|
142 | 154 | ## Acceptance Criteria |
143 | 155 |
|
144 | 156 | Each criterion names its validation method. `integration` criteria are automated tests owning their own fixtures; `manual` criteria require live published artifacts. |
145 | 157 |
|
146 | 158 | ### Persistence |
147 | 159 |
|
148 | | -- [ ] **AC1** (`integration`): A message whose `created` timestamp is well past 24 hours old is returned by `read`, counted by `status`, and wakes `watch`. |
149 | | -- [ ] **AC2** (`integration`): `send`, `read`, `status`, `ack`, and `watch` each leave an old message in place — no command deletes one as a side effect. |
150 | | -- [ ] **AC3** (`integration`): `cleanup` without `--older-than` exits non-zero with a JSON error and deletes nothing. |
151 | | -- [ ] **AC4** (`integration`): `cleanup --older-than <days>` deletes only messages older than the cutoff, removes their broadcast-ack rows, sweeps watcher rows past the same cutoff, and honors `--dry-run` by deleting nothing. |
| 160 | +- [x] **AC1** (`integration`): A message whose `created` timestamp is well past 24 hours old is returned by `read`, counted by `status`, and wakes `watch`. |
| 161 | +- [x] **AC2** (`integration`): `send`, `read`, `status`, `ack`, and `watch` each leave an old message in place — no command deletes one as a side effect. |
| 162 | +- [x] **AC3** (`integration`): `cleanup` without `--older-than` exits non-zero with a JSON error and deletes nothing. |
| 163 | +- [x] **AC4** (`integration`): `cleanup --older-than <days>` deletes only messages older than the cutoff, removes their broadcast-ack rows, sweeps watcher rows past the same cutoff, and honors `--dry-run` by deleting nothing. |
152 | 164 |
|
153 | 165 | ### Surface |
154 | 166 |
|
155 | | -- [ ] **AC5** (`integration`): No `send`, `read`, or `watch` payload contains a per-message lifetime field, and the schema documents none. |
156 | | -- [ ] **AC6** (`integration`): `describe` reports `storage.size_bytes` matching the mailbox file's size, and null when the file is absent or unreadable, still exiting 0. |
| 167 | +- [x] **AC5** (`integration`): No `send`, `read`, or `watch` payload contains a per-message lifetime field, and the schema documents none. |
| 168 | +- [x] **AC6** (`integration`): `describe` reports `storage.size_bytes` matching the mailbox file's size, and null when the file is absent or unreadable, still exiting 0. |
157 | 169 |
|
158 | 170 | ### Wait bounds |
159 | 171 |
|
160 | | -- [ ] **AC7** (`integration`): `watch` with no flags waits 90 minutes. |
161 | | -- [ ] **AC8** (`integration`): `--timeout-minutes` below 5 or above 240 exits non-zero with a JSON error naming the range, and does not wait. |
162 | | -- [ ] **AC9** (`integration`): `--once` returns immediately — the empty result when nothing is waiting, the message when one is — and is rejected when combined with `--timeout-minutes`. |
163 | | -- [ ] **AC10** (`integration`): `--timeout` and `--poll` each exit non-zero with a JSON error naming the replacement flag. |
164 | | -- [ ] **AC11** (`integration`): The blocking loop returns its empty result once the deadline elapses, verified by driving the loop directly with a sub-floor deadline so the test does not wait out the floor. |
| 172 | +- [x] **AC7** (`integration`): `watch` with no flags waits 90 minutes. |
| 173 | +- [x] **AC8** (`integration`): `--timeout-minutes` below 5 or above 240 exits non-zero with a JSON error naming the range, and does not wait. |
| 174 | +- [x] **AC9** (`integration`): `--once` returns immediately — the empty result when nothing is waiting, the message when one is — and is rejected when combined with `--timeout-minutes`. |
| 175 | +- [x] **AC10** (`integration`): `--timeout` and `--poll` each exit non-zero with a JSON error naming the replacement flag. |
| 176 | +- [x] **AC11** (`integration`): The blocking loop returns its empty result once the deadline elapses, verified by driving the loop directly with a sub-floor deadline so the test does not wait out the floor. |
165 | 177 |
|
166 | 178 | ### Compatibility and release |
167 | 179 |
|
168 | | -- [ ] **AC12** (`integration`): A mailbox created by `0.1.8`, including rows carrying `ttl_hours`, opens under `0.2.0` with no migration and returns every stored message. |
169 | | -- [ ] **AC13** (`integration`): `ruff check src/ tests/` and the full `pytest` suite pass on the existing CI matrix with no workflow changes and no new runtime dependencies. |
170 | | -- [ ] **AC14** (`manual`): GitHub Release `v0.2.0` carries the six artifacts required by spec 002 AC3, PyPI and npm both serve `0.2.0`, and the smoke workflow passes on all four platforms. |
171 | | -- [ ] **AC15** (`manual`): On JJ's machine against the live second-brain mailbox, the installed `0.2.0` rejects `--timeout-minutes 0.5`, rejects `--timeout 30` with the migration message, and a message sent before the upgrade is still readable afterwards. |
| 180 | +- [x] **AC12** (`integration`): A mailbox created by `0.1.8`, including rows carrying `ttl_hours`, opens under `0.2.0` with no migration and returns every stored message. |
| 181 | +- [x] **AC13** (`integration`): `ruff check src/ tests/` and the full `pytest` suite pass on the existing CI matrix with no workflow changes and no new runtime dependencies. |
| 182 | +- [x] **AC14** (`manual`): GitHub Release `v0.2.0` carries the six artifacts required by spec 002 AC3, PyPI and npm both serve `0.2.0`, and the smoke workflow passes on all four platforms. |
| 183 | +- [x] **AC15** (`manual`): On JJ's machine against the live second-brain mailbox, the installed `0.2.0` rejects `--timeout-minutes 0.5`, rejects `--timeout 30` with the migration message, and a message sent before the upgrade is still readable afterwards. |
172 | 184 |
|
173 | 185 | ## Testing Approach |
174 | 186 |
|
|
0 commit comments