Skip to content

Isolate and validate public REPL calls - #105

Draft
Deicyde wants to merge 50 commits into
fix/lsp-process-ownershipfrom
split/repl-disposable-lifecycle
Draft

Deicyde wants to merge 50 commits into
fix/lsp-process-ownershipfrom
split/repl-disposable-lifecycle

Conversation

@Deicyde

@Deicyde Deicyde commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Stack status: this PR is stacked on #111 at exact head 2ce7ecb4, which already contains #168. Do not merge it into the feature branch. Merge-commit #168, retarget/squash #111 onto main, then merge updated main into #105, verify that its diff contains only this disposable-REPL layer, retarget #105 to main, rerun CI, and squash-merge.

Problem

Public REPL calls used resident processes, so process-owned environment and proof-state handles could outlive a response. The first disposable implementation then introduced three new hazards: synchronous cleanup could trap request threads, 100 ms queue polling broke FIFO admission, and Lean 4.30–4.32’s fast import parser could hide an import behind a block-comment edge case.

Architecture

  • Keep only cold Python wrappers in each project pool. Every public call starts one fresh lake exe @repl/repl child and submits exactly one combined frame containing generated imports plus user source.
  • Validate the submitted header with the selected Lake toolchain. Strict bounded JSON decoding rejects malformed schemas, duplicate keys, nonstandard constants, ambiguous quoted/path-like module components, and disallowed roots.
  • For the Lean 4.30–4.32 block-comment bug, compare lean --deps-json on the original source with a context-free normalization of suspect close spellings. Reject only when the toolchain reports different header facts; strings, line comments, and body comments remain valid.
  • Preserve Retain Lean project ownership through verified cleanup #168’s process-group order: retire the whole group, then reap its leader. Header communication never reaps before verified group cleanup.
  • A dirty wrapper poisons the entire pool, wakes FIFO waiters through a shutdown sentinel, and is never requeued. Dispatch invalidates the resolved project generation before releasing its cache lease; Retain Lean project ownership through verified cleanup #168’s retained background reaper then retries cleanup indefinitely without allowing an overlapping replacement.
  • timeout is one explicit total post-admission budget—default and maximum 240 seconds—for idle-slot wait, header validation, disposable startup, generated imports, and submitted code. Retained cleanup may continue after the response budget.

The import allowlist is a deterministic policy check, not a sandbox: submitted Lean can still perform effects available through allowed imports.

Setup and configuration

  • Setup now requires lake update repl, committing the manifest, before lake build @repl/repl.
  • An absolute lake from LEAN_REPL_CMD is reused for header validation. Container or other wrappers must set the matching LEAN_REPL_HEADER_CMD explicitly.
  • A warm status means the project’s cold wrapper pool is cached; no REPL subprocess remains resident.

Scope and validation

Against #111, the diff is 23 files, +2,019/−139, predominantly adversarial protocol, lifecycle, and pinned real-REPL coverage.

At exact head 8bfdf44d:

  • full suite: 1,921 passed, 25 skipped, 1 expected failure;
  • pinned Lean 4.32.2 real-REPL suite: 19 passed;
  • focused runtime/protocol/skill suites: pass;
  • Ruff, git diff --check, and make check-example: pass;
  • exact-head GitHub CI: running.

Real regressions prove warmup-prefix use, distinct child PIDs, source-line correction, parser timeout/output caps and descendant reaping, FIFO service, shutdown ordering, dirty-generation fencing, and the escaped-module traversal control.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 5, 2026
@Deicyde
Deicyde marked this pull request as ready for review October 5, 2026 09:50
@Deicyde

Deicyde commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head adversarial review is complete at c5cfd799. The final repair closes both cleanup-ownership blockers: a dirty child remains inside the original active call/cache lease until verified clean, and cancellation during admission shutdown, retry sleep, or process close is retained in first-occurrence order. Fault injection also proves an unexpected settlement escape cannot release the old pool or admit a replacement. Focused suites pass on Python 3.10 and 3.13, repeated concurrency tests pass, and every required GitHub check is green. Ready for human review and merge.

@Deicyde

Deicyde commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Swarm pass on the REPL lifecycle base of the #105 → #106 → #107 stack.

