Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 107 additions & 21 deletions .github/workflows/live-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -495,12 +495,37 @@ jobs:
retention-days: 14

reborn-webui-v2-live-qa:
name: Reborn WebUI v2 Live QA
name: Reborn WebUI v2 Live QA (${{ matrix.shard_name }})
if: >
(github.event_name == 'schedule' && github.event.schedule == '0 */3 * * *') ||
(github.event_name == 'workflow_dispatch' && (inputs.lane == 'all' || inputs.lane == 'reborn-webui-v2-live-qa'))
runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- shard_id: qa-2
shard_name: QA 2
cases: qa_2a_gmail_connect,qa_2b_calendar_connect,qa_2c_drive_connect,qa_2d_calendar_prep_live_chat,qa_2e_calendar_prep_email_routine,qa_2f_calendar_prep_email_delivery
- shard_id: qa-3
shard_name: QA 3
cases: qa_3a_slack_connect,qa_3b_endpoint_status_live_chat,qa_3c_endpoint_status_slack_routine,qa_3d_endpoint_status_slack_delivery
- shard_id: qa-4
shard_name: QA 4
cases: qa_4a_gmail_connect,qa_4b_github_connect,qa_4c_github_release_live_chat,qa_4d_github_release_slack_routine,qa_4e_github_release_email_delivery
- shard_id: qa-5
shard_name: QA 5
cases: qa_5a_slack_connect,qa_5b_drive_connect,qa_5c_strategy_doc_knowledge_base,qa_5d_slack_strategy_doc_answer
- shard_id: qa-6
shard_name: QA 6
cases: qa_6a_gmail_connect,qa_6b_sheets_connect,qa_6c_gmail_to_sheet_live_chat,qa_6d_gmail_to_sheet_routine,qa_6e_gmail_to_sheet_delivery
- shard_id: qa-7
shard_name: QA 7
cases: qa_7a_slack_product_channel_connect,qa_7b_sheets_connect,qa_7c_slack_bug_logger_routine,qa_7d_slack_bug_message_trigger,qa_7e_slack_bug_sheet_delivery
- shard_id: qa-8
shard_name: QA 8
cases: qa_8a_slack_connect,qa_8b_hn_keyword_live_chat,qa_8c_hn_keyword_slack_routine,qa_8d_hn_keyword_slack_delivery
env:
REBORN_WEBUI_V2_LIVE_QA_LLM_API_KEY_ENV: NEARAI_API_KEY
REBORN_WEBUI_V2_LIVE_QA_LLM_PROVIDER_ID: nearai
Expand All @@ -512,28 +537,102 @@ jobs:
REBORN_WEBUI_V2_LIVE_QA_SLACK_TEAM_ID: ${{ vars.REBORN_WEBUI_V2_LIVE_QA_SLACK_TEAM_ID }}
REBORN_WEBUI_V2_LIVE_QA_SLACK_API_APP_ID: ${{ vars.REBORN_WEBUI_V2_LIVE_QA_SLACK_API_APP_ID }}
REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_CHANNEL_ID: ${{ vars.REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_CHANNEL_ID }}
REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_USER_ID: ${{ vars.REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_USER_ID }}
REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_SUBJECT_USER_ID: ${{ vars.REBORN_WEBUI_V2_LIVE_QA_SLACK_ROUTE_SUBJECT_USER_ID }}
LIVE_OPENAI_COMPATIBLE_BASE_URL: ${{ vars.LIVE_OPENAI_COMPATIBLE_BASE_URL || 'https://cloud-api.near.ai/v1' }}
LIVE_OPENAI_COMPATIBLE_MODEL: ${{ vars.LIVE_OPENAI_COMPATIBLE_MODEL || 'Qwen/Qwen3.5-122B-A10B' }}
steps:
- name: Resolve Reborn WebUI v2 live QA cases
id: resolve_reborn_webui_v2_cases
shell: bash
env:
REQUESTED_CASES: ${{ github.event_name == 'schedule' && 'all' || inputs.cases || vars.REBORN_WEBUI_V2_LIVE_QA_CASES || 'all' }}
SHARD_CASES: ${{ matrix.cases }}
ALL_SHARD_CASES: >-
qa_2a_gmail_connect,qa_2b_calendar_connect,qa_2c_drive_connect,qa_2d_calendar_prep_live_chat,qa_2e_calendar_prep_email_routine,qa_2f_calendar_prep_email_delivery,
qa_3a_slack_connect,qa_3b_endpoint_status_live_chat,qa_3c_endpoint_status_slack_routine,qa_3d_endpoint_status_slack_delivery,
qa_4a_gmail_connect,qa_4b_github_connect,qa_4c_github_release_live_chat,qa_4d_github_release_slack_routine,qa_4e_github_release_email_delivery,
qa_5a_slack_connect,qa_5b_drive_connect,qa_5c_strategy_doc_knowledge_base,qa_5d_slack_strategy_doc_answer,
qa_6a_gmail_connect,qa_6b_sheets_connect,qa_6c_gmail_to_sheet_live_chat,qa_6d_gmail_to_sheet_routine,qa_6e_gmail_to_sheet_delivery,
qa_7a_slack_product_channel_connect,qa_7b_sheets_connect,qa_7c_slack_bug_logger_routine,qa_7d_slack_bug_message_trigger,qa_7e_slack_bug_sheet_delivery,
qa_8a_slack_connect,qa_8b_hn_keyword_live_chat,qa_8c_hn_keyword_slack_routine,qa_8d_hn_keyword_slack_delivery
Comment on lines +551 to +558

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm matrix.cases union == ALL_SHARD_CASES (order-independent set compare)
f=.github/workflows/live-canary.yml
python3 - "$f" <<'PY'
import sys,re,yaml
doc=yaml.safe_load(open(sys.argv[1]))
job=doc['jobs']['reborn-webui-v2-live-qa']
matrix=set()
for inc in job['strategy']['matrix']['include']:
    matrix |= {c.strip() for c in inc['cases'].split(',') if c.strip()}
