Repository navigation
Upgrade to Lash main with facade composition and selectable RLM dialect - #4
Merged
Merged
Conversation
Move the Lash SDK dependency graph to the crates.io release and adapt the CLI to the alpha.104 runtime contracts. Release-Notes: - Update lash-cli to Lash v0.1.0-alpha.104.
lash-typescript and the session-pinned RLM dialect only exist on Lash main; the last published release (alpha.113) predates them. Drop the removed lash-plugin-plan-mode dependency. Release-Notes: Track Lash main 1d33a2c7c instead of alpha.104.
Use the current tool outcome and cross-dialect binding APIs, remove per-tool scheduling, and resolve paths without the deleted file-tool helper. Release-Notes: Port local search tools to the current Lash tool contract.
Match the current shared prose-delta representation so extension event handling remains type-correct against the Lash main turn surface. Release-Notes: Keep TUI extensions compatible with current Lash turn events.
Adopt unified plugin and tool outcomes, current cross-dialect bindings, facade turn types, and the progress-free attempt execution contract. Release-Notes: Port autoresearch to the current Lash plugin and tool APIs.
Load sessions from the Lash main durable-core catalog through the current persistence facade, with schema preflight and typed refusal before open. Replace alpha-era projection and attachment shapes, derive model metadata and display titles from persisted current data, and drop unavailable created-at and cwd columns. Require a host-owned session roster for tree exports instead of scanning per-session databases. Cover JSON, single-session HTML, and tree HTML with sessions produced by a real current Lash runtime, and pin trace schema 8 plus old-schema refusal behavior. Release-Notes: Port lash-export to current Lash stores and export formats.
Replace alpha-era persistence, provider factories, event handling, and plugin plumbing with facade-owned composition and typed durable inputs. Pin session policy and RLM dialects while keeping provider config and the session roster host-owned. Release-Notes: Move the CLI to the Lash facade with durable unified storage and selectable RLM dialects.
Keep the operator, end-to-end harness, and user documentation aligned with unified storage and dialect-pinned RLM sessions. Resolve the built binary through Cargo metadata so witness runs work with shared target directories. Release-Notes: Document RLM dialect selection and add TypeScript, resume-conflict, and operator coverage.
Semantic MCP indexing only needs the complete public signature rendering, which already includes return fields. Avoid the upstream test-private return renderer so the optional search feature compiles against Lash main. Release-Notes: Keep semantic MCP tool indexing compatible with the Lash facade.
Track the latest Lash main commit rather than a published release so the CLI consumes the current facade, dialect, and durable-format contracts. The regress patch moves with the same revision. Release-Notes: Build against Lash main bc28978d2.
zlob 1.6.1, pulled in by the fff-search update, declares a minimum Zig of 0.16.0; the workflows still installed 0.15.2 and failed compiling its Zig sources. Release-Notes: CI and release builds use Zig 0.16.0.
Cover App::handle_turn_activity at its semantic event boundary so UI state transitions do not rely solely on render and PTY coverage. Release-Notes: Internal: unit coverage for turn-event dispatch.
SamGalanakis
marked this pull request as ready for review
August 23, 2026 17:14
Repin the workspace and cut over catalog discovery, typed helpers, facade homes, read-only export, and durable Escape reconciliation so the CLI follows the current runtime contracts without compatibility shims. Release-Notes: Upgrade the Lash runtime and restore cancelled steer text to the editor instead of replaying it.
Align operator guidance, format diagnostics, export notes, and runbooks with catalog-backed sessions, the current durable versions, and id-or-name resume behavior. Release-Notes: Document session schema 41, trace schema 9, remote protocol 46, and catalog-based resume behavior.
Remove dropped active-turn inputs from the UI snapshot as soon as durable cancellation settles so restored editor text never masquerades as queued next-turn work. Release-Notes: Prevent cancelled steer text from briefly appearing as queued work after Escape.
Track the physical Lash turn identity for direct and queued work, submit cancellation without waiting in the TUI, and restore dropped input from the settled turn report. Remove the misleading recovery sidecar and derive the displayed session schema from one documented compatibility constant. Release-Notes: Keep Escape responsive during durable cancellation and restore dropped queued input after settlement.
Subagent spawns now surface as transient dock rows backed by processes.db; the runbook premise still described the pre-upgrade dock that hid them. Release-Notes: none (runbook text only).
Two defects made RLM code blocks effectively undiscoverable. `set_expand_level` applied only while the view followed output, so Alt+O and Ctrl+O were silent no-ops once the reader had scrolled up — exactly when they go looking for the code behind earlier activity. The gate is gone; in manual-scroll mode the viewport is re-anchored to the block that occupied its top row before re-layout (offset into that block preserved, then clamped to the recomputed max scroll), and follow mode is left untouched. Below full expansion a `LashlangCode` item rendered nothing at all, so nothing hinted that code existed or which key revealed it. Levels 0 and 1 now render a single dim marker row — `◇ <dialect> · <N> lines — Alt+O to expand`, using the session's RLM dialect when known and the neutral word `code` otherwise. Level 2 renders the full source as before. `/expand [compact|normal|full]` (numeric `0|1|2` accepted) sets the level directly and reports it with no argument, going through the same ungated path, so terminals that swallow Alt chords still have a way in. Release-Notes: Ctrl+O/Alt+O expansion toggles now work while scrolled up, hidden RLM code shows a dim `◇ <dialect> · <N> lines — Alt+O to expand` stub row, and the new `/expand compact|normal|full` command sets the expansion level directly. Closes #10
Latch automatic post-turn refresh after an observed failure so later turns remain quiet while explicit refresh requests continue to report errors. Reset the latch whenever the interactive app installs or clears a session. Closes #9 Release-Notes: Report a failed automatic tool-catalog refresh once per session instead of after every turn.
/clear left the previous session's UI activity journal in memory. Journal records are keyed by (turn_ordinal, lashlang_block_ordinal), and both ordinals restart in the new session, so the old session's activity rows (commands it ran, with their outputs) were spliced into the new session's timeline at matching positions — asserting executions that never happened in that session. The durable record and on-disk sidecars were unaffected; the leak was purely in-process rendering. Reset the journal, pending records, active turn ordinal, block ordinal, and block anchors in App::clear, with a regression test. Fixes #11. Release-Notes: /clear no longer leaks the previous session's activity rows into the new session's timeline.
App::clear() was a hand-maintained field list, so every new per-session field silently survived /clear unless someone remembered to add it — the exact mechanism behind the cross-session activity-row leak. Rewrite clear() around an exhaustive destructure of App with no rest pattern: every field must be explicitly classified as carried (process-scoped) or reset (session-scoped), and adding a field fails compilation until it is classified. Newly reset as a result: iteration, live stream chunks and timeline anchor, history top pad, queue suppression ids and pending option prompt responses, context window, execution mode and dialect labels, session identity, text selection, toast, and the mouse-up latch. Call clear() at session install time on both switch paths: handle_clear now clears before the new session's identity is applied (previously the setters ran first, which the exhaustive reset would have wiped), and activate_opened_session — which never cleared at all, leaking activity state, processes, plan dock, and overlays across /resume — clears before the resumed state is loaded. Release-Notes: session switches now reset all per-session UI state; resuming a session no longer carries over stray state from the previous one.
Re-pin all Lash git dependencies from 372a95fe9 to 8dac97fa5, naming the
renamed lash-internal-* packages now that lash-runtime is the sole
supported package (the facade alias and lash:: import paths are
unchanged).
Adopt the fixes we filed from the previous round:
- FIG-2109: delete the hand-mirrored session schema constant; all three
compatibility stamps (session schema 44, trace schema 10, remote
protocol 47) now derive from public upstream constants.
- FIG-2112: lash-export reads session relations from the read-only
SessionSummary/SessionReadView surfaces; the mutable metadata open is
deleted and the loaded types narrowed to a host-owned
{session_id, relation} projection.
- FIG-2114: the remaining lash_core imports move to their facade homes
(SessionNodeRecordFacadeOps is now lash::persistence::
SessionNodeProjection); lash-cli drops its lash-core dependency
entirely.
- FIG-2138: the dialect-loss bug is fixed upstream; the catalog-sync
failure e2e that depended on inducing it is replaced by a success-path
e2e proving typescript sessions sync their tool catalog after turns.
The sync-failure latch stays as generic degradation.
Range adaptations: LlmResponse.full_text is a derived method (test
provider literals updated), SessionRelation::ObserverIntent is removed
from the export tree, and PluginHost::build_session lost its second
parameter. The queued-turn path deliberately keeps drain_id and the
ActiveTurnTargetSink: the new turn_id builder is mutually exclusive with
drain idempotency and FIG-2110's start notification did not land.
Release-Notes: update to Lash main 8dac97fa5; typescript RLM sessions no longer fail post-turn tool-catalog sync; session store schema 44, trace 10, remote protocol 47.
A message queued for the next turn could be claimed by the runtime's automatic broad queued-work drain when the post-turn tool-catalog sync ran: the turn executed headlessly with no UI sink, so neither the user row nor the response ever rendered, while both committed durably — silently prefixing the visible transcript. The queue preview vanished via the pending-input snapshot refresh, making the message look swallowed. Interactive cores now disable the automatic queued-work driver, making the CLI the sole executor of user-visible next-turn input; there is no enqueue-time driver wake left to race. Catalog refreshes (bootstrap, post-turn, /clear, /resume) enqueue the admin command and drain exactly its returned batch id — a selected-batch drain structurally cannot claim pending turn input, and the helper fails closed if a model turn is unexpectedly reported. Post-turn reconciliation reads the authoritative session read view and durable turn_input_applications instead of the foreground TurnReport, and the live injected-input path is shared with durable reconciliation. The standard-slow-echo fixture now selects its reply from the request's last visible user text so retained history cannot mask a missing turn; a PTY e2e drives the full queue-during-turn flow and asserts request order, both rendered rows, durable completion, and catalog-sync health. Known limitation: with the automatic driver gone, idle process-wake queued work has no interactive executor until the next user turn. No wake producer is reachable in this CLI today (no process-engine plugin); tracked as #13 with a fix sketch over existing facade surfaces. Fixes #12. Release-Notes: messages queued for the next turn now always render and run visibly; previously they could execute invisibly when the post-turn tool-catalog sync claimed them.
- Refresh the quickstart embedding API sketch. - Require fresh committed markers for queued-turn delivery. - Clear stale capture before the cancellation idle gate. - Match the current Session identity table in the resume picker. - Align subagent dock wording and gate on settled output. Release-Notes: none (runbook documentation only)
The Session/id gates otherwise match pre-overlay capture such as the footer Idle stamp; a fresh capture confines them to the /info frame. Release-Notes: none (runbook documentation only)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrade lash-cli from Lash alpha.104 to Lash
main(git pin8dac97fa5, not a release) and rewrite the runtime composition onto the Lash host facade.What changed
lash*dependencies are git revs onAscending-AI/lashmain (372a95fe9). Upstream now ships its ownlash-regressfork, so no[patch.crates-io]block is needed.LashCore::{standard,rlm}_builderwith an explicit host policy (host_policy.rs: turn/no-progress/commit budgets, queued-work batching, RLM execution bounds) and installs the store factory, effect host, attachment/process-env stores, process registry, trigger store and trace configuration. Provider configuration is host-owned (ProviderConfig { kind, config }, same on-disk JSON shape as before).--rlm-dialect lashlang|typescript(default: configrlm_dialect→LASH_RUNBOOK_DIALECT→ lashlang). The dialect is stated on every RLM session open viaplugin_option(RLM_PROTOCOL_PLUGIN_ID, RlmCreateExtras), recorded by Lash, and fixed for the session: resuming with a conflicting dialect is refused./modeand the status bar showrlm · <dialect>; onboarding and Ctrl+P offer both dialects; subagents inherit.TurnReport, typedTurnInputApplicationreceipts,TurnInputIngress::{active_turn, NextTurn}for steering. Escape is a durableTurnCancelRequestwith an explicit undelivered-input disposition (TurnCancelDisposition::Drop), driven throughcore.turn_work_driver(); the cancel outcome'saffected_inputsrestore dropped drafts to the editor. Typed RLM execution bounds (InstructionBound/WallClockBound/MemoryBound). Forking usesLashCore::pin+fork_at.~/.lash/store/durable-core.db(plusprocesses.db,triggers.db,effects.db,artifacts.db,process-env.db,attachments/). The session catalog is the identity source of truth — enumeration and resume go throughLashCore::sessions()/read_session; the host roster (~/.lash/sessions/<id>.{ui.json,ui-activity.jsonl,host.json}) is a display-only sidecar. Startup preflights durable formats (session schema 41, trace 9, remote 46); alpha-era per-session databases are never opened — the picker counts them as not openable and explicit resume refuses them with the detected format stamps. No migration.update_planplugin replaces the removedlash-plugin-plan-mode; observational memory (--context-approach,--om-*) is removed with the upstream crate; MCP discovery moves fromsrc/examplestosrc/mcp_discovery; tool surfaces use the restoredlash-tool-supporthelpers (typed_args/typed_ok/canonicalize_under/object_schema/tool_binding) and boundary-typed plugin directives from thelash::plugins/lash::toolsfacade homes;lash-exportreads stores viaSqliteSessionStoreFactory::open_read_only+LashCore::read_session.persistence.rs,SessionStreamEventhandling, host-side fork snapshots, legacy projection branches, the interrupt draft-replay machinery (active_turn_draftset al.), and the unit suites tied to them; net −8k lines.Verification
cargo fmt --check,cargo clippy --workspace --all-targets --features lash-cli/test-provider,lash-cli/bench,lash-cli/fff-zlob -- -D warnings,cargo test --workspace(444 passed),cargo test -p lash-cli --features test-provider --test cli_e2e(10 passed, includingcli_rlm_typescript_status_and_resume_conflict_are_pinned),python3 scripts/check_docs.py, and operator witnesses:rlm · typescriptstatus line, and an Escape-mid-turn witness showing queued input dropped and restored to the editor.Release-Notes
372a95fe9); existing alpha-era~/.lash/sessions/*.dbsessions cannot be opened and are reported as such. Use--resetto start a fresh store.--rlm-dialect lashlang|typescript, fixed per session and shown in/modeand the status bar.--context-approachand--om-*options (observational memory retired upstream).Update: re-pin to
8dac97fa5(2026-08-26)372a95fe9→8dac97fa5(55 upstream commits). Upstream renamed every internal crate tolash-internal-*(onlylash-runtimeis a supported dependency); the workspace consumes internals via explicitpackage =aliases and dropslash-corefrom lash-cli entirely. Version stamps are all derived (session schema 44, trace 10, remote protocol 47). Adopted landed fixes for FIG-2109/2110/2112/2114/2138 and deleted the superseded workarounds;lash-exportnow reads session metadata from read-only surfaces (SessionSummary::durable_relation)./clearand/resumenow reset the UI activity journal, andApp::clear()is an exhaustive field destructure (no..): a new session-scoped field that isn't classified fails compilation.Review outcome
Independent fresh review of the arc-3 diff (
7ec1e23..2652454) confirmed the queued-turn ownership design (selected-batch drains structurally cannot claim turn inputs; idempotent re-drain; no double-render in reconciliation; honest e2e witness; clean re-pin). One deciding risk was verified real and deliberately deferred: with the automatic driver disabled, idle process-wake queued work has no interactive executor until the next user turn. No wake producer is reachable in this CLI today (no process-engine plugin; the test provider cannot create durable processes), and the pre-existing behavior ran wake turns invisibly — the same defect class #12 removed. Tracked with a fix sketch in #13; noted in the #12 commit body.