What changed and why

  • db02051 (golf): collapsed pool settlement into one _settle cleanup loop. Removed the retry wrappers around settlement, the closing/closed shutdown states, failed-worker retention in _close_workers, and tests that injected interrupts into private paths that socketserver request threads cannot reach. start() takes a warm flag instead of a warmup override.
  • 7ae9370 (fix): removed pool quarantine. Cleanup failure no longer stops admission; the dirty slot is withheld until clean. Quarantine never bounded processes, because ProjectResourceCache replaces a stale pool only after its active calls settle, which is after the dirty worker is clean. Its only effects were failing queued calls (reproduced with a failing test first) and dropping healthy slots when capacity > 1. run() now returns a slot only when is_clean(), so even an interrupted retry loses the slot instead of handing it out. The runtime is_valid hook and its replacement test are gone; the pool tests pin the property that test covered.
  • Merged main up to 4cf7f9e (9eea324, 2ce2d8b, c6f770e, 579e9e3). The last merge brings in Create Lean projects atomically with catalog-backed defaults #96 and Say that acquire raises on a malformed lease #146; its tests/test_skill_examples.py conflict keeps both sides' tests unchanged. main has since moved to 9e2b882 (Report unused statement dependencies, and count paths from every owning article #150, autoform_cli/impact.py only); merge-tree reports no conflict for any of the three branches, so I did not re-merge and restart CI.

Diff vs main: +1,175/-81 → +648/-86 (9 files).

Checks: exact-head CI on 579e9e3: Python 3.10/3.13, Windows, real Lean, CLA (pull_request, push). Locally: ruff, plus the REPL core, pool, shared-runtime, and skill-example tests (daemon timing tests excluded locally because of machine load; CI ran them).

Remaining risks

  • Residual of the contract change: if cleanup never succeeds, _settle retries forever, that slot is lost, and its callers time out.
  • The per-call timeout now covers spawn plus the warmup import (30s default), so cold Mathlib imports eat into the user's budget, and the timeout message does not say which phase ran out.
  • Deadline propagation from pool to child is not pinned by a test.
  • The persistent LeanRepl.run/restart path is now unused by the public tool; removing it is a separate change.
  • Merge together with Validate REPL imports with the selected Lean toolchain #106: alone, this PR's string splitter lets /- c -/ import Unsafe past the allowlist.

Posted by PR swarm: Swarm · REPL #105-107

@Deicyde Deicyde changed the title Run public REPL calls in disposable processes Isolate and validate public REPL calls Oct 6, 2026
@Deicyde Deicyde added the blocked Waiting for prerequisite work before implementation can proceed label Oct 6, 2026
@Deicyde

Deicyde commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

The landing stack is now folded atomically at 4f4f50f3: #106 and #107 are recorded merged into their base branches, while #105 contains the lifecycle implementation, Lean-authoritative header parser, fault injection, pinned real-REPL integration, workflows, and docs. This prevents the known string-parser intermediate from ever landing alone. Exact-head combined CI is running; the temporary blocked label remains only until it passes.

@Deicyde

