You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix 6 LLM-friendliness inconsistencies in Phase 4 implementation steps
- Remove FfiTransportException from 4.3 (superseded by 3.10 resolution)
- Rewrite 4.5 to use RuntimeConnection hierarchy (not rejected Transport enum)
- Fix 4.7 file path to post-restructure java/sdk/src/ location
- Add path note at 4.1 explaining src/ vs sdk/src/ relationship with 4.6a
- Expand --no-auto-update invariant in 3.9 with ABI-skew rationale
- Fix NativeSize to com.sun.jna.NativeLong in FfiOutputStream example
@@ -631,7 +631,7 @@ Complete `env_json` key inventory (these are the **only** three keys used across
631
631
632
632
**Three key invariants:**
633
633
634
-
1.**`argv_json` must never be null.** It always contains at least `[entrypoint, "--embedded-host", "--no-auto-update"]`.
634
+
1.**`argv_json` must never be null.** It always contains at least `[entrypoint, "--embedded-host", "--no-auto-update"]`.**`--no-auto-update` is mandatory** — it pins the worker to the bundled cdylib version, preventing ABI skew between the loaded library and the runtime worker. Omitting it allows the runtime to drift to a newer `~/.copilot/pkg` version whose ABI may be incompatible with the loaded cdylib.
635
635
2.**`env_json` can be null** (with `env_json_len = 0`) when no environment overrides are needed.
636
636
3.**All three metadata buffers (`ext_source`, `ext_name`, `conn_token`) are always null/0.** No current SDK uses them; they are reserved extension points.
637
637
@@ -940,7 +940,7 @@ Every implementation step in this phase **must** follow this test-driven workflo
940
940
1.**Write tests first.** Before writing or modifying production code for a step, write the unit tests (and integration tests where specified) that define the expected behavior. Tests should initially fail (red).
941
941
942
942
The test native library from `spike-3-4-jna-callback-and-threading/rust-dll/` is the test fixture for steps 4.3 and 4.4. Build it once with `cargo build --release` for the current OS and architecture and place the output at a known path before writing Java tests.
943
-
943
+
944
944
2.**Implement until green.** Write the minimum production code to make all tests pass.
945
945
3.**Refactor.** Clean up the implementation while keeping tests green. Run `mvn spotless:apply` to ensure formatting compliance.
946
946
4.**Gate before proceeding.** All tests from the current step **and all prior steps** must pass (`mvn verify`) before moving to the next step. Do not proceed with a step if any prior step's tests are broken.
@@ -956,6 +956,8 @@ Every implementation step in this phase **must** follow this test-driven workflo
956
956
957
957
**What:**`PlatformDetector` class that determines `os`, `arch`, `libc` and produces the classifier string.
958
958
959
+
> **Path note:** Steps 4.1–4.5 list file paths as `java/src/...`. After step 4.6a (reactor restructure), these become `java/sdk/src/...`. If 4.6a is performed first, use the `java/sdk/src/...` paths. If 4.6a is performed after, the files will be moved during 4.6a.
- Extracts binary to `~/.copilot/runtime-cache/<version>/<classifier>/runtime.node`. Handles concurrent extraction safely.
984
986
985
-
- When *multiple* platform JARs are on the classpath (uber-jar scenario), it sorts candidates and picks the best match. The plan's `NativeRuntimeLoader` should handle this case — in the `copilot-native-all` uber-JAR, all 8 `native/<classifier>/runtime.node` resources exist on the classpath simultaneously. The loader must filter by the detected classifier, not just grab the first `runtime.node` it finds. ❌❌❌We are not doing the uber-jar approach now, but we want to do it in the future, so we must be ready for it.❌❌❌
987
+
- When _multiple_ platform JARs are on the classpath (uber-jar scenario), it sorts candidates and picks the best match. The plan's `NativeRuntimeLoader` should handle this case — in the `copilot-native-all` uber-JAR, all 8 `native/<classifier>/runtime.node` resources exist on the classpath simultaneously. The loader must filter by the detected classifier, not just grab the first `runtime.node` it finds. ❌❌❌We are not doing the uber-jar approach now, but we want to do it in the future, so we must be ready for it.❌❌❌
986
988
987
989
### 4.3 — JNA binding interface and implementation
988
990
@@ -993,17 +995,16 @@ Every implementation step in this phase **must** follow this test-driven workflo
- Can load a native library, call functions, receive callbacks. Error cases wrapped in `FfiTransportException`.
1005
+
- Can load a native library, call functions, receive callbacks. Error cases throw `IllegalStateException` (see 3.10 resolution — no dedicated `FfiTransportException`).
1005
1006
1006
-
-**Library-never-unloads pattern** — the loaded native handle must be held in a `static` field and never released. JNA caches by library name, but the plan should make this explicit since native worker threads outlive any `FfiRuntimeHost` instance. See Rust `OnceLock<Mutex<HashMap<PathBuf, &'static Library>>>` + `Box::leak()` Missing this risks a crash if a second `FfiRuntimeHost` is created after the first is closed.
1007
+
-**Library-never-unloads pattern** — the loaded native handle must be held in a `static` field and never released. JNA caches by library name, but the plan should make this explicit since native worker threads outlive any `FfiRuntimeHost` instance. See Rust `OnceLock<Mutex<HashMap<PathBuf, &'static Library>>>` + `Box::leak()` Missing this risks a crash if a second `FfiRuntimeHost` is created after the first is closed.
1007
1008
1008
1009
### 4.4 — FFI runtime host and transport streams
1009
1010
@@ -1023,34 +1024,38 @@ Every implementation step in this phase **must** follow this test-driven workflo
1023
1024
1024
1025
-**Callback `closing` flag early-exit** — the `on_outbound` callback must check a `closing` flag and return immediately without enqueuing data. Without this, the shutdown drain may never converge. Both .NET and Rust set this flag before `connection_close`. Failing to do this can caus a hang on shutdown.
1025
1026
1026
-
-**Operation lock for concurrent write/close safety** — `FfiOutputStream.write()` can race with `FfiRuntimeHost.close()`. See how the Rust SDK uses a `parking_lot::Mutex` (`operation_lock`). See the Rust SDK `FfiShared`. Failing to do this can cause a data race during shutdown.
1027
+
-**Operation lock for concurrent write/close safety** — `FfiOutputStream.write()` can race with `FfiRuntimeHost.close()`. See how the Rust SDK uses a `parking_lot::Mutex` (`operation_lock`). See the Rust SDK `FfiShared`. Failing to do this can cause a data race during shutdown.
1027
1028
1028
-
-**`Connection` record needs `FfiRuntimeHost` field** — the current `CopilotClient.Connection` record has `(JsonRpcClient rpc, Process process, ServerRpc serverRpc)`. InProcess has no `Process`. Without an `ffiHost` field, `stop()` and `forceStop()` can't call `ffiHost.close()`. .NET's `Connection` record includes `FfiRuntimeHost? ffiHost`. Failure to do this can cause a leak of native resources on shutdown.
1029
+
-**`Connection` record needs `FfiRuntimeHost` field** — the current `CopilotClient.Connection` record has `(JsonRpcClient rpc, Process process, ServerRpc serverRpc)`. InProcess has no `Process`. Without an `ffiHost` field, `stop()` and `forceStop()` can't call `ffiHost.close()`. .NET's `Connection` record includes `FfiRuntimeHost? ffiHost`. Failure to do this can cause a leak of native resources on shutdown.
1029
1030
1030
1031
### 4.5 — Transport integration with `CopilotClient`
1031
1032
1032
-
**What:**`Transport` enum, `setTransport()` on `CopilotClientOptions`, InProcess code path in `CopilotClient` that uses `FfiRuntimeHost` instead of `CliServerManager`.
1033
+
**What:**`RuntimeConnection` sealed class hierarchy (see 3.5.1 resolution), `setConnection()` on `CopilotClientOptions`, InProcess code path in `CopilotClient` that uses `FfiRuntimeHost` instead of `CliServerManager`.**Do NOT create a `Transport` enum or `setTransport()` method — that approach was explicitly rejected in the 3.5.1 resolution in favor of the `RuntimeConnection` type hierarchy.**
1033
1034
1034
-
✅✅Remember to handle **`COPILOT_SDK_DEFAULT_CONNECTION` env var resolution in `CopilotClient` constructor**. `CopilotClient` must implement `resolveDefaultConnection()` when no `connection` is set. See NET Client.cs — search for `ResolveDefaultConnection` (private static method) and its caller `_options.Connection ?? ResolveDefaultConnection(_options)`; Rust lib.rs — search for `fn resolve_default_transport` and constant `DEFAULT_CONNECTION_ENV_VAR`.
1035
+
✅✅Remember to handle **`COPILOT_SDK_DEFAULT_CONNECTION` env var resolution in `CopilotClient` constructor**. `CopilotClient` must implement `resolveDefaultConnection()` when no `connection` is set. See .NET `dotnet/src/Client.cs` — search for `ResolveDefaultConnection` (private static method) and its caller `_options.Connection ?? ResolveDefaultConnection(_options)`; Rust `rust/src/lib.rs` — search for `fn resolve_default_transport` and constant `DEFAULT_CONNECTION_ENV_VAR`.
1035
1036
1036
-
✅✅Remember: **`ValidateEnvironmentOptions` — reject incompatible options for InProcess** — `environment`, `telemetry`, `workingDirectory`, `extraArgs` must be rejected when InProcess is selected. Without this, users set options that silently do nothing in-process. See .NET Client.cs — search for `ValidateEnvironmentOptions` (private static method, called right after `ResolveDefaultConnection`); Rust lib.rs — search for `fn validate_inprocess_options`.
1037
+
✅✅Remember: **`ValidateEnvironmentOptions` — reject incompatible options for InProcess** — `environment`, `telemetry`, `workingDirectory`, `extraArgs` must be rejected when InProcess is selected. Without this, users set options that silently do nothing in-process. See .NET `dotnet/src/Client.cs` — search for `ValidateEnvironmentOptions` (private static method, called right after `ResolveDefaultConnection`); Rust `rust/src/lib.rs` — search for `fn validate_inprocess_options`.
1037
1038
1038
1039
**Files to modify:**
1039
1040
1040
-
-`java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java` — add `transport` field
**Tests:** Unit test that InProcess transport selection uses `FfiRuntimeHost`.
1048
1053
1049
-
✅✅✅Test the backward-compatibility bridge (legacy fields → `RuntimeConnection` inference) and the `IllegalArgumentException` when both `connection` and legacy fields are set.✅✅✅
1054
+
✅✅✅Test the backward-compatibility bridge (legacy fields → `RuntimeConnection` inference) and the `IllegalArgumentException` when both `connection` and legacy fields are set.✅✅✅
**Gating criteria:**`new CopilotClientOptions().setTransport(Transport.IN_PROCESS)` routes through FFI host. `COPILOT_SDK_DEFAULT_CONNECTION=inprocess` env var works. CLI transport unchanged.
1058
+
**Gating criteria:**`new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess())` routes through FFI host. `COPILOT_SDK_DEFAULT_CONNECTION=inprocess` env var works. CLI transport unchanged.
1054
1059
1055
1060
### 4.6 — Multi-module reactor restructure and per-platform classifier JARs
0 commit comments