The check-workspace-package-graph validator globs the filesystem for package
manifests but only excluded node_modules/dist. On any machine that had run a
desktop packaging build (pnpm deploy), the gitignored electron-builder staging
dir packages/desktop/deploy/ carries a copy of desktop's package.json and
tripped the unglobbed-package violation, breaking pretest/gate locally while CI
(clean checkout, no deploy dir) stayed green. Exclude the staging path alongside
node_modules/dist since a gitignored dir is absent from the isolated worktree
this validator guards.
## Summary
Starts **U5** of the package code-organization program after wave 18
(executor peels) landed.
Peels pure free-function clusters out of `self-healing.ts` into
`packages/engine/src/self-healing/` without behavior changes. Public
imports from `./self-healing.js` remain stable via re-exports.
### Peels
| Symbol | New home |
|--------|----------|
| `autoRecoverWorktreeSessionStartFailure` |
`self-healing/auto-recover-worktree-session.ts` |
| `archiveAsGhostBug` | `self-healing/archive-ghost-bug.ts` |
| `hasStepProgress` / work-complete helpers |
`self-healing/step-progress.ts` |
### Line count
- `self-healing.ts`: ~15456 → ~15231 (baseline ratcheted to post-peel
live; main had already drifted past the prior grandfathered ceiling via
organic growth)
- New modules each well under 2,000 lines
## Test plan
- [x] `pnpm --filter @fusion/engine exec tsc --noEmit`
- [x] `self-healing-trait-rekey.test.ts` (autoRecover requeue)
- [x] `self-healing-paused-abort-recovery.test.ts`
- [x] `self-healing-model-unavailable-recovery.test.ts`
- [ ] CI gate
## Follow-ups
U5 Slice B: domain method clusters (startup, in-review, merge-status,
workspace, surfacing) into additional `self-healing/*.ts` modules.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved automatic recovery when worktree sessions fail to start,
including stale or incomplete session data.
* Tasks can be safely requeued while preserving progress, or escalated
after retry limits are reached.
* Improved handling of completed work and failures where task completion
was not recorded.
* Preserved valid task branches during recovery and provided more
reliable fallback requeue behavior.
* Ghost bugs are automatically archived with recovery details and
activity history.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Classify workflow work-item `workflowRole` comparisons as role
vocabulary in the lifecycle-column census.
- Add a regression test so triage role comparisons cannot raise a
phantom lifecycle-column guard.
## Test Plan
- `node --test scripts/__tests__/lifecycle-census*.test.mjs`
- `corepack pnpm check:lifecycle-columns`
- `corepack pnpm lint`
- `corepack pnpm check:changesets --strict`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved classification of workflow role comparisons, including
`workflowRole === "triage"`, so they are recognized separately from
lifecycle-column comparisons.
* Ensured workflow role values are correctly identified as role
vocabulary rather than lifecycle-column values.
* **Tests**
* Added automated coverage to verify accurate workflow role and column
identification across comparison patterns.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Complete the isolated `@fusion/core` mock used by the
experiment-finalize extension suite
- Classify three intentional physical/synthetic lifecycle literals
introduced on current main
- Re-record the strict lifecycle census baseline with zero unexamined
guards
## Test plan
- `pnpm --filter @runfusion/fusion exec vitest run
src/__tests__/extension-experiment-finalize.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/core exec vitest run
src/__tests__/task-intake-owner-resolver.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/engine exec vitest run --project engine-default
src/__tests__/mission-feature-sync-lanes.test.ts --silent=passed-only
--reporter=dot`
- `pnpm check:lifecycle-columns`
- `node scripts/check-mock-completeness.mjs`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved mission reconciliation previews for task links, specification
alignment, and lifecycle updates.
- Prevented stale or superseded validation runs from overwriting current
feature status or ownership.
- Improved blocked-feature diagnostics and archived-task handling across
workflow configurations.
- **Documentation**
- Clarified validation, assignment checks, and mission synchronization
behavior.
- **Tests**
- Expanded coverage for reconciliation previews and validator ownership
scenarios.
- **Chores**
- Updated lifecycle baseline data for known archived-task cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
verify:fast ran every step serially, so its wall clock was the sum of steps with no
ordering relationship between them. Static checks and per-package typechecks are
each independent, so they now run as bounded-concurrency groups.
static checks ~6.0s -> ~1.6s (11 validators, mostly node startup)
typecheck 11.0s -> 7.4s (engine + dashboard)
no-change run 28.1s -> 22.3s
Ordering that matters is untouched: bootstrap, builds, and boot smoke stay serial
and in plan order, and each group is a barrier. A failing group awaits its in-flight
siblings before throwing rather than abandoning partial tsbuildinfo/dist state, and
reports the first failure in plan order so the message does not depend on which
sibling lost the race. FUSION_VERIFY_FAST_SERIAL=1 restores the old behavior when
interleaved child output makes a failure hard to read.
Boot smoke is now 84% of a no-change run (18.8s); it re-runs initdb into a throwaway
HOME every time. Left alone -- caching that would change what the gate proves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## Problem
Dashboard typecheck fails with **TS2416** in `@fusion/core`'s
`TaskStore`:
```
Property 'emit' in type 'TaskStore' is not assignable to the same property in base type 'EventEmitter<TaskStoreEvents>'.
```
The `override emit<E extends string | symbol>(event, ...args)` generic
conflicts with the base class's generic `emit<K>(eventName: keyof
TaskStoreEvents | K, ...)`. This breaks the dashboard typecheck / CI
merge gate.
## Fix
Change the override to:
```ts
override emit(event: unknown, ...args: any[]): boolean {
return EventEmitter.prototype.emit.call(this, event as string, ...args);
}
```
`event: unknown` remains assignable to the base's generic signature
while still forwarding non-typed runtime keys (`agent:log`,
`settings:updated`, …). Internal `EventEmitter.prototype.emit` calls
cast `event as string`. Behavior-preserving.
## Verification
- `@fusion/dashboard` `tsc --noEmit` → **PASS** (previously failed with
TS2416)
- `eslint` on touched file → clean
- Single-file change (`packages/core/src/store.ts`, +6/−3)
## Scope
No behavior change, no changesets required.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved task event handling to support a broader range of event
identifiers.
* Preserved cached-lane information for single-argument task update
events.
* Maintained support for custom and arbitrary event names without
disrupting existing behavior.
* Improved classification of workflow roles, session purposes, and
outcome-related status checks in lifecycle analysis, producing more
accurate findings and reducing misleading results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Stabilize live Proceed-action handoffs and re-admit the Planning Mode flow suite.
- Settle hydration and re-query the Proceed action before direct-create test clicks.
- Remove the Planning Mode test quarantine and record its rescue in the testing ledger.
Files changed:
.../suite-only-flakes-observed-register.md | 4 ++++
docs/testing.md | 3 +++
.../PlanningModeModal.planning-flow.test.tsx | 20 ++++++++++++++++----
packages/dashboard/vitest.config.ts | 5 -----
scripts/lib/test-quarantine.json | 5 -----
5 files changed, 23 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-8936
Fusion-Task-Lineage: ed869b67-9394-458b-879c-54da0d7d327e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Operator directed deletion of tests that test pre-refactor behavior no
longer in the codebase (removed APIs, mock shape drift, stale assertions
from the 2026-08-05 full-suite quarantine wave, run 30982276306).
All 27 entries were permanently red — not flaky — testing APIs removed
during the PG cutover and workflow peel refactors (getBuiltinWorkflow,
resolveWorkflowIrForTaskWithProvenance, layer.db.select mock shapes,
vi.mock hoist errors, stale serialization/count literals).
Kept 3 actionable entries that catch real issues:
- register-model-routes-kimi-k3-supplemental (real CI flake, rescue feature ready)
- project-engine.test.ts (catches real 60s→120s assertion drift)
- PlanningModeModal.planning-flow (second-sighting real race)
Vitest config exclusions and quarantine ledger updated in lockstep.
## Summary
Restores the non-blocking full suite on `main` after consistent shard
failures (latest red: [run
30982276306](https://github.com/Runfusion/Fusion/actions/runs/30982276306);
all four shards failed on `@fusion/core`, `@fusion/engine`, and
`@fusion/plugin-sdk`).
### Fixes
- **Path / import drift** after code-organization peels: update
static-guard and integration tests to new module locations (`central/`,
`board/`, `execution/`, `merge/`, `worktree/`, `plugins/`, `types/*`
barrels, etc.).
- **Inventory re-pins**:
- SQLite production `DatabaseSync` allowlist
(`central/project-identity.ts`, `db/sqlite-validation.ts`)
- Engine blocking-shellout allowlist regenerated from live source (33
audited sites)
- Core log-severity manifest paths for peeled modules
- **Partial protocol assert update** for `isPlanReviewSatisfied` (file
also quarantined until full rescue)
### Quarantine (deletion ratchet)
Remaining behavioral reds quarantined on sight — no
timeout/retry/assertion appeasement:
- **14 core** files (incomplete unit fakes for `layer.db.select`,
ledger/census drift, 15s wedge timeout, serialization protocol drift)
- **13 engine** files (mock-hoist errors, fake-store/census/behavior
drift under suite)
Paired updates: `scripts/lib/test-quarantine.json` + package vitest
excludes. Deletion clock starts `2026-08-05`.
### Local verification
- Path-fixed core scanners: 173 passed
- Path-fixed engine scanners: 58 passed
- `@fusion/plugin-sdk` full: 16 passed
- PG smokes: mission-autopilot, research-execution, satellite,
transition-pending, workflow-sync
## Test plan
- [ ] CI PR checks green (lint/typecheck/build/gate)
- [ ] Full suite on merge to main: all 4 shards green or only
intentional non-blocking signal
- [ ] Confirm quarantined files appear in ledger + vitest excludes and
are not executed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Updated test coverage to reflect reorganized source locations and
module paths.
* Refreshed static checks, allowlists, and source-based assertions
without changing tested behavior.
* **Chores**
* Quarantined failing core and engine test suites with documented
tracking details.
* Updated test configuration and quarantine records to improve suite
stability and reporting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Add an opt-in source-development loop that restarts the dashboard and
engine when runtime TypeScript or JSON changes. Use `pnpm dev:watch`;
`pnpm dev:hmr` now combines Vite UI HMR with the same supervised
API/engine restart path.
The watcher filters tests, fixtures, generated declarations, build
output, and task state. It coalesces bursts with a two-second maximum
wait, waits for the child to acknowledge its IPC listener, and rebuilds
runtime dist artifacts before a source-triggered respawn.
## Safety model
- Close scheduler, triage, heartbeat, mission, routine, self-healing,
and merge admission before checking for active work.
- Let already-running agents reach a safe boundary; do not mutate
durable pause settings.
- Enter the existing graceful exit-code-86 shutdown and supervised
respawn path.
- Retry failed liveness reads and declined restart requests instead of
dropping the pending change.
- Keep ordinary `pnpm dev` behavior unchanged; inherited watch state
does not break nested non-dashboard development commands.
A development restart intentionally replaces the dashboard process, so
transient dashboard connections and project dev-server children
reconnect or restart with it. Agent work is the protected boundary.
## Validation
- `pnpm lint`
- `pnpm test:gate` (753 tests passed across engine, core, PostgreSQL
gate, and CI-shape suites)
- Focused CLI watcher/restart/supervision suites: 40 tests passed
- Focused engine drain/manager suites: 52 tests passed
- `pnpm --filter @runfusion/fusion typecheck`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm verify:fast` (13 steps passed, including CLI build and real
health boot smoke)
- Manual unsupported-command probe confirms explicit `--watch` fails
clearly outside the dashboard command
## Post-Deploy Monitoring & Validation
- Watch for `[fusion:dev] source changed`, `source restart deferred`,
`active work drained`, and `restart requested` logs during the first
watched development session.
- Healthy behavior is one exit-86 respawn per edit batch, no interrupted
active agents, refreshed dist artifacts, and a healthy dashboard after
respawn.
- Investigate repeated restart loops, watcher attachment warnings,
declined restart retries, or liveness-read failures.
- Immediate mitigation is to use ordinary `pnpm dev` without `--watch`;
no production runtime behavior or durable setting needs rollback.
- Validation owner: Fusion maintainers during the first source edit
after merge.
---
[](https://github.com/EveryInc/compound-engineering-plugin)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added `pnpm dev:watch` to automatically restart development runtime
processes when source files change.
* Development restarts now wait for active work to finish, preventing
new work from starting during the transition.
* Enhanced `pnpm dev:hmr` with graceful runtime source restarts while
keeping the dashboard available.
* Rapid source changes are grouped to avoid unnecessary restarts.
* **Documentation**
* Updated development setup and contribution guides with the new watch
workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Keep the Kimi K3 dashboard route test quarantined until the mandated deletion date.
- Preserve the /api/models supplemental test and paired Vitest exclusion through 2026-08-15.
- Record the explicit retention deadline in the quarantine ledger.
Files changed:
packages/dashboard/vitest.config.ts | 5 +++++
scripts/lib/test-quarantine.json | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-8774
Fusion-Task-Lineage: 8ef704e4-f682-4a97-af1a-2070ca43d8a1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Refresh the weekly test-velocity baseline with the latest measurements.
- Record current gate, boot-smoke, and changed-only test timings
- Update slowest-test attribution and quarantine counts
- Append the captured snapshot to the velocity history
Files changed:
docs/test-velocity-baseline.md | 67 +++++++++++-----------
scripts/test-velocity-history.json | 112 +++++++++++++++++++++++++++++++++++++
2 files changed, 145 insertions(+), 34 deletions(-)
Fusion-Task-Id: FN-8772
Fusion-Task-Lineage: 84d73aef-f0ca-4322-a21b-447f230bb203
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Remove the typed authorization phrase and the --yes/-y auto-confirm path so
every real release must confirm y/N in an interactive terminal. Reject --yes
with a clear error so old muscle memory cannot skip the proceed prompt.
Remove the FN-8700 PR/file-claim blocking mechanism end to end (operator
decision after FN-8728 parked on unrelated PR #2398):
- Drop the AGENTS.md claim-check rule and scripts/check-file-claimed.mjs
- Executor prompt + fn_task_done no longer accept pr:N refs or treat open
PRs as blocked-exit reasons
- execution-block-classifier classifies on Fusion task dependencies only;
legacy pr refs are discarded, reason prose never makes a block durable
- Remove the session-log BLOCKED promotion and the gh-backed
reconcile-external-pr-blockers self-healing sweep
- Legacy file-claim parks are no longer honored, so previously PR-blocked
rows recover via normal paths
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Make the useTasks hydration freshness coverage deterministic and restore it to the dashboard suite.
- Control the system clock for hydration fixtures and flush async updates without advancing time.
- Remove the rescued test from the dashboard exclusion list and quarantine ledger.
Files changed:
.../__tests__/useTasks-hydration-freshness.test.ts | 30 ++++++++++++++--------
packages/dashboard/vitest.config.ts | 8 ------
scripts/lib/test-quarantine.json | 5 ----
3 files changed, 19 insertions(+), 24 deletions(-)
Fusion-Task-Id: FN-8724
Fusion-Task-Lineage: 1d764e2c-0975-4d26-92c6-187a6a94caee
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Scan paginated pull-request file lists so claim checks remain reliable for large diffs.
- Replace truncated PR diff scans with count-validated paginated GitHub API requests.
- Fail closed when open PR or file-list data is incomplete, malformed, or exceeds the API ceiling.
- Add coverage for large diffs, API failures, count mismatches, and claim precedence.
Files changed:
scripts/__tests__/check-file-claimed.test.mjs | 186 ++++++++++++++++++++++++++
scripts/check-file-claimed.mjs | 119 +++++++++++-----
2 files changed, 272 insertions(+), 33 deletions(-)
Fusion-Task-Id: FN-8706
Fusion-Task-Lineage: 1edf3546-553b-4e35-8c41-cd5143c08e0a
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Quarantine the timing-sensitive Kimi K3 SDK catalog test without changing timeout budgets.
- Reuse the native model registry once per test file.
- Add the observed CI timeout to the dashboard quarantine ledger and config.
- Document validation and timeout-budget preservation requirements.
Files changed:
docs/testing.md | 8 ++++++++
...ister-model-routes-kimi-k3-supplemental.test.ts | 23 ++++++++++++++++++++--
packages/dashboard/vitest.config.ts | 8 ++++++++
scripts/lib/test-quarantine.json | 5 +++++
4 files changed, 42 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-8647
Fusion-Task-Lineage: 31e79677-d923-4003-a8e8-082159334e65
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The active-worktree slot-accounting fix removed two deliberate scheduler
literals (done/archived: 3 -> 2); re-record so the ratchet follows the count
down.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes the family. `check-fnxc-future-dates` was #3287,
`lifecycle-column-census` is #3289, and this is the third and last gate
that rewrote its baseline during a plain check.
## Reproduction
```
inflated one allowance by 6, ran the gate with NO flags
rc=0
entry RESET to 1 ← the check modified the tree it was checking
```
## Why it matters
The tightening is right in substance — an allowance nobody spends is a
hole a literal can be regrown into. Performing it as a **side effect of
checking** handed every worker a byte-identical uncommitted diff they
had not authored, which they then reasonably committed.
Measured across the family: nine PRs chased three defects on
2026-07-31/08-01, two of them (#3283/#3285, five minutes apart, `+0/-1`
each) deleting the **same line neither author wrote**.
#3289 states the class best — *a check that writes turns every reader
into an author*. Two of us separately mis-attributed a gate-written
baseline to our own work while debugging something else.
## Measured, all four directions
| scenario | result |
|---|---|
| plain run, stale baseline | `rc=0`, reports `allowed 7, now 1`;
**inflation survived** — read-only |
| a new SQL literal added | **`rc=1`**, names `__sql_probe.ts` —
regression detection intact |
| `--update-baseline` | `rc=0`, entry written |
| clean tree, plain run | `rc=0`, **zero files dirty** |
Row 2 is the one worth checking: a read-only change to a gate is
worthless if it also stops catching the thing it exists for. The rise
path is untouched.
`census --strict` 0, `check-fnxc-future-dates` 0, eslint clean.
## Correcting my own delay
I measured this defect family on #3267 and then **declined to fix two of
the three**, reasoning that the census *"deliberately fails on a drop"*
so the port might be unsafe. That was wrong: it tightened and exited
`0`, exactly as its own test asserts — *"TIGHTENS on a drop and exits 0,
so somebody else's merge cannot redden the gate."* I had read the
`--exact` contract and attributed it to the default path.
The caution cost hours and prevented nothing. #3289 was written by
someone else in the meantime; this finishes what I should have finished
then.
## What
**Port of #3287 to the sibling tool.** `lifecycle-column-census.mjs
--strict` called `writeBaseline()` during a plain **check**, so running
the gate modified the tree it was checking.
```
clean: 0 files dirty
$ node scripts/lifecycle-column-census.mjs --strict # no --update-baseline
rc=0
after: M scripts/lib/lifecycle-column-census-baseline.json
```
## Why it matters — measured by #3287, reproduced here
#3287 established what this costs: every worker who runs the gate
receives a **byte-identical uncommitted diff they did not author**, and
reasonably commits it. #3283 and #3285 are the same `+0/-1`, five
minutes apart, by two different authors, **neither of whom wrote that
line** — the gate wrote it in both checkouts.
I hit this one the same way, which is the part worth recording: I saw a
modified baseline on my own branch and started reasoning about where
*my* change had touched it. It had not. A check that writes turns every
reader into an author.
The tightening is right in substance, and this tool's `COMMIT IT`
message made the diff *explained* rather than mysterious — better than
fnxc's was. **Neither addresses the mechanism.**
## The shape, matching #3287
Still computed, still reported loudly, written only under an explicit
`--update-baseline` (which has its own path above and is untouched):
```
lifecycle-column-census --strict: baseline CAN BE TIGHTENED — the tree has fewer guards than it allowed
packages/engine/src/scheduler.ts: allows 1, tree has 0
Not written. Record it deliberately, so the diff has one author:
node scripts/lifecycle-column-census.mjs --strict --update-baseline
```
**A plain run stays green rather than failing.** Guard counts drop when
someone *else's* merge removes a literal, so failing on a tightening
would redden main on a change the author never made. Report, don't
enforce — same reasoning #3287 gives for stamps aging into the past.
## Measured, all three directions
| scenario | result |
|---|---|
| plain `--strict`, stale baseline | reports + hint; **tree clean**
(was: 1 file dirty) |
| `--strict --update-baseline` | writes, rc=0 |
| a new guard added | **rc=1** — regression detection intact |
```
lint clean
```
## Note
Claimed on #3287 before starting, since it is that author's fix and they
may have had the port in flight. The two differences from the fnxc case
are noted there: this one fires under `--strict` rather than a bare run
(but `--strict` is what `package.json` and CI invoke, so it is the
common path), and its message was already loud.
Root-cause fix for the duplicate-PR pileup tracked in #3267. **Running
the check modified the working tree.**
## Reproduction
```
clean: 0 files dirty
$ node scripts/check-fnxc-future-dates.mjs # no flags, no --update-baseline
exit 0
after: 1 file dirty → M scripts/lib/fnxc-future-dates-baseline.json
```
`:227` auto-tightened and `writeFileSync`'d on every run.
## Why that produced nine PRs
The tightening is **right in substance** — the comment above it explains
why banking a stale allowance is worse than re-recording. Doing it as a
*side effect of checking* is what hurt: every worker who ran the gate
received an identical uncommitted diff they had not written, and
reasonably committed it.
The clearest evidence is #3283 and #3285 — five minutes apart, `+0/-1`
each, both deleting the same baseline line. **Neither author wrote that
line.** The gate wrote it, in both of their checkouts.
I also mis-attributed my own dirty tree to leftover work while
retracting a measurement on #3277/#3278. The dirt was this script.
## The change
Still computed, still reported loudly — only **written** under
`--update-baseline`:
```
[check-fnxc-future-dates] baseline CAN BE TIGHTENED for 1 file(s):
packages/cli/src/__tests__/cli-active-count-lanes.test.ts: 10 -> 5
run `pnpm check:fnxc-future-dates --update-baseline` to record it (one commit, one author)
```
**A plain run stays green rather than failing on a tightening.** Stamps
age into the past on their own, so failing would redden main on a clock
tick — which is precisely why the auto-write existed. Report, don't
enforce.
## Measured, both directions
| scenario | result |
|---|---|
| stale allowance, plain run | reports + hint; baseline **unchanged**
(verified still inflated at 10) |
| stale allowance, `--update-baseline` | `baseline written: 122 stamp(s)
in 63 file(s)`; value reset to 5 |
| clean tree, plain run | exit 0, **zero files dirty** |
| `census --strict` / eslint | 0 / clean |
The first row is the one that matters: I inflated an allowance, ran the
check, and confirmed the file was **still inflated afterwards**.
Asserting only "exit 0, no diff" would have passed even if the write had
silently succeeded and produced no net change.
## Scope
One script. CI is unaffected — it never committed the side-effect write,
so that write was always discarded there. The only behaviour change is
that an interactive run no longer edits your tree.
This is a smaller intervention than the claim-protocol I proposed
earlier in #3267, and I now think that one was treating a symptom:
workers were not colliding because they lacked a protocol, but because
the tool handed each of them the same diff.
**The date gate passes on `main` — but not because this was fixed.**
## What actually happened
UTC rolled over to `2026-08-01`. The gate compares against the later of
local and UTC, so two `2026-08-01` stamps in `scheduler.ts` became valid
on their own. That is the ratchet's normal drop path and is fine.
`#3278` then re-recorded the baseline "after the UTC rollover", which
set `scheduler.ts` to **allow 1** — exactly enough to absorb the one
stamp that did *not* age out:
```
FNXC:ConcurrencyAdmission 2026-08-06-09:00 ← six days out, wrong on any calendar
```
So the gate reports `123 known future-dated stamp(s), none added` and
exits 0, with a stamp inside it that will not be valid until next week.
## Why this is the failure the gate exists to catch
A blanket re-record cannot distinguish **aged out** from **still
wrong**, so it launders the second past the first. The sibling ratchet
states the rule outright:
> Do NOT re-record the baseline to clear this — that is the same false
green one layer up.
This is that, one layer up again: not a guard cleared by a baseline, but
a *baseline refresh* clearing a guard as a side effect.
## The fix
- stamp repointed to `2026-08-01` — today in UTC, which is the calendar
the gate actually compares against
- **allowance removed**, not left at 1, so the entry cannot be regrown
into
**Mutation-verified**: with the allowance gone, restoring `2026-08-06`
exits **1**. Before this change the same stamp exited **0**. That is the
whole point — the ratchet can now see it.
## One thing worth carrying forward
A six-days-out stamp is not a timezone slip. Neither the old `date -u`
guidance nor the current local-date guidance in AGENTS.md would have
prevented it, and CI-only checking cannot catch it before merge. This is
the concrete case for running the date check at author time, which I
have flagged but not landed since it changes the gate's contract.
## Verification
- `check-fnxc-future-dates` — exit 0, allowance removed
- `scheduler` suites — **148 pass**
- `tsc --noEmit` (engine) — 0 errors
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## What
**Three separate commits landed a future-dated FNXC stamp this
evening**, each turning this blocking gate red on main (#3261 fixed six
across five engine files; #3270 fixes a third in core). This makes the
failure message actionable. Tooling only.
The message said *"Use the current date and a real clock time."* That
tells the author to use the value they already believed they had. It now
prints the exact stamp:
```
Current UTC stamp to use: 2026-07-31-23:39
```
Copy-paste instead of a second judgement call, computed only on a path
that has already failed.
## Why a message change rather than a rule
**The offsets are the evidence.** `00:50` against `23:34`; the engine
batch similar — consistently **1–2 hours into tomorrow**, not wrong
dates. That is the shape of a clock or timezone difference, not
carelessness, and no amount of restating the rule fixes a clock.
AGENTS.md already says to take the stamp from `date -u`; three actors
violated it in one evening anyway.
**I am one of those actors** — I have broken this rule twice today. So
this is not a complaint about anyone's diligence; it is an argument that
the instruction is doing less work than a printed value would.
## I made the same mistake inside this change
The FNXC comment documenting the fix was stamped **two minutes ahead**
of the real UTC time. Corrected from `date -u`.
**The gate did not catch it** — it scans `packages/` and not `scripts/`,
so FNXC stamps in the tooling itself are entirely unchecked. That is a
genuine scope gap and I am reporting rather than closing it: pulling
`scripts/` into scope would surface existing stamps across the tooling
and needs its own baseline pass, which does not belong in a message fix.
There is something clarifying about writing a future-dated stamp *in the
fix for future-dated stamps*, in a file the checker cannot see. It is
the same lesson this whole session kept producing — **an instrument's
blind spot is invisible in exactly the way its subject is** — and I
walked into it while holding the flashlight.
```
lint clean; gate prints the stamp on failure
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved validation feedback for future-dated entries by showing the
exact UTC timestamp format and value to use when corrections are needed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->