Deicyde commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Independent review of the folded stack at 4f4f50f. Before the fold I reviewed the three parts separately: the lifecycle at 579e9e3 (this PR), the header parser at f96c69e (#106), and the fault and real-REPL coverage at 88997e2 (#107). The fold merges have no conflict resolutions, and the effective diff against main is identical to the 88997e2 stack's, so everything below applies to this head. Line numbers are at 4f4f50f.

Verdict: fix first.

  • Every public call now pays the Lean spawn plus import Mathlib out of the 30s request timeout, so cold or slow nodes fail every call (issue 1).
  • On the bundled v4.32.2 toolchain, a --/ inside a block comment still hides imports from the allowlist. That is the problem the header parser was added to fix (issue 5).
  • The real-REPL tests stay green if the warmup prefix stops being added or if calls go back to a resident process, so neither main runtime claim has an end-to-end check (issues 10 and 11).

Removing quarantine in 7ae9370 is safe; see the note near the end.

Lifecycle

1. servers/repl/core.py:1020-1025: the request timeout now covers spawn and the Mathlib import (medium, contract).

  • run_disposable calls self.start(startup_timeout=remaining(), warm=False) and then self._run(..., timeout=remaining()), so the spawn and the injected import Mathlib are charged to the caller's deadline (default 30s). The lake env ... lean --deps-json launch in _check_header (core.py:986) is charged to it too. _repl_creation_budget (lean_runtime.py:81-83) no longer includes the 180s startup allowance.
  • After a node reboot (cold oleans) or on slow storage, every call returns "REPL command timed out after 30 seconds", and the next call pays the import again. A caller passing timeout=5, which worked against a warm pool, now fails.
  • Fix, pick one and put it in the contract-change note:
    • (a) Give each call a bounded startup allowance (min(DEFAULT_REPL_STARTUP_TIMEOUT, configured)) for the header check, start() and the import, on top of the request timeout. Add the same allowance to the response-timeout check at lean_runtime.py:207-216. Growing _repl_creation_budget alone does nothing, because it never reaches run_disposable.
    • (b) Raise DEFAULT_REPL_REQUEST_TIMEOUT (it must stay at or below 240) and document that it includes startup and the import.
  • Either way, have the timeout message say which phase ran out.

2. servers/repl/pool.py:118: waiting callers lose FIFO order (medium, bug).

  • The 100 ms polling self._idle.get(timeout=min(0.1, remaining)) sends each waiter that times out to the back of Queue.not_empty's waiter list, so service order follows timing phase, not arrival. A simulation served six queued callers in the order [4,2,5,3,1,0]. Main served them in arrival order, and its docstring promised a FIFO queue.
  • At the default capacity of 1, with every call now cold, early callers can time out while later ones succeed.
  • Fix: go back to one blocking self._idle.get(timeout=remaining). In shutdown(), put a _SHUTDOWN sentinel under the condition before waiting; a waiter that receives it puts it back and raises "pool is shut down". _close_workers and the re-queue in finally must skip the sentinel. Don't switch to _condition.wait() with notify_all, which is unordered too. Add a capacity-1 test that staggers waiters and checks they are served in arrival order.

3. tests/test_repl_pool_lifecycle.py:244: the shutdown-wait test can't detect a missing wait (medium, test gap).

  • The fake close() blocks on every dirty call, so a non-waiting shutdown also blocks inside close(), and stopper.is_alive() holds either way. Deleting while self._active_calls: self._condition.wait() (pool.py:145-146) keeps CI green and lets _close_workers and _settle close the same LeanRepl at the same time.
  • Fix: record threading.current_thread() in close() and block only on the first dirty call. At the 0.1s check, before allow_cleanup.set(), assert stopper.is_alive(), close_threads == [first] and pool._active_calls == 1.

4. tests/test_repl_core_protocol.py:740: the line shift for the injected import is untested (medium, test gap).

  • The only prefix test returns messages: [] and a sorry without pos. Removing or negating _adjust_line_numbers(response, -len(added_imports)) at core.py:1031 or core.py:1049 passes CI, and every diagnostic would come back one line off.
  • Fix: have the fake _run return a message and a sorry with pos/endPos at lines 2 and 3, and assert lines 1 and 2 with proofState stripped. Add one ReplStderrBacklog case carrying a positioned response. Optionally add a case where the code already imports Mathlib and the lines stay unchanged. Issue 10 has the real-Lean version of this check.

Header validation (from #106)

5. servers/repl/core.py:990: the header check can be bypassed on Lean v4.30.0 to v4.32.x (medium).

  • lean --deps-json goes through printImportsJson in Lean's fast import parser (Lean/Elab/ParseImportsFast.lean). In v4.30.0 through v4.32.2, its finishCommentBlock handles a - that isn't followed by / with s.next' input i h, which skips the next character too, so --/ never closes a block comment. The real parser (Lean/Parser/Basic.lean) uses s.setPos i there and does close it. v4.33.0 changes the fast parser to s.setPos i. I checked all three in the toolchain sources.
  • Example: "/- a --/\nimport Unsafe\n-- -/\n#check Nat\n". The fast parser reports no user imports, so the allowlist passes. The REPL then receives import Mathlib\n/- a --/\nimport Unsafe... and imports Unsafe.
  • Scope: the bundled example, tests/fixtures/skeleton-project and tests/fixtures/repl-smoke all pin v4.32.2.
  • Severity: import Mathlib plus #eval already allows arbitrary IO, so the allowlist is a policy check, not a sandbox. That lowers the severity, but the PR's stated problem ("a leading comment could hide an import") is still not fixed.
  • Fix, cheapest: before _check_header, fail closed on any non-doc /- block comment containing an even-length run of dashes immediately followed by /. Fix, robust: get the import list from Lean.Parser.parseHeader, run through the same launcher. Don't require v4.33.0+, which breaks the bundled v4.32.2 example and the v4.27.0 floor.
  • Test: add ((), "/- a --/\nimport Lake\n-- -/\n#check Nat\n", <the fix's error>) to test_disposable_imports_are_checked_by_lean in tests/test_real_repl.py, so the real-REPL job checks it on v4.32.2.

6. skills/setup/SKILL.md:85: setup tells agents to add the REPL require but never to lock it (low to medium).

  • project new with the default release ships a manifest with no repl entry. The validation block (L143) says to run lake update "only when the project has no lake-manifest.json", so an agent skips it. lake build @repl/repl then fails with Lake's "not in manifest" error, and autoform project inspect reports lake-manifest-incomplete.
  • Fix: after the require is added, say to run lake update repl (it keeps the Mathlib lock), commit the manifest, then run lake build @repl/repl. Add lake update repl # after adding the REPL require to a project that already has a manifest to the L143 block. test_skill_examples.py:197 still sees the lake update substring.

7. servers/repl/core.py:531: the header parser ignores LEAN_REPL_CMD (low, regression).

  • header_deps_command defaults to ["lake", "env", sys.executable, "-c", _LEAN_HEADER_LAUNCHER], and the runtime's default_repl_factory (lean_runtime.py:643-651) passes only repl_command. Every disposable call now needs a bare lake on PATH.
  • Setups that use an absolute-path lake or a container wrapper in LEAN_REPL_CMD worked before the header parser and now get repl_error: [Errno 2] ... 'lake' on every call. The failure is loud, and LEAN_REPL_CMD is undocumented.
  • Fix: add LEAN_REPL_HEADER_CMD in LeanRuntimeConfig.from_environment. If it is unset and the basename of repl_command[0] is lake, default to [repl_command[0], "env", sys.executable, "-c", _LEAN_HEADER_LAUNCHER]. Pass it through LeanReplPoolConfig, keep the check fail-closed, and have the ENOENT error name the variable. Add a runtime test that an absolute lake path carries into the header command.

8. tests/test_repl_core_protocol.py:931: the duplicate-key and NaN cases don't test strict decoding (low).

  • With plain json.loads, both inputs still fail the schema checks with the same "unrecognized output" message. Swapping _decode_repl_json for json.loads in _decode_header_analysis keeps the suite green, and last-key-wins could then drop an import.
  • Fix: use inputs that only strict decoding rejects:
    • duplicate key: '{"imports":[{"errors":[],"result":{"isModule":false,"imports":[{"module":"Unsafe","isMeta":false}],"imports":[]}}]}'
    • NaN in a field the decoder never type-checks: '{"imports":[{"errors":[],"result":{"isModule":false,"imports":[{"module":"Mathlib","isMeta":false,"importAll":NaN}]}}]}'
    • Not in isMeta: the bool check rejects that even without strict decoding.

9. tests/test_repl_core_protocol.py:935: nothing tests the parser timeout, output cap or reaping (low).

  • Every fake parser exits immediately. Replacing _communicate_bounded with communicate(), deleting the cap, or dropping the reap in finally all stay green. The code looks right today; the guarantees the PR body states just aren't pinned.
  • Fix: one parametrized test where the fake writes its pid to a tmp file. Hang case: the fake forks a sleeping grandchild, then sleeps with stdout open; call run_disposable(timeout=0.5). Flood case: max_buffer_bytes=64 and 100000 bytes written. Assert in both: repl_error, start never called, repl.is_clean(), and os.killpg(pid, 0) raises ProcessLookupError.

Real-REPL coverage (from #107)

10. tests/test_real_repl.py:43: no real-REPL test runs the warmup-prefix path (medium).

  • The real-REPL job does run the real header parser: test_disposable_imports_are_checked_by_lean uses the default lake env ... lean --deps-json command on the v4.32.2 fixture. But every success case has either an empty warmup or a module/prelude header, and none of those get a prefix. The fixture's Mathlib.lean is only a comment, so the code compiles whether or not import Mathlib is prepended. The _deps_json unit tests build the schema by hand.
  • The module/prelude cases would catch a regression to "always add the prefix". They can't catch one to "never add it". If Lean's --deps-json schema drifts (for example the meta Init marker goes away) or the decoder regresses, _decode_header_analysis quietly returns accepts_leading_imports=False and all 10 tests still pass. In production, every submission without imports then loses its import Mathlib and fails on unknown identifiers.
  • Fix: add def autoformWarmupMarker : Unit := () to tests/fixtures/repl-smoke/Mathlib.lean, then add the success case (("Mathlib",), "#check autoformWarmupMarker", None) to test_disposable_imports_are_checked_by_lean. It can only pass if the prefix was built from real lean --deps-json output.
  • Optional, for the line offset against real Lean: a separate test (the parametrized one only checks repl_error) that runs run_disposable("#check missingIdent", timeout=180) with a Mathlib warmup and asserts the error in messages has pos.line == 1.

11. tests/test_real_repl.py:65: test_runtime_calls_do_not_share_lean_state passes on the old resident process (medium).

  • It sends the same theorem twice and asserts "Compiles successfully" both times. On main, every plain call is dispatched from _base_env_id (main's core.py:770), and REPL envs are immutable snapshots. So call 2 never sees call 1's declaration, even in one long-lived process.
  • Reverting pool.py:122 from run_disposable to run leaves CI green. This is the only real-REPL check of the PR's main claim.
  • Fix: with AUTOFORM_REPL_TOTAL_WORKERS=1, send #eval IO.Process.getPID in two repl.run calls. Pull the PID out of each info message, assert neither call has errors, and assert the two PIDs differ. If that isn't done, rename the test (for example test_runtime_calls_start_from_the_warmup_env) and drop the isolation claim.

Golf (about 55 lines)

  • Drop ReplCleanupError (core.py:726-731, core.py:1075-1080, pool.py:123-124; about 35 lines). Its only caller unwraps error.result and ignores the error, and its message ("the result was not returned") says the opposite of what happens. Also remove the core test at test_repl_core_protocol.py:787-812, and change the FakeRepl raises to dirty = True; return {...}. Add one docstring line: on a non-cancellation cleanup failure the result is returned and the slot stays dirty, so callers must settle it.
  • pool.py:102-103 (2 lines): the pre-admission shutdown check duplicates the check at the top of the loop, which raises the same error and runs the same counter cleanup.
  • core.py:387 (6 lines): the post-loop close_stdin() is a no-op; lines 350-351 repeat the top-of-loop deadline check; the except InterruptedError at 347-348 can't fire, because Python retries select on EINTR (PEP 475).
  • core.py:993 (about 6 lines): move the allowlist check into the else: branch of the header-check try and delete the second identical guard.
  • core.py:476: narrow the tuple to except (ReplProtocolError, json.JSONDecodeError):; TypeError and KeyError can't be raised there.
  • tests/test_repl_core_protocol.py:1003-1007 (5 lines): the REPL.Frontend prelude case takes the same branch as the Init prelude case, which covers more.
  • tests/test_repl_core_protocol.py:1075 (about 2 lines): the fake script binds child and writes {'child': child.pid, ...}, but the test only reads ["group"]. Write str(os.getpgrp()), drop the child = binding and the json import, and assert not repl_core._process_group_has_live_members(int(process_record.read_text())). Unlike the sibling test, nothing kills the 30s sleeper if reaping regresses.

Disputed, worth a human look

  • pool.py:87: _settle has no deadline. If cleanup never succeeds (for example a child stuck in D state), the owning pool.run never returns. It keeps its cache lease, so LeanReplPool.shutdown() and ProjectResourceCache.close() hang, and logger.exception fires about once a second. Reviewers agree on the mechanics. The open question is whether that is the intended price of never releasing an unkillable child: bounding _settle by releasing the lease would let a replacement pool spawn next to the orphan. The PR body's residual-tradeoff line says callers time out; that holds for queued callers but not for the caller that owns the dirty slot. Rate-limiting the log is cheap either way.
  • servers/README.md:20: "A cold tool call stays pending while Lean warms up" is literally true, but hides that warm-up now counts against timeout on every call. Worth saying in the README and in the run_lean_code timeout docstring. Lines 40-41 ("worker pools ... 15 minutes to warm") are stale regardless.
  • core.py:992: escaped module components get past the root-only allowlist check. import Mathlib.«/abs/path/Secret» reads as root Mathlib, but Lean resolves the absolute path and loads that .olean. The string splitter had the same hole, in a policy check that #eval IO already gets around, so it may belong in a follow-up. The fix is cheap either way: reject any component that isn't a plain identifier, and apply the same check to the persistent path at core.py:1104-1106.

On 7ae9370 (quarantine removal)

  • Nothing still calls is_usable(), and the cache's is_valid hook for REPL pools went out in the same commit. Nothing else relied on either.
  • A dirty slot is never re-queued: pool.py:132 checks repl.is_clean() before re-queueing, so capacity still bounds the process count.
  • If cleanup never succeeds, queued callers fail on their own deadline with "timed out ... waiting for an idle Lean REPL" (pool.py:111-113), the same end state quarantine gave them. The owning caller is the gap (see Disputed).

Tests deleted by db02051

  • The two admission-cancellation tests pinned admission sleep/close code that the same commit deleted. Nothing is lost there.
  • test_unexpected_settle_escape_cannot_release_dirty_pool_ownership: the code it covered still exists, and nothing pins it now. If something escapes _settle outside its try (for example a KeyboardInterrupt during time.sleep), run()'s finally decrements _active_calls and the cache lease drops while the slot is still dirty. Not verified; decide whether that path matters.

Not verified

  • run_disposable's own cancellation and cleanup-precedence branches, which the pool tests bypass with fakes.
  • Whether the budget test catches the old, larger budget coming back.
  • get_repl_status reporting "warm" for a pool that owns no processes.
  • Diagnostics inside the injected header being reported at line 0, and a Mathlib-submodule import dropping the rest of Mathlib.
  • Non-POSIX behaviour: the parser spawns before the POSIX guard, so the slot may get stuck (core.py:986).
  • The process-group leader being reaped before close() signals the group (core.py:388).
  • Warmup imports being prefixed onto module/prelude headers when validation is off (core.py:980).
  • The Create Lean projects atomically with catalog-backed defaults #96 merge resolution in skills/setup/SKILL.md beyond the REPL paragraph, and the example project's REPL lock (test_skill_examples.py:164).
  • .github/workflows/tests.yml:96: the real-REPL job installs Lean through lean-action, not the pinned, checksum-verified elan install the real-Lean job uses. This is the main supply-chain question and worth checking before merge.
  • CONTRIBUTING.md:21: no local recipe for building tests/fixtures/repl-smoke and running the module.
  • tests/test_repl_core_protocol.py:1062: the combined-output-limit test only writes stdout, so a per-stream limit would also pass. :1053: the deadline test is named for the kill but only checks that TimeoutError is raised promptly. :1067: the reaping test's 10s end-to-end deadline may flake under load.
  • tests/test_real_repl.py:22: the fixture can't catch a regression of the @repl/repl shadowing fix.

Spot-checked at 4f4f50f: issues 1 and 2 (core.py:1020-1024 pass remaining() to both start and run; pool.py:118 polls with a 0.1s timeout), 7 (core.py:531-533, lean_runtime.py:643-651), 10 (the fixture's Mathlib.lean is one comment line; the three success cases have an empty warmup or a module/prelude header) and 11 (main's core.py:770). Issue 5 was checked in the Lean v4.30.0 to v4.33.0 sources.

Posted by PR swarm: PR Swarm Lead

@Deicyde Deicyde added review: ready Review complete with no known merge blockers and removed blocked Waiting for prerequisite work before implementation can proceed labels Oct 6, 2026
@Deicyde
Deicyde marked this pull request as ready for review October 6, 2026 04:40
@Deicyde

Deicyde commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Final combined gate at 4f4f50f3: #106 and #107 are folded into this PR, so lifecycle isolation, Lean-authoritative header validation, fault injection, pinned real-REPL integration, workflows, and docs land atomically. Both Python 3.10/3.13 runs, both Windows runs, both real-Lean runs, both real-REPL runs, and CLA pass. No code, coverage, or merge-order blocker remains.

@Deicyde Deicyde added awaiting author Review is complete and author action is required and removed review: ready Review complete with no known merge blockers labels Oct 6, 2026
@Deicyde
Deicyde marked this pull request as draft October 6, 2026 16:53
@Deicyde

Deicyde commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Correcting the live status at unchanged head 4f4f50f3: the detailed exact-head review posted at 04:20 says fix first, and its medium blockers remain because no commit followed. The later same-SHA readiness comment and label were unsupported. This is now draft/awaiting-author. Repair the timeout contract, FIFO waiters, shutdown/line-offset regressions, Lean v4.32 header-parser bypass, and the two missing real-REPL claims; then manually restack after #168 → #111 and rerun exact-head CI.

@Deicyde Deicyde removed the awaiting author Review is complete and author action is required label Oct 7, 2026
@Deicyde
Deicyde changed the base branch from main to fix/lsp-process-ownership October 7, 2026 07:01
@Deicyde

Deicyde commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Published the non-force integration at exact head 8bfdf44d and retargeted this draft to #111 so Files Changed shows only the disposable-REPL layer. Local exact validation: full suite 1,921 passed / 25 skipped / 1 expected failure; pinned Lean 4.32.2 real-REPL suite 19/19; lint, diff, and strict example pass. Keeping draft until exact-head CI and final adversarial review complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant