Skip to content

fix(startup): preserve loading admission until repository identity is ready - #1162

Merged
zzet merged 347 commits into
mainfrom
fix/startup-repo-loading-recovery
Nov 1, 2026
Merged

zzet merged 347 commits into
mainfrom
fix/startup-repo-loading-recovery

Conversation

@zzet

@zzet zzet commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Configured repositories could appear untracked between socket startup and registration, especially through a cold symlink or linked-worktree alias. Publish loading declarations before listening, resolve identity through the existing warmup owner, and keep unresolved admission retryable without granting graph scope or suggesting another track operation.

This integrates the exact startup-probe commit from #1236 without rewriting it. The probe records readiness, initialize/discover, CLI and hook behavior across restart, including the legacy private-daemon path, facade routing and an unavailable remote. Five incoming MCP fixtures now use the existing repository-registration helper after the obsolete production indexing methods were removed. No compatibility API is added.

The integration includes the validated #1169, #1159 and #1212 stack and the two-test scoped-read coverage follow-up #1239. The benchmark runner retains both startup probes and the incoming edit/pressure options. Land after those dependencies. #1236 is superseded only after this validated absorption is published.

Validation at clean integration head 0a4bdfedc passed: 383 distinct parent race tests and three additional restart-probe repetitions, with zero skips/failures. Repository-wide pinned uncapped lint reported zero issues, and formatting, CGO, workflow-layout and module-tidy guards passed. Native vet over all affected packages and Linux/Windows benchmark vet passed. The checks prove the actual five-fixture compile failure by restoring the removed-method calls, restore every fixture, then run the affected startup/identity/registration/read-repair/daemon and probe controls under the race detector. They also cover Linux and Windows benchmark vet, repository-wide pinned uncapped lint, formatting, CGO flags, workflow layout and module tidy.

Earlier full-package and hosted results remain attributed to their original heads. In particular, CI 40669758039 belongs to the earlier startup implementation; it is not evidence for this refreshed integration. The previously interrupted local indexer run and the unstarted probe gate remain recorded as incomplete. The first integration gate also retained a Darwin fixture failure: its supposed physical temporary path was itself a /var alias. A test-only correction explicitly resolves that root before checking the canonical snapshot key; the original assertion and production behavior are unchanged.

Refs #1023. The implementation may land while reference-host measurements remain open: warm 220-worktree ×10, cold 20-worktree plus two repositories, historical v21 upgrade, readiness/CLI/hooks and 24-coordinator accounting. This PR does not claim those runtime gates passed and does not close the issue.

zzet added 27 commits October 28, 2026 14:12
zzet added 7 commits October 31, 2026 22:03
…itor

The run-level stats receipt after the workload went through the scenario's
first session. In the restart cell that session is the editor of the daemon
the cell killed, so the receipt failed on a closed socket, the phase gained
an error and the report could never be complete.

The closing receipt now goes through editor 0 of the edit-probe runtime,
which the restart cell reopens on the restarted daemon; other scenarios keep
their first session. The fake restarter now closes every pre-kill session
the way a real kill does, the stub flags any pre-kill use after the restart
(not only tools/call), and a test takes the closing receipt after a restart.
Startup loading is covered by unit and integration tests only. Nothing
records what an agent actually receives while a daemon starts: before its
socket exists (including a first boot that runs a schema migration), while
configured repositories load, and once they are ready, on a cold start and
on warm restarts.

The opt-in startup scenario starts probing before the first daemon start.
Every second, from the primary, a linked worktree and a directory a
servers.toml roster claims, it opens fresh sessions and records the
initialize instructions variant, search_symbols, the index-health resource
and workspace(operation:"index"); asks the PreToolUse hook about a native
Read; and runs `gortex call search_symbols` through a pipe and a pty until
each first succeeds in that boot. Every answer keeps error_code, retryable,
phase, percent, latency, exit code and whether a refusal advises tracking
or reconciling. A watcher stamps socket, readiness and enrichment, and
reads the checkout coordinator count for 2 s after the primary registers.

--seed-store copies an existing store into the private run so the initial
boot is an upgrade; --restarts adds warm restarts. The gate counts answers
per surface, directory and window and reports time to ready and time to
first success per boot, so `delta` can compare startup between builds.

The scenario lives in new files; the shared harness files gain only its
registration, and the fixture daemon answers the CLI and hook probes so
the whole scenario runs end to end in tests.
…anding

