Repository navigation
Replace lua shared with require, and fix Lua error line mapping - #144
Merged
Merged
Conversation
The Workshop server no longer has a build mode without the web UI. That mode skipped the UI build and swapped the embedded asset layer for one that served nothing, so server-only tests could run without Node.js or the UI bundle, but it needed its own test run in CI. The build script now always bundles the UI, the asset routes always serve the embedded bundle, and the interface that chose between the two asset layers folds into the embedded one. - `EmbeddedAssets` is now the whole asset layer. The `AssetServer` trait and `NoopAssets` are deleted, and `get` and `manifest` become associated functions with no `self`. - `ui_asset` drops its `server: &dyn AssetServer` parameter and reads through `EmbeddedAssets::get`. Every route handler loses the matching argument, `logical_asset` calls `assets::EmbeddedAssets::manifest()` directly, and `asset_server()` is deleted. - `crates/workshop/server/build.rs` always runs the UI build after the ceiling check, since the `CARGO_FEATURE_HEADLESS` branch and `empty_asset_dir` are gone. Building `workshop-server` therefore always needs Node.js 22 and one `npm ci` per checkout. - `the_server_boots_and_serves_the_ui_with_an_unreachable_gateway` now always requests the UI index and asserts `reqwest::StatusCode::OK`, with no feature gate around that check. - `crates/workshop/server/Cargo.toml` no longer defines `headless`, and `.github/workflows/ci.yml` drops the `Test (workshop-server headless)` step that ran it. - `crates/workshop/server/src/routes/assets-headless-tests.rs` and `the_noop_asset_layer_serves_nothing` are deleted along with the feature they covered. The remaining asset tests run under plain `#[cfg(test)]`. Design: removes feature-flag @ crates/workshop/server/Cargo.toml::headless Design: removes strategy @ crates/workshop/server/src/assets.rs::AssetServer Plan: vibe/2026-10-13-3-remove-hakari.md
The Windows Workshop job now runs all of its tests from a single nextest command instead of three separate test steps. A filter keeps coverage as it was: every test in the four Workshop crates, including the workspace jail's Windows path tests, plus the five Gateway process-ownership race tests selected by name. Default features are turned off for every selected package and only the Gateway's test fixtures are turned on. The Linux Workshop job drops its copy of the race tests because the main Linux test job already runs the Gateway suites with every feature enabled. - `Test (Windows)` replaces `Test (workshop, concurrent via nextest)`, `Test (workshop-workspace, Windows jail)`, and the Windows `Test Gateway process ownership races` step with one six-package `cargo nextest run`. Because it is one cargo command, dependency features unify across all six packages, so the Workshop crates now build with the union of features that all six request. - `--no-default-features --features gateway/test-fixtures` applies to every selected package, with the fixtures feature enabled on the Gateway alone. - `-E` selects whole suites for `workshop`, `workshop-server`, `workshop-server-api`, and `workshop-workspace`, plus tests matching the five race-test names in `gateway-api-discovery` and the gateway `it` binary. nextest matches `package()` against the whole name, so `package(workshop)` does not pull in the other Workshop crates. - `cargo test` no longer runs the five race tests one at a time; they now run under nextest in parallel with the Workshop suites. - `--all-features` no longer applies to `workshop-workspace`; the merged command builds it with default features off. - `Test Gateway process ownership races` is gone from the Linux Workshop job. The main Linux test job already runs every `gateway` and `gateway-api-discovery` test with `--all-features`, so Linux keeps race-test coverage. Design: removes clone-block @ .github/workflows/ci.yml Plan: vibe/2026-10-13-3-remove-hakari.md
This change deletes the hakari-generated feature-unification crate that nearly every workspace member depended on, along with its configuration and the CI steps that installed and verified it. That crate put test-only features into the normal dependencies of every member that used it, and it let crates compile against features their own manifests never declared. Feature unification now falls back to cargo's default, where a crate gets the features its manifest declares, unified only with the other packages the same command selects. The crate whose tests relied on a supplied feature now declares it, and the comments that described the old arrangement are reworded. - `crates/workspace-hack` is deleted with `.config/hakari.toml`, its root workspace dependency entry, and the `workspace-hack.workspace = true` line in each of the 49 member manifests that carried it. It was the most-depended-on package in the workspace, so one generated feature list no longer decides what those 49 crates compile against. - `crates/harness-internal/runner/Cargo.toml` adds `test-util` to its tokio dev-dependency, the feature behind the controllable test clock its suites pace performers with. The removed crate had supplied it through its normal tokio line, so it now stays out of builds without dev-dependencies. - `Cargo.lock` only loses lines: the `workspace-hack` package block and the `"workspace-hack",` entry in each member's dependency list. No third-party package leaves the lockfile and no version changes. - `.github/workflows/ci.yml` no longer installs `cargo-hakari` or runs `cargo hakari verify`. The one-crypto-backend gateway check is unchanged, and its comment drops hakari regeneration as a way for `ring` to come back. - `tauri-plugin-updater` keeps `default-features = false`. Its comment now says the plugin's `rustls-tls` default would put `ring` into the Workshop binary, instead of into every member through the removed crate. - `system-proxy` reached reqwest in every member through the removed crate's reqwest line, and no reqwest line in this change declares it in its place. The root reqwest pin still lists only `json`, `query`, `rustls`, and `stream`. Design: removes god-module @ crates/workspace-hack/Cargo.toml Design: removes hidden-dependency @ crates/harness-internal/runner/Cargo.toml Plan: vibe/2026-10-13-3-remove-hakari.md
Turn on system proxy support in the workspace's shared HTTP client dependency. With hakari's unified feature set gone, that dependency entry has to ask for the feature itself, or the gateway would stop honoring proxy settings from the Windows registry and macOS system configuration. Default features stay off, and the rest of the feature list is unchanged. - `reqwest` gains `system-proxy` on the root workspace pin rather than on a gateway crate's own dependency line, so every member that inherits the pin gets system proxy lookup, not only the gateway. Plan: vibe/2026-10-13-3-remove-hakari.md
With hakari feature unification retired, the build tooling drops every allowance it made for hakari's crate. Outside their own family, Harness crates may now name only the Engine's two public crates, and Plugin crates may name only the Plugin contract, shared crates, and outside libraries. The build-script ceiling check loses its separate line-count path for a crate with no build script, so every crate meets the same wiring rule. Module docs and guard comments that listed the crate as an allowed dependency now match the narrower rules. - `WORKSPACE_HACK` leaves both `product.rs` and `tidy-wiring.rs`, and `LINE_LIMIT` and `workspace_hack_violations` go with it. - `ceiling_wiring_violations` runs the former `else` arm for every walked crate with no branch. Each crate has its `Cargo.toml` read and goes through `crate_violation`, so a crate without a build script meets the same `build.rs` rule as the rest. - `boundary_breach` makes the `(Family::Harness, Family::Unaffiliated)` arm unconditional, so any unaffiliated dependency of a Harness crate is a breach. That arm and the `(Family::Harness, Family::Build)` arm now say Harness crates may name only `promptforge` and `promptforge-plugin` outside their family. - `plugin_breach` allows `[PLUGIN_CONTRACT, package.package.as_str()]` plus `shared-*` crates, and its breach message no longer names `workspace-hack`. - `a_harness_crate_depending_on_workspace_hack_passes` and `a_workspace_hack_file_over_the_limit_fails` go away. `a_plugin_crate_naming_the_contract_and_a_shared_crate_passes` replaces the old passing Plugin test without its `workspace-hack` line, and the remaining assertions expect the new messages. - `## Invariants` sections in the Plugin crates, `promptforge-plugin`, and `promptforge-types` drop `workspace-hack` from their allowed dependencies. The guard comments in `engine_deps.rs`, `harness_bans.rs`, and `test_support_leak.rs` keep only that each check reads declared dependencies. Plan: vibe/2026-10-13-3-remove-hakari.md
Every exit criterion for the hakari removal now passes on Windows, Linux, and macOS, and this records that. The checks needed no fixes, so the change touches no code, manifests, or workflows. Plan: vibe/2026-10-13-3-remove-hakari.md
Plan: vibe/2026-10-13-3-remove-hakari.md
This branch was successfully deployed
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.
This branch lands two plans and one doc fix, in 12 commits. Shared Lua moves from an automatic replay into an explicit
require, and runtime errors map to the right prompt line in more cases.requirereplaceslua shared. A prompt used to hold onelua sharedblock under the H1, and the Engine replayed it into every section before the section's own code, whether the section used it or not. Nowrequire '## heading'runs a section as a library in the calling section's own Lua environment, inside the calling block's coroutine, and returns its value. A library runs once per section, can make suspending calls such asstore, model, and tool calls, gets its ownprosewith substitutions from the caller, and reports errors at its own prompt lines.requirereaches further thancallandjump: a section's direct children, its siblings, and the siblings of each of its ancestors, so a nestedtasks.fanoutworker can require a top-level library. Nothing is inherited, so each section requires what it uses. Thelua sharedfence is now a parse error that points torequire, and three library-load events that name the library replace the three shared-load events.requirework found that prompt-line mapping identified a block by its full display name inside Lua's chunk id. Lua cuts chunk names of 45 bytes or more, so a long or shared section title lost its mapping, and every H1 Lua block shared one name, so one H1 block's error could map through another's start line. Prompt blocks now compile under a shortline Nchunk name, and the mapper writes the display name back.LuaProgram::map_runtime_error, which lost its last production caller, is now test-only.run_chunk, a link toVfsError::Conflictwith no target, and a redundant explicit target onSortKey.Plans:
vibe/2026-10-12-1-require-replaces-lua-shared.mdvibe/2026-10-12-2-require-run-debt-removal.mdChanges
requirereplaceslua shared:Require run debt removal:
Doc links (no plan):
The two
Close plancommits retire each plan's active marker, and the second also marks its plan record's todos completed.Behavior changes
For prompt authors:
lua sharedfence is gone. A prompt that still has one, under the H1 or anywhere else, fails to parse with "thelua sharedfence form was removed; put shared code in a section and load it withrequire '## heading'". Move the shared code into an ordinary section andrequireit from each section that uses it. Shared setup such asmodels.defaultortools.always_offermoves into a library the H1 requires. Both still set prompt-wide state, so later sections see it without requiring the library.lua promptfence now reads "thelua promptfence form was removed; useluafor a live H1 block andrequirefor shared code".require '## heading'names a section the same waycalldoes. A library's blocks run in order. The first block that returns a value stops the library, andrequirehands back that value, whatever its type. A block with noreturn, or withreturn nil, lets the next block run, and a library where no block returns anything givestrue.requireof the same library in a section returns the saved value without running it again,falseincluded. A library that raises while loading stays out of the cache, so the nextrequireruns it again, and globals it set before the error stay set.localvariables stay inside it. Load-time code, such as a model call, runs once in each section that requires the library.requiremade while a library loads resolves from the library's own position, so a library can pull in its own nested helpers. Everything else in library code resolves from the calling section:call,jump,prompt.section,prompt.list,tasks.spawn,tasks.fanout, and anyrequirea library function makes later.prompt.sectionandprompt.listkeep the narrower reach ofcall, so from a nested workerrequire '## house_style'works whileprompt.section '## house_style'readsnil.require cycle: ## a -> ## b -> ## a.jumpraises while a library loads ("jump is unavailable while a library loads: ..."), and a library function that callsjumpafter the load acts on the caller as usual. A non-string heading raises "require takes a section heading string such as '## helpers', got ...".RequirementsUnmet, like any uncaught H1 error. The old shared replay ran outside the H1's code and kept the Lua error kind.requireis a reserved Engine global, so the reserved list grows to 61 and a Plugin prelude can't define it. A prelude that callsrequirefails to load with Lua's "attempt to call a nil value (global 'require')", becauserequireinstalls after the preludes.section `X` lua N, and when two sections share a title their block names add the heading line, as insection `Helpers` (line 18) prologue. Mapped positions readlocation:line:instead of[string "location"]:line:. An error that stays unmapped, such as one raised in a function one block defined and another block called, shows[string "line 120"]:3:: the prompt line the block starts on, then the line inside the block.For hosts:
Event::LuaSharedLoadStarted,LuaSharedLoadSucceeded, andLuaSharedLoadFailedand their lifecycle constants are gone.Event::LuaLibraryLoadStarted,LuaLibraryLoadSucceeded, andLuaLibraryLoadFailedreplace them, each with alibraryfield beside the usual coordinates that holds the library's heading with its markers, such as## shared_stuff. Every section used to emit the shared pair even with no shared code. The new events appear only on a library's first load in a section, and a cachedrequireemits nothing.crates/promptforge/public-api.txtchanges to match.For contributors:
LuaProgram::compilenow compiles prompt blocks under the chunk nameline {source_line}, andlocation()still returns the display name.compile_internalkeeps its explicit names.LuaProgram::map_runtime_errornow compiles only undertestor thetest-supportfeature, andpromptforge-parsertakespromptforge-luawithtest-supportas a dev-dependency for its tests. Production mapping goes throughmap_runtime_error_among, which maps through the started block and every library loaded into the VM.replay_shared,section_vm_with_shared, the threeLuaSharedLoad*variants, and theirLUA_SHARED_LOAD_*constants) are inRETIRED_SEEDS.Known limits
requirestill resolves the heading and copies the library's programs before it finds the saved value. The copies are reclaimed, but they sit outside the Lua memory limit until the collector runs.requirecalls in that section resolve from the library's position andjumpkeeps refusing. It needs the heap at its limit at that instant plus apcallaroundrequire.crates/promptforge-internal/lua/src/tests/require.rssits at 498 of its 500 allowed lines, so its next change has to move something out first.promptforge-docsrepository still describeslua sharedin its language guide and needs its own update.Testing
requireplan's last step also passed the facade API check.lua sharedoutsidevibe/finds only the parser's rejection, its docs, and its tests.lua/src/tests/require.rsandlua/src/tests/require/cover returns, caching, a savedfalse, cycles, a retry after a failed load, resolver keys during and after a load, thejumprefusal, suspending calls in library blocks, prose swap and restore, error mapping, and the load events.engine/src/execute/tests/suite/require/cover each rule end to end: a sibling library's table, a nested fanout worker requiring a top-level library,store, model, and tool calls during a load, an H1 setup library's prompt-wide state, the walk reaching a library, error lines during and after a load, the load events, a child starting without its parent's libraries, and each raise.lua sharedmoved torequire, includingmodels.defaultsetup, which moved into a## setuplibrary the H1 requires. The fixtureshared-library.mdbecamerequire-library.md.a_prelude_write_across_two_arms_ends_the_run_with_determinism_caught_or_notpins that a prelude's store conflict ends the run as a determinism violation whether or not the prelude catches it, now that the conflict check sits right after the preludes.67be02738and pass now:every_h1_block_maps_only_its_own_errors,a_location_longer_than_lua_keeps_in_a_chunk_name_still_maps,a_long_unique_section_title_maps_its_prologue_line,a_shared_title_whose_heading_line_reaches_100_maps_its_prologue_line, anda_raise_while_a_long_titled_library_loads_reports_the_library_line. A check inlua/src/tests/require/errors.rsthat the chunk rename made vacuous now matches any unmapped[string "marker.