Skip to content

Commit 0350b87

Browse files
authored
test(ci): repair plugin prerelease contracts (#105272)
* test(ci): repair plugin prerelease contracts * docs(agents): clarify stale Testbox recovery * docs(agents): require Bash for Testbox compounds
1 parent a824078 commit 0350b87

6 files changed

Lines changed: 46 additions & 16 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,17 @@ Skills own workflows; root owns hard policy and routing.
131131

132132
- Use `$openclaw-testing` for test/CI choice and `$crabbox` for remote/full/E2E proof.
133133
- At task start, if code changes, tests, builds, typechecks, lint fan-out, Docker, packaging, E2E, or live proof are likely, classify source trust and immediately pre-warm the safe Crabbox backend in a background command session. Trusted maintainer code defaults to Blacksmith Testbox; untrusted contributor/fork code uses secretless fork CI or sanitized direct AWS Crabbox under the rule above. Continue inspection/editing while it hydrates; sync the current checkout for every run, reuse the lease, then stop it before handoff.
134-
- Warm Testbox from the task checkout; lease ownership is checkout-path scoped.
134+
- Warm Testbox from the task checkout; ownership is checkout-path scoped; `--reclaim` only for intentional transfer.
135+
- Base/head changed: stop and rewarm Testbox; never override stale lease checks.
136+
- Compound Testbox commands: `bash -lc`, never `sh -lc`; job env uses Bash `declare`.
135137
- Testbox cleanup: `blacksmith testbox stop --id <tbx_id>`; id is not positional.
136138
- PR review artifacts: keep template enum values; put evidence detail in summaries.
137139
- Crabbox request means real scenario proof: install/update/call/repro user path; not just copy tests and run them remotely.
138140
- Visual proof: use Crabbox, set up like a user, then screenshot-verify. No harness/bypass/shortcut unless explicitly asked.
139141
- Local agent work is limited to lightweight non-test checks such as `git diff --check`, targeted formatting, and cheap static probes. Tests and computationally intensive work default to the selected remote box.
140142
- In Codex or linked worktrees, direct local `pnpm test*`, `pnpm check*`, `pnpm crabbox:run`, and `scripts/committer` can trigger pnpm dependency reconciliation or install prompts. Prefer `node` wrappers locally and Crabbox/Testbox for pnpm-gated proof.
141143
- Crabbox wrapper `stop` has no `--timing-json`; use `node scripts/crabbox-wrapper.mjs stop --provider <provider> --id <id>`.
142-
- Repo-native PR worktree may omit `node_modules`; run dependency-backed formatter/docs scripts via Testbox or hosted CI.
144+
- Repo-native PR worktree may omit `node_modules`; prove remotely, then use `git commit --no-verify`, not `scripts/committer`.
143145
- Parallel agents share the checkout; never switch its branch while sibling work runs.
144146
- Full suites, changed gates, builds, typechecks, lint fan-out, Docker/package/E2E/live/cross-OS proof, or anything computationally intensive: Crabbox/Testbox.
145147
- If an allowed local fallback fans out or becomes expensive, stop it and move the work to the pre-warmed remote box.

extensions/codex/prompt-overlay-runtime-contract.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ describe("Codex prompt overlay runtime contract", () => {
1717

1818
expect(contribution?.stablePrefix).toContain("<persona_latch>");
1919
expect(contribution?.sectionOverrides?.interaction_style).toContain(
20-
"Live chat tone: short, natural, human.",
20+
"Live chat: short, natural, human.",
2121
);
2222
expect(contribution?.sectionOverrides?.interaction_style).not.toContain(
23-
"Use heartbeats to create useful proactive progress",
23+
"Heartbeat = useful proactive progress",
2424
);
2525
});
2626

extensions/codex/provider.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,8 @@ describe("codex provider", () => {
704704
stablePrefix: CODEX_GPT5_BEHAVIOR_CONTRACT,
705705
});
706706
const interactionStyle = contribution?.sectionOverrides?.interaction_style;
707-
expect(interactionStyle).toContain("Live chat tone: short, natural, human.");
708-
expect(interactionStyle).not.toContain("Use heartbeats to create useful proactive progress");
707+
expect(interactionStyle).toContain("Live chat: short, natural, human.");
708+
expect(interactionStyle).not.toContain("Heartbeat = useful proactive progress");
709709
});
710710

711711
it("does not add the GPT-5 prompt overlay to non-GPT-5 Codex provider runs", () => {

extensions/codex/src/app-server/run-attempt.context-engine.test.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
1717
import { createMockPluginRegistry } from "openclaw/plugin-sdk/plugin-test-runtime";
1818
import { registerSandboxBackend } from "openclaw/plugin-sdk/sandbox";
19+
import { formatSqliteSessionFileMarker } from "openclaw/plugin-sdk/sqlite-runtime-testing";
1920
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2021
import { CODEX_TURN_START_TEXT_INPUT_MAX_CHARS } from "./context-engine-projection.js";
2122
import type { CodexServerNotification } from "./protocol.js";
@@ -86,6 +87,31 @@ function createParams(sessionFile: string, workspaceDir: string): EmbeddedRunAtt
8687
} as EmbeddedRunAttemptParams;
8788
}
8889