The restart cell read its edit's receipt status once after the ACK and
required it to be pending. On the normal path the ACK itself waits (up to
three seconds) for that receipt, so the status read after it almost always
found it terminal and the cell failed as "receipt terminal before kill"
instead of measuring a kill with a receipt pending.

The cell now sends the edit and, while the ACK is outstanding, polls the
edit's status by its mutation_id every 5 ms. Once the bytes are committed and
the graph receipt is not yet terminal, the harness stops waiting for the ACK
and kills the daemon. The record's ACK time is unavailable, the receipt comes
from the committed status, and the abandoned call is counted censored rather
than as a workload error. An ACK that returns first still leaves one status
read just before the kill to decide, so a terminal receipt keeps qualifying
the record. The stub now holds the ACK the way a daemon waiting on its
receipt does, and the test checks the kill happened with the ACK outstanding.
… completes

A create/delete pair's delete step starts its probes at its own scheduled
activation, but it is sent only after the paired create has every probe
first-correct. Its search, text and caller oracles are absence-shaped, so
while the created file is not yet visible they answer "correct". With the
delete not yet started, each such answer was recorded as "marker visible
before its own edit" and a valid delete became a diagnostic failure whenever
its Poisson gap was shorter than the create's visibility time.

The delete's state now records when its dependency was met, and a correct
answer to a probe sent before that is not applicable to it. A correct
absence answer after the dependency but before the delete itself still
fails the record. A test activates the delete while the create's file does
not exist yet.
Every edit.write request carries whole-file content precomputed in ordinal
order. With several editors, and always in hot_file pairs that share a due
time, two writes can land out of order: the earlier ordinal's content then
drops the later ordinal's marker, and the next ordinal's write puts it back.
The dropped record could still complete, with a first-correct time borrowed
from someone else's write, while the README claimed such lost writes were
measured as failed records.

Several-editor plans now refuse edit.write, and hot_file accepts only
edit.file. edit.batch stays allowed: its old_string precondition makes an
out-of-order write fail visibly, and edit.file anchor insertions do not
depend on order. The README now says this instead of the earlier claim.
The summary ranked only the values it observed. A record that hit the
deadline (or the run end, or failed) before an oracle answered correctly was
dropped from that measure's ranking, so p95/p99 for file-graph, relations,
search and readback came out low exactly in the high-load cells, and the
relations p95 feeding the file-graph lane go/no-go would be understated. The
"missing" count also mixed those records with values that do not apply, such
as a native write's ACK or a coalesced receipt's derived time.

A value missing from a record that did not complete is now censored: it
joins the ranking as +Inf, and a quantile or bootstrap bound whose nearest
rank falls among censored records is unavailable. A value missing from a
completed record, or a measure the record does not carry, is counted as not
applicable. The read table does the same for reads cut off by their deadline
or the run end. The schema goldens replace "missing" with "censored" and
"not_applicable".
… pending

A pure read was classed under_edit while any edit's whole completion loop
was running: the ACK, the readbacks and the long wait for every probe to
become first-correct, up to the edit deadline. That is not the "reads issued
while receipts are pending" the measurement defines, it diluted the writer
and WAL contention the comparison is meant to isolate, and it disagreed with
the per-record pending-read count in the same report.

A sent edit now counts as pending from its send until the harness first
observes its receipt terminal (single receipt, batch transaction or move
reconcile), or until its loop ends without that, as for a failed edit or a
native write, which has no receipt. A test turns the receipt terminal while
the edit still waits for its probes and checks that reads issued then are
between edits.
zzet added 19 commits November 1, 2026 10:20
# Conflicts:
#	bench/load/plan.go
#	bench/load/run.go
#	bench/load/scenario.go
#	bench/load/schema.go
# Conflicts:
#	internal/daemon/mcp_request_lifetime.go
#	internal/mcp/batch_transaction.go
#	internal/mcp/edit_serialization.go
* commit '553ed1990e84e74ed44d647500bd230a17d0001c': (277 commits)
  fix(load): snapshot native file identity before replacement
  test(mcp): request full output for deletion measurement evidence
  fix(load): accept native write evidence in edit-probe phase validation
  test(load): decode restart receipt status before checking its fields
  fix(load): count reads under edit only while a sent edit's receipt is pending
  fix(load): rank censored edit records above observed values in quantiles
  fix(load): refuse whole-file edit.write with several editors
  fix(load): ignore a native delete's absence answers before its create completes
  fix(load): kill the restart cell's daemon while its edit ACK is outstanding
  fix(load): take the closing receipt on the restart cell's reopened editor
  docs(load): document live refusals, native writes and lifecycle cells
  feat(load): measure a daemon kill and restart with an acknowledged receipt pending
  feat(load): add native create/delete pairs and a two-editor hot-file sender
  feat(load): add native shell-write and editor-save senders with their own write windows
  feat(load): measure explicit TypeScript and Python relocation refusals live
  docs(load): describe compact, CLI and unavailable GCX accounting
  feat(load): probe through the CLI call verb as its own transport
  feat(load): decode compact text answers for presence-proved call additions
  test(mcp): pin the actual compact text producers used by edit probes
  feat(load): account GCX as an explicitly unavailable output mode
  ...
