mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
fix(ui): wait for current guided runner settings
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
4440df2e44
commit
0d998fad49
3 files changed
+101
-3
No files matched your search
@@ -614,3 +614,38 @@ assertions remain unchanged. The full fake-transport suite passes 72 with one
|
||||
existing skip; runner noemit and maps pass. Fresh exact-head runner CI remains
|
||||
required after this test-only correction. No package campaign, paid provider
|
||||
call, local native build, Docker build or upstream prerequisite edit.
|
||||
|
||||
## Guided creation with refreshed settings — 2026-10-12
|
||||
|
||||
Review 4253594222 on behavior head `4440df2e44e346080308e40ec3287c94c7f776a2`
|
||||
identified stale cached environment selection during a background instance-settings
|
||||
read. The mounted reproduction actually calls setup against cached SSH/Legacy
|
||||
before the pending current settings select local/native, rather than merely
|
||||
observing a loading label. Guided Claude/Codex setup now requires current completed
|
||||
environment, settings, execution policy, experimental policy and matching Auto
|
||||
runner discovery reads before testing or hiring. Existing connection controls stay
|
||||
mounted inside a disabled fieldset while refreshing, preserving an API-key draft.
|
||||
Both the cached SSH-to-local case and same-environment discovery refresh pass;
|
||||
all 65 guided setup controls, UI noemit, token gates and structural maps pass.
|
||||
Logs: `/private/tmp/claude-guided-current-settings-before.log`,
|
||||
`/private/tmp/claude-guided-current-settings-draft-controls.log`, and
|
||||
`/private/tmp/claude-guided-current-settings-final-{types,tokens,maps}.log`.
|
||||
This repair is prepared locally while the source owner integrates the common
|
||||
transport correction; published behavior head remains the literal 4440 revision.
|
||||
No paid turns, local native builds or Docker builds were performed.
|
||||
|
||||
Platform head `173a73ec95386d7b9eac50d87699a16ec27da3b3` failed a credential
|
||||
fixture during its two-port reservation (EADDRINUSE before the production staging
|
||||
call). The existing bounded three-port fixture reservation helper is extended to
|
||||
hold two candidates, retrying fixture preparation only and never a production
|
||||
staging call. A foreign-collision control verifies that foreign ownership survives,
|
||||
the auth sentinel stays unchanged, and preparation stops within eight attempts.
|
||||
All 42 credential controls and runner noemit pass locally after supplying the
|
||||
worktree's declared tsx test loader. Logs:
|
||||
`/private/tmp/claude-quorum-fixture-complete-controls.log` and
|
||||
`/private/tmp/claude-quorum-fixture-final-types.log`. This test-only correction
|
||||
is prepared in the protocol worktree and has not been published during the parent
|
||||
integration hold. The independent serialized issue-route timeout remains under
|
||||
phase diagnosis; a subsequent full mocked suite passes 36 controls but does not
|
||||
waive the cloud failure or prove its latency cause. Final source composition,
|
||||
required CI and the provider-free cloud packaging campaign remain pending.
|
||||
@@ -340,7 +340,7 @@ function Setup({
|
||||
!adapter.disabled &&
|
||||
!getAdapterDisplay(adapterType).comingSoon,
|
||||
) && (requestAdapterType !== "paperclip_runner" || adapterType === "paperclip_runner" || adapterInventory.data?.some(adapter => adapter.type === "paperclip_runner" && adapter.loaded && !adapter.disabled));
|
||||
const ready = Boolean(
|
||||
const connectionSettingsReady = Boolean(
|
||||
available &&
|
||||
(!["codex_local", "claude_local"].includes(adapterType) || runner !== "auto" || Boolean(adapters.data)) &&
|
||||
!environmentError &&
|
||||
@@ -358,6 +358,14 @@ function Setup({
|
||||
!caps.error &&
|
||||
(!(managedOnly || forced.forced) || environmentId),
|
||||
);
|
||||
const needsCurrentSetup = ["codex_local", "claude_local"].includes(adapterType);
|
||||
const ready = connectionSettingsReady && (!needsCurrentSetup || (
|
||||
envs.isSuccess && !envs.isFetching
|
||||
&& settings.isSuccess && !settings.isFetching
|
||||
&& experimental.isSuccess && !experimental.isFetching
|
||||
&& general.isSuccess && !general.isFetching
|
||||
&& (runner !== "auto" || (adapters.isSuccess && !adapters.isFetching))
|
||||
));
|
||||
const busy = testState === "running" || saving;
|
||||
|
||||
function buildConfig(
|
||||
@@ -762,7 +770,7 @@ function Setup({
|
||||
center
|
||||
/>
|
||||
</div>
|
||||
{ready ? <AgentProviderConnection
|
||||
{connectionSettingsReady ? <fieldset disabled={!ready} className="contents"><AgentProviderConnection
|
||||
key={environmentId ?? "local"}
|
||||
companyId={companyId}
|
||||
adapterType={connectionAdapter}
|
||||
@@ -814,7 +822,7 @@ function Setup({
|
||||
setRuntimeAiBinding(undefined);
|
||||
setScreen("runtime");
|
||||
}}
|
||||
/> : <p role="status" className="text-sm text-muted-foreground">Loading connection settings…</p>}
|
||||
/></fieldset> : <p role="status" className="text-sm text-muted-foreground">Loading connection settings…</p>}
|
||||
{["codex_local", "claude_local"].includes(adapterType) && <details className="mt-5 space-y-3">
|
||||
<summary className="cursor-pointer text-sm text-muted-foreground">Advanced</summary>
|
||||
<AgentRunnerSelect value={runner} disabled={busy}
|
||||
|
||||
@@ -200,6 +200,61 @@ afterEach(async () => {
|
||||
container.remove();
|
||||
});
|
||||
describe("New agent setup", () => {
|
||||
it("waits for the current instance default instead of testing cached SSH discovery", async () => {
|
||||
let finish!: (value: object) => void;
|
||||
settings.get.mockReturnValue(new Promise<object>(resolve => { finish = resolve; }));
|
||||
const environments = [
|
||||
{ id: "local-1", name: "Local", driver: "local", config: {} },
|
||||
{ id: "linux-ssh", name: "SSH", driver: "ssh", config: {} },
|
||||
];
|
||||
envApi.list.mockResolvedValue(environments);
|
||||
cache.setQueryData(queryKeys.environments.list("company-1"), environments);
|
||||
cache.setQueryData(queryKeys.instance.settings, { defaultEnvironmentId: "linux-ssh" });
|
||||
adapterApi.list.mockImplementation(async (context?: { environmentId?: string | null }) => state.adapters.map((adapter: any) => adapter.type === "claude_local"
|
||||
? { ...adapter, supportedRunners: ["paperclip", "legacy"], defaultRunner: context?.environmentId === "local-1" ? "paperclip" : "legacy" } : adapter));
|
||||
await render("claude_local");
|
||||
await connect("Claude");
|
||||
expect(api.testEnvironment).not.toHaveBeenCalled();
|
||||
expect(api.hire).not.toHaveBeenCalled();
|
||||
await act(async () => finish({ defaultEnvironmentId: "local-1" }));
|
||||
await settle();
|
||||
await connect("Claude");
|
||||
await click("Finish setup");
|
||||
expect(api.testEnvironment).toHaveBeenCalledTimes(1);
|
||||
expect(api.testEnvironment.mock.calls[0][2].environmentId).toBe("local-1");
|
||||
expect(api.hire.mock.calls[0][1].adapterConfig).not.toHaveProperty("maxTurnsPerRun");
|
||||
});
|
||||
|
||||
it("keeps the API key draft while waiting for refreshed discovery", async () => {
|
||||
state.adapters = state.adapters.map((adapter: any) => adapter.type === "claude_local"
|
||||
? { ...adapter, defaultRunner: "paperclip", supportedRunners: ["paperclip", "legacy"] } : adapter);
|
||||
await render("claude_local");
|
||||
await click("ClaudeAPI key");
|
||||
await fill("API key", "example-test-secret");
|
||||
const draft = container.querySelector('[aria-label="API key"]') as HTMLInputElement;
|
||||
let finish!: (value: object[]) => void;
|
||||
adapterApi.list.mockReturnValue(new Promise<object[]>(resolve => { finish = resolve; }));
|
||||
let refresh!: Promise<void>;
|
||||
await act(async () => {
|
||||
refresh = cache.invalidateQueries({ queryKey: queryKeys.adapters.availability("company-1", "local-1") });
|
||||
});
|
||||
await settle();
|
||||
expect(container.querySelector('[aria-label="API key"]')).toBe(draft);
|
||||
expect(draft.value).toBe("example-test-secret");
|
||||
await click("Connect");
|
||||
expect(managedApi.create).not.toHaveBeenCalled();
|
||||
expect(api.testEnvironment).not.toHaveBeenCalled();
|
||||
await act(async () => { finish(state.adapters); await refresh; });
|
||||
await settle();
|
||||
expect(container.querySelector('[aria-label="API key"]')).toBe(draft);
|
||||
expect(draft.value).toBe("example-test-secret");
|
||||
await click("Connect");
|
||||
await click("Finish setup");
|
||||
expect(api.testEnvironment).toHaveBeenCalledTimes(1);
|
||||
expect(api.hire).toHaveBeenCalledTimes(1);
|
||||
expect(api.hire.mock.calls[0][1].adapterConfig).not.toHaveProperty("maxTurnsPerRun");
|
||||
});
|
||||
|
||||
it.each(["paperclip", "legacy", undefined] as const)("uses Claude's discovered %s default for Auto setup and hire", async (defaultRunner) => {
|
||||
settings.getExperimental.mockResolvedValue({ enableNativeRunner: false });
|
||||
state.adapters = (state.adapters as any[]).map(adapter => adapter.type === "claude_local"
|
||||
|
||||
Reference in new issue
Block a user