Skip to content

fix(studio): load the app module by computed specifier in the law-4 arm - #525

Closed
KnockOutEZ wants to merge 1541 commits into
mainfrom
studio-handoff-core
Closed

fix(studio): load the app module by computed specifier in the law-4 arm#525
KnockOutEZ wants to merge 1541 commits into
mainfrom
studio-handoff-core

Conversation

@KnockOutEZ

Copy link
Copy Markdown
Owner

The type-gate CI job deliberately does not build, so a static import of
apps/studio/src/main/run-view-model.ts from tests/integration put a module
whose own wigolo/studio specifier resolves through dist/ into the root
type-check program — three green legs locally, three red in CI, for a resolution
fact rather than a type error. The app is a Bundler-resolution project and the
root program is nodenext; the two are not meant to see each other.

The runtime import is unchanged — same module, same arm — and the surface it
drives is restored as two local shapes, so the seam stays typed. run-view-model
goes back to the extensionless specifier its 34 siblings use.

KnockOutEZ and others added 30 commits August 23, 2026 22:52
The type-gate CI job deliberately does not build, so a static import of
`apps/studio/src/main/run-view-model.ts` from `tests/integration` put a module
whose own `wigolo/studio` specifier resolves through `dist/` into the root
type-check program — three green legs locally, three red in CI, for a resolution
fact rather than a type error. The app is a Bundler-resolution project and the
root program is nodenext; the two are not meant to see each other.

The runtime import is unchanged — same module, same arm — and the surface it
drives is restored as two local shapes, so the seam stays typed. `run-view-model`
goes back to the extensionless specifier its 34 siblings use.
…replay-ra

fix(studio): three adopt/replay races leave the app projection permanently stale or law-4-violating
The SD1 exit gate compares REST, the menu, SQLite and the JSONL for the run id
but pins only the badge's NULL branch, so truncating the id or reading it off
runs[0] left every suite green while law 8's shared address space quietly broke.
K7 falsified 'with no change in the store to do it' — listRuns decides the
filter on the projection. The header was rewritten in 49ac2c9; the title was
not, so it still advertised a premise the row no longer asserts.
…ting

`before-quit` was fire-and-forget, so Electron tore the process down while
`shutdown()` was still awaiting `endRun`'s append. On the most ordinary path in
the product — Cmd-Q, or the last window closing — a live session's run stayed
`running` in the durable log forever, and boot `reconcile()` rewrites only
visibility, never status. The log and every projection of it then disagree
permanently, which is precisely what the run spine exists to prevent.

The first quit is now cancelled and re-issued once shutdown resolves, bounded by
a 5s deadline: a wedged broker child must not be able to make the app
unquittable. A crash is already survivable; an app the user cannot close is not.

The e2e forces the race rather than hoping for it — one session lands inside the
window Electron's own teardown leaves open, so the arm opens eight and asserts
every one comes back terminal, read through a daemon that never saw them. The
SIGKILL arm alongside it is the outside signal: crash-orphaned runs stay
`running`, so the graceful arm cannot be passing on a store default.
Two defects on the /v1/runs events surface, both invisible in the happy case.

The drain wait had no deadline. `waitForDrain` resolved only on 'drain' or
'close', and a client that keeps the TCP connection open while it stops reading
emits neither: the await parks, so no further byte is handed over and
`stalledBytes` cannot reach its budget; the heartbeat returns early on
`needsDrain`, so no ping is written and the silence reconcile never runs; and
the events route is exempt from the router's slot and deadline discipline by
design. Nothing was left to notice. The connection slot was then held until the
daemon restarted, and the default 32 of them 429 the route permanently for any
authed client whose run's log is long enough to reach the pace gate. The wait
now carries its own clock — four heartbeat intervals, so the knob that already
governs "how long may this stream say nothing" governs this too — and an expiry
takes the same door every other back-pressure case takes: `endStalled`, which
ends the stream and gives the slot back, and the client resumes from
Last-Event-ID against the durable log.

`pumpDurable` treated any short page as end-of-log. The requested page size is
what this process asks for, not what it gets: the broker clamps every read to
MAX_EVENTS_PAGE whatever the limit says, so setting
WIGOLO_STUDIO_RUN_REPLAY_PAGE above the clamp silently truncated every
host-side replay mid-log, and the heal path then looped end/reconnect at the
client's backoff. It now stops only on an EMPTY page, which is the contract
run-view-model.ts already documents and enforces, with a non-advancing-cursor
guard taking over the accidental termination the short-page check provided.
The broker states the clamp as a contract at its own end.

Tests force both conditions rather than hoping for them: a response whose write
returns false and which never emits 'drain' or 'close', and a store that clamps
its pages below what the route asked for. A must-not-fire control pins that an
ordinarily slow but draining client is not killed.
`""` passed validation and was persisted verbatim: the `?? DEFAULT_SPACE_ID`
substitution downstream fires only on `undefined`, so the run was then
invisible to `?spaceId=default` — the filter every surface lists with — while
still being a live run holding tabs. The client strings failed the other way:
the store rebuilds the badge with `name && version`, so an empty one was
accepted on write and erased on read, and the caller got a 201 for a driver
identity no surface would ever show. Law 3 makes the driver shown identically
everywhere, and a field that evaporates between write and read cannot be.

Both now 400 with the field named. Validated on the trimmed value and persisted
verbatim, which is what `task` already does — trimming here would quietly
rewrite a caller's identifier, and the durable log is the wrong place for that.
Behaviour on `undefined` is unchanged.

The test enumerates the shapes the schema ACCEPTS rather than only the
revealing one — the blanks include a tab, a newline, a CRLF and a U+00A0 — and
lists the shapes that must still work beside them, including a spaceId whose
padding is blank but whose value is not, and the non-string types a guard
written as `!spaceId` would newly let through.
…re append path

Every append recompiled five statements inside BEGIN IMMEDIATE — the write lock
on a database search caching, embeddings and artifacts also queue behind — because
better-sqlite3 does not cache them. cost.recorded is one event per browser action
by design, so a long run paid the compiler continuously.

Statements now resolve through a WeakMap keyed by Database handle, so the broker
child, the daemon and every test database keep their own. Only constant sql goes
through it; the two reads whose text varies with their argument count keep their
own prepare.

Measured 20k appends on this machine: the transaction alone 74.0 -> 17.4 us/append
(76.5% of it was compilation), and the held write lock 106.8 -> 47.6 us/append.
`task` carried `minLength: 1` from the start; `spaceId` and the client strings
carried only a maximum, so the served document advertised a blank as valid
input for both. A generated client would emit one and take a 400 the contract
said could not happen — and before the router refused it, the worse outcome: a
201 for a run invisible to the default space filter, or for a driver badge the
store erases on read.

The new row is a drift gate rather than a restatement: it reads the served
document, so relaxing either side alone goes red.
…il-badge-s

test(studio): pin the rail badge's positive render; retitle a falsified fanout row (SD1 exit-7)
…an guessed

The 5s bound was written on the assumption that `shutdown()` is sub-second. The
demo falsified it: eight live sessions cost ~4.2s, because the walk is serial
and every detach and append is a broker round trip. At 5s the backstop would
have started truncating at roughly ten sessions — a fleet size that is not
unusual — which turns a bound meant for a wedged broker into a routine
truncation with a timer.

10s is 2.4x the measured cost and still under the 15s the e2e lane bounds
`close()` at, so a slow quit cannot become a red lane either. A-98-1 updated
with the measurement and with the thing to fix before the bound is raised again:
the serial walk itself.
…ared-state

perf(studio): cache prepared statements on the run-store append path (SD1 exit-7, #102)
…it-needs

fix(daemon): SSE drain wait needs a deadline; replay short-page contract; empty spaceId validation
…uit-must

fix(studio): a graceful quit must land the terminal events before the process exits
…us index

The list page reads studio_runs by keyset and orders by (created_at, id).
With no index over those columns SQLite planned SCAN + TEMP B-TREE: the whole
table read and the whole table sorted, per page, on a table that grows forever
by design.

Two indexes because the read has two live shapes and neither can use the
other's — an unscoped read cannot use an index led by space_id, and a
space-scoped read cannot seek without one. Both cost a b-tree write only at
INSERT, once per run, since created_at, id and space_id are fixed at creation.

idx_studio_runs_status is the opposite trade and is now dead: nothing selects
studio_runs by status since the filter moved onto the projection, but the index
still had to be rewritten by the status UPDATE on every append.
Bounding the READS was never a bound on the WORK. #94 moved the status filter
onto the projection, correctly, but the scan loop then fully projected every row
it looked at — so a filter matching nothing paid up to eight pages of unbounded
per-run reads to return an empty page, synchronously, with no await for the
router's deadline to fire in. Measured at 2000 runs x 122 events,
?limit=200&status=cancelled: 163 ms of blocked event loop.

Two changes, and the pair is the bound:

- The scan is two-phase. A row's status comes from its seeds — bounded
  newest-row seeks plus the auto-deny-windowed decision read — and only
  survivors are projected. Rows fully projected per call is limit + 1, filtered
  or not. The seeds are computed once and handed to the projection rather than
  re-read there: re-reading would put a second status answer in front of the row
  it admitted, which is the exact defect K7 deleted.
- The eight reads now share one row ceiling, so pages get shorter as the
  caller's page gets longer and the ceiling belongs to the store rather than to
  the request. The first read is still the full limit + 1, so an unfiltered call
  is the one read it always was.

The keyset predicate is a row value rather than the equivalent OR form: against
the OR SQLite declined the index constraint and planned SCAN ... USING INDEX,
walking from the newest row on every page. The row value plans
SEARCH ... ((created_at,id)<(?,?)) and seeks straight to the cursor.

163 ms -> 2.5 ms on the no-match case.

The docstring quantified only the look-ahead row at 2%, which understated the
reachable 8x multiplier; it now states the bound it actually has.
runListLogs learned an oversized run's serialized size by materializing it
— up to 2000 rows read, every payload JSON.parsed, the array re-serialized
— and on failing the char check returned the projection without spending
either budget. charsLeft was therefore still the full 4M for the next run
on the page, which read itself in full to fail the same way, so a page of
fifty 5M-char runs moved a quarter of a gigabyte to answer with fifty
few-hundred-byte projections, and the next hydration page did it again.

Two changes. A SUM(LENGTH(payload)) probe decides the char bound before the
read where it can: it under-states the serialized size by construction, so
a run it rules out could not have fitted and the accepted path is decided
by exactly the check it was decided by before. And the budgets are now
charged for the READ rather than for the acceptance, which terminates the
overrun the probe's soundness margin still admits — charsLeft goes
non-positive and the guard stops the reads for the rest of the page.

Forced test, five oversized runs on one page: 5 full-log reads of 83 rows
and 20,505,815 payload chars before, 0 materializations and 870 chars
after. A second row pins the charging arm on its own, with runs built into
the band the probe cannot reject.
…t run end

Three races in the decision mirror, each producing a durable log that
contradicts what actually happened.

A human double-submit double-wrote. `settle` dropped the `runOf` link and
then awaited the append, so a second submit inside that await missed the
link — and the `runForDecision` fallback still found the card pending,
because the first fold had not landed. `resolveDecision` appends
unconditionally, so nothing downstream deduped the second resolution.

An answer arriving while the card was still being recorded was dropped.
`runOf` is set only after `requestDecision`'s round-trip, and the renderer
was told about the card before `parked` ran at all, so a fast click found
neither the link nor a projection entry and returned without writing. Two
minutes later the log said `auto_denied` for a card the broker approved.

Both are one mechanism: the mirror now takes one turn at a time per card.
The second submit runs after the first append has folded and finds nothing
left to write; the early answer waits for the request to land and then
settles it. Per card, not global, so a slow append never holds up another
card's answer. `onParked` records before it notifies, so the claim on the
card's turn exists before any answer can arrive.

And a card can outlive its run: `endRun` has no channel into the mirror, so
a timer parked when the run ended appended `decision.resolved` after
`run.completed` — out-of-order in an append-only log, and a forced full
re-read on a condensed run. Refused at the write rather than announced at
the call site, through the same `isTerminal` every other seam uses, so
every path in is covered by one check instead of by remembering to notify.
The block said a size 'would still have to read and parse an arbitrary
number of rows to find out what it was'. That was the justification for
the pair of bounds and it is no longer true: the size now comes from a
SUM that never parses a payload. States what each bound actually does,
and that a read is charged whether or not its envelopes ship.
The union test's POST_MERGE_ADDED list is the file's own designed growth point —
its comment says an entry there is a deliberate addition and an unrecognised
name in neither released set is still a failure. Every new migration has to land
in it or the count assertions red.
…-hydration

perf(daemon): charge the hydration char budget when the read happens, not when it is accepted
…filtered-l

perf(studio): bound the filtered list scan's projection cost; index the list read; drop the dead status index (SD1 exit-7)
…rror-doubl

fix(studio): decision mirror — double-submit double-write, dropped fast resolve, timers past run end
…y takes

Law 4's enforcement seam was half-serialised. `attachTab` queued per tab
because the ownership check reads the projection while the append is a
round-trip; `detachTab` and `endRun`'s release loop read `ownerOf` and
appended off that queue entirely, so two ordinary sequences reached the
durable log.

A human closing a tab while its attach was still on the wire — the broker
can take seconds — found no owner, returned, and let the attach commit
behind it: the run then owned a destroyed tab permanently, listed by
`agentVisibleTabs` and focused by `promote()`. And a human close racing
`endRun`'s `run_ended` release had both sides read the owner before either
folded, writing one detachment to the append-only log twice.

Attach and detach now share ONE lane per tab, because the pairs that race
are mixed — two lanes would serialise each kind against itself and leave
both sequences as exposed as no queue at all. `endRun` keeps its membership
read outside the lane: it names the tabs to try, and the lane decides per
tab whether anything is still there to release.

Also: the coalescing window was a raw `setTimeout` with no handle kept, so
`dispose()` could not clear it. Shutdown ends every live run, so a terminal
append landing in the final 16 ms fanned out into listeners one line after
the tray was destroyed. It is on the injected timer now, tracked, and
released with the horizons.

Tests: forced-race arms for both sequences at the durable store, a pin that
a global FIFO lock fails, and a dispose arm that drives a change into the
window.
…ayload

The tests/ debt ratchet type-checks this arm under a looser program than the
root type-check does, where `RunEventInput.payload` is optional.
…d-endrun

fix(studio): detachTab and endRun must take the per-tab queue attachTab relies on
studio_open's agent-supplied startUrl reached the tab.attached payload
verbatim, so a magic link's token landed in an append-only log with no
prune path and was served over GET /v1/runs/{id}/events and the SSE tail
to every client past the REST gate. The audit path already strips the
query unconditionally, on the same rationale: a secret can ride the query
of any page and no classifier catches them all.

Narrow at the event constructor rather than the call site, so every path
that reaches attachTab with a url is covered. The rule moves to one
url-policy module shared with the audit path, and its parse-failure
branch now cuts at the first ?/# instead of returning the string whole —
an unparseable url is not a harmless one.
…-s-startur

fix(studio): store only the origin of an attach url in the run log (SD1 exit-7)
…rship

A run condensed at boot is condensed because it is LONG, and a live long run is
exactly the one that emits next — so the first live envelope after boot routed
through adopt(replace) to a readLog from seq 0 with no total cap, and then to a
retain with no projection, so nothing condensed it again. At a hundred thousand
envelopes that is two hundred sequential broker round-trips, a JSON.parse per
envelope on the thread that paints, and every envelope retained for the rest of
the run's life: the whole boot bound handed back one envelope after boot.

The bound is re-applied on replay, on the same quantity the store decides it on.
A condensed run still past it is answered the way boot answered it — the store's
own projection, one round-trip and a few hundred bytes — and stays condensed. A
condensed run UNDER it is materialized, because a short run at the end of a full
boot page is condensed by the page's budget rather than by its own size and is
better off folding its next envelope for free.

The clock-driven status re-read of a condensed run is now bounded to once per
tail: with the run staying condensed, asking again at the same tail cannot learn
anything the last answer did not carry, and a store that disagreed about an
expiring card by a hair would otherwise narrow and ask forever.

Same fold seam: ownerOf walked every run and projected it to ask whether that run
held the tab, and ipc-host's state() asks it once per tab per broadcast — tabs ×
runs × tabs-per-run on the thread that paints, growing with every run the machine
had ever seen. It now answers from a tab-to-run index maintained on the fold by
projectRun's own two rules and rebuilt from the projection whenever a log is
replaced.
KnockOutEZ and others added 27 commits August 28, 2026 11:22
A decline arrived back through the shared promise as a bare null, so it was
remembered for 60s exactly like a launch that ran and timed out. The commonest
decline is the substrate presence TTL disagreeing with an uncached record read,
which a reinstall resolves in the next second — so a fixed machine was locked
out for the rest of the window. The pass now reports whether anything was
started, and only that case is remembered.

memoMs 0 is honoured at the read as well as the write, so the documented
escape hatch is not silently declined by another caller's window.
normalizeLaunch throws on a thenable or on an object with no boolean started,
instead of reading undefined as a decline and skipping the poll.
…-memo-reme

fix(studio): memoize only a launch that was actually attempted
…tracts

Three arms, all red on this tip: normalizeLaunch keeps a truthy non-function
`failed` (the `??` merge only replaces nullish), the poll's first `failed()`
call sits outside the try/catch so that shape rejects out of a path documented
never to throw, and defaultLaunch's env spread leaves two hidden-flag keys when
the parent carried another casing.
normalizeLaunch now rejects a defined non-function `failed` the same way it
rejects a non-boolean `started`. The `??` merge only replaced a nullish probe,
so a truthy non-function reached the handle poll, and the poll's first
`failed()` call sits a tick past the try/catch that guards the launcher: the
TypeError rejected the shared promise, the memo mapper had no rejection branch,
and `await inFlight` handed it to studioBridgeFetch, which awaits with no catch.
Thrown at the seam instead, it is the synchronous throw the try/catch already
turns into a clean decline — so the never-throws contract holds and the
non-memo rule for an unattempted launch still applies.

defaultLaunch strips the inherited hidden flag in any casing before re-adding
the canonical one, mirroring runStudio's mechanism. The env spread collapses
win32's case-insensitive proxy into a plain object, so an inherited
`wigolo_studio_hidden=0` left two keys and the child resolved whichever the OS
handed back first — a window could surface for a session that exists only to
serve the agent.
…-honours-i

fix(studio): auto-launch honours its own seam contracts — validate LaunchOutcome.failed, strip HIDDEN case-insensitively
…type contracts

The injected-source seam never passes `readSubstrateManifest`, so the type
established there does not hold for a manifest handed in through `deps.source` —
the seam a published channel re-enters with a manifest of its own. The
pre-flight guards answer from outside the try, and `staysInsideItsDirectory`
calls `isAbsolute()`, which throws `ERR_INVALID_ARG_TYPE` on a non-string
straight past a documented NEVER THROWS into `warmup`'s unguarded await.

The discriminator is settle-vs-reject rather than an outcome string, because
that is the property the contract is about: an assertion on `outcome` alone
cannot tell a refusal apart from a crash.

`readSubstrateRecord` checks truthiness where `readSubstrateManifest`
establishes `typeof === 'string'`, so a numeric `version` reads back PRESENT
and flows into the detail text, the logs and every doctor row downstream.

Red against unfixed source: three executable-shape arms reject, and the numeric
record version reads back present.
…the try

`8f465f67` moved the join inside the try so NEVER THROWS would be true of the
function's shape rather than of its guards, and then left the guards themselves
outside — where `staysInsideItsDirectory` calls `isAbsolute()`, which throws
`ERR_INVALID_ARG_TYPE` on a non-string. The class reopened one line higher, on
the one seam a manifest can still arrive mistyped through: `deps.source`, which
never passes `readSubstrateManifest` and which is where a published channel
plugs in. `warmup` awaits this call unguarded on the strength of the contract,
so the throw took the browser, model and search phases with it.

Both guards now sit inside the try, and the type is established at the seam the
way `readSubstrateManifest` establishes it for a manifest on disk — so the
refusal names its own reason instead of surfacing as a generic install failure.
What the guards refuse is unchanged; only where they answer from.

Same file, the sibling asymmetry: `readSubstrateRecord` checked truthiness
where `readSubstrateManifest` establishes `typeof === 'string'`. The other two
fields threw their way to null through its own catch, so the shape that got
through was a numeric `version`, reading back as PRESENT and flowing into the
detail text, the log line and every doctor row downstream as a number.
…trate-pre

fix(studio): acquireSubstrate's pre-flight guards answer from inside the try
…casing

Both arms are red against the source in the parent commit.

The plant helper writes a forged acquisition record at `substrateRoot()`'s
ambient answer and its caller `rmSync(root, { recursive: true, force: true })`s
that directory. `substrateRoot()` follows the memoized data dir, and
`tests/setup.ts` repoints `WIGOLO_DATA_DIR` only when unset — deliberately — so
a developer who exported the documented knob and ran `npm test` lost their real
substrate root while the suite reported 37/37 and exited 0. The new arm forces
the export, proves the forcing took effect via `resetConfig()`, and asserts the
plant lands in the per-test dir with the decoy's file surviving both the plant
and the cleanup. It reds naming the decoy root as the plant target.

The off switch compares exact-match against two lowercase literals, so `False`,
`FALSE` and `Off` all read as "not disabled" and the substrate spawns detached
and hidden against the operator's stated intent. Eight spellings plus two
anti-vacuity arms — an unset variable and a set-but-unrecognised one must both
still launch, or a bare `return null` satisfies the whole suite.
…e-insensitively

The plant helper repoints `WIGOLO_DATA_DIR` at its per-test temp dir and drops
the memoized config, so `substrateRoot()` — which takes no data dir, because
`studioLaunchable()` reaches it through `substratePresent()` — answers inside a
directory this file created. Its caller's recursive remove then targets that,
not a root the suite never made. The containment check stays as the assert that
the repoint took effect: a dropped `resetConfig()` or a config that stopped
reading the var would silently restore the original defect, and the refusal is
what fails loudly instead. It also moves the SIGKILL residue — a forged record
naming a real on-disk executable — out of the path the launcher reads.

The off switch was exact-match against two lowercase literals, so `False`,
`FALSE` and `Off` read as "not disabled" and the substrate spawned detached and
hidden against the operator's stated intent. It now matches lowercased and
trimmed against `0`/`false`/`off`. `envBool` (src/config.ts) is the house
comparison and normalises for the same reason; the two `WIGOLO_STUDIO_HIDDEN`
fixes this phase were the env KEY's casing, which no value comparison inherits.

Both widenings past the house set — `off`, and the trim — are recorded with
their reversal conditions as A-189-1 and A-189-2.
…-exported

fix(studio): auto-launch — confine the substrate plant, and read the off switch case-insensitively
…o-invalidation contracts

Six arms, all red on tip: a throwing failed/sleep/readHandleFn rejects out of ensureStudioRunning; a falsy non-false launcher answer reads as started and pays the full poll budget; a handle observed inside the poll leaves the negative memo standing.
…memo-invalidation contracts

The handle poll calls failed(), the handle reader and sleep() a tick past the try
that guarded the launcher, and the top-of-call read sat outside every try, so a
throw from any of them rejected out of ensureStudioRunning into a caller that
awaits with no catch — and skipped the memo write, making the fan-out re-pay the
poll budget per URL. Both are guarded now: the top read falls through to the
launch path, and a poll fault resolves as an attempt that published nothing.

normalizeLaunch ended in "anything else means started", so null/0/''/NaN bought
a 30s poll for a process nobody launched plus a 60s lockout. The legal answers
are now enumerated and anything else throws at the seam, which the launcher
catch already turns into a zero-tick decline.

A handle observed inside the poll now clears the negative memo, which only the
top-of-call read did — so a substrate that came up on the second attempt left a
stale window that declined the next caller after its session ended.
…-seam-cont

fix(studio): auto-launch seam contract holes — throwing probe rejects past NEVER-THROWS, falsy launch reads as started, poll-observed handle leaves stale memo
WIGOLO_SKIP_PREPARE is gated on bare truthiness, so =0/=false/=off all mean
SKIP — the inverse of the operator's intent, and the opposite of the rule
autoLaunchDisabled establishes one file over for the same class. Two arms:
the off spellings must BUILD and must not print the skip line, and the
values CI actually sets must still skip and still print it.
…ng on them

The opt-out read any non-empty value as "skip", so a leaked
WIGOLO_SKIP_PREPARE=0 made a local npm ci exit 0 with an unbuilt tree and
surfaced weeks later as module-not-found. Normalize the way the phase's own
rule does: trim, lowercase, and treat 0/false/off as off. Every value CI sets
still skips, and still prints the line those legs grep.
…prepare-is

fix(build): WIGOLO_SKIP_PREPARE honours 0/false/off instead of skipping on them
…ressed root npm publish

Red against the tip classifier: its alternation enumerates install verbs, so a
planted un-suppressed `npm publish` at the repo root is classified as not
prepare-firing and the sweep reports an empty set. The arm plants the shape in a
scratch workflow directory under $TMPDIR and runs the real parser over it,
because every other arm in this block can only agree with the workflows that
already exist.
…m publish

`npm publish` packs, and packing fires the root `prepare` hook, so the release
publish step is a root prepare-firing step — but the sweep's alternation listed
only install verbs, so the step never entered the affected set and the list a
reader takes as "every root step that fires the hook" omitted it.

Adds `publish` to the alternation and the release publish step to the expected
set. Deleting its `WIGOLO_SKIP_PREPARE` now reds two arms naming the step
instead of one, and the enumeration no longer implies the publish path is
exempt. Corrects the pack/publish block's claim that the install sweep cannot
see the shape. No change to the workflow.
…-step-s-pr

fix(ci): the install-register classifier and its enumeration cover npm publish
…ainment escapes

Both shapes reach the record that backs the prompt-less substrate spawn, and
both pass every containment check the module has: the symlinked root because
each side resolves through the link, the nested path because it really is
inside the root. Red at tip, with controls that the escapes are real and that
a linked ANCESTOR of the root still reads.
… not root/<version>

The record backs a prompt-less spawn, and its docstring justified that by
saying the acquirer writes path as substrateRoot()/<version> and nothing else.
Two shapes made that false. A root that is itself a link resolved along with
the candidate, so every containment comparison agreed for a tree outside the
data dir; readSubstrateRecord and acquireSubstrate now both refuse it via
lstat, which is blind to a linked ancestor and so keeps the macOS
/var -> /private/var shape working. And path was only required to be somewhere
under the root; it must now resolve to join(root, version), with version
required to be one directory name so the join cannot be '.' or a traversal.

Two fixtures planted a path the acquirer cannot produce and are corrected.
…he SEC-2 arm

The nested-path arm was green against a build with the equality deleted: with
no real <root>/<version> on disk the refusal came from the expected location
being absent, which is the weaker half of the rule. It now plants a legitimate
install alongside the decoy, so both sides resolve and only the inequality
refuses; the absent-location case keeps its own arm.

Dropping isInside(raw.path, root) likewise changed no test — the equality
dominates it and the executable check is what refuses a linked <root>/<version>
— so it is removed rather than left to be trusted.
…, not on the platform

The nine skipIf(win32) arms cited 'creating a symlink needs elevation', which
this same file contradicts by planting an unskipped junction. The accurate rule
is about link TYPE: a junction needs no elevation but is a directory link that
Node normalises to an absolute target, and the arms driving acquireSubstrate
have their plant re-created by cpSync with no type hint, so a junction does not
survive the copy.

The two record-level arms need no copy, so they now plant junctions and run
unconditionally on all three shipped OSes; the executable arm links bin/ rather
than bin/run to make that possible. The seven copy-driven arms ask the machine
whether it can make ordinary links instead of assuming Windows cannot, so they
run on a Windows runner with Developer Mode. Un-skipping them blindly would
have been worse than the skip: four expect outcome 'failed', which is also what
a cpSync EPERM produces.

A probe arm keeps a silently-false capability answer from vacating the family.
…ontainment

fix(studio): substrate containment defeated by a symlinked root and a non-version path (PX0 exit SEC-1+SEC-2)
The absolute-link containment arm pinned the refusal with /bin\/run/, but the
message names the offending entry via relative(), so the separator is the
host's: bin/run on POSIX, bin\run on win32. Both Windows jobs at public tip
5fc9287 were red on this assertion while the refusal it exists to pin had
fired correctly; every other arm passed on win32.

Match either separator. The arm still requires the message to name the entry,
so no kill is lost: stripping the entry from the refusal reds this assertion
and nothing else.
…parator-in

fix(test): match the substrate refusal separator-agnostically on win32
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 787 files, which is 487 over the limit of 300.

To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7125e022-9b36-4fe6-91ce-0929ab5f095d

📥 Commits

Reviewing files that changed from the base of the PR and between c6ad447 and 6c33d69.

⛔ Files ignored due to path filters (7)
  • assets/sponsors/testmu-ai-dark.svg is excluded by !**/*.svg
  • assets/sponsors/testmu-ai.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
  • site/public/sponsors/testmu-ai-dark.svg is excluded by !**/*.svg
  • site/public/sponsors/testmu-ai.svg is excluded by !**/*.svg
  • benchmarks/visual/corpus/l-det.json.gz is excluded by !**/*.gz
  • tests/integration/__snapshots__/studio-flow-g1.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (787)
  • .github/FUNDING.yml
  • .github/workflows/agent-benchmark.yml
  • .github/workflows/binary-build.yml
  • .github/workflows/ci.yml
  • .github/workflows/docker-publish.yml
  • .github/workflows/extraction-benchmark.yml
  • .github/workflows/release.yml
  • .github/workflows/search-benchmark.yml
  • .github/workflows/site.yml
  • .github/workflows/star-chart.yml
  • .gitignore
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Dockerfile
  • README.md
  • SPONSORS.md
  • assets/blocks/claude-code/CLAUDE.md.block
  • benchmarks/search/fixtures/queries.json
  • benchmarks/search/fixtures/relevance.json
  • benchmarks/search/runner.ts
  • benchmarks/search/types.ts
  • docs/README.md
  • docs/cli.md
  • docs/configuration.md
  • docs/getting-started.md
  • docs/installation.md
  • docs/privacy-security.md
  • docs/rest-api.md
  • docs/sdks.md
  • docs/self-hosting.md
  • docs/tools.md
  • docs/troubleshooting.md
  • examples/one-shot-cli/README.md
  • examples/one-shot-cli/run.sh
  • examples/rest-curl/README.md
  • examples/rest-curl/demo.sh
  • examples/sdk-typescript-research/README.md
  • examples/shell-ndjson-pipeline/README.md
  • examples/shell-ndjson-pipeline/pipeline.sh
  • examples/vercel-ai-sdk-tools/README.md
  • examples/watch-changelog-webhook/README.md
  • examples/watch-changelog-webhook/watch.sh
  • package.json
  • packages/wigolo-vercel-ai-sdk/package.json
  • sdks/python/README.md
  • sdks/python/src/wigolo/__init__.py
  • sdks/python/src/wigolo/_aio.py
  • sdks/python/src/wigolo/_client.py
  • sdks/typescript/README.md
  • sdks/typescript/src/client.ts
  • sdks/typescript/src/index.ts
  • sdks/typescript/src/types.ts
  • site/public/robots.txt
  • site/src/app/go/[slug]/SponsorCard.tsx
  • site/src/app/go/[slug]/SponsorRedirect.module.css
  • site/src/app/go/[slug]/SponsorRedirect.tsx
  • site/src/app/go/[slug]/page.tsx
  • site/src/app/page.tsx
  • site/src/components/Footer.tsx
  • site/src/components/Sponsors.module.css
  • site/src/components/Sponsors.tsx
  • site/src/lib/goatcounter.ts
  • site/src/lib/sponsors.ts
  • src/agent/executor.ts
  • src/agent/pipeline.ts
  • src/cache/db.ts
  • src/cache/migrations/runner.ts
  • src/cache/sqlite-vec-store.ts
  • src/cache/store.ts
  • src/cli/agents/skills/receipts.ts
  • src/cli/config.ts
  • src/cli/daemon.ts
  • src/cli/doctor.ts
  • src/cli/help.ts
  • src/cli/index.ts
  • src/cli/shutdown.ts
  • src/cli/status.ts
  • src/cli/tui/actions/setup-status.ts
  • src/cli/tui/actions/verify-e2e.ts
  • src/cli/tui/status-format.ts
  • src/cli/tui/system-check.ts
  • src/cli/warmup.ts
  • src/config.ts
  • src/crawl/crawler.ts
  • src/crawl/url-utils.ts
  • src/daemon/health-check.ts
  • src/daemon/http-server.ts
  • src/daemon/proxy.ts
  • src/daemon/rest/dispatch.ts
  • src/daemon/rest/errors.ts
  • src/daemon/rest/firecrawl-compat.ts
  • src/daemon/rest/openapi.ts
  • src/daemon/rest/router.ts
  • src/extraction/brand-palette.ts
  • src/extraction/defuddle.ts
  • src/extraction/markdown.ts
  • src/extraction/v1/extract-provider.ts
  • src/extraction/v1/routed.ts
  • src/fetch/browser-acquire.ts
  • src/fetch/browser-pool.ts
  • src/fetch/browser-probe.ts
  • src/fetch/cdp-direct.ts
  • src/fetch/challenge-classify.ts
  • src/fetch/error-describe.ts
  • src/fetch/http-client.ts
  • src/fetch/playwright-tier.ts
  • src/fetch/router.ts
  • src/fetch/stealth.ts
  • src/fetch/tls-tier.ts
  • src/index.ts
  • src/instructions.ts
  • src/integrations/cloud/llm/anthropic.ts
  • src/integrations/cloud/llm/gemini.ts
  • src/integrations/cloud/llm/groq.ts
  • src/integrations/cloud/llm/openai.ts
  • src/logger.ts
  • src/repl/commands/crawl.ts
  • src/repl/commands/extract.ts
  • src/repl/commands/fetch.ts
  • src/research/brief.ts
  • src/research/pipeline.ts
  • src/research/synthesis-local.ts
  • src/research/synthesize.ts
  • src/search/answer-synthesis.ts
  • src/search/content-fetch.ts
  • src/search/core/brand-collision.ts
  • src/search/core/core-provider.ts
  • src/search/core/engine-base.ts
  • src/search/core/orchestrator.ts
  • src/search/core/rerank-fold.ts
  • src/search/engines/bing.ts
  • src/search/engines/duckduckgo.ts
  • src/search/engines/mojeek.ts
  • src/search/evidence.ts
  • src/search/find-similar.ts
  • src/search/find-similar/crawl-rank.ts
  • src/search/find-similar/mode.ts
  • src/search/highlights.ts
  • src/search/hybrid/router.ts
  • src/search/hybrid/signals.ts
  • src/search/language-filter.ts
  • src/search/tokens.ts
  • src/search/truncate.ts
  • src/search/validator.ts
  • src/server.ts
  • src/server/search-response.ts
  • src/server/tool-schemas.ts
  • src/tools/agent.ts
  • src/tools/cache.ts
  • src/tools/crawl.ts
  • src/tools/diff.ts
  • src/tools/extract.ts
  • src/tools/fetch.ts
  • src/tools/find-similar.ts
  • src/tools/research.ts
  • src/types.ts
  • src/util/abort.ts
  • src/util/packaged.ts
  • src/watch/scheduler.ts
  • src/watch/ssrf.ts
  • src/watch/store.ts
  • tests/e2e/doctor-clean-exit.e2e.test.ts
  • tests/e2e/init-command.e2e.test.ts
  • tests/integration/build-output.test.ts
  • tests/integration/fetch-pdf.test.ts
  • tests/integration/fetch-router-tuning.test.ts
  • tests/integration/fetch-site-data.test.ts
  • tests/integration/instructions-v3.test.ts
  • tests/integration/llm-fallback-e2e.test.ts
  • tests/integration/repl-e2e.test.ts
  • tests/integration/rest-api.test.ts
  • tests/integration/rest-firecrawl-compat.test.ts
  • tests/integration/rest-tools.test.ts
  • tests/integration/schema-extract-pipeline.test.ts
  • tests/integration/search-v1-pipeline.test.ts
  • tests/integration/warmup-force.test.ts
  • tests/setup.ts
  • tests/unit/cache/antibot-clearance.test.ts
  • tests/unit/cache/db-vec-packaged.test.ts
  • tests/unit/cache/db.test.ts
  • tests/unit/cache/migrations-runner.test.ts
  • tests/unit/cache/store-search-key.test.ts
  • tests/unit/cache/store.test.ts
  • tests/unit/cli/agents/skills/receipts.test.ts
  • tests/unit/cli/daemon.test.ts
  • tests/unit/cli/doctor-tls-tier.test.ts
  • tests/unit/cli/doctor.test.ts
  • tests/unit/cli/help-completeness.test.ts
  • tests/unit/cli/init.test.ts
  • tests/unit/cli/status.test.ts
  • tests/unit/cli/tui/status-format.test.ts
  • tests/unit/cli/tui/system-check.test.ts
  • tests/unit/cli/warmup-setup-json-contract.test.ts
  • tests/unit/config-persistence.test.ts
  • tests/unit/daemon/firecrawl-compat.test.ts
  • tests/unit/daemon/health-check.test.ts
  • tests/unit/daemon/http-server.test.ts
  • tests/unit/daemon/proxy.test.ts
  • tests/unit/daemon/rest-dispatch.test.ts
  • tests/unit/daemon/rest-errors.test.ts
  • tests/unit/daemon/rest-openapi.test.ts
  • tests/unit/daemon/rest-router.test.ts
  • tests/unit/embedding/embed.test.ts
  • tests/unit/extraction/llm-fallback.test.ts
  • tests/unit/extraction/markdown.test.ts
  • tests/unit/fetch/browser-acquire.test.ts
  • tests/unit/fetch/browser-pool.challenge.test.ts
  • tests/unit/fetch/browser-pool.channel.test.ts
  • tests/unit/fetch/browser-pool.stealth.test.ts
  • tests/unit/fetch/browser-pool.timeout.test.ts
  • tests/unit/fetch/cdp-direct-egress-policy.test.ts
  • tests/unit/fetch/cdp-direct-fetch.test.ts
  • tests/unit/fetch/challenge-classify.test.ts
  • tests/unit/fetch/challenge-status-distinguishable.test.ts
  • tests/unit/fetch/router-antibot-domains.test.ts
  • tests/unit/fetch/router-browser-acquire.test.ts
  • tests/unit/fetch/router-clearance.test.ts
  • tests/unit/fetch/router-escalation-tuning.test.ts
  • tests/unit/fetch/router-escape-hatch.test.ts
  • tests/unit/fetch/router-mode.test.ts
  • tests/unit/fetch/router-politeness.test.ts
  • tests/unit/fetch/router-reddit-api.test.ts
  • tests/unit/fetch/router-tls.test.ts
  • tests/unit/fetch/router.test.ts
  • tests/unit/fetch/stealth.test.ts
  • tests/unit/fetch/tls-tier.test.ts
  • tests/unit/instructions-v3.test.ts
  • tests/unit/instructions.test.ts
  • tests/unit/integrations/llm-runner.test.ts
  • tests/unit/logger.test.ts
  • tests/unit/mcp-description-budget.test.ts
  • tests/unit/package-deps.test.ts
  • tests/unit/plugins/validate.test.ts
  • tests/unit/repl/commands/crawl.test.ts
  • tests/unit/repl/commands/extract.test.ts
  • tests/unit/repl/commands/fetch.test.ts
  • tests/unit/repl/formatters.test.ts
  • tests/unit/research/brief.test.ts
  • tests/unit/research/render-brief.test.ts
  • tests/unit/research/synthesis-local.test.ts
  • tests/unit/research/synthesize.test.ts
  • tests/unit/search/answer-synthesis.test.ts
  • tests/unit/search/brand-collision-warning.test.ts
  • tests/unit/search/core/rerank-fold.test.ts
  • tests/unit/search/degraded-recovery.test.ts
  • tests/unit/search/engine-throttle.test.ts
  • tests/unit/search/evidence-default.test.ts
  • tests/unit/search/evidence.test.ts
  • tests/unit/search/find-similar.test.ts
  • tests/unit/search/hybrid/router.test.ts
  • tests/unit/search/hybrid/signals.test.ts
  • tests/unit/search/passage-spans.test.ts
  • tests/unit/search/v1/v1-provider.test.ts
  • tests/unit/server/schema-registration.test.ts
  • tests/unit/server/search-response.test.ts
  • tests/unit/server/server-factory.test.ts
  • tests/unit/server/tool-schemas.test.ts
  • tests/unit/sponsors-links.test.ts
  • tests/unit/tools/cache-hybrid.test.ts
  • tests/unit/tools/cache.test.ts
  • tests/unit/tools/crawl.test.ts
  • tests/unit/tools/diff.test.ts
  • tests/unit/tools/extract.test.ts
  • tests/unit/tools/fetch.test.ts
  • tests/unit/tools/search-filtering.test.ts
  • tests/unit/watch/ssrf.test.ts
  • tests/unit/watch/store.test.ts
  • tsup.config.ts
  • vitest.config.ts
  • .github/workflows/base-guard.yml
  • .github/workflows/scrape-quality-live.yml
  • .github/workflows/scrape-quality.yml
  • benchmarks/profile/README.md
  • benchmarks/profile/boot-breakdown.ts
  • benchmarks/profile/boot-probe.mjs
  • benchmarks/profile/import-bisect.ts
  • benchmarks/profile/mcp-child.ts
  • benchmarks/profile/output/boot-breakdown.json
  • benchmarks/profile/output/import-bisect.json
  • benchmarks/profile/output/profile.json
  • benchmarks/profile/probe-hook.mjs
  • benchmarks/profile/probe-loader.mjs
  • benchmarks/profile/report.ts
  • benchmarks/profile/runner.ts
  • benchmarks/scrape-quality/baseline.json
  • benchmarks/scrape-quality/corpus-gate.ts
  • benchmarks/scrape-quality/drift-build.ts
  • benchmarks/scrape-quality/drift.ts
  • benchmarks/scrape-quality/firecrawl.ts
  • benchmarks/scrape-quality/fixtures/html/bls-cpi-timeseries.html
  • benchmarks/scrape-quality/fixtures/html/cloudflare-interstitial.html
  • benchmarks/scrape-quality/fixtures/html/epa-air-quality.html
  • benchmarks/scrape-quality/fixtures/html/epa-ghg-emissions.html
  • benchmarks/scrape-quality/fixtures/html/github-node-contributors.html
  • benchmarks/scrape-quality/fixtures/html/github-node-readme.html
  • benchmarks/scrape-quality/fixtures/html/github-react-repo.html
  • benchmarks/scrape-quality/fixtures/html/github-repo.html
  • benchmarks/scrape-quality/fixtures/html/mdn-status.html
  • benchmarks/scrape-quality/fixtures/html/nasa-global-temperature.html
  • benchmarks/scrape-quality/fixtures/html/wikidata-statistics.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-article.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-base64.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-browser-comparison.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-climate-attribution.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-climate-change.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-covid19.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-einstein.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-machine-learning.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-nobel-laureates.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-python.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-renewable-energy.html
  • benchmarks/scrape-quality/fixtures/html/wikipedia-sea-level-rise.html
  • benchmarks/scrape-quality/fixtures/html/worldbank-population.html
  • benchmarks/scrape-quality/fixtures/manifest.json
  • benchmarks/scrape-quality/fixtures/recipes/manifest.json
  • benchmarks/scrape-quality/flow-drift-run.ts
  • benchmarks/scrape-quality/flow-drift.ts
  • benchmarks/scrape-quality/inversion-sweep.ts
  • benchmarks/scrape-quality/live-lane.ts
  • benchmarks/scrape-quality/runner.ts
  • benchmarks/scrape-quality/score.ts
  • benchmarks/scrape-quality/types.ts
  • benchmarks/scrape-quality/visible-only-probe.ts
  • benchmarks/search/fixtures/baseline.json
  • benchmarks/search/fixtures/corpus-spec.ts
  • benchmarks/search/fixtures/generate.ts
  • benchmarks/search/responses/api-001.json
  • benchmarks/search/responses/api-002.json
  • benchmarks/search/responses/api-003.json
  • benchmarks/search/responses/api-004.json
  • benchmarks/search/responses/code-001.json
  • benchmarks/search/responses/code-002.json
  • benchmarks/search/responses/code-003.json
  • benchmarks/search/responses/code-004.json
  • benchmarks/search/responses/code-005.json
  • benchmarks/search/responses/code-006.json
  • benchmarks/search/responses/comparison-001.json
  • benchmarks/search/responses/comparison-002.json
  • benchmarks/search/responses/conceptual-001.json
  • benchmarks/search/responses/conceptual-002.json
  • benchmarks/search/responses/conceptual-003.json
  • benchmarks/search/responses/conceptual-004.json
  • benchmarks/search/responses/conceptual-005.json
  • benchmarks/search/responses/config-001.json
  • benchmarks/search/responses/config-002.json
  • benchmarks/search/responses/config-003.json
  • benchmarks/search/responses/debug-001.json
  • benchmarks/search/responses/debug-002.json
  • benchmarks/search/responses/docs-001.json
  • benchmarks/search/responses/docs-002.json
  • benchmarks/search/responses/docs-003.json
  • benchmarks/search/responses/docs-004.json
  • benchmarks/search/responses/docs-005.json
  • benchmarks/search/responses/docs-006.json
  • benchmarks/search/responses/docs-007.json
  • benchmarks/search/responses/docs-008.json
  • benchmarks/search/responses/error-001.json
  • benchmarks/search/responses/error-002.json
  • benchmarks/search/responses/error-003.json
  • benchmarks/search/responses/error-004.json
  • benchmarks/search/responses/error-005.json
  • benchmarks/search/responses/error-006.json
  • benchmarks/search/responses/multi-query-001.json
  • benchmarks/search/responses/multi-query-002.json
  • benchmarks/search/responses/recent-001.json
  • benchmarks/search/responses/recent-002.json
  • benchmarks/search/responses/security-001.json
  • benchmarks/search/responses/security-002.json
  • benchmarks/search/responses/security-003.json
  • benchmarks/search/responses/tutorial-001.json
  • benchmarks/search/responses/tutorial-002.json
  • benchmarks/truncation/runner.ts
  • benchmarks/visual/capture.ts
  • benchmarks/visual/corpus.ts
  • benchmarks/visual/gate-config.ts
  • benchmarks/visual/gate.ts
  • benchmarks/visual/mutants.ts
  • benchmarks/visual/runner.ts
  • benchmarks/visual/score-real.ts
  • benchmarks/visual/score.ts
  • benchmarks/visual/synth.ts
  • benchmarks/visual/urls.ts
  • contracts/studio-mcp/package.json
  • contracts/studio-mcp/src/harness.ts
  • contracts/studio-mcp/src/index.ts
  • contracts/studio-mcp/src/schemas.ts
  • contracts/studio-mcp/src/tool-names.ts
  • contracts/studio-mcp/src/wire.ts
  • contracts/studio-mcp/tests/schema-drift.test.ts
  • contracts/studio-mcp/tests/wire.test.ts
  • contracts/studio-mcp/tsconfig.build.json
  • contracts/studio-mcp/tsconfig.json
  • contracts/studio-mcp/vitest.config.ts
  • docs/export.md
  • examples/plugin-search-engine/index.d.mts
  • scripts/budget/measure.mjs
  • scripts/budget/protocol.mjs
  • scripts/check-no-nul.mjs
  • scripts/check-src-no-electron.mjs
  • scripts/check-typecheck-gate.mjs
  • scripts/derive-cache-budget.mjs
  • scripts/prepare-build.mjs
  • scripts/prune/ort-platforms.mjs
  • scripts/prune/ort-web-payload.mjs
  • scripts/prune/run.mjs
  • scripts/prune/wreq-binaries.mjs
  • scripts/studio/screencast-latency-spike.mjs
  • scripts/studio/viewer.html
  • scripts/typecheck-debt-ratchet.mjs
  • scripts/verify-better-sqlite3-prebuild.mjs
  • scripts/verify-llm-bundle-resolution.mjs
  • src/cache/artifact-registry.ts
  • src/cache/export-corpus.ts
  • src/cache/migrations/008-studio-artifacts.sql
  • src/cache/migrations/009-content-completeness.sql
  • src/cache/migrations/009-studio-artifacts-content.sql
  • src/cache/migrations/010-studio-audit.sql
  • src/cache/migrations/011-tool-audit.sql
  • src/cache/migrations/012-url-cache-content-hash-index.sql
  • src/cache/migrations/013-studio-flows.sql
  • src/cache/migrations/013-url-versions.sql
  • src/cache/migrations/014-url-versions-hash-index.sql
  • src/cache/migrations/015-url-cache-origin-authenticated.sql
  • src/cache/migrations/016-studio-runs.sql
  • src/cache/migrations/017-studio-run-cost.sql
  • src/cache/migrations/018-studio-runs-list-index.sql
  • src/cache/output-budget.ts
  • src/cache/vec-availability.ts
  • src/cache/version-read.ts
  • src/cache/version-store.ts
  • src/cli/export.ts
  • src/cli/flow.ts
  • src/cli/node-floor.ts
  • src/cli/studio.ts
  • src/daemon/rest/runs-owner.ts
  • src/daemon/rest/runs-store.ts
  • src/daemon/rest/runs.ts
  • src/daemon/rest/untrusted-mode.ts
  • src/daemon/studio-db-broker.ts
  • src/daemon/studio-dispatch.ts
  • src/daemon/studio-mcp-server.ts
  • src/extraction/completeness.ts
  • src/extraction/passthrough.ts
  • src/extraction/site-extractors/x.ts
  • src/extraction/visibility.ts
  • src/fetch/browser-driver.ts
  • src/fetch/browser-request-guard.ts
  • src/fetch/browser-tier.ts
  • src/fetch/driver-acquire.ts
  • src/fetch/studio-bridge.ts
  • src/fetch/tier-occupancy.ts
  • src/search/core/domain-filter-cause.ts
  • src/search/core/subject-anchor.ts
  • src/search/engines/challenge.ts
  • src/security/ssrf.ts
  • src/security/untrusted.ts
  • src/server/content-fence.ts
  • src/server/tool-audit.ts
  • src/server/tool-registry.ts
  • src/studio/act.ts
  • src/studio/agent-drive-gate.ts
  • src/studio/approvals.ts
  • src/studio/artifact-provider.ts
  • src/studio/audit-retention.ts
  • src/studio/audit.ts
  • src/studio/auth-origin-store.ts
  • src/studio/auth.ts
  • src/studio/authenticated-origin.ts
  • src/studio/auto-launch.ts
  • src/studio/bind.ts
  • src/studio/broker-stop.ts
  • src/studio/capture/artifacts.ts
  • src/studio/capture/handler.ts
  • src/studio/capture/hash.ts
  • src/studio/control-token.ts
  • src/studio/credential.ts
  • src/studio/escalation-counters.ts
  • src/studio/event-queue.ts
  • src/studio/extract-set.ts
  • src/studio/flow/record.ts
  • src/studio/flow/resolve-step.ts
  • src/studio/flow/run.ts
  • src/studio/flow/store.ts
  • src/studio/handle.ts
  • src/studio/handoff.ts
  • src/studio/human-solve-bridge.ts
  • src/studio/index.ts
  • src/studio/input-events.ts
  • src/studio/layout/harvest.ts
  • src/studio/layout/signature.ts
  • src/studio/login-capture.ts
  • src/studio/mark/generalize.ts
  • src/studio/mark/heal.ts
  • src/studio/mark/inspect.ts
  • src/studio/mark/pick.ts
  • src/studio/mark/store.ts
  • src/studio/mark/target.ts
  • src/studio/metrics.ts
  • src/studio/nav-epoch.ts
  • src/studio/nav-policy.ts
  • src/studio/nav.ts
  • src/studio/observe.ts
  • src/studio/origin-budget.ts
  • src/studio/paths.ts
  • src/studio/perception/diff.ts
  • src/studio/perception/id.ts
  • src/studio/perception/resolve.ts
  • src/studio/perception/snapshot.ts
  • src/studio/perception/spill.ts
  • src/studio/perception/vision.ts
  • src/studio/pre-grant.ts
  • src/studio/profile-store.ts
  • src/studio/registry.ts
  • src/studio/risk.ts
  • src/studio/run-bus.ts
  • src/studio/run-store.ts
  • src/studio/session-browser.ts
  • src/studio/session-control.ts
  • src/studio/session-drive.ts
  • src/studio/session.ts
  • src/studio/studio-fetch.ts
  • src/studio/substrate-acquire.ts
  • src/studio/synthesize.ts
  • src/studio/tool-provider.ts
  • src/tools/session-target.ts
  • tests/fixtures/electron-quarantine/electron-host.mjs
  • tests/fixtures/extraction/github-issue-list-real.html
  • tests/fixtures/extraction/weblog-table-listing.html
  • tests/fixtures/pages/injection.html
  • tests/fixtures/studio/canvas.html
  • tests/fixtures/studio/heavy.html
  • tests/fixtures/studio/rerender.html
  • tests/fixtures/studio/webcomponents.html
  • tests/global-setup.ts
  • tests/helpers/app-archive.ts
  • tests/helpers/declared-fields.ts
  • tests/helpers/envelope-allowlist.ts
  • tests/helpers/envelope-fence.ts
  • tests/helpers/envelope-fixtures.ts
  • tests/helpers/envelope-leaves.ts
  • tests/helpers/fetch-result.ts
  • tests/helpers/migration-test-db.ts
  • tests/helpers/provider-isolation.ts
  • tests/helpers/untrusted-fence.ts
  • tests/integration/budget-tarball-gate.test.ts
  • tests/integration/compat-crawl-envelope-fence.test.ts
  • tests/integration/content-fence-dispatch.test.ts
  • tests/integration/crawl-cache-envelope-fence.test.ts
  • tests/integration/crawl-link-target-untrusted.test.ts
  • tests/integration/envelope-fence-invariant.test.ts
  • tests/integration/envelope-unfenced-by-default.test.ts
  • tests/integration/error-envelope-fence.test.ts
  • tests/integration/error-envelope-orientation.test.ts
  • tests/integration/extract-table-row-keys.test.ts
  • tests/integration/extraction/table-cell-links.test.ts
  • tests/integration/fetch/browser-redirect-ssrf-backstop.test.ts
  • tests/integration/fetch/browser-redirect-ssrf.test.ts
  • tests/integration/rest-runs.test.ts
  • tests/integration/scrape-referee-visible-only.test.ts
  • tests/integration/search-ranking-notice.test.ts
  • tests/integration/studio-auto-deny-fanout.test.ts
  • tests/integration/studio-broker-run-reads.test.ts
  • tests/integration/studio-flow-g1.test.ts
  • tests/integration/studio-flow-g2.test.ts
  • tests/integration/studio-flow-g3.test.ts
  • tests/integration/studio-observe-seam.test.ts
  • tests/integration/studio-perception.test.ts
  • tests/integration/studio-runs-host-child.mjs
  • tests/integration/studio-runs-proxy.test.ts
  • tests/integration/studio-session-target.test.ts
  • tests/integration/success-envelope-inband-fence.test.ts
  • tests/integration/tool-audit-dispatch.test.ts
  • tests/integration/unfenced-siblings.test.ts
  • tests/integration/visual-ldet-gate.test.ts
  • tests/net-fence.ts
  • tests/security-regression.test.ts
  • tests/unit/agent/executor-unretrieved-source.test.ts
  • tests/unit/agent/pipeline-all-refused.test.ts
  • tests/unit/agent/pipeline-containment.test.ts
  • tests/unit/base-guard-workflow.test.ts
  • tests/unit/benchmarks/firecrawl-keypool.test.ts
  • tests/unit/benchmarks/scrape-quality-inversion.test.ts
  • tests/unit/benchmarks/scrape-quality-live.test.ts
  • tests/unit/benchmarks/scrape-quality.test.ts
  • tests/unit/benchmarks/search-instrument.test.ts
  • tests/unit/budget-protocol.test.ts
  • tests/unit/cache/artifact-bootstrap-diagnostic.test.ts
  • tests/unit/cache/artifact-registry.test.ts
  • tests/unit/cache/bundle-provider-inlining.test.ts
  • tests/unit/cache/clear-evicts-vectors.test.ts
  • tests/unit/cache/core-artifact-neutrality.test.ts
  • tests/unit/cache/db-vec-app-archive.test.ts
  • tests/unit/cache/export-corpus.test.ts
  • tests/unit/cache/migration-test-db.test.ts
  • tests/unit/cache/migrations-merge-union.test.ts
  • tests/unit/cache/origin-authenticated.test.ts
  • tests/unit/cache/output-budget.test.ts
  • tests/unit/cache/studio-artifacts-009-content-fts.test.ts
  • tests/unit/cache/studio-artifacts-migration.test.ts
  • tests/unit/cache/studio-flows-013-migration.test.ts
  • tests/unit/cache/studio-runs-016-migration.test.ts
  • tests/unit/cache/url-versions-read.test.ts
  • tests/unit/cache/url-versions.test.ts
  • tests/unit/cache/vec-availability.test.ts
  • tests/unit/cli/agents/skills/receipts-lock-eperm.test.ts
  • tests/unit/cli/config-origin-override.test.ts
  • tests/unit/cli/config-prune-audit.test.ts
  • tests/unit/cli/doctor-browser-tier.test.ts
  • tests/unit/cli/doctor-wreq-probe.test.ts
  • tests/unit/cli/export-command.test.ts
  • tests/unit/cli/flow-command.test.ts
  • tests/unit/cli/node-floor.test.ts
  • tests/unit/cli/studio-teardown.test.ts
  • tests/unit/cli/studio.test.ts
  • tests/unit/cli/warmup-browser-tier.test.ts
  • tests/unit/cli/warmup-driver-acquire.test.ts
  • tests/unit/cli/warmup-tier-acquisition.test.ts
  • tests/unit/crawl/link-edge-key.test.ts
  • tests/unit/daemon/proxy-roundtrip.test.ts
  • tests/unit/daemon/rest-compat-untrusted.test.ts
  • tests/unit/daemon/rest-runs-owner-cache.test.ts
  • tests/unit/daemon/rest-runs-proxy.test.ts
  • tests/unit/daemon/rest-runs.test.ts
  • tests/unit/daemon/rest-status-from-producer.test.ts
  • tests/unit/daemon/rest-table-row-keys.test.ts
  • tests/unit/daemon/rest-untrusted-mode.test.ts
  • tests/unit/daemon/rest-untrusted-representation.test.ts
  • tests/unit/daemon/serve-lockedb-closure.test.ts
  • tests/unit/daemon/studio-db-broker-bounds.test.ts
  • tests/unit/daemon/studio-dispatch.test.ts
  • tests/unit/daemon/studio-mcp-server.test.ts
  • tests/unit/daemon/token-exchange.test.ts
  • tests/unit/dist-rebuild-serialization.test.ts
  • tests/unit/electron-quarantine.test.ts
  • tests/unit/embedding/embed-frame-budget.test.ts
  • tests/unit/extraction/brand-palette-decoder-absent.test.ts
  • tests/unit/extraction/completeness.test.ts
  • tests/unit/extraction/defuddle-async-switch.test.ts
  • tests/unit/extraction/defuddle-no-third-party-egress.test.ts
  • tests/unit/extraction/passthrough.test.ts
  • tests/unit/extraction/site-extractors/x.test.ts
  • tests/unit/extraction/v1/passthrough.test.ts
  • tests/unit/extraction/visibility.test.ts
  • tests/unit/fetch/browser-driver.test.ts
  • tests/unit/fetch/browser-pool.screenshot-bounds.test.ts
  • tests/unit/fetch/browser-request-guard.test.ts
  • tests/unit/fetch/browser-tier.test.ts
  • tests/unit/fetch/cdp-direct-companion-gate.test.ts
  • tests/unit/fetch/cdp-direct-log-hygiene.test.ts
  • tests/unit/fetch/cdp-direct-reachability.test.ts
  • tests/unit/fetch/driver-acquire.test.ts
  • tests/unit/fetch/http-client-redirect-ssrf.test.ts
  • tests/unit/fetch/router-companion-rung.test.ts
  • tests/unit/fetch/router-ssrf.test.ts
  • tests/unit/fetch/router-tier-occupancy.test.ts
  • tests/unit/fetch/router.studio-bridge.test.ts
  • tests/unit/fetch/stage-error-guard.test.ts
  • tests/unit/fetch/studio-bridge.test.ts
  • tests/unit/fetch/system-browser-presence.test.ts
  • tests/unit/fetch/tier-occupancy.test.ts
  • tests/unit/harness/data-dir-isolation.test.ts
  • tests/unit/harness/test-home-lifecycle.test.ts
  • tests/unit/instructions-param-policy.test.ts
  • tests/unit/integrations/llm-lazy-load.test.ts
  • tests/unit/net-fence.test.ts
  • tests/unit/ort-platform-prune.test.ts
  • tests/unit/ort-web-payload-prune.test.ts
  • tests/unit/prepare-build.test.ts
  • tests/unit/research/pipeline-content-gate-waiver.test.ts
  • tests/unit/research/pipeline-studio-source.test.ts
  • tests/unit/research/pipeline-unretrieved-source.test.ts
  • tests/unit/research/synthesize-containment.test.ts
  • tests/unit/search/content-fetch-refused.test.ts
  • tests/unit/search/domain-filter-cause.test.ts
  • tests/unit/search/engines/challenge-detection.test.ts
  • tests/unit/search/find-similar-shell.test.ts
  • tests/unit/search/find-similar-studio-fts.test.ts
  • tests/unit/search/find-similar-studio-leak.test.ts
  • tests/unit/search/language-filter-accuracy.test.ts
  • tests/unit/search/searxng-validatelinks-reachability.test.ts
  • tests/unit/search/subject-collision.test.ts
  • tests/unit/search/tokens-accuracy.test.ts
  • tests/unit/search/truncate-boundary.test.ts
  • tests/unit/search/validator-ssrf.test.ts
  • tests/unit/security/d8a-2-truncation-fence.test.ts
  • tests/unit/security/d8a-synthesis-fence.test.ts
  • tests/unit/security/ssrf.test.ts
  • tests/unit/security/untrusted-nonce.test.ts
  • tests/unit/security/untrusted.test.ts
  • tests/unit/security/v8-json-parse-echo-window.test.ts
  • tests/unit/server/content-fence-seams.test.ts
  • tests/unit/server/content-fence-table-keys.test.ts
  • tests/unit/server/content-fence.test.ts
  • tests/unit/server/tool-audit.test.ts
  • tests/unit/server/tool-registry.test.ts
  • tests/unit/studio/act.test.ts
  • tests/unit/studio/agent-drive-gate.test.ts
  • tests/unit/studio/approvals.test.ts
  • tests/unit/studio/audit-retention.test.ts
  • tests/unit/studio/audit.test.ts
  • tests/unit/studio/auth.test.ts
  • tests/unit/studio/authenticated-origin.test.ts
  • tests/unit/studio/auto-launch.test.ts
  • tests/unit/studio/bind.test.ts
  • tests/unit/studio/broker-dispatch.test.ts
  • tests/unit/studio/broker-run-store.test.ts
  • tests/unit/studio/broker-stop.test.ts
  • tests/unit/studio/broker-transport.test.ts
  • tests/unit/studio/capture/artifacts-onartifact-hook.test.ts
  • tests/unit/studio/capture/artifacts.test.ts
  • tests/unit/studio/capture/handler-onartifact.test.ts
  • tests/unit/studio/capture/handler.test.ts
  • tests/unit/studio/capture/hash.test.ts
  • tests/unit/studio/capture/list-session-artifacts.test.ts
  • tests/unit/studio/capture/screenshot.test.ts
  • tests/unit/studio/capture/studio-embed-jobs-observability.test.ts
  • tests/unit/studio/control-token.test.ts
  • tests/unit/studio/d9-seam.test.ts
  • tests/unit/studio/escalation-counters.test.ts
  • tests/unit/studio/event-queue.test.ts
  • tests/unit/studio/extract-set.test.ts
  • tests/unit/studio/fixtures/run-store-exit-drain-child.mjs
  • tests/unit/studio/flow/record-broker.test.ts
  • tests/unit/studio/flow/record.test.ts
  • tests/unit/studio/flow/resolve-step.test.ts
  • tests/unit/studio/flow/run.test.ts
  • tests/unit/studio/flow/slots.test.ts
  • tests/unit/studio/flow/store.test.ts
  • tests/unit/studio/generalize.test.ts
  • tests/unit/studio/handle.test.ts
  • tests/unit/studio/handoff-signal-change.test.ts
  • tests/unit/studio/handoff.test.ts
  • tests/unit/studio/heal.test.ts
  • tests/unit/studio/human-solve-bridge.test.ts
  • tests/unit/studio/inspect.test.ts
  • tests/unit/studio/installed-substrate-exists.test.ts
  • tests/unit/studio/layout/harvest.test.ts
  • tests/unit/studio/layout/inversion-probes.test.ts
  • tests/unit/studio/layout/serialize.test.ts
  • tests/unit/studio/layout/signature.test.ts
  • tests/unit/studio/login-capture.test.ts
  • tests/unit/studio/mark-store.test.ts
  • tests/unit/studio/metrics.test.ts
  • tests/unit/studio/nav-policy.test.ts
  • tests/unit/studio/nav.test.ts
  • tests/unit/studio/observe.test.ts
  • tests/unit/studio/paths.test.ts
  • tests/unit/studio/perception/diff.test.ts
  • tests/unit/studio/perception/id.test.ts
  • tests/unit/studio/perception/resolve.test.ts
  • tests/unit/studio/perception/snapshot.test.ts
  • tests/unit/studio/perception/spill.test.ts
  • tests/unit/studio/perception/target.test.ts
  • tests/unit/studio/perception/vision.test.ts
  • tests/unit/studio/pick.test.ts
  • tests/unit/studio/pre-grant.test.ts
  • tests/unit/studio/profile-store.test.ts
  • tests/unit/studio/registry.test.ts
  • tests/unit/studio/risk.test.ts
  • tests/unit/studio/run-store-disk-projection.test.ts
  • tests/unit/studio/run-store-exit-drain.test.ts
  • tests/unit/studio/run-store-restart.test.ts
  • tests/unit/studio/run-store.test.ts
  • tests/unit/studio/s5-agent-drive.test.ts
  • tests/unit/studio/session-browser.test.ts
  • tests/unit/studio/session-control.test.ts
  • tests/unit/studio/session-drive.test.ts
  • tests/unit/studio/session.test.ts
  • tests/unit/studio/studio-fetch.test.ts
  • tests/unit/studio/substrate-acquire.test.ts
  • tests/unit/studio/synthesize.test.ts
  • tests/unit/studio/tool-provider.test.ts
  • tests/unit/tools/cache-check-changes-bound.test.ts
  • tests/unit/tools/cache-output-budget.test.ts
  • tests/unit/tools/cache-studio-union.test.ts
  • tests/unit/tools/fetch.content-type-passthrough.test.ts
  • tests/unit/tools/fetch.pool-saturated.test.ts
  • tests/unit/tools/fetch.screenshot-omitted.test.ts
  • tests/unit/tools/find-similar-include-web.test.ts
  • tests/unit/tools/research-local-citation-trust.test.ts
  • tests/unit/tools/research-studio-note-local-synth-trust.test.ts
  • tests/unit/tools/research-studio-note-trust.test.ts
  • tests/unit/tools/research-trust.test.ts
  • tests/unit/util/app-archive-detection.test.ts
  • tests/unit/wreq-binary-prune.test.ts
  • tsconfig.test.json
  • tsconfig.tests-debt.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@KnockOutEZ

Copy link
Copy Markdown
Owner Author

Closed by supervisor (2026-08-31) — accidental PR, forbidden base, zero new work at risk.

This PR was cut from the shared studio-handoff-core branch itself straight to main, so its 776-file / +176k diff is just the branch-vs-main delta — not the law-4 fix the body describes. The branch tip is still 6c33d69e (merge of #524): the fix this body narrates was never pushed, here or in the studio repo, so closing this PR loses nothing.

Why it cannot merge:

  • CEO ruling 2: runner studio work never targets main.
  • The one sanctioned core→main merge (PX2 amendment) happens at PX2's RC gate, and is currently HELD by the CEO 2026-08-28 deploy deferral (hosting/domain/email creds + green production deploy first).

Exposure check, for the record: the diff contains no apps/ (private studio app) code, no internal-docs/, no secrets — the 199 studio-path files are the core-side host seams that already live publicly on this branch by design.

Runner note: the verifier's forbidden-base alarm only scans sd-NNN-* head branches, so a whole-branch PR like this one evades it. Flagged to the CEO for a scoped alarm widening (must not catch external contributors' PRs, e.g. #521).

@KnockOutEZ KnockOutEZ closed this Aug 30, 2026
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