`options.sessionId` inside a generic function whose type parameter's
constraint declares `sessionId?: string` fenced with SC1090 when the call
site's options record has no such slot:
function applyAuth<TModel, TOptions extends ProviderRequestOptions<TModel> & Transforms>(
model: TModel, options: TOptions | undefined) {
const sessionId =
options && "sessionId" in options && typeof options.sessionId === "string"
? options.sessionId : undefined;
}
applyAuth(model, { apiKey, azureApiVersion });
The checker admits the read through the constraint (and, with the `in`
guard, through a synthetic `T & Record<"sessionId", unknown>`), while the
instantiated record — the value's actual layout — stores no such field.
`fieldTarget` found no record slot and the read fell through to the
generic unsupported-property fence, even though Node simply reads the
property off the object it has: undefined when the caller omitted it, the
extra property when a JavaScript caller supplied one.
fieldTarget now recognizes a generic receiver (a type parameter, or an
`in`-narrowed intersection that contains one) whose stored record lacks
the member, and lowers the read as the checked-dynamic keyed read of that
record value, entering the static code at the type the checker gave the
read. Reads that keep a slot take their record get as before, and the
programs this path opens did not compile at all before, so typed code
keeps its representation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside an optional-chain body the checker types the guarded receiver by its
non-nullish narrowing, often one record of a discriminated union, while the
bound chain value still carries the whole present union: a runtime-optional
local that crossed an `await` and a prior `stored?.type === "oauth"` return
holds `ApiKeyCredential | OAuthCredential` at runtime.
lowerUnionProperty gated on the spelled receiver type being a union, so the
read fell through to the generic SC1090 fence ("reading 'type' from a value
of type 'ApiKeyCredential'"). Take the union from the chain binding, which is
the value lowerExpr is about to read; unionDisc then dispatches on the stored
tag and maybeNarrow applies the checker's narrowing to the result.
Adds the differential corpus program union-read-after-chain-guard and its
emission test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`return typeof options === "string" ? {...} : options || {}` refused:
flow narrowing ruled the string arm out, but the stored value keeps the
wider union, so the logical operator saw a union-typed left operand
outside a condition.
When the checker's result is one of the left value's own arms, test the
value in its own union and extract the result arm through the checked
single-arm narrowing: the falsy arm takes the default, which keeps its
lazy evaluation, and a value of a ruled-out arm traps rather than
masquerading as the result type.
Corpus 8526 pins the narrowed `||` default over an optional interface, a
union of records, and lazy default evaluation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block-bodied callback whose enclosing call has a contextual array
type infers a tuple union rather than an array union: `return pair ?
[pair] : []` in a `f(): T[]` infers `[] | [pair]`. The flatten plan
classified only array arms, so such a callback refused.
The empty-arm detection now recognizes the empty tuple (a bare `[]` arm
under a contextual array type), and the plan gains a `tuple` arm for a
one-field-or-more tuple whose fields all contribute: each field is
appended in order, exactly as an array arm's elements are. `checker.
isArrayType` is false for tuples, so the arm scan accepts
`isTupleType` too.
Corpus 8525 pins the tuple arm, a two-field tuple arm, and an array arm
beside a scalar arm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A written type predicate usually names FEWER members than the
receiver's element has — `(c): c is { text: string } => ...` over a
block that also carries a `textSignature` — and may name a structural
type that is no arm of the element union at all. Both shapes refused,
so `commitments.filter(pred)` over a union of records with differing
members did not compile.
Record targets now convert when their members are a SUBSET of the
source's: each target member is read and converted, the copy keeps the
source's keys in the source's order, and a member the source lacks
still has no slot to read and stays refused.
When no union arm matches the predicate's type, `structuralUnionConversion`
converts tag by tag: each arm the target can be read from returns its
converted value and an arm with no such read throws the same checked
TypeError a lying predicate throws, so the checked extraction still
traps rather than silently passing a foreign value through.
Corpus 8524 pins the subset-record and structural-union conversions;
`narrow-guard-traps.test.ts` pins the TypeError a predicate that
returns true for a foreign value raises.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`settings[key]` over a settings record with an `unknown` member
(`type: unknown`) refuses even though every field reads through a
checked-dynamic slot: the checker's answer collapses to `unknown`
because the union of the field types absorbs into it, so there is no
typed join for the keyed read to dispatch into.
Recognize that shape — the read's type is `unknown`, the shape has no
index signature, and every declared field's value is checked-dynamic
representable — and dispatch per declared key answering a checked-
dynamic read. The key type is the record's own `keyof`, so the answer
is always present.
Corpus 8523 pins the read differentially.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reading a member off a union-typed value is what most pi refusals at
`SC1090` came down to: `credential.type` on `ApiKeyCredential |
OAuthCredential`, `session.role`, `entry.timestamp`, `tool.operations`,
`arms.length`, `renderer.hasOverlayEntries`.
Three read shapes reach the same dispatch and now answer:
- A checker-NARROWED receiver keeps the wider union it stores, because
lowering erases flow narrowing (`stored?.type` after the earlier arm
returned, `(entry as { role: string }).role`). The dispatcher now reads
the member off whichever arm the value holds — an arm that does not
declare it answers undefined, exactly Node's get — and narrows the
answer to the checker's type, which traps on an arm the narrowing
ruled out (types do not lie). This fallback sits after every existing
lowering, so only refusals gain one.
- A member the arms answer through an ACCESSOR (a getter inherited from
a shared base class, `renderer.hasOverlayEntries`) has no field slot
`unionDisc` could read, so the read dispatches on the tag and calls
each arm's own getter. Data-field unions keep their `unionDisc`
lowering.
- `.length` on a union of arrays and strings (`Model[] | ModelRef[]`,
`string[] | Buffer[]`) dispatches on the tag and answers each arm's
own length.
The member read moves out of `lowerUnionProperty` into
`lowerUnionValueRead`, which both entry points share, and
`missingFieldAsUndefined` threads the narrowed-receiver get semantics
through the keyed read.
Corpus 8520/8521/8522 pin the three shapes differentially.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lambda declaring an optional or defaulted trailing parameter — the
`(a, b, loose?) => …` semver comparator, the sort callback whose surplus
position is a flag — was refused at its birth fence (SC1090) even though
every read of it spells that position as `T | undefined`, the completed
native ABI. The fence compared the slot's mapped type against the
contextual lambda's, which misses two shapes the checker accepts: a
lambda whose own `any` parameter mapType leaves unmapped, and a
*contextually* typed lambda whose OWN signature is wider than the slot
it is written under.
requireExactArityValue now admits a lambda whose own call signature maps
to the slot position for position, and cleanFuncAdaptable accepts a
source with extra trailing parameters when each of them completes with
the undefined value, the same absence a call through the slot passes.
`sort`/`toSorted` separately required a comparator of at most two
parameters; it now adapts the first two and lets the surplus ones
complete as undefined, so the imported three-parameter comparator sorts.
Corpus 8529 pins the surplus-parameter comparators (optional, defaulted,
`any`-typed), the direct calls through them, `toSorted`'s untouched
receiver, and remains byte-exact against Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`new Map()` has no type arguments, and the no-argument constructor gives
the checker nothing to infer them from: the expression types as
Map<unknown, unknown> however the destination spells it. Construction
adopted a contextual type only when it mapped to a map, so a nullable or
readonly annotation — `Map<K, V> | undefined`, `Map<K, V> | null`,
`ReadonlyMap<K, V> | undefined`, an optional record field — refused the
very value its slot was built for (SC2003; pi's extension registry, whose
`handlers?: Map<...>` fields start empty).
A Map value can only inhabit a union's map arm, so construction now
adopts that arm when the contextual union has exactly one: the remainder
of the annotation is the nullish absence the value never is. Two
different map arms stay refused (SC2003), because the layout would be
ambiguous.
Corpus 8528 pins the nullable, readonly and optional-field annotations,
a parameter slot, iteration after the adoption, and remains byte-exact
against Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Array.from({ length: n }, () => undefined)` and `.map(() => undefined)`
type as `undefined[]`, which the type mapping represents as an array of
unit-union elements. Callback-driven producers take the element from the
lowered callback's return type, and a standalone `undefined` in return
position maps to the void convention — its answer is erased before it
could be stored — so every discarding callback was refused (SC2009; pi's
tui terminal-color palette).
A producer whose result element is the unit-only union now gives the
callback that union as its contextual return, exactly as the array
element mapping substitutes it for a `void` element: the arrow's
`undefined` answer becomes the present unit value. `[undefined, ...]`,
sparse literals and an explicit `undefined[]` annotation already had
that representation.
Corpus 8527 pins the palette field, the mapper's call order and index,
the `.map` and `Array.from(array, ...)` forms, `in`/JSON and the empty
case under Node and natively.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty binding pattern still performs its one observable step:
RequireObjectCoercible for `{}` and GetIterator for `[]`. Over a statically
non-nullish source that step is invisible, which is why the empty-pattern
lowering emitted nothing; a checked-dynamic source can be null or undefined
at runtime, where V8 throws.
Emit the runtime null/undefined test for dyn sources in the empty-pattern
branch, and let a JS-file empty object pattern take the source-spelling
message dynDestrCheck already produces for non-empty ones.
Test262 (static-adapted-v6, --workers 8), affected chapters before -> after:
language/statements/class/dstr pass 1518 -> 1526, fail 424 -> 416;
language/expressions/class/dstr pass 1518 -> 1526, fail 424 -> 416. No
pass -> fail.
Co-Authored-By: Claude 5.5 <noreply@anthropic.com>
A class whose methods are reachable through `X.prototype` widens its method
signatures to checked values so a JS caller can hand anything across. The
widening replaced every non-void return with DYN, which erased a GENERATOR
method's generator type; the generator branch below then had no generator
channels to build its metadata from and refused the whole class (SC2001 in
`%init`, re-emitted as a runtime fence in a JS file). Every
`assert.sameValue(c.m, C.prototype.m)` variant of the campaign therefore
refused at runtime, 759 of them in the generator-method chapters alone.
A generator's return kind is already a checked-crossable ABI: dynFrom boxes a
generator by reference and dyn calls resume it through the dyn generator ops,
so only the arguments need the widening. Guard the return widening to
non-generator methods.
Test262, 1,110 affected variants, before/after on the candidate
(class/gen-method 52->76, class/async-gen-method 76->128,
class/scope-gen-meth 0->8); no pass->fail and no typed-path delta.
Co-Authored-By: Claude 5.5 <noreply@anthropic.com>
Reading the name of a function stored in a const followed const aliases to
the definition, but stopped at destructuring: `const { cwd } = process`
gave the binding's name instead of Node's "wrappedCwd". A member
destructured from the process global or a builtin module's namespace now
gives the module table's name for it.
An anonymous function wrapped in parentheses (`const f = (function () {})`)
answered an empty name. As in Node, it takes the name of the binding or
property it initializes through the parentheses.
A builtin read as a property (`util.debuglog.name`) answered the name its
shared adapter was first created under, so after `util.debug` it answered
"debug". The name now comes from the property and the module table, and
util.debug's value is named "debuglog", as in Node.
A borrowed element read of a reference array falls back to
scr_arr_borrow_ref when the element is outside the dense fast path. The
call carried no memory attributes, so LLVM assumed it could write any
memory: a loop holding such a read reloaded the array's header on every
iteration, after inlining too. The fallback only reads the array (a hole
or an out-of-range index traps), so it now carries the attributes of the
other array read fallbacks, and the header loads stay hoisted.
- A BigIntStats Date member is built from its truncated millisecond member,
as Node's getters do, where the Stats getters round the fractional one: a
timestamp at least 0.5 ms past a millisecond no longer reads one
millisecond later than Node.
- statSync, lstatSync and fstatSync evaluate a bigint option whose value can
have an effect (`bigint: (count++, true)`), in member order after the path
and before the stat; an exception it throws is the call's.
- An object spread of a node:path namespace in JavaScript copies the module's
members, read through the module value so the copy holds the same
functions: an ES module namespace with its keys sorted and `default`, and
posix, win32 and the default import in Node's member order.
- scr_lib.h declares the BigIntStats readers, so every runtime name the
backend can emit has a prototype in scr_runtime.h.
A scanner reading a mostly ASCII text front to back (slices and
positioned startsWith at increasing positions) walked from the hot
cursor on every read within 64 units of it, since the near-cursor walk
came before the ASCII-run growth. A forward read past the run now grows
the run first. When a non-ASCII character ends the run, a nearby read
walks from the run's end or the cursor and starts a new run where it
lands.
The helper's program target options left UseInitArray off, so a program
constructor went to .ctors. The C startup files and lld run only
.init_array entries, so such a constructor never ran. Runtime units
already set the option (runtime-unit.cpp); program code now does too.
No program emits a constructor today, so executables are unchanged.
native-constructors.test.ts emits a constructor through a single object,
split code generation and program partitions, checks that it lands in
.init_array on ELF, and runs it.
The tag test and the walk share one evaluation: a call in the loop head
appears in the IR exactly once, and the loop walks the value the test saw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- lowerForOfEmptyArrayArm binds the `Set | Map | string | never[]` source
to a hidden local before the tag test, so a call or getter in the loop
head runs once and the loop walks the value the test saw.
- markTarget marks a structural view as a collection view when its source
is already one, and reruns the fixpoint when a view marked earlier
becomes a collection view, so a Map or Set handed through two views keeps
answering through the collection.
- Promise.all in a JavaScript source whose call types as Promise<any[]>
(an unannotated parameter) runs the engine's Promise.all over the entries
as checked-dynamic values when they are typed promises. A merged
`Promise<number | string>[]` argument reaches that call since union
promise arms merge.
Corpus 8419 and 6956 and the js-own-tostring npm fixture cover each case.
`const proc = globalThis.process` lowers to alias plumbing: the declaration
emits no storage and no code, because member and call reads resolve through
the global it names. A read that needs the VALUE therefore failed at the use
with the generic "binding form with no lowering", which names neither the
declaration nor the cause.
Register every stdlib-global snapshot declaration (TypeScript sources only,
matching the elision) and report the cause at the declaration (SC2020),
naming the global and, when it has a short spelling, the type the program
asked the compiler to hold. The use site keeps the inherited-blocker cascade
(SC2004) instead of the generic text.
An unread snapshot stays elided: reading a global has no side effects, so
dropping an unread `const p = process` is observationally identical to Node,
and refusing it would break programs that compile harmlessly today.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An object literal whose get/set accessor satisfies a member the target type
declares as DATA was refused with SC1090: a record member is one storage
slot, so the accessor could only be snapshotted at construction. Node keeps
the accessor live, and the pi sites that hit this (agent.ts:90,
runner.ts:889, codemode/execution.ts:562) are all live getters — one throws
until the object observes later state — so a snapshot is disqualifying.
One static record layout cannot hold both member kinds at one name, so the
analysis marks the TARGET type instead of inventing a literal-only variant:
when a literal's member kind disagrees with the member the contextual type
declares at the same name, that type keeps checked identity, exactly as a
structural type that carries a class instance's identity does. The literal
then lowers through the checked-dynamic literal path, which defines accessor
members as live get/set properties and data members as plain properties, and
reads, writes, Object.keys, `in` and JSON.stringify on the declared type
dispatch dynamically — Node's own model.
The mirrored direction is closed too: a data property satisfying a declared
accessor member (`interface Live { get size(): number }`) used to be an
SC2002 missing-`%get:`-slot refusal; it now keeps the declared type's
identity as well.
Cost, the accepted trade: every value of a MARKED type becomes a checked
dynamic view — a property table and closure dispatch instead of a static
struct slot, so reads and writes on those types are slower (never wrong).
The cost is scoped to the marked type: a program with no such literal marks
nothing, and the frontend analysis itself adds only one array push per object
literal and one Set lookup per data member, with no checker query unless a
member name is an accessor somewhere in the program. IR is byte-identical
for 64 existing corpus programs compiled before and after. The whole-type
accessor-shape form (static reads, closure slots) remains available as the
faster representation if profiles ever show this matters; it needs the
accessor enumeration surfaces (keys, JSON, inspect, for-in, spread, rest)
that this route gets for free.
Only a single object type can be the literal's target: a multi-arm union
picks its arm from the value itself and an intersection merges members, so
neither is marked and both keep today's behavior. A value that reaches the
slot through a variable rather than a literal keeps today's refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An exhaustive switch's `default:` branch declares `const unreachable: never =
event`, and the warning reporter reads a member off that value through an
erasure (`(event as any).type`, `(event as unknown as { type: string }).type`) —
the shape pi's proxy warning takes. The union shared-field read refused it
because the checker types the receiver `never` (or the erasure), not the
union the value still holds.
`lowerUnionProperty` now proceeds when the receiver expression is a `never`
value, directly or through the parenthesized/parenthesized-assertion chain
that escapes it, and reads the field off what the lowered value actually is:
the shared-field read for the union it holds, the record read for a record,
the keyed read for checked-dynamic. `never` admits no reachable call site, so
no reachable program changes shape.
Corpus: tests/corpus/erased-never-member-read.ts (`as any`, `as unknown as`,
and a helper function taking the same shape). Node-identical stdout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A method declaring `ctx?: Ctx` completes to `(id, Ctx | undefined) => string`;
a slot that requires the narrower `(id, ToolCtx) => string` was refused with
SC1090 at the value's birth, because the gate only compared the two
signatures for equality. The slot coercion (funcCoerceAdapter) already
converts every parameter through coercibleValue, the record width conversion
(which also selects a width-liftable arm of a union destination — the
`ToolCtx` into `Ctx | undefined` shape), the partial checked-dynamic
extraction, and the union arm preservation. `funcSlotAdaptable` now asks the
same question through `cleanParamPair`, so the gate admits exactly the pairs
the adapter converts without a trap-only path, and both call sites (the
object-literal method slot and the mapped value target) use it.
The gate also learns the two slot shapes it could not name: an object
literal contextualized by an INDEX SIGNATURE (`Record<string, Handler>`)
takes its member slot from the index signature's value type, and a literal
with no contextual type takes the member's own completed type — the type
every later read of the member spells.
Nested function DECLARATIONS are no longer judged at birth: lowering a
declaration builds the body, not a value, and each value use re-checks the
completed signature against that use's own target. An overloaded nested
declaration with an optional parameter, returned as a value (`return write`
into a one-parameter `Writer`), is now accepted instead of refused.
Corpus: tests/corpus/optional-param-function-values.ts (a required slot over
an optional parameter, an index-signature member with an extra trailing
optional parameter, an annotated value with a defaulted parameter, the nested
overloaded declaration returned as a value, direct-call controls, and a
union-typed slot). Node-identical stdout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node answers a BigIntStats when the stat call is passed `bigint: true`: the
same snapshot, read through a bigint member table. Every number member of
Stats reads as a bigint — the integers, the `*Ms` millisecond members (the
truncated nanosecond quotient, as the binding computes them) and the
BigIntStats-only `*Ns` nanosecond members (sec * 1e9 + nsec, exactly) —
while only the four time members stay Dates. scriptc refused the option
outright.
The representation is static, chosen by the option literal: the lowerer
validates `bigint` against the call's result type (so
`{ bigint: true, throwIfNoEntry: false }` still narrows
`BigIntStats | undefined`), refuses a runtime-valued option and an option
that disagrees with the destination, and keeps every other option key
fenced. fstatSync joins statSync/lstatSync on the same path, with the
descriptor evaluated before the options.
The IR gains a `bigintStats` handle kind beside `stats`: the same RC
family, the same non-traced leaf and non-dynamic handle treatment, so no
shape, tracing or dyn-check behavior moves for any program that never asks
for one. Its member table is a `bigstats.*` lib-call family whose Date
members reuse the existing stat reads.
The runtime support is its own unit, scr_stats_bigint.c: the snapshot
layout moved into scr_stats_private.h so the bigint getters can read the
same fields without scr_lib.c carrying their code. The unit is gated on the
existing `bigint` feature in both the runtime-pack matrix and the
compiler's from-source unit table (the sanitized lane links from that one),
and `-ffunction-sections` keeps a bigint program that never reads a member
from linking any of the getters. No typed program's IR or LLVM changes.
tests/corpus/7018-bigint-stats.ts pins the member table, the Date/bigint
split, ms/ns agreement, arithmetic on a member, lstat over a symlink,
fstatSync over a descriptor, the throwIfNoEntry union, and the plain Stats
answers of the un-optioned and `bigint: false` forms against Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A spread merge like `{ ...base, ...override }` whose sources are union
types has one field set per combination of source arms, so the merged value
can carry fields from several arms at once. No single arm of the
destination union can hold it — naming one would drop the other arms'
fields, which Node keeps — and no runtime tag would be honest. The merge
was refused with the generic "cannot be compiled yet" (SC2001), which says
nothing about the shape the program is building.
The new SC2003 names the destination union and lists the candidate arms,
and its hint gives the two repairs: merge ONE arm per path (narrow every
spread source to the same arm first), or declare one open record type whose
fields are all optional instead of a union of all-optional arms. The
diagnostic fires only for a union-typed destination; other non-record
destinations keep badType.
`NonNullable<T>` over a union already maps to the union value itself, so
tests/corpus/7017-nonnullable-union.ts pins the mapped-type path
(assertions, generic instantiations, narrowing, serialization) against
Node. tests/diagnostics/union-spread-merge.ts re-pins the refusal, and the
union-clone-override-width snapshot moves from SC2001's generic text to
the arm-naming SC2003.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A union of records whose every arm declares the same field optional has a
definite answer for `delete u.k`: the field becomes absent, exactly as the
monomorphic optional-field delete writes it. The delete statement used to
fall through to the receiver-agnostic path and refuse the union.
lowerUnionFieldDelete dispatches over the arms (one tag test per arm, the
last arm as the fall-through else) and writes each slot's absent value
through a recordSet on the narrowed arm; the union value is retained once,
so every arm deletes the object the program passed rather than a
projection. Sloppy-mode writes keep their frozen-record semantics.
Everything else keeps its fence: unit arms, class arms, required fields
(a slot with no absence to represent), accessors, index signatures,
optional chains and tuples all return null and reach the existing
diagnostics.
tests/corpus/7016-union-field-delete.ts pins the delete, the absence
afterwards (`in`, optional reads, JSON.stringify), the retained identity
the caller's alias sees, and deleting an already-absent field.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
path.posix and path.win32 are module objects, not declarations: they are
the same handle `process.getBuiltinModule("node:path")` answers, so
`posix === path.posix` holds and a mutation through one spelling shows
through the other. Reading either namespace as a value mapped to a module
namespace type with no value lowering, and `import { posix } from
"node:path"` was refused outright.
The platform namespaces now lower to the checked-dynamic module value, as
a `path.posix` property read, as a named import, and as a receiver
(`win32.join(...)`); pathModuleValue answers the module handle instead of
building a fresh object literal, which is what makes the identity and the
cached members hold. Type mapping treats a path platform namespace and
@types/node's PlatformPath interface as the dynamic type they are, so a
union with a plain record arm collapses onto it instead of refusing.
tests/corpus/7015-path-platform-namespace-values.ts pins the identity,
cached members, joins and the import forms against Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The socket registry, the http client registry, an agent's entry list and
fetch's list of transfers in flight were singly linked: each append walked
to the tail and each removal walked to the element. A client request on a
pooled connection registers and unregisters a socket and a client and adds
and removes an agent entry, so every request walked lists as long as the
open connections. These lists now link both ways with a last pointer, in
the same order as before. A client finds its agent entry through a
back-pointer, and an agent counts its queued entries, so a finished
request scans for a queued one only when there is one.
Adds the technical-writer skill from vercel/chat to .agents/skills, with a
.claude symlink to .agents so Claude Code finds it. AGENTS.md now asks agents
to use the skill for prose in this repo.
CONTRIBUTING.md gets a matching "Writing with AI coding agents" section.
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
scr_str_case_conv scans ASCII text for the first letter to convert eight
bytes at a time, then converts the rest eight bytes at a time; the tail
and big-endian targets take the byte loop. With a refreshed profile, the
byte-at-a-time scan compiled worse for short strings with nothing to
convert, which made validate-errors slower.
The runtime profile is collected again on this change with
scripts/refresh-runtime-profile.mjs (326 raw profiles). Changed
functions hold 0.0% of its counts, against 3.7% for the previous one.
tests/corpus/string-case-ascii-words.ts covers ASCII strings of every
length from 0 to 40, with letters at block boundaries and next to the
bytes just outside A-Z and a-z.
An uncork() flushed a head writeHead stored even when nothing was
written while corked. Node keeps the stored head until write(), end() or
flushHeaders(), so the flush now sends it only when a write() or
flushHeaders() ran while corked. The http-held-head fixture covers the
three cases.