Chromium also targets the document when a handler-bearing element is attached to the live page and removed before the CSP event is sent, so the readHTML filter alone (directive, source file, line, target document) could not tell that from the inert parse. The page now also records, with a MutationObserver on the live document, every element that enters the page carrying an on* attribute and every on* attribute set on an element in the page, and h.quiet() asserts the record is empty. The live-handler test gains a case for a handler attached and removed in the same turn, and checks per case that the observer saw the handler enter the page (not for the element never attached). Test-only; no product change.
The factory profile of scripts/case2-unit.sh now takes the bare pair
--sso --send, ported from the lane's serve_flags rule (b00019548). Either
flag alone refuses, and the proxy preview set and the proof pair still
refuse. Before any unit write, a bounded query run as the stack user,
through the existing config-query supervisor, checks that the stack
user's system config has console.sso; the Console refuses --sso without
it at start (packages/webui/src/cli.mjs:95). The server needs no change.
The library profile is unchanged: still read-only, still refusing --send
and --sso. Taking library --send with forward-auth and proof would need
the #385 refusal reverted in cli.mjs:63, serve.mjs:681 and the service,
release and upgrade scripts, so it is not done here.
docs/guides/case1-business.mosaic-stack.json now launches
claude-opus-5-5 with provider proxy, route anthropic, under account
factory with credential proxy.
Chromium 156 (host upgrade 2026-11-03) checks script-src-attr when an
inline handler attribute is created, in a document with no window as well
(WPT PR 62762; the follow-up PR 64290 that skips windowless documents is
not in 156). prosemirror-view's readHTML parses pasted HTML into the body
of a new createHTMLDocument, so the paste test at :154 got one report per
pasted handler (img onerror, a onclick, a onmouseover, svg onload), each
at editor.js:8212 with the document as target. No pasted handler reaches
the live document; Chromium 153, 151 and Brave 154 report nothing there.
The page record now sets aside only a script-src-attr report whose event
names editor.js at that readHTML line and whose target is the document.
The line is found from the bundle's text, and the lines that make the
parse target a new createHTMLDocument body are pinned; the test file
refuses to load if they change. h.quiet(), inert(), injected, errors and
rejections are unchanged. No product or CSP change.
New test: after a hostile paste, three live handlers each still fail
h.quiet() with one script-src-attr report: an onclick set on an editor
paragraph and clicked, an img onerror parsed into a page-owned detached
element, and an onclick prosemirror-view sets through its attributes prop
(a report naming editor.js at another line).
Route selection moves into one helper, pickRoute(proxyOf), that both checks call: the
primary preflight, and the fallback with its own preflight only when the primary's
fails, with the same messages. The final check passes proxyFor and is otherwise
unchanged. The first check of a proxy launch passes a stand-in session proxy (marker
key PENDING_ADMISSION_KEY and the configured loopback base URL), which reaches only
the preflights, then runs gate() with a null session proxy on the predicted route only.
The round 1 union over both routes is gone.
The managed fixture takes extra (more harnesses switched on), real (harnesses loaded
unfaked) and hostEnv. Seven tests in proxy-launch-order.test.mjs.
The negative lookbehind in DYNAMIC and in the dynamic branch of IMPORT excluded any dot
before `import`. That also skipped the last dot of the spread punctuator, so
`${({...import('../x.mjs')})}` returned [] for an existing, a missing and an outside
target, and `{...import(name)}` in plain code was neither returned nor refused.
Both patterns now share one fragment: a word character or $ before `import`, or a single
dot (x.import, x?.import), makes it a property call; a dot that follows another dot ends a
spread and does not exclude it. `1..import(` is read as an import too, which errs toward
refusal; the header names it.
Tests: a spread inside a substitution refuses for an existing, a missing and an outside
target and for array spread; a spread in plain code is returned, reaches the target
checks, and is refused when its argument is not a plain string; x.import and x?.import
stay property calls in plain code and in a substitution.
The business-imports helper blanked a whole template literal, so a dynamic import inside a
${...} substitution was neither found nor refused and skipped the existence and containment
checks. The scanner now reads a substitution to its closing brace, refuses it with
"unsupported import inside a template substitution" when it holds a dynamic import, and drops
it otherwise, so a nested template and a brace inside a string no longer end it early.
A slash after a postfix ++ or -- was read as the start of a regular expression, so
`n++ / 2` threw "unterminated regular expression". After a ++ or -- pair that follows an
operand the slash is division; after one that follows an operator it still starts a regular
expression. The header names the rare form this still misreads.
Output on the real tree is byte-identical.
F1: with system.proxy set, the first launcher check no longer returns
before the account, sandbox, capacity and limit checks. It skips only the
harness preflight and the session proxy key match, which need the key P2
admits in startLocked. A launch that can never start now refuses before
authorizeLaunch, the workspace and checkouts, and modelPreflight. The first
proxy pass accepts only credential "proxy" for a declared account, tries
the fallback model when the primary refuses, and lets mayLaunch
key-quota-no-model-key through so P2 can issue the first key. The final
check is unchanged and still runs before newRun and spawn.
F4: the proxy test option is renamed testProxy and createLauncher throws
test-proxy-refused when it is given with system.proxy.
launch-account.test.mjs is a second caller, so the option stays.
Tests: proxy-launch-order.test.mjs (6), launcher.test.mjs F4 throw test;
the managed launch fixture gains bwrap, admit and trace.ops.
control-board serve.test: the non-loopback refusal test took a port from a
probe-and-close helper and then called startServer on it. It now binds its
own listener, keeps it for the whole test, and closes it in finally, so the
port cannot be taken by another process in between.
stack-release.test T-I4: the free port goes only to webui-service.sh install
rows that refuse before any listen, so it is now a closed lease
(leaseClosedPort), which refuses connects and blocks other binds.
The 22 other freePort callers hand the number to `stack-release.sh rehearse`,
which probes it and starts a Console later. That needs a script change and
is left as a design, with a comment above freePort naming the window.
The helper read imports with a regex that treated a commented import as live, missed
`import (` written with a space, skipped a missing target inside packages/business/src
before checking it existed, and resolved targets lexically. It now blanks comments
(and ignores import forms inside strings and regular expressions) before matching,
counts a spaced or line-broken dynamic import, throws on a dynamic import whose
argument is not a plain string and on a file with an unterminated string, comment,
template or regular expression, checks every target exists and is a regular file
before the inside-own-directory skip, and refuses a target whose real path is outside
the real repository root. The output for the real tree is unchanged.
business-imports.test.mjs builds small trees under a private temp directory.
The usage queue read drains the proxy. A batch that is then not written (the
client or the bus refuses its shape, an ambiguous key owner, a failed write) is
lost, and the Console showed proxy totals with no sign of it. The reader now
counts these per enabled business since the bus started, the proxy view returns
the count as droppedBatches, and the Console proxy section says so above 0.
A read that failed before the proxy answered or that it refused drained nothing
and is not counted. Telemetry only; no quota reads it. The log line stays
proxy-usage-read-failed.
observeOrphans kept the whole remembered set when the lookup failed, so a leaf
removed and added back during the failure logged no second orphan line. It now
keeps only the ids the refreshed document still holds and adds none. Quota
decisions are unchanged: a failed lookup still refuses.
Ruling 1: the bus runtime passes launchSettings a key lineage lookup read
from its own credential records (ids only). A new daily key quota takes the
earliest stored since among the old document's daily key quotas in its key's
lineage, and now only when there is none. A reset still sets now, a
caller-supplied since still refuses, and a quota removed and added back in a
later write is new. A failed or malformed lookup refuses proxy-key-refused; a
write that adds no new daily key quota never calls it.
Ruling 2: every limits refresh updates the remembered orphan set, an empty
set when no daily key quota is left, so a quota removed and added back logs
again. In memory only: a restart is a fresh observation.
Ruling 3: pollSettledAfterTick yields on the real setImmediate, captured at
load, inside its deadline.
Tests: p5-key-lineage (writer move counts 160, reset restarts, lookup
failure, orphan remove and re-add), p5-runtime (exhausted on A, moved to B
through the real runtime, refuses), p5-background (barrier with every timer
mocked), token-limit-settings (earliest, no match, re-add, caller since,
malformed answers, default refusal). READMEs: business, plugins, daily-quota,
cliproxy-queue.
Appends a BUILD-LOG entry that corrects the R1 entry without editing it: the
R1 walk change also dropped the recursive lstat, which R2 restores; the
tester-cli freePort helper has no callers and binds nothing; the stack-release
and control-board probe-then-bind window stays a known risk for a follow-up.
Appends one SESSIONS line.
R1 passed the parent's stat of a listed directory into the recursive walk,
so a recursive entry no longer took its own lstat. A directory replaced by a
symlink after that stat passed the stale directory check and readdirSync
followed the link; a directory replaced by a regular file leaked a raw
ENOTDIR instead of refusing with project-repository.
The walk now takes a root flag. The .git root keeps its raw lstat and its
own error; every recursive entry takes a fresh lstat through statEntry, so
a vanished entry still maps ENOENT to 'git metadata changed during
inspection' and a replaced one refuses with 'expected local .git directory'.
The mapper is unchanged: ENOENT only.
Two deterministic tests replace a directory under .git/objects inside the
lstat call that stats it as a listed entry, after the real stat is taken:
once with a symlink to a fixture-only directory outside the repo, once with
a regular file. Both refuse with project-repository.
leasePort({closed:true}) ignored its option: it probed an ephemeral port and
closed it, so a parallel test file could bind it and answer a request the
test expected to fail (preview.test.mjs:209 saw 200 instead of 502).
leaseClosedPort holds a bound, non-listening socket in a perl child until the
test's after hook. Connects still get ECONNREFUSED, bind() from another
process gets EADDRINUSE, and the kernel's ephemeral allocation skips the port.
Lane mode keeps its band and per-port lock, and releases the socket before
the lock. leasePort now refuses a closed option, so a stale call cannot get
the unheld port again.
Unanswerable callers (webui preview, serve, service) move to leaseClosedPort(t).
Callers that listen on the port themselves (stack-release rehearse, control-board
serve, tester-cli) keep leasePort and drop the ignored option.
Fixture helpers that commit to a repository the product later inspects now pass
maintenance.auto=false and gc.auto=0, as the product's own git calls already do,
so a detached git maintenance run no longer writes pack temp files mid-walk.
inspectGit now refuses a git metadata entry that vanishes between readdir and
lstat (or between listing a directory and reading it) with project-repository,
'git metadata changed during inspection', instead of leaking a raw ENOENT.
It still refuses rather than skipping or retrying. A missing .git keeps its
existing error.
A deterministic test removes the named entry from inside a replaced readdirSync,
for both a file and a directory.
Ruling 1: model records keep a bounded previous list (64 ids, oldest dropped)
of ids that left by rotation, grace expiry or re-issue; a fire removes the
record. A tokens.keys leaf matches and counts every id in the lineage that
holds it. Lineage ids are unique host-wide. An orphaned leaf limits nothing,
logs one value-free line when it becomes orphaned, and the Console usage
view lists it.
Ruling 2: an agent with no model key under a key quota refuses
key-quota-no-model-key, passed through the broker, launcher and stop reason.
Ruling 3: covered by the lineage; no mayLaunch change.
Ruling 4: the usage module receives a frozen { readUsageQueue } only.
Ruling 5: every telemetry fixture wait races a 5 s real-timer deadline.
GPT review of the 1.19 rebase found the test header still called the PRD a candidate and the rebase record placed lines 793 and 645 in the wrong requirements. Comment and record only; no assertion changes.
Rebased onto refactor as 1.19 because row 335 landed 1.18 first. Requirement text, IDs, order and the 101
records match the approved fd9ae104d apart from version labels. GPT review APPROVE with two P3s, fixed in the
next commit.
Adds agents/gosalyn/work/disk-prd118/REBASE-1.19.md: the cherry-pick
conflicts and their resolutions, every label moved to 1.19 and every 1.18
kept, the byte check against fd9ae104d with version labels normalized, and
the test runs and version mutant.
Row 335 landed PRD version 1.18 first, for the REQ-INST-2 correction. The
disk-space governance text that Jason approved as 1.18 in Q289 therefore
lands as 1.19. Only version labels move: the status line, the disk version
source bullet, the "since version" labels in the disk sections, the disk
milestone, and requirements.json "version". The requirement text, IDs,
order, defaults, settings and the 101 records are unchanged.
The prior approval history bullet now runs through version 1.18 and carries
the row 335 lines. The two test pins follow the renumbering.
Records the replayed join of row 333 R2 (0ef269da9) onto landed row 332 (5df0e9d6f) in merge c0c291cda: the two conflict hunks and their resolution, the comparison with the earlier join resolution, and the nine suite results. Adds the docs/SESSIONS.md line.