* commit '553ed1990e84e74ed44d647500bd230a17d0001c': (397 commits)
  fix(load): snapshot native file identity before replacement
  test(mcp): request full output for deletion measurement evidence
  fix(perf): explicitly enable cgo for performance artifacts
  fix(load): accept native write evidence in edit-probe phase validation
  fix(mcp): pass a compatible exact-name fallback to session resolution
  fix(bench): keep PR performance smoke report-only
  test(load): decode restart receipt status before checking its fields
  test(mcp): count scoped file location rows in the shared reader
  fix(mcp): scope bare-name targets for callers and why
  test(mcp): pin bound-session shorthand to the session's repositories
  perf(mcp): resolve bound-session symbol shorthand per repository
  test(mcp): stop claiming the golden test compares dynamic boundaries
  test(mcp): count symbol index rows in the scoped search_symbols test
  test(mcp): show the name-only equality test takes the narrowed read
  docs(sqlite): say what the repo-scoped incoming read still visits
  perf(sqlite): probe a call site's named repositories before the empty prefix
  perf(query): narrow name-only placeholder reads only for a few repositories
  test(sqlite): fence the repo-scoped incoming read by generation
  test(mcp): count smart_context graph rows, not only retrieval rows
  test(mcp): pin repo-scoped search_symbols reads as repositories are added
  ...
…very

* commit '386b2e834b81ca09e7977295a54afc4a2de4f6fd': (396 commits)
  fix(load): snapshot native file identity before replacement
  test(mcp): request full output for deletion measurement evidence
  fix(perf): explicitly enable cgo for performance artifacts
  fix(load): accept native write evidence in edit-probe phase validation
  fix(mcp): pass a compatible exact-name fallback to session resolution
  fix(bench): keep PR performance smoke report-only
  test(load): decode restart receipt status before checking its fields
  test(mcp): count scoped file location rows in the shared reader
  fix(mcp): scope bare-name targets for callers and why
  test(mcp): pin bound-session shorthand to the session's repositories
  perf(mcp): resolve bound-session symbol shorthand per repository
  test(mcp): stop claiming the golden test compares dynamic boundaries
  test(mcp): count symbol index rows in the scoped search_symbols test
  test(mcp): show the name-only equality test takes the narrowed read
  docs(sqlite): say what the repo-scoped incoming read still visits
  perf(sqlite): probe a call site's named repositories before the empty prefix
  perf(query): narrow name-only placeholder reads only for a few repositories
  test(sqlite): fence the repo-scoped incoming read by generation
  test(mcp): count smart_context graph rows, not only retrieval rows
  test(mcp): pin repo-scoped search_symbols reads as repositories are added
  ...

# Conflicts:
#	bench/load/run.go
* commit '386b2e834b81ca09e7977295a54afc4a2de4f6fd':
  test: join completed-request relay before transport cleanup
  test: observe anonymous Linux vacuum files by their actual descriptor
  test: document intentional nil-context vacuum rejection
  test: exceed the real vacuum cache before observing temp spill
  test: compare physical vacuum directories across path aliases
  fix: defer boot compaction after a supported schema rebuild
  fix: defer owned boot compaction until enrichment completes
  test: cover live vacuum cancellation and cross-store scratch ownership
  fix: isolate cancelable compaction on a file-temp connection
* commit 'df40f3621dc72f70dcee08555b4c5389fbcd6600':
  test(mcp): compare scoped unwrapped handler payloads to Go oracle
  test(mcp): count whole-edge reads in scoped file search
@zzet
zzet marked this pull request as ready for review November 1, 2026 14:20
@zzet
zzet merged commit 8f53413 into main Nov 1, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant