diff --git a/.github/workflows/npm-telegram-beta-e2e.yml b/.github/workflows/npm-telegram-beta-e2e.yml index 2f1125c754e6..2516ceb3658c 100644 --- a/.github/workflows/npm-telegram-beta-e2e.yml +++ b/.github/workflows/npm-telegram-beta-e2e.yml @@ -352,7 +352,7 @@ jobs: } attempt_started_at="$(jq -er '.run_started_at | fromdateiso8601' <<< "$attempt_json")" if [[ "$ARTIFACT_RUN_ID" == "$GITHUB_RUN_ID" ]]; then - jq -e '(.status == "queued" or .status == "in_progress") and .conclusion == null' \ + jq -e '(.status == "pending" or .status == "queued" or .status == "in_progress") and .conclusion == null' \ <<< "$attempt_json" >/dev/null || { echo "Current-run Package Telegram artifact is not from the active workflow attempt." >&2 exit 1 diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml index 68961097a974..c14a0bad4e3b 100644 --- a/.github/workflows/openclaw-release-checks.yml +++ b/.github/workflows/openclaw-release-checks.yml @@ -1665,6 +1665,10 @@ jobs: expected_sha: ${{ needs.resolve_target.outputs.revision }} run_matrix: true fail_fast: ${{ fromJSON(needs.resolve_target.outputs.fail_fast) }} + secrets: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }} + OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }} qa_live_buzz_release_checks: name: Run QA Lab live Buzz lane @@ -1679,6 +1683,10 @@ jobs: expected_sha: ${{ needs.resolve_target.outputs.revision }} run_buzz: true buzz_scenario: channel-canary,channel-mention-gating + secrets: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }} + OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }} # The dispatched child owns Telegram evidence/status artifacts; this blocking job # carries its exact conclusion into the parent summary without copying secrets or artifacts. diff --git a/.github/workflows/qa-live-transports-convex.yml b/.github/workflows/qa-live-transports-convex.yml index e5096edfdbc2..ab657e69b296 100644 --- a/.github/workflows/qa-live-transports-convex.yml +++ b/.github/workflows/qa-live-transports-convex.yml @@ -58,6 +58,16 @@ on: required: false default: false type: boolean + secrets: + OPENAI_API_KEY: + description: OpenAI API key used by live QA scenarios + required: false + OPENCLAW_QA_CONVEX_SITE_URL: + description: Convex credential broker URL + required: false + OPENCLAW_QA_CONVEX_SECRET_CI: + description: Convex CI credential + required: false workflow_dispatch: inputs: ref: diff --git a/extensions/qa-lab/src/scenario-catalog-channels.test.ts b/extensions/qa-lab/src/scenario-catalog-channels.test.ts index e9c6fac77586..057a4091925d 100644 --- a/extensions/qa-lab/src/scenario-catalog-channels.test.ts +++ b/extensions/qa-lab/src/scenario-catalog-channels.test.ts @@ -79,6 +79,7 @@ describe("qa scenario catalog channel contracts", () => { expect(flow).toContain('"call":"startAgentRun"'); expect(flow).not.toContain('"call":"runAgentPrompt"'); + expect(flow).toContain('"taskTracking":false'); expect(flow).toContain('"saveAs":"parentOutbound"'); expect(flow).toContain("messages.slice(parentOutboundStartIndex)"); expect(flow).not.toContain("waitForAgentHistoryReply"); diff --git a/qa/scenarios/agents/subagent-fanout-synthesis.yaml b/qa/scenarios/agents/subagent-fanout-synthesis.yaml index bf90728c933d..a2968359b2aa 100644 --- a/qa/scenarios/agents/subagent-fanout-synthesis.yaml +++ b/qa/scenarios/agents/subagent-fanout-synthesis.yaml @@ -105,6 +105,7 @@ flow: ref: sessionKey message: ref: prompt + taskTracking: false timeoutMs: expr: liveTurnTimeoutMs(env, 30000) - call: waitForCondition diff --git a/scripts/test-live-shard.mjs b/scripts/test-live-shard.mjs index f02a9b4d75d0..b0fc932c9eae 100644 --- a/scripts/test-live-shard.mjs +++ b/scripts/test-live-shard.mjs @@ -29,6 +29,11 @@ const OPTIONAL_LIVE_SHARD_FILE_ENVS = new Map([ ["src/agents/subagent-announce.live.test.ts", ["OPENCLAW_LIVE_SUBAGENT_E2E"]], ["src/agents/tools/image-tool.ollama.live.test.ts", ["OPENCLAW_LIVE_OLLAMA_IMAGE"]], ["src/agents/tools/image-tool.providers.live.test.ts", ["OPENCLAW_LIVE_IMAGE_TOOL_TEST"]], + [ + "extensions/openai/realtime-quicksilver-gateway-bridge.live.test.ts", + ["OPENCLAW_LIVE_GPT_LIVE"], + ], + ["extensions/openai/realtime-quicksilver.live.test.ts", ["OPENCLAW_LIVE_GPT_LIVE"]], ["src/skills/workshop/experience-review.live.test.ts", ["OPENCLAW_LIVE_SKILL_EXPERIENCE_REVIEW"]], ["src/system-agent/rescue-channel.live.test.ts", ["OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL"]], ["src/gateway/android-node.capabilities.live.test.ts", ["OPENCLAW_LIVE_ANDROID_NODE"]], diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index fb539addc943..34dee02c5849 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -245,7 +245,7 @@ function runNpmTelegramInputValidation(overrides: Record) { function runNpmTelegramArtifactValidation(params: { currentRunId: string; producerRunId: string; - producerStatus: "completed" | "in_progress" | "queued"; + producerStatus: "completed" | "in_progress" | "pending" | "queued"; producerConclusion: "success" | null; }) { const job = workflowJob(NPM_TELEGRAM_WORKFLOW, "run_package_telegram_e2e"); @@ -2583,7 +2583,11 @@ describe("package artifact reuse", () => { const releaseJob = workflowJob(RELEASE_CHECKS_WORKFLOW, "qa_live_release_checks"); expect(releaseJob.uses).toBe("./.github/workflows/qa-live-transports-convex.yml"); - expect(releaseJob.secrets).toBeUndefined(); + expect(releaseJob.secrets).toEqual({ + OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}", + OPENCLAW_QA_CONVEX_SECRET_CI: "${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}", + OPENCLAW_QA_CONVEX_SITE_URL: "${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}", + }); expect(releaseJob.permissions).toEqual({ contents: "read", "pull-requests": "read" }); expect(releaseJob.if).toContain('contains(fromJSON(\'["all","qa","qa-live"]\')'); expect(releaseJob.with).toMatchObject({ @@ -2650,7 +2654,11 @@ describe("package artifact reuse", () => { const releaseJob = workflowJob(RELEASE_CHECKS_WORKFLOW, "qa_live_buzz_release_checks"); expect(releaseJob.uses).toBe("./.github/workflows/qa-live-transports-convex.yml"); - expect(releaseJob.secrets).toBeUndefined(); + expect(releaseJob.secrets).toEqual({ + OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}", + OPENCLAW_QA_CONVEX_SECRET_CI: "${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}", + OPENCLAW_QA_CONVEX_SITE_URL: "${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}", + }); expect(releaseJob.permissions).toEqual({ contents: "read", "pull-requests": "read" }); expect(releaseJob.if).toContain('contains(fromJSON(\'["all","qa","qa-live"]\')'); expect(releaseJob.if).toContain("needs.resolve_target.outputs.qa_live_buzz_enabled == 'true'"); @@ -3212,6 +3220,7 @@ describe("package artifact reuse", () => { '--arg digest "sha256:${ARTIFACT_DIGEST}"', "actions/runs/${ARTIFACT_RUN_ID}/attempts/${ARTIFACT_RUN_ATTEMPT}", 'if [[ "$ARTIFACT_RUN_ID" == "$GITHUB_RUN_ID" ]]', + '.status == "pending"', '.status == "queued" or .status == "in_progress"', ".conclusion == null", "Package Telegram artifact predates the active producer run attempt.", @@ -3269,6 +3278,17 @@ describe("package artifact reuse", () => { expect(result.status, result.stderr).toBe(0); }); + it("accepts active artifacts while GitHub reports the workflow as pending", () => { + const result = runNpmTelegramArtifactValidation({ + currentRunId: "123", + producerConclusion: null, + producerRunId: "123", + producerStatus: "pending", + }); + + expect(result.status, result.stderr).toBe(0); + }); + it("rejects queued artifacts after GitHub assigns a conclusion", () => { const result = runNpmTelegramArtifactValidation({ currentRunId: "123", diff --git a/test/scripts/test-live-shard.test.ts b/test/scripts/test-live-shard.test.ts index 3d4d866f94a9..3b6b9dc469af 100644 --- a/test/scripts/test-live-shard.test.ts +++ b/test/scripts/test-live-shard.test.ts @@ -420,6 +420,40 @@ describe("scripts/test-live-shard", () => { }); }); + it("allows GPT-Live files to be skipped until their shared opt-in is enabled", () => { + const quicksilverFiles = [ + "extensions/openai/realtime-quicksilver-gateway-bridge.live.test.ts", + "extensions/openai/realtime-quicksilver.live.test.ts", + ]; + const payload = { + numPassedTests: 1, + numTotalTests: 3, + testResults: [ + { + name: path.join(process.cwd(), "extensions/openai/openai.live.test.ts"), + assertionResults: [{ status: "passed" }], + }, + ...quicksilverFiles.map((file) => ({ + name: path.join(process.cwd(), file), + assertionResults: [{ status: "skipped" }], + })), + ], + }; + const expectedFiles = ["extensions/openai/openai.live.test.ts", ...quicksilverFiles]; + + expect(validateLiveShardReportPayload(payload, expectedFiles, process.cwd(), {})).toEqual({ + ok: true, + }); + expect( + validateLiveShardReportPayload(payload, expectedFiles, process.cwd(), { + OPENCLAW_LIVE_GPT_LIVE: "1", + }), + ).toEqual({ + ok: false, + reason: `Vitest report selected live test files had no passing assertions: ${quicksilverFiles.join(", ")}`, + }); + }); + it("does not count disabled opt-in sentinel assertions as live shard proof", () => { const payload = { numPassedTests: 1,