B1 truncates due dates to the second; B2 records what landed when the
final read fails; tests for V9, V21 and S5. Candidate manifest
e10e30e3, 28 files over c9ef7ee4. Mutants 46/63. Live provider cases
in test-release and test-task failed on a zai usage limit (429).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
176 lines
8.0 KiB
Markdown
176 lines
8.0 KiB
Markdown
# Row 38, S3: round 2
|
|
|
|
Darkwing, 2026-10-09. Issue #1520, reviewer Filbert. Round 1 review:
|
|
`agents/filbert/work/slice1-s3-review/review-r1.md` (comment 26850,
|
|
rev 183). Scope from Sage (rev 184): B1, B2, and tests for V9, V21 and
|
|
S5. Nothing in the candidate is committed, staged or pushed.
|
|
|
|
Base is c9ef7ee4. Nothing under `packages/`, `scripts/` or `tools/`
|
|
changed between bee89d10, where the candidate was built, and c9ef7ee4.
|
|
In a fresh worktree at c9ef7ee4 the patch applies and the result matches
|
|
the manifest 28/28.
|
|
|
|
## Files
|
|
|
|
- `build-r2.patch` (sha256 `71ce87e631b04d35591e2df966aae565d5f665ac2a9d5d252322ce1b0d5be69c`): the whole candidate against
|
|
c9ef7ee4, the same 28 files as round 1.
|
|
- `build-r2-manifest.sha256` (sha256 `e10e30e3d68c12177afba438ac02a06c208ea0370c7198af1f9d9cf163d637f4`) pins all 28.
|
|
- `r1-to-r2.diff`: what changed since round 1. Five files under
|
|
`packages/tasks`: `src/verbs.mjs`, `src/fake.mjs`, `README.md`,
|
|
`tests/verbs.test.mjs`, `tests/sync.test.mjs`. Nothing in
|
|
`packages/bus` changed.
|
|
- `r2-filbert-probes.txt`, `r2-mutants.txt`, `r2-gate-summary.txt`:
|
|
evidence, below.
|
|
|
|
## B1: due dates with milliseconds
|
|
|
|
Sage's ruling: truncate in `due()`, don't refuse. `due()` now keeps the
|
|
first 19 characters and appends `.000Z`. Every use of the value comes
|
|
after that: the PATCH body, the expected fields for the self snapshot,
|
|
the digest handed back, and the check an uncertain PATCH's re-read runs.
|
|
`task.create` goes through the same `due()`.
|
|
|
|
`FakeVikunja` now stores a due date to the second, the way the pinned
|
|
image does in Filbert's `r1-vk-due-probe.txt`. A write's answer echoes
|
|
the value sent, in Go's form, as the image does. The probe can't tell
|
|
whether Vikunja floors or rounds. The adapter only sends whole seconds,
|
|
so that doesn't matter to it.
|
|
|
|
The README says due dates have one-second precision, with an example.
|
|
|
|
Test: "a due date with milliseconds is written to the second" covers a
|
|
create with `.789Z` and a schedule with `.456Z`. Then:
|
|
|
|
- the fake holds whole seconds;
|
|
- the self snapshot holds `.000Z`, and its digest is the one returned;
|
|
- a tick records nothing;
|
|
- the same schedule again sends no PATCH;
|
|
- the returned digest works as `expect`;
|
|
- a PATCH whose answer is lost settles on the re-read.
|
|
|
|
Filbert's probe D1 against this tree shows no external event after the
|
|
tick and no second PATCH. `expect` set to the returned digest gets past
|
|
the conflict check and stops at `decision-not-found`, the next check,
|
|
because the probe passes a made-up decision.
|
|
|
|
## B2: a write lands and the final read fails
|
|
|
|
`handle()` counts the writes that land. The final read's refusal is no
|
|
longer passed on:
|
|
|
|
- Every write landed: the verb succeeds and records its event and a
|
|
self snapshot of `work.expect(before.fields)`. `updated` is the
|
|
compare-read's, the newest one read. Filbert offered this or a null
|
|
`updated`. Keeping the compare-read's value means `recordTask` didn't
|
|
have to change.
|
|
- Some landed and a later write failed: `write-uncertain`, and nothing is
|
|
recorded. The poll records what the tracker holds.
|
|
- None landed: the failed write's own code, as in round 1.
|
|
|
|
Filbert's fix allowed refusing `write-uncertain` or returning when every
|
|
write landed. I return. The outcome isn't uncertain: each write was
|
|
confirmed, and the record is what a successful read would have written,
|
|
apart from `updated`. A refusal would send the caller back to a verb
|
|
that then refuses `task-assigned`, which is the W1 problem again.
|
|
|
|
Probe W1 against this tree: the caller sees ok, one `task.assigned`
|
|
event, one new self snapshot, no external event on the poll, and a
|
|
retry refuses `task-assigned`.
|
|
|
|
One addition beyond Filbert's list. `create()` had the same shape: the
|
|
create, labels and relations land, the final read fails, and the
|
|
caller got `tracker-unavailable`. That reads as "nothing happened" and
|
|
invites a second create. Now, when every write landed and the final
|
|
read failed, the snapshot is built from the create's answer plus the
|
|
labels written, in the default bucket. `task.created` is recorded and the
|
|
verb succeeds. If a label or relation write failed, the verb refuses with
|
|
that write's code, as in round 1, and `task.created` is still recorded.
|
|
|
|
Tests:
|
|
|
|
- "every write landed and the final read failed: the verb succeeds and
|
|
records what it wrote" (assign, Filbert's W1 as a test).
|
|
- "some writes landed and the final read failed: write-uncertain, and
|
|
nothing is recorded" (schedule: the PATCH lands, the label write gets
|
|
403, the final read fails).
|
|
- "a create whose final read fails succeeds and records task.created".
|
|
A tick afterwards records nothing, so the fallback snapshot matches
|
|
what the poll reads.
|
|
|
|
## T1, T2, T3
|
|
|
|
- T1 (V9): "an edit between the last write and the final read shows as
|
|
external on the next poll". A UI title edit lands between the assignee
|
|
write and the final read. The self snapshot keeps the old title, and
|
|
the tick records `changed: ["title"]`.
|
|
- T2 (V21): "task.created is recorded when a later label write fails".
|
|
- T3 (S5): "the first look at a task counts only comments inside the
|
|
window". The fake's clock is moved back two hours for the create and
|
|
one comment, then forward for a second comment. The tick counts 1.
|
|
|
|
## Do the tests catch the round 1 code?
|
|
|
|
With round 1's `src/verbs.mjs` and `src/fake.mjs` under the round 2
|
|
tests, four tests fail: the due date test, both final-read tests for
|
|
`handle()`, and the create final-read test. T1, T2 and T3 pass there,
|
|
since they test behaviour round 1 already had. The mutants show they
|
|
hold it.
|
|
|
|
## Mutants
|
|
|
|
Filbert's `mutants.sh`, unchanged, all 63, on a copy of this tree:
|
|
46 of 63 killed. V9, V21, S5 and G1 are killed. Round 1 was 42/63.
|
|
The 17 survivors are round 1's survivors less those four: V12, V19, V20,
|
|
V23, V24, S3, S6, S7, S9, S10, U5, U6, U7, K1, K3, G2 and B8. Filbert
|
|
called each of them a test gap or near-equivalent, and none was in this
|
|
round's scope.
|
|
|
|
## Gate
|
|
|
|
In the worktree at c9ef7ee4 with the patch applied, run one after
|
|
another, each to its own file:
|
|
|
|
| Suite | Result |
|
|
|---|---|
|
|
| `node --test 'packages/tasks/tests/*.test.mjs'` | 51/51 |
|
|
| `node --test 'packages/bus/tests/*.test.mjs'` | 67/67 |
|
|
| `node --test 'packages/business/tests/*.test.mjs'` | 60/60 |
|
|
| `node --test 'packages/discord/tests/*.test.mjs'` | 173/173 |
|
|
| `test-auth.sh` | 15/15 |
|
|
| `test-conductor.sh` | 17/17 |
|
|
| `test-config.sh` | 24/24 |
|
|
| `test-discord.sh` | 64/64 |
|
|
| `test-extension-package.sh` | 18/18 |
|
|
| `test-foundation.sh` | 44/44 |
|
|
| `test-queue.sh` | 27/27, the canonical-root check skipped in a scratch tree |
|
|
| `test-release.sh` | 11 pass, 3 fail |
|
|
| `test-task.sh` | 90 pass, 8 fail |
|
|
|
|
The 11 failures are every case that runs a real model turn: the release
|
|
health check and the live, fork and recall cases in test-task. The zai
|
|
account had hit its 5-hour usage limit. A sandboxed `release.sh activate`
|
|
in the same tree two minutes later left a run whose stderr ends in a 429,
|
|
"Usage limit reached for 5 hour". The mock-adapter cases in test-task all
|
|
pass.
|
|
|
|
I tried a clean base at c9ef7ee4 as a control and it didn't work as one.
|
|
Docker couldn't create its compose network ("all predefined address pools
|
|
have been fully subnetted"), so every container case failed there, mock
|
|
ones included: test-task 67 pass, 31 fail, twice. The candidate tree's
|
|
network already existed. I didn't remove other networks to make room.
|
|
`r2-gate-summary.txt` has the case names and the error lines.
|
|
|
|
So the live cases are unproven this round, not failed by the patch.
|
|
Neither suite loads `packages/tasks` or `packages/bus`, nothing under
|
|
`scripts/` changed since round 1, and round 1 had 14/14 and 98/98. I'll
|
|
rerun both suites in the candidate tree after the limit resets and add
|
|
the result.
|
|
|
|
## Not in this round
|
|
|
|
Filbert's notes 1 to 14 stay as they were, except note 9 (the fake kept
|
|
millisecond due dates), which B1 closes. Note 7, `secretCheck` on the
|
|
result after a write, has B2's shape. It fails closed, and the fix is a
|
|
change to the broker's result check, which is outside this round's scope.
|
|
I'd take it with the follow-ups listed in round 1.
|