90+
function createSqliteParams(workspaceDir: string, storeName: string): EmbeddedRunAttemptParams {
91+
const sessionId = "session-1";
92+
const sessionKey = "agent:main:session-1";
93+
const storePath = path.join(tempDir, `${storeName}.sqlite`);
94+
const sessionFile = formatSqliteSessionFileMarker({
95+
agentId: "main",
96+
sessionId,
97+
storePath,
98+
});
99+
const params = createParams(sessionFile, workspaceDir);
100+
params.sessionTarget = {
101+
agentId: "main",
102+
sessionId,
103+
sessionKey,
104+
storePath,
105+
};
106+
const message = userMessage("hello", Date.now());
107+
params.userTurnTranscriptRecorder = {
108+
message,
109+
resolveMessage: async () => message,
110+
markRuntimePersisted() {},
111+
} as EmbeddedRunAttemptParams["userTurnTranscriptRecorder"];
112+
return params;
113+
}
114+
89115
const DISABLED_CODEX_WEB_SEARCH_THREAD_CONFIG_FINGERPRINT = JSON.stringify({
90116
"features.standalone_web_search": false,
91117
web_search: "disabled",
@@ -1929,15 +1955,17 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => {
19291955
] as const)(
19301956
"keeps $name turns heartbeat-classified through afterTurn maintenance",
19311957
async (testCase) => {
1932-
const sessionFile = path.join(tempDir, "session.jsonl");
19331958
const workspaceDir = path.join(tempDir, "workspace");
19341959
const afterTurn = vi.fn(
19351960
async (_params: Parameters<NonNullable<ContextEngine["afterTurn"]>>[0]) => undefined,
19361961
);
19371962
const maintain = vi.fn(async () => ({ changed: false, bytesFreed: 0, rewrittenEntries: 0 }));
19381963
const contextEngine = createContextEngine({ afterTurn, maintain, bootstrap: undefined });
19391964
const harness = createStartedThreadHarness();
1940-
const params = createParams(sessionFile, workspaceDir);
1965+
const params = createSqliteParams(
1966+
workspaceDir,
1967+
`heartbeat-${testCase.bootstrapContextRunKind}`,
1968+
);
19411969
params.contextEngine = contextEngine;
19421970
params.trigger = testCase.trigger;
19431971
params.bootstrapContextRunKind = testCase.bootstrapContextRunKind;
@@ -2056,7 +2084,6 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => {
20562084
});
20572085

20582086
it("falls back to ingestBatch and skips turn maintenance on prompt failure", async () => {
2059-
const sessionFile = path.join(tempDir, "session.jsonl");
20602087
const workspaceDir = path.join(tempDir, "workspace");
20612088
const ingestBatch = vi.fn(async () => ({ ingestedCount: 2 }));
20622089
const maintain = vi.fn(async () => ({ changed: false, bytesFreed: 0, rewrittenEntries: 0 }));
@@ -2067,7 +2094,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => {
20672094
bootstrap: undefined,
20682095
});
20692096
const harness = createStartedThreadHarness();
2070-
const params = createParams(sessionFile, workspaceDir);
2097+
const params = createSqliteParams(workspaceDir, "prompt-failure");
20712098
params.contextEngine = contextEngine;
20722099

20732100
const run = runCodexAppServerAttempt(params);

extensions/codex/src/app-server/thread-lifecycle.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,10 @@ describe("Codex app-server native code mode config", () => {
511511
});
512512

513513
expect(instructions).toContain("## Skill Workshop");
514-
expect(instructions).toContain("Route durable skill work");
515-
expect(instructions).toContain("through the `skill_workshop` tool");
516-
expect(instructions).toContain("Generated skills are pending proposals.");
517-
expect(instructions).toContain("only when the user explicitly asks");
514+
expect(instructions).toContain("Durable reusable skill/playbook/workflow work");
515+
expect(instructions).toContain("`skill_workshop`");
516+
expect(instructions).toContain("Generated = pending proposal");
517+
expect(instructions).toContain("only explicit user ask");
518518
});
519519

520520
it("keeps developer instructions compact when no dynamic tools are deferred", () => {
@@ -1268,7 +1268,7 @@ describe("Codex app-server turn params", () => {
12681268
"This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake",
12691269
);
12701270
expect(heartbeatCollaborationMode.settings.developer_instructions).toContain(
1271-
"Use heartbeats to create useful proactive progress",
1271+
"Heartbeat = useful proactive progress",
12721272
);
12731273
expect(heartbeatCollaborationMode.settings.developer_instructions).toContain(
12741274
"If `heartbeat_respond` is not already available and `tool_search` is available",

src/plugin-sdk/plugin-test-api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function createTestPluginApi(api: TestPluginApiInput = {}): OpenClawPlugi
2525
registerHostedMediaResolver() {},
2626
registerChannel() {},
2727
registerGatewayMethod() {},
28+
registerSessionCatalog() {},
2829
registerCli() {},
2930
registerNodeCliFeature() {},
3031
registerCliBackend() {},
@@ -93,7 +94,7 @@ export function createTestPluginApi(api: TestPluginApiInput = {}): OpenClawPlugi
9394
},
9495
on() {},
9596
...flatApi,
96-
} as OpenClawPluginApiWithoutFacades;
97+
} satisfies OpenClawPluginApiWithoutFacades;
9798
// Facades derive nested `agent`, `lifecycle`, `runContext`, and `session`
9899
// views from the flat API; explicit overrides below let tests replace only
99100
// the nested surface under test without rebuilding every no-op method.

0 commit comments

Comments
 (0)