You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The api promise file misses Launch::new and Launch::run #352
public-api.api.txt is meant to be the frozen wf promise, where any diff is a breaking change by definition. As built by #338 it is not: renaming api::Launch::run leaves it byte-identical.
cargo public-api renders methods and impls only at a type's canonical path — devlaunch_core::flows::launch::Launch::run, never devlaunch_core::api::Launch::run — so the classifier's devlaunch_core::api\b match cannot see them. The review of PR #347 measured it precisely: the generator emits the api section as 79 contiguous rows and the filter keeps 37. The 42 that fall through into the "regenerate freely" tripwire file include:
Launch::new and Launch::run — the only constructor and method of the central promised type
CommandContext::new
DevcontainerPath::as_str
every derived Clone / Debug / PartialEq impl on the four promised types
Widen the classifier so a promised type's canonical-path rows are classified as promise, not as tripwire. The obvious shape is to resolve each api re-export to the canonical path it names and claim that type's rows — but check whether cargo public-api offers this directly before building it by hand, and make sure the partition stays a partition (every row in exactly one file, which rust/devlaunch-core/tests/public_api_snapshots.rs already asserts).
Red-first, and the red is the finding itself: rename Launch::run, and public-api.api.txt must change. A test that mutates nothing proves nothing here — the existing suite passed while this hole was open.
Note the docs were corrected in #338 to stop asserting the converse, so this ticket closes the gap rather than a contradiction.
Claimed 2026-08-29: queued in the session working map #299's build tickets (waves behind #340/#349's PRs so the api surface and shared JSON writer move once). Build to follow from an opus agent in an isolated worktree. If you are another session seeing this, do not double-build.
breadcrumb: building this now, stacked on #340's PR (branch wayfinder/devlaunch-340) so the surface moves once rather than twice.
Restating the contract: the promise file must carry the promised types' methods and impls, so that renaming api::Launch::run diffs it. Verified live on the stacked base first: the regenerated public-api.api.txt is 126 rows and not one of them is a ::new( or ::run(.
Seams I intend to test at, both of which the ticket names:
The classifier, which today is one grep inside the regeneration script and is reachable only by running the whole nightly toolchain. I am giving it a way in that needs no toolchain, so the predicate itself can be exercised on crafted rows: the interesting cases are a canonical-path method row (must be claimed) against a row that merely mentions a promised type in an argument position (must not be). That second one is the real hazard, because a promised type appears in half the signatures in the crate.
The checked-in snapshots, where the existing Rust guard already holds the partition. Extended to assert what the ticket says is missing: Launch::new and Launch::run present in the promise file, absent from the tripwire.
One thing found while measuring that changes the shape of the fix, and which the ticket does not mention: the generator emits each promised type's impl rows twice, once in the api section and again in its own module's section, byte-identical. A path match cannot tell the two copies apart, so claiming the canonical rows moves both. That is the right answer rather than a compromise, since the alternative leaves an identical row in both files and the partition guard is what would catch it, but it means the tripwire file loses those rows entirely.
breadcrumb: built, PR #518. Stacked on #514 (base wayfinder/devlaunch-340), so it retargets to main when that merges.
The classifier reads the api module's own re-exports, resolves each to the canonical path it names, and claims that item's rows as well as the api-path ones. 395 rows moved from the tripwire file to the promise file and none appeared from nowhere: the two files' totals are conserved exactly, and both were regenerated with the real tool after first reproducing the base's three files byte-identically.
The ticket's red was met on its own terms. Launch::run renamed and the snapshots regenerated: the promise file now diffs, where before the same rename left it byte-identical.
Two things worth carrying out of this that the ticket did not anticipate:
The generator emits each promised type's impl rows twice, byte-identical, once under the api section and once under the module that owns them. A path match cannot tell the copies apart, so claiming the canonical rows moves both and 143 of the promise file's 521 rows are duplicates. Deduplicating would break the partition, since the two files are complements by construction.
The limit moved rather than closed. A type api never re-exports but a promised signature hands back is still binary surface: Launch::run returns flows::launch::Launched, so renaming one of that type's fields breaks a consumer and diffs the tripwire file alone. That is now what the three places describing the promise file say, in place of the old caveat about methods.
One guard had to go rather than be extended: "every promised row is a devlaunch_core::api declaration" stopped being true and could not be made true without a second copy of the classifier living in Rust. It is replaced by a fixed point, feed the classifier both files and it must hand back the same two, which covers what that one covered and also catches a pair regenerated by different rules. The classifier is reachable without a nightly toolchain now, as a filter over rows on stdin, which is what lets both the Rust and Python guards exercise the real rule instead of restating it.
wf-review verdict: Comment (nothing blocking) on #518 — reviewed against merge base 9925291; two material findings (residual caveat understates scale 1 vs 38 types; the doc guard passes with that caveat deleted).
Activity
Say what the promise file cannot see, and take the file list from the…
blooop commentedon Aug 29, 2026
Claimed 2026-08-29: queued in the session working map #299's build tickets (waves behind #340/#349's PRs so the api surface and shared JSON writer move once). Build to follow from an opus agent in an isolated worktree. If you are another session seeing this, do not double-build.
blooop commentedon Aug 29, 2026
breadcrumb: building this now, stacked on #340's PR (branch
wayfinder/devlaunch-340) so the surface moves once rather than twice.Restating the contract: the promise file must carry the promised types' methods and impls, so that renaming
api::Launch::rundiffs it. Verified live on the stacked base first: the regeneratedpublic-api.api.txtis 126 rows and not one of them is a::new(or::run(.Seams I intend to test at, both of which the ticket names:
grepinside the regeneration script and is reachable only by running the whole nightly toolchain. I am giving it a way in that needs no toolchain, so the predicate itself can be exercised on crafted rows: the interesting cases are a canonical-path method row (must be claimed) against a row that merely mentions a promised type in an argument position (must not be). That second one is the real hazard, because a promised type appears in half the signatures in the crate.Launch::newandLaunch::runpresent in the promise file, absent from the tripwire.One thing found while measuring that changes the shape of the fix, and which the ticket does not mention: the generator emits each promised type's impl rows twice, once in the
apisection and again in its own module's section, byte-identical. A path match cannot tell the two copies apart, so claiming the canonical rows moves both. That is the right answer rather than a compromise, since the alternative leaves an identical row in both files and the partition guard is what would catch it, but it means the tripwire file loses those rows entirely.blooop commentedon Aug 29, 2026
breadcrumb: built, PR #518. Stacked on #514 (base
wayfinder/devlaunch-340), so it retargets to main when that merges.The classifier reads the
apimodule's own re-exports, resolves each to the canonical path it names, and claims that item's rows as well as theapi-path ones. 395 rows moved from the tripwire file to the promise file and none appeared from nowhere: the two files' totals are conserved exactly, and both were regenerated with the real tool after first reproducing the base's three files byte-identically.The ticket's red was met on its own terms.
Launch::runrenamed and the snapshots regenerated: the promise file now diffs, where before the same rename left it byte-identical.Two things worth carrying out of this that the ticket did not anticipate:
apisection and once under the module that owns them. A path match cannot tell the copies apart, so claiming the canonical rows moves both and 143 of the promise file's 521 rows are duplicates. Deduplicating would break the partition, since the two files are complements by construction.apinever re-exports but a promised signature hands back is still binary surface:Launch::runreturnsflows::launch::Launched, so renaming one of that type's fields breaks a consumer and diffs the tripwire file alone. That is now what the three places describing the promise file say, in place of the old caveat about methods.One guard had to go rather than be extended: "every promised row is a
devlaunch_core::apideclaration" stopped being true and could not be made true without a second copy of the classifier living in Rust. It is replaced by a fixed point, feed the classifier both files and it must hand back the same two, which covers what that one covered and also catches a pair regenerated by different rules. The classifier is reachable without a nightly toolchain now, as a filter over rows on stdin, which is what lets both the Rust and Python guards exercise the real rule instead of restating it.Give the typed refusal its red test, and fix four stale doc markers
blooop commentedon Aug 29, 2026
wf-review verdict: Comment (nothing blocking) on #518 — reviewed against merge base
9925291; two material findings (residual caveat understates scale 1 vs 38 types; the doc guard passes with that caveat deleted).State the residual honestly, and guard the sentence that states it