mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-20 05:31:59 +08:00
Compare commits
8 Commits
qa-fold-ht
...
migrate-ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f1112a576 | ||
|
|
5e932c9f18 | ||
|
|
343bb68a99 | ||
|
|
1cbe7d98cc | ||
|
|
a740724b87 | ||
|
|
4df934a516 | ||
|
|
3fa4fdaec1 | ||
|
|
efc36d71bd |
124
.github/workflows/maturity-scorecard.yml
vendored
Normal file
124
.github/workflows/maturity-scorecard.yml
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
name: Maturity scorecard
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source_run_id:
|
||||
description: Optional workflow run id containing qa-evidence.json artifacts
|
||||
required: false
|
||||
type: string
|
||||
artifact_pattern:
|
||||
description: Artifact name pattern to download from source_run_id
|
||||
required: false
|
||||
default: "*qa*"
|
||||
type: string
|
||||
strict_inputs:
|
||||
description: Fail when score or QA evidence inputs have non-fatal drift
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "taxonomy.yaml"
|
||||
- "docs/maturity-scores.yaml"
|
||||
- "docs/maturity-scorecard.md"
|
||||
- "docs/taxonomy.md"
|
||||
- "docs/taxonomy-outline.md"
|
||||
- "scripts/render-maturity-docs.ts"
|
||||
- "package.json"
|
||||
- ".github/workflows/maturity-scorecard.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "taxonomy.yaml"
|
||||
- "docs/maturity-scores.yaml"
|
||||
- "docs/maturity-scorecard.md"
|
||||
- "docs/taxonomy.md"
|
||||
- "docs/taxonomy-outline.md"
|
||||
- "scripts/render-maturity-docs.ts"
|
||||
- "package.json"
|
||||
- ".github/workflows/maturity-scorecard.yml"
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ format('{0}-{1}', github.workflow, github.ref) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
NODE_VERSION: "24.x"
|
||||
|
||||
jobs:
|
||||
render:
|
||||
name: Render maturity docs
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
install-bun: "false"
|
||||
|
||||
- name: Check committed maturity docs
|
||||
run: pnpm maturity:check
|
||||
|
||||
- name: Download QA evidence artifacts
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.source_run_id != '' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SOURCE_RUN_ID: ${{ inputs.source_run_id }}
|
||||
ARTIFACT_PATTERN: ${{ inputs.artifact_pattern }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p .artifacts/maturity-evidence
|
||||
gh run download "$SOURCE_RUN_ID" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--pattern "$ARTIFACT_PATTERN" \
|
||||
--dir .artifacts/maturity-evidence
|
||||
find .artifacts/maturity-evidence -name qa-evidence.json -print
|
||||
|
||||
- name: Render artifact docs
|
||||
env:
|
||||
STRICT_INPUTS: ${{ github.event_name == 'workflow_dispatch' && inputs.strict_inputs }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
args=(--output-dir .artifacts/maturity-docs --static-assets-dir .artifacts/maturity-docs/assets/maturity)
|
||||
if [[ "$STRICT_INPUTS" == "true" ]]; then
|
||||
args+=(--strict-inputs)
|
||||
fi
|
||||
if find .artifacts/maturity-evidence -name qa-evidence.json -print -quit 2>/dev/null | grep -q .; then
|
||||
args+=(--evidence-dir .artifacts/maturity-evidence)
|
||||
fi
|
||||
pnpm maturity:render -- "${args[@]}"
|
||||
{
|
||||
echo "### Maturity scorecard docs"
|
||||
echo
|
||||
echo "- Committed docs check: passed"
|
||||
echo "- Artifact docs: \`.artifacts/maturity-docs\`"
|
||||
echo "- Strict inputs: \`${STRICT_INPUTS:-false}\`"
|
||||
if find .artifacts/maturity-evidence -name qa-evidence.json -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "- QA evidence: included"
|
||||
else
|
||||
echo "- QA evidence: none downloaded"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload maturity docs artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: maturity-scorecard-docs-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: .artifacts/maturity-docs/
|
||||
retention-days: 30
|
||||
if-no-files-found: error
|
||||
120
.github/workflows/openclaw-release-checks.yml
vendored
120
.github/workflows/openclaw-release-checks.yml
vendored
@@ -768,6 +768,122 @@ jobs:
|
||||
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
|
||||
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
|
||||
|
||||
qa_profile_release_evidence_release_checks:
|
||||
name: Generate QA profile release evidence
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","qa"]'), needs.resolve_target.outputs.rerun_group)
|
||||
continue-on-error: true
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
environment: qa-live-shared
|
||||
env:
|
||||
OPENCLAW_BUILD_PRIVATE_QA: "1"
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS: "180000"
|
||||
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
install-bun: "true"
|
||||
|
||||
- name: Validate required QA credential env
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
|
||||
echo "Missing required OPENAI_API_KEY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build private QA runtime
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
run: node scripts/build-all.mjs qaRuntime
|
||||
|
||||
- name: Run release QA profile
|
||||
id: run_profile
|
||||
shell: bash
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
output_dir=".artifacts/qa-e2e/release-profile-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
|
||||
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
pnpm openclaw qa run \
|
||||
--repo-root . \
|
||||
--qa-profile release \
|
||||
--output-dir "${output_dir}"
|
||||
|
||||
- name: Upload release QA profile evidence
|
||||
id: upload_profile_evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-qa-profile-release-${{ needs.resolve_target.outputs.revision }}
|
||||
path: ${{ steps.run_profile.outputs.output_dir || '.artifacts/qa-e2e/' }}
|
||||
retention-days: 30
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Record advisory status
|
||||
if: always()
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_CHECK_JOB: qa_profile_release_evidence_release_checks
|
||||
JOB_STATUS: ${{ job.status }}
|
||||
RELEASE_CHECK_STEP_OUTCOMES: ${{ steps.run_profile.outcome }} ${{ steps.upload_profile_evidence.outcome }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
status="success"
|
||||
mark_status() {
|
||||
case "$1" in
|
||||
failure) status="failure" ;;
|
||||
cancelled)
|
||||
if [[ "$status" != "failure" ]]; then
|
||||
status="cancelled"
|
||||
fi
|
||||
;;
|
||||
success|skipped|"") ;;
|
||||
*) status="failure" ;;
|
||||
esac
|
||||
}
|
||||
mark_status "${JOB_STATUS:-}"
|
||||
for outcome in ${RELEASE_CHECK_STEP_OUTCOMES:-}; do
|
||||
mark_status "$outcome"
|
||||
done
|
||||
mkdir -p .artifacts/release-check-status
|
||||
status_path=".artifacts/release-check-status/${RELEASE_CHECK_JOB}.env"
|
||||
{
|
||||
printf 'job=%s\n' "$RELEASE_CHECK_JOB"
|
||||
printf 'status=%s\n' "$status"
|
||||
printf 'job_status=%s\n' "${JOB_STATUS:-}"
|
||||
printf 'step_outcomes=%s\n' "${RELEASE_CHECK_STEP_OUTCOMES:-}"
|
||||
} > "$status_path"
|
||||
|
||||
- name: Upload advisory status
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: release-check-status-qa-profile-release-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/release-check-status/qa_profile_release_evidence_release_checks.env
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
|
||||
qa_lab_parity_lane_release_checks:
|
||||
name: Run QA Lab parity lane (${{ matrix.lane }})
|
||||
needs: [resolve_target]
|
||||
@@ -1947,6 +2063,7 @@ jobs:
|
||||
- docker_e2e_release_checks
|
||||
- package_acceptance_release_checks
|
||||
- qa_lab_parity_lane_release_checks
|
||||
- qa_profile_release_evidence_release_checks
|
||||
- qa_lab_parity_report_release_checks
|
||||
- qa_lab_runtime_parity_release_checks
|
||||
- runtime_tool_coverage_release_checks
|
||||
@@ -2016,7 +2133,7 @@ jobs:
|
||||
}
|
||||
advisory_status_override_allowed() {
|
||||
case "$1" in
|
||||
qa_lab_parity_lane_release_checks|qa_lab_parity_report_release_checks|qa_lab_runtime_parity_release_checks|qa_live_matrix_release_checks|qa_live_telegram_release_checks|qa_live_discord_release_checks|qa_live_whatsapp_release_checks|qa_live_slack_release_checks)
|
||||
qa_lab_parity_lane_release_checks|qa_profile_release_evidence_release_checks|qa_lab_parity_report_release_checks|qa_lab_runtime_parity_release_checks|qa_live_matrix_release_checks|qa_live_telegram_release_checks|qa_live_discord_release_checks|qa_live_whatsapp_release_checks|qa_live_slack_release_checks)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
@@ -2032,6 +2149,7 @@ jobs:
|
||||
"docker_e2e_release_checks=${{ needs.docker_e2e_release_checks.result }}" \
|
||||
"package_acceptance_release_checks=${{ needs.package_acceptance_release_checks.result }}" \
|
||||
"qa_lab_parity_lane_release_checks=${{ needs.qa_lab_parity_lane_release_checks.result }}" \
|
||||
"qa_profile_release_evidence_release_checks=${{ needs.qa_profile_release_evidence_release_checks.result }}" \
|
||||
"qa_lab_parity_report_release_checks=${{ needs.qa_lab_parity_report_release_checks.result }}" \
|
||||
"qa_lab_runtime_parity_release_checks=${{ needs.qa_lab_runtime_parity_release_checks.result }}" \
|
||||
"runtime_tool_coverage_release_checks=${{ needs.runtime_tool_coverage_release_checks.result }}" \
|
||||
|
||||
@@ -37,7 +37,7 @@ that agent; if you copy credentials manually, copy only portable static
|
||||
`api_key` or `token` profiles.
|
||||
</Warning>
|
||||
|
||||
Skills are loaded from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filtered by the effective agent skill allowlist when configured. Use `agents.defaults.skills` for a shared baseline and `agents.list[].skills` for per-agent replacement. See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent skill allowlists](/tools/skills#agent-skill-allowlists).
|
||||
Skills are loaded from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filtered by the effective agent skill allowlist when configured. Use `agents.defaults.skills` for a shared baseline and `agents.list[].skills` for per-agent replacement. See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent skill allowlists](/tools/skills#agent-allowlists).
|
||||
|
||||
The Gateway can host **one agent** (default) or **many agents** side-by-side.
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ it disabled for read-only shared skill roots.
|
||||
|
||||
Related:
|
||||
|
||||
- [Skills config](/tools/skills-config#symlinked-sibling-repos)
|
||||
- [Skills config](/tools/skills-config#symlinked-skill-roots)
|
||||
- [Configuration examples](/gateway/configuration-examples#symlinked-sibling-skill-repo)
|
||||
|
||||
## Anthropic 429 extra usage required for long context
|
||||
|
||||
@@ -98,16 +98,6 @@ export type HarnessParityResult = {
|
||||
firstDriftTurn?: number;
|
||||
};
|
||||
|
||||
export type HarnessParityReport = {
|
||||
generatedAt: string;
|
||||
providerMode: string;
|
||||
left: HarnessVariant;
|
||||
right: HarnessVariant;
|
||||
results: HarnessParityResult[];
|
||||
pass: boolean;
|
||||
failures: string[];
|
||||
};
|
||||
|
||||
function sha256(value: string) {
|
||||
return createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ export type QaRuntimeCapabilityLayer =
|
||||
| "optional-profile-or-plugin"
|
||||
| "structural-text";
|
||||
|
||||
export type QaCodexToolLoading = "direct" | "searchable";
|
||||
|
||||
export type RuntimeParityComparisonMode = "default" | "codex-native-workspace" | "outcome-only";
|
||||
|
||||
export type QaRuntimeToolCoverageMetadata = {
|
||||
|
||||
@@ -50,19 +50,22 @@ describe("qa scenario catalog", () => {
|
||||
expect(
|
||||
scenarioIds.filter((scenarioId) => requiredScenarioIds.includes(scenarioId)).toSorted(),
|
||||
).toEqual(requiredScenarioIds);
|
||||
const nativeExecutionScenarios = pack.scenarios.filter(
|
||||
(scenario) => scenario.execution.kind !== "flow",
|
||||
expect(
|
||||
pack.scenarios
|
||||
.filter((scenario) => scenario.execution?.kind !== "flow")
|
||||
.map((scenario) => scenario.id)
|
||||
.toSorted(),
|
||||
).toStrictEqual(
|
||||
[
|
||||
"channel-message-flows",
|
||||
"control-ui-chat-flow-playwright",
|
||||
"gateway-smoke",
|
||||
"package-openclaw-for-docker",
|
||||
"plugin-lifecycle-probe",
|
||||
"qa-otel-smoke",
|
||||
"ux-matrix-evidence-dashboard",
|
||||
].toSorted(),
|
||||
);
|
||||
expect(nativeExecutionScenarios.length).toBeGreaterThan(0);
|
||||
for (const scenario of nativeExecutionScenarios) {
|
||||
const execution = scenario.execution;
|
||||
if (execution.kind === "flow") {
|
||||
throw new Error(`expected native execution scenario: ${scenario.id}`);
|
||||
}
|
||||
expect(["playwright", "script", "vitest"]).toContain(execution.kind);
|
||||
expect(fs.existsSync(execution.path), `${scenario.id} execution.path exists`).toBe(true);
|
||||
expect(execution.flow).toBeUndefined();
|
||||
}
|
||||
expect(
|
||||
pack.scenarios
|
||||
.filter((scenario) => scenario.execution.kind === "flow")
|
||||
@@ -173,21 +176,6 @@ describe("qa scenario catalog", () => {
|
||||
expect(uxMatrix.coverage?.primary).toContain("qa.artifact-safety");
|
||||
});
|
||||
|
||||
it("loads folded HTTP API script scenarios with primary taxonomy coverage", () => {
|
||||
expect(readQaScenarioById("openai-compatible-chat-tools").coverage?.primary).toStrictEqual([
|
||||
"gateway.openai-compatible-apis",
|
||||
]);
|
||||
expect(readQaScenarioById("openai-web-search-minimal").coverage?.primary).toStrictEqual([
|
||||
"runtime.reasoning-and-cache-controls",
|
||||
]);
|
||||
expect(
|
||||
readQaScenarioById("openai-web-search-native-assertions").coverage?.primary,
|
||||
).toStrictEqual(["web-search.openai-native-web-search", "plugins.web-search-and-fetch"]);
|
||||
expect(readQaScenarioById("openwebui-openai-compatible").coverage?.primary).toStrictEqual([
|
||||
"gateway.openai-compatible-apis",
|
||||
]);
|
||||
});
|
||||
|
||||
it("loads runtime parity tier metadata for first-hour and soak lanes", () => {
|
||||
const firstHour = readQaScenarioById("runtime-first-hour-20-turn");
|
||||
const soak = readQaScenarioById("runtime-soak-100-turn");
|
||||
|
||||
@@ -1573,6 +1573,8 @@
|
||||
"docs:list": "node scripts/docs-list.js",
|
||||
"docs:spellcheck": "bash scripts/docs-spellcheck.sh",
|
||||
"docs:spellcheck:fix": "bash scripts/docs-spellcheck.sh --write",
|
||||
"maturity:check": "node --import tsx scripts/render-maturity-docs.ts --check",
|
||||
"maturity:render": "node --import tsx scripts/render-maturity-docs.ts",
|
||||
"dup:check": "node scripts/check-duplicates.mjs",
|
||||
"dup:check:coverage": "node scripts/check-duplicates.mjs --coverage",
|
||||
"dup:check:json": "node scripts/check-duplicates.mjs --json",
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
title: OpenAI-compatible chat tools HTTP API
|
||||
|
||||
scenario:
|
||||
id: openai-compatible-chat-tools
|
||||
surface: runtime
|
||||
coverage:
|
||||
primary:
|
||||
- gateway.openai-compatible-apis
|
||||
secondary:
|
||||
- runtime.hosted-tool-use
|
||||
objective: Verify the OpenAI-compatible chat-completions client and Docker lane preserve strict tool-call API behavior.
|
||||
successCriteria:
|
||||
- The Docker lane fails missing or placeholder OpenAI auth before Docker build work starts.
|
||||
- The generated config preserves strict positive gateway port and timeout values.
|
||||
- The chat-completions client posts to `/v1/chat/completions` with the expected gateway token and model header.
|
||||
- Tool-call-only responses are accepted, visible content beside a tool call is rejected, and response bodies remain bounded.
|
||||
docsRefs:
|
||||
- docs/gateway/protocol.md
|
||||
- docs/help/testing.md
|
||||
- docs/concepts/qa-e2e-automation.md
|
||||
codeRefs:
|
||||
- scripts/e2e/lib/openai-chat-tools/client.mjs
|
||||
- scripts/e2e/lib/openai-chat-tools/write-config.mjs
|
||||
- scripts/e2e/openai-chat-tools-docker.sh
|
||||
- test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts
|
||||
summary: Vitest coverage for OpenAI-compatible chat-completions tool-call API behavior.
|
||||
@@ -1,29 +0,0 @@
|
||||
title: OpenAI web_search minimal reasoning gate
|
||||
|
||||
scenario:
|
||||
id: openai-web-search-minimal
|
||||
surface: model-provider
|
||||
coverage:
|
||||
primary:
|
||||
- runtime.reasoning-and-cache-controls
|
||||
secondary:
|
||||
- web-search.openai-native-web-search
|
||||
- tools.web-search
|
||||
objective: Verify the OpenAI web_search minimal-reasoning E2E client distinguishes successful grounded turns from provider schema rejection.
|
||||
successCriteria:
|
||||
- Reject mode accepts the expected raw OpenAI schema rejection and the gateway schema wrapper.
|
||||
- Reject mode fails if the agent run unexpectedly succeeds or fails for unrelated transport reasons.
|
||||
- Success mode requires an `ok` agent result with the expected marker in visible reply payloads.
|
||||
- Gateway ports are parsed strictly before connecting.
|
||||
docsRefs:
|
||||
- docs/tools/web.md
|
||||
- docs/help/testing.md
|
||||
- docs/concepts/qa-e2e-automation.md
|
||||
codeRefs:
|
||||
- scripts/e2e/lib/openai-web-search-minimal/client.mjs
|
||||
- scripts/e2e/openai-web-search-minimal-docker.sh
|
||||
- test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts
|
||||
summary: Vitest coverage for OpenAI web_search minimal-reasoning success and rejection validation.
|
||||
@@ -1,30 +0,0 @@
|
||||
title: OpenAI native web_search request assertions
|
||||
|
||||
scenario:
|
||||
id: openai-web-search-native-assertions
|
||||
surface: model-provider
|
||||
coverage:
|
||||
primary:
|
||||
- web-search.openai-native-web-search
|
||||
- plugins.web-search-and-fetch
|
||||
secondary:
|
||||
- web-search.model-and-filter-routing
|
||||
- tools.web-search
|
||||
objective: Verify the OpenAI web_search Docker lane assertions require native Responses web_search evidence with bounded diagnostics.
|
||||
successCriteria:
|
||||
- A successful request must hit `/v1/responses` with native `web_search` and non-minimal reasoning.
|
||||
- Large request logs are scanned without missing later success requests.
|
||||
- Failure diagnostics are bounded and do not dump stale or oversized request bodies.
|
||||
- Function-shaped `web_search` is rejected as native Responses proof.
|
||||
docsRefs:
|
||||
- docs/tools/web.md
|
||||
- docs/help/testing.md
|
||||
- docs/concepts/qa-e2e-automation.md
|
||||
codeRefs:
|
||||
- scripts/e2e/lib/openai-web-search-minimal/assertions.mjs
|
||||
- scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs
|
||||
- test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts
|
||||
summary: Vitest coverage for native OpenAI web_search request-log assertions.
|
||||
@@ -1,28 +0,0 @@
|
||||
title: OpenWebUI OpenAI-compatible API probe
|
||||
|
||||
scenario:
|
||||
id: openwebui-openai-compatible
|
||||
surface: runtime
|
||||
coverage:
|
||||
primary:
|
||||
- gateway.openai-compatible-apis
|
||||
secondary:
|
||||
- runtime.hosted-provider-turns
|
||||
- runtime.provider-specific-model-options
|
||||
objective: Verify the OpenWebUI E2E probe exercises OpenClaw through OpenWebUI's OpenAI-compatible model and chat APIs.
|
||||
successCriteria:
|
||||
- Probe environment limits are parsed strictly and control-plane requests time out quickly.
|
||||
- Sign-in and model-list error bodies are bounded before diagnostics are emitted.
|
||||
- Models mode authenticates and finds the OpenClaw model exposed by OpenWebUI.
|
||||
- Chat mode posts to `/api/chat/completions`, validates the expected nonce, and fails when the reply omits it.
|
||||
docsRefs:
|
||||
- docs/help/testing.md
|
||||
- docs/concepts/qa-e2e-automation.md
|
||||
codeRefs:
|
||||
- scripts/e2e/openwebui-probe.mjs
|
||||
- scripts/e2e/openwebui-docker.sh
|
||||
- test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts
|
||||
summary: Vitest coverage for OpenWebUI model and chat-completions probe behavior.
|
||||
1345
scripts/render-maturity-docs.ts
Normal file
1345
scripts/render-maturity-docs.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1067,70 +1067,28 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
|
||||
"src/image-generation/openai-compatible-image-provider.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-chat-tools/client.mjs",
|
||||
["test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-chat-tools/scenario.sh",
|
||||
["test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-chat-tools/write-config.mjs",
|
||||
["test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/openai-chat-tools-docker.sh",
|
||||
[
|
||||
"test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-web-search-minimal/assertions.mjs",
|
||||
["test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-web-search-minimal/client.mjs",
|
||||
["test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs",
|
||||
[
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-web-search-minimal/scenario.sh",
|
||||
[
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts",
|
||||
],
|
||||
["test/scripts/openai-chat-tools-client.test.ts", "test/scripts/docker-e2e-plan.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/openai-web-search-minimal-docker.sh",
|
||||
[
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-client.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-assertions.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openwebui/http-probe.mjs",
|
||||
["test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/openwebui-docker.sh",
|
||||
[
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts",
|
||||
"test/scripts/openwebui-probe.test.ts",
|
||||
"test/scripts/fixture-config.test.ts",
|
||||
],
|
||||
],
|
||||
["scripts/e2e/openwebui-probe.mjs", ["test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts"]],
|
||||
[
|
||||
"scripts/e2e/plugin-binding-command-escape-docker.sh",
|
||||
[
|
||||
|
||||
@@ -885,6 +885,7 @@ describe("test-projects args", () => {
|
||||
"test/scripts/ios-team-id.test.ts",
|
||||
"test/scripts/ios-version.test.ts",
|
||||
"test/scripts/kitchen-sink-rpc-walk.test.ts",
|
||||
"test/scripts/openai-chat-tools-client.test.ts",
|
||||
"test/scripts/report-test-temp-creations.test.ts",
|
||||
"test/scripts/test-projects.test.ts",
|
||||
"test/test-env.test.ts",
|
||||
@@ -911,7 +912,6 @@ describe("test-projects args", () => {
|
||||
config: "test/vitest/vitest.e2e.config.ts",
|
||||
forwardedArgs: [
|
||||
"test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts",
|
||||
"test/openclaw-launcher.e2e.test.ts",
|
||||
],
|
||||
includePatterns: null,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// OpenAI-compatible chat tools tests cover QA Lab HTTP tool-call evidence.
|
||||
// Openai Chat Tools Client tests cover openai chat tools client script behavior.
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { createServer, type Server } from "node:http";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import { createBoundedChildOutput } from "../../../helpers/bounded-child-output.js";
|
||||
import { cleanupTempDirs, makeTempDir } from "../../../helpers/temp-dir.js";
|
||||
import { createBoundedChildOutput } from "../helpers/bounded-child-output.js";
|
||||
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
|
||||
|
||||
const clientPath = path.resolve("scripts/e2e/lib/openai-chat-tools/client.mjs");
|
||||
const dockerRunnerPath = path.resolve("scripts/e2e/openai-chat-tools-docker.sh");
|
||||
@@ -14,6 +14,7 @@ const writeConfigPath = path.resolve("scripts/e2e/lib/openai-chat-tools/write-co
|
||||
|
||||
interface ClientResult {
|
||||
error?: Error;
|
||||
signal: NodeJS.Signals | null;
|
||||
status: number | null;
|
||||
stderr: string;
|
||||
stdout: string;
|
||||
@@ -68,12 +69,13 @@ function runClient(
|
||||
}, timeout);
|
||||
child.on("error", (error) => {
|
||||
clearTimeout(timer);
|
||||
resolve({ error, status: null, stderr: stderr.text(), stdout: stdout.text() });
|
||||
resolve({ error, signal: null, status: null, stderr: stderr.text(), stdout: stdout.text() });
|
||||
});
|
||||
child.on("exit", (status) => {
|
||||
child.on("exit", (status, signal) => {
|
||||
clearTimeout(timer);
|
||||
resolve({
|
||||
error: timedOut ? new Error(`client timed out after ${timeout}ms`) : undefined,
|
||||
signal,
|
||||
status,
|
||||
stderr: stderr.text(),
|
||||
stdout: stdout.text(),
|
||||
@@ -1,4 +1,4 @@
|
||||
// OpenAI web-search minimal assertion tests cover QA Lab native web_search evidence.
|
||||
// Openai Web Search Minimal Assertions tests cover openai web search minimal assertions script behavior.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -1,6 +1,6 @@
|
||||
// OpenAI web-search minimal tests cover QA Lab hosted provider schema evidence.
|
||||
// Openai Web Search Minimal Client tests cover openai web search minimal client script behavior.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { testing } from "../../../../scripts/e2e/lib/openai-web-search-minimal/client.mjs";
|
||||
import { testing } from "../../scripts/e2e/lib/openai-web-search-minimal/client.mjs";
|
||||
|
||||
describe("scripts/e2e/lib/openai-web-search-minimal/client.mjs", () => {
|
||||
it("accepts only the expected raw schema rejection in reject mode", () => {
|
||||
@@ -1,16 +1,17 @@
|
||||
// OpenWebUI probe tests cover QA Lab OpenAI-compatible API evidence.
|
||||
// Openwebui Probe tests cover openwebui probe script behavior.
|
||||
import { spawn } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createServer, type IncomingMessage, type Server as HttpServer } from "node:http";
|
||||
import { createServer as createTcpServer, type Server as TcpServer, type Socket } from "node:net";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createBoundedChildOutput } from "../../../helpers/bounded-child-output.js";
|
||||
import { createBoundedChildOutput } from "../helpers/bounded-child-output.js";
|
||||
|
||||
const probePath = path.resolve("scripts/e2e/openwebui-probe.mjs");
|
||||
|
||||
interface ProbeResult {
|
||||
error?: Error;
|
||||
signal: NodeJS.Signals | null;
|
||||
status: number | null;
|
||||
stderr: string;
|
||||
stdout: string;
|
||||
@@ -66,12 +67,13 @@ function runProbe(baseUrl: string, env: Record<string, string> = {}, timeout = 3
|
||||
}, timeout);
|
||||
child.on("error", (error) => {
|
||||
clearTimeout(timer);
|
||||
resolve({ error, status: null, stderr: stderr.text(), stdout: stdout.text() });
|
||||
resolve({ error, signal: null, status: null, stderr: stderr.text(), stdout: stdout.text() });
|
||||
});
|
||||
child.on("exit", (status) => {
|
||||
child.on("exit", (status, signal) => {
|
||||
clearTimeout(timer);
|
||||
resolve({
|
||||
error: timedOut ? new Error(`probe timed out after ${timeout}ms`) : undefined,
|
||||
signal,
|
||||
status,
|
||||
stderr: stderr.text(),
|
||||
stdout: stdout.text(),
|
||||
@@ -294,7 +294,7 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
it("routes nested scripts through conventional owner tests", () => {
|
||||
expect(resolveChangedTestTargetPlan(["scripts/e2e/openwebui-probe.mjs"])).toEqual({
|
||||
mode: "targets",
|
||||
targets: ["test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts"],
|
||||
targets: ["test/scripts/openwebui-probe.test.ts"],
|
||||
});
|
||||
expect(resolveChangedTestTargetPlan(["scripts/lib/docker-e2e-plan.mjs"])).toEqual({
|
||||
mode: "targets",
|
||||
@@ -374,24 +374,21 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-chat-tools/write-config.mjs",
|
||||
["test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts"],
|
||||
["test/scripts/openai-chat-tools-client.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-chat-tools/scenario.sh",
|
||||
["test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts"],
|
||||
["test/scripts/openai-chat-tools-client.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/openai-chat-tools-docker.sh",
|
||||
[
|
||||
"test/e2e/qa-lab/runtime/openai-compatible-chat-tools.e2e.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
],
|
||||
["test/scripts/openai-chat-tools-client.test.ts", "test/scripts/docker-e2e-plan.test.ts"],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs",
|
||||
[
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-client.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-assertions.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -399,8 +396,8 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
[
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal.e2e.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openai-web-search-minimal-assertions.e2e.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-client.test.ts",
|
||||
"test/scripts/openai-web-search-minimal-assertions.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -408,14 +405,11 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
[
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
"test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts",
|
||||
"test/scripts/openwebui-probe.test.ts",
|
||||
"test/scripts/fixture-config.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"scripts/e2e/lib/openwebui/http-probe.mjs",
|
||||
["test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts"],
|
||||
],
|
||||
["scripts/e2e/lib/openwebui/http-probe.mjs", ["test/scripts/openwebui-probe.test.ts"]],
|
||||
[
|
||||
"scripts/e2e/lib/plugins/npm-registry-server.mjs",
|
||||
["test/scripts/plugins-assertions.test.ts"],
|
||||
|
||||
Reference in New Issue
Block a user