Repository navigation
Commit 172be37
fix(pm): an accepted relay dispatch with no run is UNCONFIRMED under auto too, never a direct write (#22101)
Fixes #21892
Clause-②: no
## What changes
After a relay dispatch the platform ACCEPTED (HTTP 204), "no run
appeared within the start window" — or a run that never completed — is
now answered **UNCONFIRMED** by every relay sender: exit 6, the shared
`unconfirmedText` sentence (go READ the card, never re-run blind), under
`auto` exactly as under an explicit `dispatch`. No sender takes a direct
write after an accepted dispatch any more, so a relay that is merely
queued can no longer turn into a write under the seat's personal login
(and a second write when the queued run executes).
`OS_FLEET_TRANSPORT=direct` stays the only way to write as the personal
account; the route-selection read (condition ③ in
`fleet-write/dispatch.mjs`: the workflow file's 404,
`disabled_manually`) is unchanged.
| sender | on `main` (`3d9188502e`) under `auto` + `no-run` | on this
branch |
|---|---|---|
| `scripts/pm/label-write.mjs` | `fallbackText(...)` then the direct
additive POST/DELETE | exit 6, zero requests after the pre-read |
| `scripts/pm/issue-create.mjs` | `fallbackText(...)` then the direct
POST | exit 6, zero requests |
| `scripts/pm/issue-transfer.mjs` | `fallbackText(...)` then the direct
GraphQL mutation | exit 6, zero requests after the pre-read |
| `scripts/pm/post-stamped.mjs` | `fallbackText(...)` then the direct
comment POST / body PATCH | exit 6, zero requests |
| `scripts/pm/close-cards.mjs` | already exit 6, no direct fall-back |
unchanged; carries the conformance probe |
`fallbackText` is deleted from `fleet-write/dispatch.mjs`: after the
change it has no caller anywhere in the tree (`git grep -n fallbackText`
at `776ab7f2ff`: 0 hits; `with-fleet.sh` never called it — its `auto`
asks `dispatch.mjs --route` before anything is sent, and an actions file
goes to this file's CLI, which has no direct path).
## The enumeration pin (the triage grade's "a tool added later fails the
pin")
`node scripts/pm/fleet-write/dispatch.mjs --self-test` gains one
battery, **the no-run conformance** (27 cases, floor 18 batteries):
- the senders are DISCOVERED from source on every run — every code file
under `scripts/` except `dispatch.mjs` that names `sendFleetWrite` —
with a by-name floor of today's five, so a scan gone blind reds;
- each sender must export `relayMissProbe({ route, send, pace })`: ONE
write through its real write path against its own offline fake platform.
A sender without it fails the pin;
- each is driven through the REAL `sendFleetWrite` over a fake platform
with an accepted (204) dispatch and (a) no run under `auto`, (b) no run
under explicit `dispatch`, (c) a run that never completes under `auto`.
Each must answer exit 6, after exactly ONE dispatch, with ZERO direct
writes (any request but GET/HEAD), printing the shared UNCONFIRMED
sentence;
- the control drives each probe under `direct`: its write is issued
directly and seen by the recorder, and nothing is dispatched — so the
empty lists above are a measurement;
- `dispatch.mjs`'s own CLI answers the same (exit 6, one dispatch, no
other write).
The per-tool pins that said "auto falls back only on no-run" are flipped
in `label-write`, `issue-create` and `issue-transfer` to exit 6 **and**
the exact request list after the dispatch (pre-read only, or none);
`close-cards` already pinned it. `post-stamped`'s self-test is
synchronous, so its behavioural pin is the conformance; its own gate now
also carries the structural half (main writes only through
`writeThroughRoute`, whose one direct write sits on the non-dispatch
branch ahead of the relay, and whose no-run/timeout branch returns
UNCONFIRMED). CI runs the dispatcher self-test on every PR (the
`Fleet-write dispatcher self-test` step in `lint.yml` carries no `if:`).
## Inherited commits — read as UNVERIFIED, verdict per hunk
The branch continues the released claim's work at `4c5f2c15fc` (7
commits over `607463d736`). `origin/main` was merged in (`69f2346dca`,
merge of `3d9188502e`, no rebase — the branch is shared); `main` had not
touched the six files since `607463d736` (`git diff --stat 607463d
origin/main` over the six paths: empty), and the merge left the branch's
delta at exactly these six files (+402 / -94).
| commit | hunk | verdict |
|---|---|---|
| `a6bc9b3e08` label-write | header paragraph; import drops
`fallbackText`; the `auto && no-run` arm removed, the remaining arm
answers `no-run`/`timeout` UNCONFIRMED for every requested transport |
keep |
| | `relayMissProbe` over `driveOffline` (the real `runLabelWrite`) |
keep |
| | battery renamed; flipped case asserts exit 6, ONE dispatch, calls
equal to the pre-read alone; second case asserts the shared sentence |
keep |
| | battery pin left at 12 while the battery registers 14 |
**corrected** to 14 in `776ab7f2ff` |
| `51139a6248` issue-create | header, import, branch, `relayMissProbe`,
flipped case (exit 6, number null, zero requests) | keep; pin 8 → 9
**corrected** |
| `b1cebeea98` issue-transfer | header, import, branch,
`relayMissProbe`, flipped case (exit 6, pre-read only) | keep; pin 8 → 9
**corrected** |
| `f99f21158a` post-stamped | `deps` door on `rest` / `readCardTail` /
`writeArtefact` / `writeViaRelay` (the live path passes none; every
default is the old value) | keep |
| | `writeThroughRoute` extracted from `main`: the old branches, minus
the `auto` + `no-run` fall-back; `main` now one call, and its catch
still classifies a direct write's failure | keep |
| | `relayMissProbe`; structural pin retargeted to `writeThroughRoute(`
| keep; own-gate structural pin **added** in `776ab7f2ff` (battery pin
12 → 13) |
| `27f2f3ed9f` close-cards | `stubs.send` passthrough; `relayMissProbe`
spelling the two child tools' writes as the requests they stand for |
keep |
| `846527f4c0` dispatch.mjs | header: never fallen back from under any
transport request; the no-run conformance section | keep |
| | `fallbackText` deleted; its self-test case replaced by "accepted
(204), no run known" | keep (no caller remains) |
| | the conformance battery (27) and floor 17 → 18 | keep |
| | `RELAY_MISS_PROBE` declared between `DEFAULT_START_MS` and
`DEFAULT_CEILING_MS` | **corrected**: moved below the three timing
exports; the success line now names the conformance |
| `4c5f2c15fc` | a probe that throws fails as a case (`drive` wrapper),
per-tool second cases guarded with `?.` | keep — ablation B below fails
as cases, not as a crash |
Dropped: none.
## Self-tests at `776ab7f2ff` (rule ⑤: each edited script's own; no
`*.test.*` under `scripts/` or `tests/` names any of the six — `git
grep` exit 1)
```text
✓ fleet-write/dispatch self-test: 200 cases pass across 18 batteries — … (the no-run conformance).
✓ label-write self-test: 92 cases pass across 10 batteries — …
✓ issue-create self-test: 48 cases pass across 7 batteries — …
✓ issue-transfer self-test: 76 cases pass across 9 batteries — …
✓ post-stamped self-test: 630 cases pass across 22 batteries — offline, no network, no token.
OK close-cards self-test: 227 cases pass across 20 batteries — offline, no network, no token.
```
The inherited tree alone (`69f2346dca`, before `776ab7f2ff`) was green
on the same six, post-stamped at 629 cases.
What each probe's recorder sees (a scratch driver handing each
`relayMissProbe` an accepted no-run result, then `direct`):
```text
close-cards auto+no-run exit 6 [GET issues/88, GET …/timeline, GET …/comments]
direct exit 0 [… PATCH issues/88, POST …/comments, DELETE …/labels]
issue-create auto+no-run exit 6 []
direct exit 0 [POST /issues, GET issues/12]
issue-transfer auto+no-run exit 6 [GET issues/7]
direct exit 0 [GET issues/7, GET repo, POST /graphql, GET issues/7, GET target issues/31]
label-write auto+no-run exit 6 [GET issues/7]
direct exit 0 [GET issues/7, POST issues/7/labels, GET issues/7]
post-stamped auto+no-run exit 6 []
direct exit 0 [POST issues/7/comments, GET issues/comments/501]
```
## Ablation — both directions, at `776ab7f2ff`, through
`scripts/ablation-replace.mjs` (anchor must hit, blob must change,
restore proven)
| leg | mutation | own self-test | dispatcher self-test |
|---|---|---|---|
| A | post-stamped re-opens `auto` + `no-run` → `writeArtefact` | exit
1: the structural pin got `[[],2,true,true,true]` | exit 1:
`post-stamped.mjs: auto + an ACCEPTED dispatch + no-run` got
`[0,1,204,"no-run",["POST
/repos//issues/7/comments"],false]` |
| B | label-write stops exporting `relayMissProbe` | exit 0 (the export
is the conformance's door, not label-write's) | exit 1: `exports
relayMissProbe` got `"undefined"`; battery floor 23 below 27 |
| C | label-write re-opens `auto` + `no-run` → direct | exit 1: 2 cases,
calls `[GET issues/7, POST issues/7/labels, GET issues/7]` | exit 1:
`label-write.mjs: auto + … + no-run` got `[0,1,204,"no-run",["POST
…/issues/7/labels"],false]` |
Every leg restored: blob equals `HEAD` (`post-stamped.mjs` `f51c37c8f5`,
`label-write.mjs` `b364caeacc`), `git diff HEAD` 0 bytes, `git status
--porcelain` empty. The green direction is the same commands on the
restored tree (the self-test block above). The first attempt of legs A
and C was refused by `ablation-replace` (the replacement still contained
the anchor, "the anchor count moved 1 to 1") — no command ran, not a
measurement; both were re-anchored and re-run.
## Gate families (derived by `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, no paths: 6 paths vs
merge base `3d9188502e`, 38 commands)
All at `776ab7f2ff`, exit codes captured before any pipe:
- exit 0 (37): `check-ci-filter-parity`, `check-closing-keyword-parity`
(+ `--self-test`), `check-comment-mask-corpus`,
`check-declaration-mirrors` (+ `--self-test`),
`check-scripts-symbol-anchors` (+ `--self-test`),
`check-self-test-wired` (+ `--self-test`),
`check-self-test-workflow-commands` (+ `--self-test`),
`check-whole-set-label-write` (+ `--self-test`), `pm/bare-root-worklist
--self-test`, `check:agent-test-spelling`, `check:bash32-floor`,
`check:cli-command-ids`, `check:cross-package-test-inputs`,
`check:driver-memory-census`, `check:entry-guard`,
`check:gitlink-declared`, `check:nul-bytes`, `check:parse-guard`,
`check:pm-close-cards`, `check:pm-fleet-write-dispatch`,
`check:pm-fleet-write-execute`, `check:pm-fleet-write-validate`,
`check:pm-issue-create`, `check:pm-issue-transfer`,
`check:pm-label-write`, `check:pm-post-stamped`, `check:pm-write-pace`,
`check:pnpm-filter-targets`, `check:ratchet-remedy-authority`,
`check:refd-timer-probe`, `check:watch-hint-literal`.
- `check:pm-dispatch-gates` (the battery, an unlocked run): exit 0 — `✓
check:pm-dispatch-gates --self-test: the exit contract holds in all
three directions.` then `✓ dispatch-gates self-test: 1976 cases pass.`
(1279.8 s on this box).
- `--ran` reconciliation, exit 0: `Run reconciliation — 38 derived, 38
run, 0 NOT-MEASURED, 0 UNRUN.` (all 38 carry a recorded exit code).
`origin/main` has since moved to `b04a5295f7` without touching the six
files or their imports.
- Roster families whose roster sits in a directory this diff touches:
exit 0 — `check-published-list-mirrors` (+ `--self-test`),
`check:engine-double-contract`, `check:i18n-stale-fill`,
`check:pm-label-desc-cap`, `check-dts-references --self-test`. NOT
MEASURED — `check:dts-closure` and `check:published-readme-exports`
(exit 3 PREREQUISITE NOT MET: no package `dist/`; this diff touches no
package), `check:console-injection` (exit 0 but skipped: no console
dist).
- Lint, a declared narrowing: `pnpm exec eslint --no-inline-config
--format json` over the six files — 6 files, 0 errors, 0 warnings (none
reported as ignored). Population: `eslint.config.mjs`'s
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` block covers `scripts/pm/*.mjs`.
Invariance: the config never enables type-aware linting (no
`parserOptions.project`, said in its own header) and its plugin rules
are per-file, so this diff cannot move any untouched file's verdict. The
repo-wide `pnpm lint` is CI's.
## Acceptance notes
- The local derivation for a diff touching only `post-stamped.mjs` names
`check:pm-post-stamped` but not `check:pm-fleet-write-dispatch`, where
the conformance lives; CI runs the dispatcher self-test unconditionally,
and post-stamped's own gate now carries the structural half. Noted, not
filed (the derivation script is frozen).
- `post-stamped.mjs`'s size route (a body over the relay's 60,000-byte
cap under `auto` goes direct for that write, with one line naming bytes,
cap and identity) is a pre-dispatch, declared and pinned route decision,
untouched here. It sits beside the grade's sentence that
`OS_FLEET_TRANSPORT=direct` stays the only way to write as the personal
account; whether that sentence covers it is a ruling, not this card.
Noted, not filed.
- #19774 is the route-selection half of this class and is not touched;
#19990, #21294, #22052 and #22053 remain open and are not addressed
here.
## Writes
`git push` (twice: the merge, then `776ab7f2ff`); REST through the relay
as `objectstack-fleet[bot]`: this `pr_create` (draft), one `label-write`
(`skip-changeset` + PR assignee), one `os-dev-report` comment on the
card. MCP GitHub calls: 0. No changeset: `scripts/pm/**` publishes
nothing.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent dfa427b commit 172be37
6 files changed
Lines changed: 402 additions & 94 deletions
File tree
- scripts/pm
- fleet-write
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1599 | 1599 | | |
1600 | 1600 | | |
1601 | 1601 | | |
1602 | | - | |
| 1602 | + | |
1603 | 1603 | | |
1604 | 1604 | | |
1605 | 1605 | | |
| |||
1622 | 1622 | | |
1623 | 1623 | | |
1624 | 1624 | | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
| 1628 | + | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
| 1632 | + | |
| 1633 | + | |
| 1634 | + | |
| 1635 | + | |
| 1636 | + | |
| 1637 | + | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
1625 | 1644 | | |
1626 | 1645 | | |
1627 | 1646 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
102 | 102 | | |
103 | 103 | | |
104 | 104 | | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
111 | 117 | | |
112 | 118 | | |
113 | 119 | | |
| |||
193 | 199 | | |
194 | 200 | | |
195 | 201 | | |
196 | | - | |
197 | | - | |
| 202 | + | |
| 203 | + | |
198 | 204 | | |
199 | 205 | | |
200 | 206 | | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
201 | 235 | | |
202 | 236 | | |
203 | 237 | | |
| |||
292 | 326 | | |
293 | 327 | | |
294 | 328 | | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
295 | 332 | | |
296 | 333 | | |
297 | 334 | | |
| |||
514 | 551 | | |
515 | 552 | | |
516 | 553 | | |
517 | | - | |
518 | | - | |
519 | | - | |
520 | | - | |
521 | | - | |
522 | | - | |
523 | | - | |
524 | | - | |
525 | | - | |
526 | 554 | | |
527 | 555 | | |
528 | 556 | | |
| |||
1112 | 1140 | | |
1113 | 1141 | | |
1114 | 1142 | | |
| 1143 | + | |
1115 | 1144 | | |
1116 | | - | |
| 1145 | + | |
1117 | 1146 | | |
1118 | 1147 | | |
1119 | 1148 | | |
| |||
1479 | 1508 | | |
1480 | 1509 | | |
1481 | 1510 | | |
1482 | | - | |
| 1511 | + | |
1483 | 1512 | | |
1484 | 1513 | | |
1485 | 1514 | | |
| |||
1878 | 1907 | | |
1879 | 1908 | | |
1880 | 1909 | | |
| 1910 | + | |
| 1911 | + | |
| 1912 | + | |
| 1913 | + | |
| 1914 | + | |
| 1915 | + | |
| 1916 | + | |
| 1917 | + | |
| 1918 | + | |
| 1919 | + | |
| 1920 | + | |
| 1921 | + | |
| 1922 | + | |
| 1923 | + | |
| 1924 | + | |
| 1925 | + | |
| 1926 | + | |
| 1927 | + | |
| 1928 | + | |
| 1929 | + | |
| 1930 | + | |
| 1931 | + | |
| 1932 | + | |
| 1933 | + | |
| 1934 | + | |
| 1935 | + | |
| 1936 | + | |
| 1937 | + | |
| 1938 | + | |
| 1939 | + | |
| 1940 | + | |
| 1941 | + | |
| 1942 | + | |
| 1943 | + | |
| 1944 | + | |
| 1945 | + | |
| 1946 | + | |
| 1947 | + | |
| 1948 | + | |
| 1949 | + | |
| 1950 | + | |
| 1951 | + | |
| 1952 | + | |
| 1953 | + | |
| 1954 | + | |
| 1955 | + | |
| 1956 | + | |
| 1957 | + | |
| 1958 | + | |
| 1959 | + | |
| 1960 | + | |
| 1961 | + | |
| 1962 | + | |
| 1963 | + | |
| 1964 | + | |
| 1965 | + | |
| 1966 | + | |
| 1967 | + | |
| 1968 | + | |
| 1969 | + | |
| 1970 | + | |
| 1971 | + | |
| 1972 | + | |
| 1973 | + | |
| 1974 | + | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
| 1978 | + | |
| 1979 | + | |
| 1980 | + | |
| 1981 | + | |
| 1982 | + | |
| 1983 | + | |
| 1984 | + | |
| 1985 | + | |
| 1986 | + | |
| 1987 | + | |
| 1988 | + | |
| 1989 | + | |
| 1990 | + | |
| 1991 | + | |
| 1992 | + | |
| 1993 | + | |
| 1994 | + | |
| 1995 | + | |
| 1996 | + | |
| 1997 | + | |
| 1998 | + | |
| 1999 | + | |
| 2000 | + | |
| 2001 | + | |
| 2002 | + | |
| 2003 | + | |
| 2004 | + | |
| 2005 | + | |
| 2006 | + | |
| 2007 | + | |
| 2008 | + | |
| 2009 | + | |
| 2010 | + | |
| 2011 | + | |
| 2012 | + | |
| 2013 | + | |
| 2014 | + | |
1881 | 2015 | | |
1882 | 2016 | | |
1883 | 2017 | | |
| |||
1914 | 2048 | | |
1915 | 2049 | | |
1916 | 2050 | | |
1917 | | - | |
| 2051 | + | |
| 2052 | + | |
1918 | 2053 | | |
1919 | 2054 | | |
1920 | 2055 | | |
| |||
0 commit comments