fix(fnxc): three future-dated scheduler stamps — main red through three closed fixes (#3280)

**`check-fnxc-future-dates` exits 1 on `origin/main`.**

```
packages/engine/src/scheduler.ts: 3 future-dated stamps, baseline allows 2
  FNXC:ConcurrencyAdmission     2026-08-06-09:00   (six days out)
  FNXC:WorkflowLifecycleColumns 2026-08-01-05:00
  FNXC:WorkflowScheduling       2026-08-01-01:05
```

All three repointed to `2026-07-31`, times preserved. Gate now exits 0.

## This red has outlived three owners

#3270, #3272 and #3274 were each opened against it and each **closed
without merging**. Main has been red on this gate for hours while three
fixes came and went.

Claimed with `check-file-claimed.mjs` before starting — only #3262
touches `scheduler.ts`, and it is a terminal-role refactor rather than a
stamp fix, so this was genuinely unowned.

## Why this keeps recurring

Seven incidents in roughly two hours. The mechanism, in one line: **the
date check runs only in CI** (`pr-checks.yml:66`, no pre-commit or
pre-push hook), so every PR is validated against main's baseline *at its
own CI time* and cannot see a concurrent or later change. Two PRs
stamping the same file both pass, then compose into a red main. One case
(#3273) was a stale branch **reverting** an already-merged fix.

Patching instances has not converged — this PR is the eighth attempt at
the same class. Two structural options, neither of which I am landing
unilaterally since the second changes the gate's contract:

- run the date check at **author time** (pre-push); it needs no baseline
for "is this date in the future", so it cannot be raced
- make the date rule **baseline-free** — a future-dated stamp is always
wrong, unlike a lifecycle literal that may be a deliberate fallback

`2026-08-06` being six days out also suggests these are not off-by-one
timezone slips but stamps written from an intended future date.

## Verification

- `check-fnxc-future-dates` — **exit 0** (was exit 1 on main)
- `scheduler` suites — **148 pass**
- `tsc --noEmit` (engine) — 0 errors
- comment-only diff, no behaviour change

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-31 17:28:14 -07:00
committed by GitHub
parent 475bb2d641
commit 5acd8e987b

View File

@@ -960,7 +960,7 @@ export class Scheduler {
logger: schedulerLog,
});
/*
FNXC:ConcurrencyAdmission 2026-08-06-09:00:
FNXC:ConcurrencyAdmission 2026-07-31-09:00:
FN-8453's union must outlive a single scheduler poll. A temporary provider
was gone before planning/merge asked for capacity, allowing newer work to
overtake ready execute work. The refreshed map is the durable lane view.
@@ -1404,7 +1404,7 @@ export class Scheduler {
const deletedParked = await resolveTaskParkedColumns(this.store, task.id);
/*
FNXC:WorkflowLifecycleColumns 2026-08-01-05:00:
FNXC:WorkflowLifecycleColumns 2026-07-31-05:00:
A HALF-CONVERTED PAIR, one line apart. The hold read above already resolved its lane while
the wip read below stayed on the literal, so on a renamed board this dependent sweep saw
the queued cards and none of the running ones — a dependency held by an in-flight task was
@@ -2255,7 +2255,7 @@ export class Scheduler {
&& typeof task.worktree === "string" && task.worktree.length > 0)
.map((task) => task.id);
/*
FNXC:WorkflowScheduling 2026-08-01-01:05 (self-deadlock in the widened ledger, observed live):
FNXC:WorkflowScheduling 2026-07-31-01:05 (self-deadlock in the widened ledger, observed live):
A planned Ready card RETAINS its planning worktree for execution reuse, so counting it as a
holder must not block ITS OWN release — on release the slot TRANSFERS (the card executes in
the same worktree), it does not add. Without this exclusion the first unpause released only