allc=set()
for step in job['steps']:
    env=step.get('env',{})
    if 'ALL_SHARD_CASES' in env:
        allc={c.strip() for c in env['ALL_SHARD_CASES'].split(',') if c.strip()}
print("only in matrix:", sorted(matrix-allc))
print("only in ALL_SHARD_CASES:", sorted(allc-matrix))
print("in sync:", matrix==allc)
PY

Repository: nearai/ironclaw

Length of output: 214


Derive ALL_SHARD_CASES from the matrix cases instead of hand-maintaining a second copy. Drift here can reject valid QA shards or admit invalid ones; a small sync guard or generated list removes that risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/live-canary.yml around lines 551 - 558, ALL_SHARD_CASES is
manually duplicated and can drift from the matrix definition, so update the
live-canary workflow to derive this value from the same shard case source used
by the matrix instead of hardcoding a second list. Adjust the workflow logic
around ALL_SHARD_CASES so it is generated or validated from the matrix cases,
keeping the shard names in sync automatically.

Source: Path instructions

run: |
set -euo pipefail
cases="$(echo "${REQUESTED_CASES:-}" | xargs)"
if [[ -z "${cases}" || "${cases}" == "all" || "${cases}" == "ALL" || "${cases}" == "*" ]]; then
echo "CASES=${SHARD_CASES}" >> "${GITHUB_ENV}"
echo "REBORN_WEBUI_V2_LIVE_QA_CASES_RESOLUTION=matrix-shard" >> "${GITHUB_ENV}"
echo "skip_shard=0" >> "${GITHUB_OUTPUT}"
echo "CASES=${cases:-<default>} requested; running shard cases: ${SHARD_CASES}"
else
selected=()
IFS=',' read -ra requested_cases <<< "${cases}"
IFS=',' read -ra shard_cases <<< "${SHARD_CASES}"
IFS=',' read -ra all_cases <<< "${ALL_SHARD_CASES}"
for requested in "${requested_cases[@]}"; do
requested="$(echo "${requested}" | xargs)"
[[ -n "${requested}" ]] || continue
known=0
for known_case in "${all_cases[@]}"; do
known_case="$(echo "${known_case}" | xargs)"
if [[ "${requested}" == "${known_case}" ]]; then
known=1
break
fi
done
if [[ ${known} -ne 1 ]]; then
echo "::error::Unknown Reborn WebUI v2 live QA case: ${requested}"
exit 1
fi
for shard_case in "${shard_cases[@]}"; do
if [[ "${requested}" == "${shard_case}" ]]; then
selected+=("${requested}")
break
fi
done
done
if [[ ${#selected[@]} -eq 0 ]]; then
echo "REBORN_WEBUI_V2_LIVE_QA_SKIP_SHARD=1" >> "${GITHUB_ENV}"
echo "REBORN_WEBUI_V2_LIVE_QA_CASES_RESOLUTION=requested-not-in-shard" >> "${GITHUB_ENV}"
echo "skip_shard=1" >> "${GITHUB_OUTPUT}"
echo "Requested cases do not belong to this shard; skipping."
Comment thread
coderabbitai[bot] marked this conversation as resolved.
else
joined="$(IFS=,; echo "${selected[*]}")"
echo "CASES=${joined}" >> "${GITHUB_ENV}"
echo "REBORN_WEBUI_V2_LIVE_QA_CASES_RESOLUTION=requested-filtered" >> "${GITHUB_ENV}"
echo "skip_shard=0" >> "${GITHUB_OUTPUT}"
echo "Running requested cases in this shard: ${joined}"
fi
fi
- name: Reject unsafe Reborn WebUI v2 live QA target refs
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1' && github.event_name == 'workflow_dispatch' && inputs.target_ref != ''
run: |
echo "::error::target_ref is disabled for reborn-webui-v2-live-qa because this lane runs with live secrets."
exit 1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
with:
ref: ${{ inputs.target_ref || github.ref }}
persist-credentials: false
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
with:
targets: wasm32-wasip2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
with:
python-version: "3.12"
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
with:
key: live-canary-reborn-webui-v2-live-qa
- name: Install cargo-component
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
run: cargo install cargo-component --locked || true
- name: Build WASM channels
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
run: ./scripts/build-wasm-extensions.sh --channels
- name: Materialize Reborn WebUI v2 live QA sensitive secrets to files
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
shell: bash
env:
NEARAI_API_KEY: ${{ secrets.NEARAI_API_KEY || secrets.LIVE_OPENAI_COMPATIBLE_API_KEY }}
Expand Down Expand Up @@ -573,37 +672,24 @@ jobs:
write_secret "AUTH_LIVE_GITHUB_TOKEN" "${AUTH_LIVE_GITHUB_TOKEN:-}"
write_secret "TELEGRAM_BOT_TOKEN" "${TELEGRAM_BOT_TOKEN:-}"
write_secret "TELEGRAM_WEBHOOK_SECRET" "${TELEGRAM_WEBHOOK_SECRET:-}"
- name: Resolve Reborn WebUI v2 live QA cases
shell: bash
env:
REQUESTED_CASES: ${{ github.event_name == 'schedule' && 'all' || inputs.cases || vars.REBORN_WEBUI_V2_LIVE_QA_CASES || '' }}
run: |
set -euo pipefail
cases="$(echo "${REQUESTED_CASES:-}" | xargs)"
if [[ "${cases}" == "all" ]]; then
echo "CASES=all" >> "${GITHUB_ENV}"
echo "REBORN_WEBUI_V2_LIVE_QA_CASES_RESOLUTION=promoted-non-telegram" >> "${GITHUB_ENV}"
echo "CASES=all requested; running the promoted non-Telegram Reborn QA suite."
else
echo "CASES=${cases}" >> "${GITHUB_ENV}"
echo "REBORN_WEBUI_V2_LIVE_QA_CASES_RESOLUTION=requested" >> "${GITHUB_ENV}"
fi
- name: Run Reborn WebUI v2 live QA lane
if: steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
env:
LANE: reborn-webui-v2-live-qa
PROVIDER: reborn-webui-v2
PROVIDER: reborn-webui-v2-${{ matrix.shard_id }}
COMMAND_TIMEOUT: 90m
PLAYWRIGHT_INSTALL: with-deps
run: scripts/live-canary/run.sh
- name: Scrub artifacts
if: always()
if: always() && steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
run: scripts/live-canary/scrub-artifacts.sh artifacts/live-canary
- name: Upload artifacts
if: always()
if: always() && steps.resolve_reborn_webui_v2_cases.outputs.skip_shard != '1'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: live-canary-reborn-webui-v2-live-qa
name: live-canary-reborn-webui-v2-live-qa-${{ matrix.shard_id }}
path: artifacts/live-canary/
if-no-files-found: ignore
retention-days: 14

persona-rotating:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ schema_version = "reborn.extension_manifest.v2"
id = "google-drive"
name = "Google Drive"
version = "0.1.0"
description = "Google Drive capabilities for searching, accessing, uploading, sharing, and organizing files and folders."
description = "Google Drive capabilities for searching, accessing, uploading, sharing, and organizing files and folders, including finding Google Sheets/spreadsheets, Docs, and Slides by name or title."
trust = "first_party_requested"

[runtime]
Expand All @@ -11,7 +11,7 @@ module = "wasm/google_drive_tool.wasm"

[[capabilities]]
id = "google-drive.list_files"
description = "Search or list files and folders."
description = "Search or list files and folders, including Google Sheets/spreadsheets by name or title."
effects = ["dispatch_capability", "network", "use_secret"]
runtime_credentials = [
{ handle = "google_runtime_token", source = { type = "product_auth_account", provider = "google", setup = { kind = "oauth", scopes = ["https://www.googleapis.com/auth/drive.readonly"] } }, provider_scopes = ["https://www.googleapis.com/auth/drive.readonly"], audience = { scheme = "https", host_pattern = "www.googleapis.com" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Search or list files and folders.

Use this to find Google Workspace files by name/title, including Google Sheets. For example, to find a spreadsheet by exact title, query `name = '<spreadsheet title>' and mimeType = 'application/vnd.google-apps.spreadsheet' and trashed = false`.

The host selects this operation from the capability id. Provide only the parameters described by the input schema; do not include an action field.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Google Drive list_files",
"description": "Search or list files and folders.",
"description": "Search or list files and folders, including Google Sheets/spreadsheets by name or title.",
"type": "object",
"required": [],
"properties": {
Expand All @@ -10,7 +10,7 @@
"string",
"null"
],
"description": "Drive search query."
"description": "Drive search query. To find a Google Sheet by exact title, use a query like name = '<spreadsheet title>' and mimeType = 'application/vnd.google-apps.spreadsheet' and trashed = false."
},
"page_size": {
"type": "integer",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Google Drive WASM Tool for IronClaw.
//!
//! Provides Google Drive integration for searching, accessing, uploading,
//! sharing, and organizing files and folders. Supports both personal and
//! sharing, and organizing files and folders, including finding Google Sheets/
//! spreadsheets, Docs, and Slides by name/title. Supports both personal and
//! shared (organizational) drives.
//!
//! # Capabilities Required
Expand All @@ -28,6 +29,7 @@
//!
//! ```json
//! {"action": "list_files", "query": "name contains 'report' and mimeType = 'application/pdf'"}
//! {"action": "list_files", "query": "name = '<spreadsheet title>' and mimeType = 'application/vnd.google-apps.spreadsheet' and trashed = false"}
//! {"action": "list_files", "corpora": "drive", "drive_id": "0ABcd...", "query": "trashed = false"}
//! {"action": "share_file", "file_id": "abc123", "email": "alice@company.com", "role": "writer"}
//! ```
Expand Down Expand Up @@ -70,7 +72,8 @@ impl exports::near::agent::tool::Guest for GoogleDriveTool {

fn description() -> String {
"Google Drive integration for searching, accessing, uploading, sharing, and organizing \
files and folders. Supports personal drives and shared (organizational) drives via the \
files and folders, including finding Google Sheets/spreadsheets, Docs, and Slides by \
name or title. Supports personal drives and shared (organizational) drives via the \
corpora parameter. Can search with Drive query syntax, download text files, upload new \
files, manage folder structure, and control sharing permissions. The host injects a \
Google product-auth credential with the Drive scope. \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ schema_version = "reborn.extension_manifest.v2"
id = "google-sheets"
name = "Google Sheets"
version = "0.1.0"
description = "Google Sheets capabilities for creating, reading, writing, and formatting spreadsheets."
description = "Google Sheets capabilities for creating, reading, writing, and formatting spreadsheets. Use Google Drive list_files to find existing spreadsheets by name/title."
trust = "first_party_requested"

[runtime]
Expand All @@ -25,7 +25,7 @@ required_host_ports = ["host.runtime.http_egress"]

[[capabilities]]
id = "google-sheets.get_spreadsheet"
description = "Get spreadsheet metadata."
description = "Get spreadsheet metadata by spreadsheet ID. If the user only provided a spreadsheet name/title, search Google Drive first."
effects = ["dispatch_capability", "network", "use_secret"]
runtime_credentials = [
{ handle = "google_runtime_token", source = { type = "product_auth_account", provider = "google", setup = { kind = "oauth", scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"] } }, provider_scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"], audience = { scheme = "https", host_pattern = "sheets.googleapis.com" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
Expand All @@ -39,7 +39,7 @@ required_host_ports = ["host.runtime.http_egress"]

[[capabilities]]
id = "google-sheets.read_values"
description = "Read cell values from a range."
description = "Read cell values from a range by spreadsheet ID. If the user only provided a spreadsheet name/title, search Google Drive first."
effects = ["dispatch_capability", "network", "use_secret"]
runtime_credentials = [
{ handle = "google_runtime_token", source = { type = "product_auth_account", provider = "google", setup = { kind = "oauth", scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"] } }, provider_scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"], audience = { scheme = "https", host_pattern = "sheets.googleapis.com" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
Expand Down Expand Up @@ -81,7 +81,7 @@ required_host_ports = ["host.runtime.http_egress"]

[[capabilities]]
id = "google-sheets.append_values"
description = "Append rows after existing data."
description = "Append rows after existing data by spreadsheet ID. If the user only provided a spreadsheet name/title, search Google Drive first."
effects = ["dispatch_capability", "network", "use_secret", "external_write"]
runtime_credentials = [
{ handle = "google_runtime_token", source = { type = "product_auth_account", provider = "google", setup = { kind = "oauth", scopes = ["https://www.googleapis.com/auth/spreadsheets"] } }, provider_scopes = ["https://www.googleapis.com/auth/spreadsheets"], audience = { scheme = "https", host_pattern = "sheets.googleapis.com" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Append rows after existing data.
Append rows after existing data by spreadsheet ID.

If the user provided only a spreadsheet name/title, first use Google Drive `google-drive.list_files` to find the spreadsheet file ID.

The host selects this operation from the capability id. Provide only the parameters described by the input schema; do not include an action field.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Get spreadsheet metadata.
Get spreadsheet metadata by spreadsheet ID.

If the user provided only a spreadsheet name/title, first use Google Drive `google-drive.list_files` to find the spreadsheet file ID.

The host selects this operation from the capability id. Provide only the parameters described by the input schema; do not include an action field.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Read cell values from a range.
Read cell values from a range by spreadsheet ID.

If the user provided only a spreadsheet name/title, first use Google Drive `google-drive.list_files` to find the spreadsheet file ID.

The host selects this operation from the capability id. Provide only the parameters described by the input schema; do not include an action field.
Loading
Loading