diff --git a/packages/compiler/src/frontend/lowering/lower-node-island.ts b/packages/compiler/src/frontend/lowering/lower-node-island.ts
index 053e5039f..76456833e 100644
--- a/packages/compiler/src/frontend/lowering/lower-node-island.ts
+++ b/packages/compiler/src/frontend/lowering/lower-node-island.ts
@@ -1226,7 +1226,11 @@ export function classConstructHelper(
/** Native values NI-5 exports into the island by reference: functions,
* promises, and object types other than arrays, tuples, Maps and Sets
* (records, interfaces, class instances). A union qualifies when every
- * arm does or is copied. */
+ * arm does or is copied, and an intersection of them qualifies: a merged
+ * generic record (`Omit & Extras`, the `Type.Object(props)`
+ * shape) is one native object, and the island reads the program's own
+ * object, not a copy of it. An intersection with a primitive arm
+ * (`string & {brand}`) is a primitive value, which the runtime copies. */
function exportedByReference(lowerer: Lowerer, type: ts.Type): boolean {
const checker = lowerer.checker;
if (type.isUnionType())
@@ -1237,6 +1241,14 @@ function exportedByReference(lowerer: Lowerer, type: ts.Type): boolean {
(t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0 ||
exportedByReference(lowerer, t),
);
+ if ((type.flags & ts.TypeFlags.Intersection) !== 0)
+ return ts
+ .constituentTypes(type)
+ .every(
+ (t) =>
+ (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0 ||
+ exportedByReference(lowerer, t),
+ );
if ((type.flags & ts.TypeFlags.Object) === 0) return false;
// Arrays and tuples cross as Proxies over the native array (NI-5b).
void checker;
diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6900-node-island-merged-record-in/main.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6900-node-island-merged-record-in/main.ts.json
new file mode 100644
index 000000000..4818b61b1
--- /dev/null
+++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6900-node-island-merged-record-in/main.ts.json
@@ -0,0 +1,6 @@
+{
+ "order": [
+ "/tests/corpus/6900-node-island-merged-record-in/main.ts"
+ ],
+ "diags": []
+}
diff --git a/tests/corpus/6900-node-island-merged-record-in/main.ts b/tests/corpus/6900-node-island-merged-record-in/main.ts
new file mode 100644
index 000000000..51e286916
--- /dev/null
+++ b/tests/corpus/6900-node-island-merged-record-in/main.ts
@@ -0,0 +1,31 @@
+// @requires-libnode
+// A merged generic record crossing INTO the island (`object(properties)`
+// is `Type.Object(props)`): the merged type is an intersection — the base's
+// fields without one, plus the overrides — and the island reads the
+// program's own object, not a copy of it.
+import { keys, object } from "recordkit";
+
+interface ProviderBase {
+ name: string;
+ retries: number;
+}
+
+interface ProviderOverrides {
+ stream: boolean;
+}
+
+/** A merged record spelled as an intersection of a mapped type and an interface. */
+type MergedProvider = Omit & ProviderOverrides;
+
+function merged(name: string, stream: boolean): MergedProvider {
+ return { name, stream };
+}
+
+const props: MergedProvider = merged("openai", true);
+const schema = object(props);
+console.log(schema.kind, schema.describe());
+console.log(keys(props), keys(schema.properties));
+console.log(schema.properties.name, schema.properties.stream);
+
+const inline = object({ name: "anthropic", stream: false });
+console.log(inline.kind, inline.describe(), inline.properties.name);
diff --git a/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.d.ts b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.d.ts
new file mode 100644
index 000000000..ed760c9d1
--- /dev/null
+++ b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.d.ts
@@ -0,0 +1,12 @@
+export interface Schema {
+ readonly kind: string;
+ describe(): string;
+}
+/** Declares the type of the value it is given, like typebox's
+ * `Type.Unsafe`: the island hands back the caller's own type, so the
+ * caller's declaration must describe what the island really returns. */
+export declare function unsafe(value: T): T;
+/** A schema over the properties record it is given, like `Type.Object`. */
+export declare function object(properties: T): Schema & { readonly properties: T };
+/** The sorted own keys of a value, joined. */
+export declare function keys(value: unknown): string;
diff --git a/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.js b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.js
new file mode 100644
index 000000000..4dec16d1f
--- /dev/null
+++ b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/index.js
@@ -0,0 +1,9 @@
+"use strict";
+
+exports.unsafe = (value) => value;
+exports.object = (properties) => ({
+ kind: "object",
+ describe: () => "object " + Object.keys(properties).sort().join(","),
+ properties,
+});
+exports.keys = (value) => Object.keys(value).sort().join(",");
diff --git a/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/package.json b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/package.json
new file mode 100644
index 000000000..ef697c47c
--- /dev/null
+++ b/tests/corpus/6900-node-island-merged-record-in/node_modules/recordkit/package.json
@@ -0,0 +1 @@
+{ "name": "recordkit", "version": "1.0.0", "main": "index.js", "types": "index.d.ts" }
diff --git a/tests/corpus/6900-node-island-merged-record-in/package.json b/tests/corpus/6900-node-island-merged-record-in/package.json
new file mode 100644
index 000000000..1f5c5ec87
--- /dev/null
+++ b/tests/corpus/6900-node-island-merged-record-in/package.json
@@ -0,0 +1 @@
+{ "name": "node-island-merged-record-in", "private": true, "type": "module", "scriptc": { "packages": { "recordkit": "node" } } }
diff --git a/tests/harness/corpus-costs/6900-node-island-merged-record-in/main.ts.json b/tests/harness/corpus-costs/6900-node-island-merged-record-in/main.ts.json
new file mode 100644
index 000000000..b7c2b4e52
--- /dev/null
+++ b/tests/harness/corpus-costs/6900-node-island-merged-record-in/main.ts.json
@@ -0,0 +1 @@
+{ "plain": 3000, "san": 34000 }