Skip to content

(MOT-4268) feat(harness): add live quickstart canary - #619

Merged
ytallo merged 9 commits into
mainfrom
feat/harness-quickstart-canary
Jul 29, 2026
Merged

(MOT-4268) feat(harness): add live quickstart canary#619
ytallo merged 9 commits into
mainfrom
feat/harness-quickstart-canary

Conversation

@ytallo

@ytallo ytallo commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • streamline the published Harness quickstart validator while preserving install, boot, function, Console, and generated-file checks
  • exercise a real Z.AI GLM request through the Console WebSocket proxy and record its result
  • keep one Slack message per run in #worker-releases, update its final status, and publish diagnostics in its thread

Why

The deterministic E2E suite runs repository binaries, so it does not prove that published registry artifacts can install and complete a real Harness request. This adds that production-path canary while keeping artifacts and notifications concise.

Validation

  • full Ubuntu 24.04 published-install run with iii 0.22.0, provider-zai 0.5.0, and zai/glm-5.2 (message_check: passed, 52 seconds)
  • Actionlint
  • ShellCheck
  • bash -n harness/tests/quickstart/run-ci.sh
  • python3 -m py_compile harness/tests/quickstart/console_send.py
  • git diff --check
  • verified that ZAI_API_KEY was not persisted in artifacts

Fixes MOT-4268

Summary by CodeRabbit

  • New Features

    • Quickstart validation now covers both latest and next installer channels.
    • CI runs can record terminal activity and share results and recordings in Slack.
    • Validation summaries now include clear pass/fail status and downloadable artifacts.
  • Bug Fixes

    • Improved detection of engine readiness, required functions, Console connectivity, and assistant responses.
    • Added fallback handling when recordings or result files are unavailable.
  • Documentation

    • Updated quickstart guidance with expanded prerequisites, local execution steps, validation coverage, and troubleshooting details.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jul 29, 2026 12:01am
workers-tech-spec Ready Ready Preview, Comment Jul 29, 2026 12:01am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The quickstart validator now tests latest and next installer channels, engine and Console readiness, optional ZAI messaging, and generated artifacts. CI adds VHS recordings, consolidated results, job summaries, Slack notifications, threaded recording uploads, and updated documentation.

Changes

Quickstart validation

Layer / File(s) Summary
Validator lifecycle and readiness checks
.github/workflows/harness-quickstart.yml, harness/tests/quickstart/run-ci.sh, harness/tests/quickstart/README.md
The validator runs in an isolated project, polls engine functions and models, checks Console surfaces and generated worker artifacts, and writes compact result.json output.
Console message canary
harness/tests/quickstart/console_send.py, harness/tests/quickstart/run-ci.sh
When ZAI_API_KEY is available, the flow installs the provider, sends one Console WebSocket message, and requires a non-empty assistant reply.
Recorded CI execution and result publication
.github/workflows/harness-quickstart.yml, harness/tests/quickstart/quickstart.tape
Workflow runs select latest or next, execute the validator through VHS, validate recordings, write step summaries, and upload the complete channel artifact.
Slack status and recording notifications
.github/workflows/harness-quickstart.yml
The workflow posts and updates Slack thread messages and uploads valid terminal recordings to the thread.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • iii-hq/workers#616: Modifies the same quickstart result, evidence, timing, and artifact flow.
  • iii-hq/workers#617: Overlaps on the ZAI Console canary and next installer channel support.

Suggested labels: no-ticket

Poem

A bunny watched the quickstart run,
Through VHS beneath the sun.
Slack threads twinkled, results passed,
A tiny canary hopped by fast.
“Latest and next!” the rabbit cheered,
While clean recordings appeared.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: adding a live quickstart canary for harness.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/harness-quickstart-canary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 49 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@ytallo
ytallo marked this pull request as ready for review July 29, 2026 00:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
harness/tests/quickstart/console_send.py (1)

137-141: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep the traceback for unexpected failures.

except Exception collapses every failure to a one-line message. Deliberate RuntimeError/TimeoutError messages read well, but an unexpected KeyError/AttributeError prints e.g. FAIL: 'session_id' with no location — and this stderr is the only diagnostic the shell wrapper points reviewers at.

♻️ Proposed tweak
     try:
         asyncio.run(run(parser.parse_args()))
+    except (RuntimeError, TimeoutError, OSError) as error:
+        progress(f"FAIL: {error}")
+        raise SystemExit(1) from error
     except Exception as error:
-        progress(f"FAIL: {error}")
+        progress(f"FAIL: {error!r}\n{traceback.format_exc()}")
         raise SystemExit(1) from error

The json.dumps static-analysis hint on line 95 is a false positive — this is a CLI script, not a web handler.

🤖 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 `@harness/tests/quickstart/console_send.py` around lines 137 - 141, Update the
top-level exception handling around asyncio.run in the console script to
preserve full tracebacks for unexpected failures while retaining concise FAIL
messages for deliberate RuntimeError and TimeoutError cases. Keep the existing
exit status behavior and do not change the unrelated json.dumps usage.
harness/tests/quickstart/run-ci.sh (2)

47-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the three near-identical numeric validators into a helper.

Three copies of the same 9-line validate-and-normalize block; a small helper keeps future timeouts one-liners.

♻️ Proposed helper
+require_positive_int() {
+  local name=$1 value=$2
+  [[ "$value" =~ ^[0-9]+$ ]] || {
+    echo "$name must be a positive integer" >&2
+    exit 2
+  }
+  value=$((10#$value))
+  ((value > 0)) || {
+    echo "$name must be a positive integer" >&2
+    exit 2
+  }
+  printf '%s' "$value"
+}
+
+wait_seconds=$(require_positive_int HARNESS_QUICKSTART_WAIT_SECONDS "$wait_seconds")
+add_timeout_seconds=$(require_positive_int HARNESS_QUICKSTART_ADD_TIMEOUT_SECONDS "$add_timeout_seconds")
+turn_timeout_seconds=$(require_positive_int HARNESS_QUICKSTART_TURN_TIMEOUT_SECONDS "$turn_timeout_seconds")

Note the helper exits from a subshell, so pair it with an explicit status check if you adopt it verbatim.

🤖 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 `@harness/tests/quickstart/run-ci.sh` around lines 47 - 73, Introduce a shared
helper for validating and normalizing positive numeric timeout values, including
the variable name in its error message and returning failure rather than
terminating from a subshell. Replace the duplicated validation blocks for
wait_seconds, add_timeout_seconds, and turn_timeout_seconds with helper calls,
each followed by an explicit status check that exits with status 2 on failure.

382-384: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin websockets for the quickstart canary.

The canary installs websockets without a version constraint, so it can fail or start producing deprecation warnings if PyPI serves a release that changes the imported API surface. Pin the dependency through an env var/default so the canary result stays tied to a known version.

♻️ Proposed pin
-  "$run_root/venv/bin/pip" install --quiet --disable-pip-version-check websockets \
+  "$run_root/venv/bin/pip" install --quiet --disable-pip-version-check \
+    "websockets==${HARNESS_QUICKSTART_WEBSOCKETS_VERSION:-16.0.1}" \
     >>"$log_dir/console-send.log" 2>&1 \
     || die "pip install websockets failed (see console-send.log)"
🤖 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 `@harness/tests/quickstart/run-ci.sh` around lines 382 - 384, Update the
quickstart canary’s websockets installation in the surrounding run-ci flow to
use a configurable version environment variable with a known pinned default, and
install that exact requirement instead of the unversioned package. Preserve the
existing logging and die-on-failure behavior.
harness/tests/quickstart/README.md (1)

24-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the remaining quickstart tunables.

The validator accepts HARNESS_QUICKSTART_ARTIFACTS_DIR, III_INSTALL_URL, III_CHANNEL, HARNESS_QUICKSTART_WAIT_SECONDS, HARNESS_QUICKSTART_ADD_TIMEOUT_SECONDS, HARNESS_QUICKSTART_TURN_TIMEOUT_SECONDS, and HARNESS_QUICKSTART_PROMPT; this README currently only documents HARNESS_QUICKSTART_MODEL and HARNESS_QUICKSTART_TRACE.

🤖 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 `@harness/tests/quickstart/README.md` around lines 24 - 34, Update the
quickstart validator documentation near the local execution instructions to
document all remaining tunables: HARNESS_QUICKSTART_ARTIFACTS_DIR,
III_INSTALL_URL, HARNESS_QUICKSTART_WAIT_SECONDS,
HARNESS_QUICKSTART_ADD_TIMEOUT_SECONDS, HARNESS_QUICKSTART_TURN_TIMEOUT_SECONDS,
and HARNESS_QUICKSTART_PROMPT, alongside the already documented channel, model,
and trace settings.
.github/workflows/harness-quickstart.yml (1)

67-67: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Set persist-credentials: false on checkout.

The job only reads the repo; leaving the token in .git/config is unnecessary exposure (zizmor artipacked).

🔒 Proposed change
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v5
+        with:
+          persist-credentials: false
🤖 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/harness-quickstart.yml at line 67, Update the
actions/checkout@v5 step in the workflow to set persist-credentials to false,
ensuring the repository read-only job does not retain the checkout token in Git
configuration.

Source: Linters/SAST tools

🤖 Prompt for all review comments with 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.

Inline comments:
In @.github/workflows/harness-quickstart.yml:
- Around line 158-172: Update the jq failure condition in the result-summary
block to apply the alternative operator to failure_reason before comparing it
with an empty string, preventing missing values from rendering as null. Also
expose matrix.channel through an env variable and reference that variable in the
terminal-recording message to resolve the template-injection finding.
- Around line 42-56: Update the Slack post step around the payload and curl
invocation to use the configured Slack channel ID rather than the mutable
channel name, while preserving the existing message content. Remove
--retry-all-errors and retain only bounded retries appropriate for the
non-idempotent chat.postMessage request.

In `@harness/tests/quickstart/quickstart.tape`:
- Around line 23-26: Update the Wait assertion in the quickstart tape to use a
line-anchored match for the bare __HARNESS_QUICKSTART_DONE__ prompt, allowing an
optional trailing space, so it cannot match the typed run-ci.sh command. Keep
the subsequent Sleep unchanged.

In `@harness/tests/quickstart/run-ci.sh`:
- Around line 386-395: Update the console_send.py failure path in run-ci.sh to
redirect stderr directly to console-send.log instead of using asynchronous
process substitution with tee. When the command fails, emit the saved log
contents to stderr before calling die, while preserving the existing failure
message and artifact paths.

---

Nitpick comments:
In @.github/workflows/harness-quickstart.yml:
- Line 67: Update the actions/checkout@v5 step in the workflow to set
persist-credentials to false, ensuring the repository read-only job does not
retain the checkout token in Git configuration.

In `@harness/tests/quickstart/console_send.py`:
- Around line 137-141: Update the top-level exception handling around
asyncio.run in the console script to preserve full tracebacks for unexpected
failures while retaining concise FAIL messages for deliberate RuntimeError and
TimeoutError cases. Keep the existing exit status behavior and do not change the
unrelated json.dumps usage.

In `@harness/tests/quickstart/README.md`:
- Around line 24-34: Update the quickstart validator documentation near the
local execution instructions to document all remaining tunables:
HARNESS_QUICKSTART_ARTIFACTS_DIR, III_INSTALL_URL,
HARNESS_QUICKSTART_WAIT_SECONDS, HARNESS_QUICKSTART_ADD_TIMEOUT_SECONDS,
HARNESS_QUICKSTART_TURN_TIMEOUT_SECONDS, and HARNESS_QUICKSTART_PROMPT,
alongside the already documented channel, model, and trace settings.

In `@harness/tests/quickstart/run-ci.sh`:
- Around line 47-73: Introduce a shared helper for validating and normalizing
positive numeric timeout values, including the variable name in its error
message and returning failure rather than terminating from a subshell. Replace
the duplicated validation blocks for wait_seconds, add_timeout_seconds, and
turn_timeout_seconds with helper calls, each followed by an explicit status
check that exits with status 2 on failure.
- Around line 382-384: Update the quickstart canary’s websockets installation in
the surrounding run-ci flow to use a configurable version environment variable
with a known pinned default, and install that exact requirement instead of the
unversioned package. Preserve the existing logging and die-on-failure behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f086d02-28e9-4449-ae36-ed716594b245

📥 Commits

Reviewing files that changed from the base of the PR and between fef309a and 83c3a41.

📒 Files selected for processing (5)
  • .github/workflows/harness-quickstart.yml
  • harness/tests/quickstart/README.md
  • harness/tests/quickstart/console_send.py
  • harness/tests/quickstart/quickstart.tape
  • harness/tests/quickstart/run-ci.sh

Comment on lines +42 to +56
SLACK_CHANNEL: worker-releases
III_CHANNEL: ${{ matrix.channel }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
text="⏳ *Harness quickstart* started · \`${III_CHANNEL}\` · <${RUN_URL}|view run>"
payload=$(jq -n \
--arg channel "$SLACK_CHANNEL" \
--arg text "$text" \
'{channel: $channel, text: $text}')
response=$(curl -sS --retry 3 --retry-all-errors \
-X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json; charset=utf-8" \
-d "$payload")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Slack chat.postMessage still support channel name instead of channel ID?

💡 Result:

Yes, the Slack chat.postMessage API method still supports using a channel name instead of a channel ID [1][2]. According to the official Slack API documentation, you can pass either the channel name (e.g., #general) or the encoded channel ID to the channel parameter [1]. However, while this is supported, it is generally discouraged in production environments. Channel names can change over time, whereas channel IDs remain permanent identifiers [3]. Furthermore, most other Slack API methods that require a channel identifier do not support names and specifically require the channel ID [3]. Using the channel ID is considered the best practice to ensure reliability and consistency across your integration [3].

Citations:


🌐 Web query:

Slack chat.postMessage channel parameter "channel" accept bare channel name without # Slack API

💡 Result:

Yes, the Slack API chat.postMessage channel parameter accepts a bare channel name (without the # prefix) [1][2][3][4]. According to official Slack API documentation, the channel argument can be an encoded channel ID (e.g., C123456) or the channel's name [1][2]. When passing the name, you do not need to include the hash (#) symbol [1][2]. While the API allows using channel names, it is generally recommended to use the channel's unique ID instead [5]. Channel names can be renamed, whereas channel IDs are persistent, ensuring your application remains reliable if a channel's name is changed [5][6]. You can retrieve the ID associated with a channel name by using the conversations.list API method [1][5][7].

Citations:


🌐 Web query:

Slack API channel names chat.postMessage channel parameter no # examples

💡 Result:

The Slack API chat.postMessage method's channel parameter accepts either an encoded channel ID (e.g., C12345678) or a channel name [1][2]. When using a channel name, you do not need to include the hash (#) prefix [2]. You can pass the name as a plain string (e.g., "general") [2]. While the API documentation has historically permitted channel names [2], using the channel ID is strongly recommended because channel names are not globally unique across workspaces and can be changed by users, whereas the channel ID is immutable [1]. To obtain the correct channel ID, you can use the conversations.list API method [1][3].

Citations:


🌐 Web query:

Slack API channel_not_name channel_param_is_not_valid chat.postMessage channel name

💡 Result:

The channel_not_found error in the Slack API's chat.postMessage method indicates that the Slack platform cannot locate the specified channel or that your application does not have the necessary permission to post into it [1][2][3]. Common causes and solutions include: 1. App Membership: For private channels, your Slack app or bot must be explicitly added to the channel as a member [2][4]. You can invite your bot by typing /invite @YourBotName in the channel [3][4]. 2. Incorrect Identifier: Ensure the value passed for the channel argument is correct [5]. While Slack often supports both channel names (e.g., #general) and channel IDs (e.g., C024BE91L), using the unique channel ID is more reliable and recommended to avoid ambiguity [6][5][4]. 3. Permission Scopes: Verify that your application has the required scopes for the requested action, such as chat:write or chat:write.public (for public channels where the bot is not a member) [7][4]. 4. Token Context: Ensure you are using the correct token associated with the intended workspace. Errors can occur if an OAuth token is used for a workspace where the app was not installed, or if there is a mismatch between the token and the channel access [8][5]. 5. Formatting: If making manual API requests, ensure parameters are correctly encoded in the request body (e.g., application/x-www-form-urlencoded) rather than incorrectly placed in headers or improperly formatted [8][9][10].

Citations:


🌐 Web query:

Slack chat.postMessage channel parameter "channel" accepts channel name or encoded channel ID

💡 Result:

The channel parameter in the Slack chat.postMessage method accepts either a Slack-encoded channel ID (e.g., C123456) or a channel name (e.g., #general) [1][2][3]. While it is possible to use a channel name, it is generally recommended to use the channel ID [4]. Channel names are mutable and can be changed by workspace members, whereas channel IDs are persistent and unique identifiers [4]. Furthermore, most other Slack API methods that require a channel reference only accept the channel ID, making the use of IDs a more consistent practice across your integration [4]. If you use a channel name, the API will resolve it to the corresponding channel, but relying on IDs ensures greater stability for your application [1][4].

Citations:


🌐 Web query:

curl --retry-all-errors retry POST request duplicate submission non-idempotent documentation

💡 Result:

Using the --retry-all-errors option in curl is considered a "sledgehammer" approach to retrying transfers and should be used with extreme caution, particularly with non-idempotent HTTP methods like POST [1][2]. Key Considerations: 1. Risk of Duplicate Submissions: When you use --retry-all-errors, curl will attempt to retry the request for any error it encounters [3][4]. If the initial request was a POST or another non-idempotent method that partially reached the server—or was processed before a connection error occurred—the retry will trigger a duplicate submission on the server side [1][2][5]. 2. Scope of Option: This option must be used in conjunction with --retry [1][4]. By default, curl only retries on certain connection errors and transient HTTP 5xx server errors [5]. Enabling --retry-all-errors forces retries on nearly all failures, including many 4xx client errors that would otherwise be considered final [1][5]. 3. Data Handling: The documentation warns against using this option with redirected input or output (e.g., using pipes | or >), as curl cannot automatically "reset" these streams before a retry, which can lead to receiving or sending duplicate data [1]. 4. Best Practices: * Avoid --retry-all-errors for any request that is not explicitly safe to repeat [2][5]. * Prioritize idempotent methods (like GET or HEAD) when configuring automatic retry logic [2][5]. * For POST/PUT requests, handle retries at the application level where you can verify the state of the previous submission before attempting another [2][5]. * If you must use it, ensure the command is part of a controlled, well-understood environment rather than a default configuration [1][2]. Official documentation emphasizes that you are generally better off handling specific, expected failure scenarios within a script rather than relying on this broad, indiscriminate retry mechanism [1].

Citations:


Use the Slack channel ID and limit retries for non-idempotent Slack posts.

chat.postMessage accepts channel names, but names are mutable and can fail with channel_not_found; channel_id is preferred for reliability. Also, --retry-all-errors will retry the POST submission more broadly than --retry 3, which risks duplicate Slack posts if the first request was processed before a failure was reported.

🤖 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/harness-quickstart.yml around lines 42 - 56, Update the
Slack post step around the payload and curl invocation to use the configured
Slack channel ID rather than the mutable channel name, while preserving the
existing message content. Remove --retry-all-errors and retain only bounded
retries appropriate for the non-idempotent chat.postMessage request.

Comment on lines 158 to +172
jq -r '
"| Status | CLI | Duration |",
"| --- | --- | ---: |",
"| \(.status) | \(.cli_version) | \(.elapsed_ms) ms |",
(if .failure_reason != "" then "", "Failure: \(.failure_reason)" else empty end)
"| Status | Channel | CLI | GLM canary | Duration |",
"| --- | --- | --- | --- | ---: |",
"| \(.status) | \(.channel) | \(.cli_version) | \(.message_check) | \(.elapsed_ms) ms |",
(if .failure_reason != "" then "", "**Failure:** \(.failure_reason)" else empty end)
' target/harness-quickstart/result.json
else
echo "### Harness quickstart"
echo
echo "No quickstart result was produced."
fi
if [[ -f target/harness-quickstart/quickstart.mp4 ]]; then
echo
echo "Terminal recording: \`quickstart.mp4\` in the \`harness-quickstart-${{ matrix.channel }}\` artifact."
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

failure_reason renders as null when the key is absent.

.failure_reason != "" is true for null, so a missing key emits **Failure:** null in the job summary. Use the alternative operator first.

Separately, moving ${{ matrix.channel }} (Line 171) into an env: var silences the zizmor template-injection finding; the value itself is safe here since it comes from an enumerated choice input.

🐛 Proposed fix
-                (if .failure_reason != "" then "", "**Failure:** \(.failure_reason)" else empty end)
+                (if (.failure_reason // "") != "" then "", "**Failure:** \(.failure_reason)" else empty end)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jq -r '
"| Status | CLI | Duration |",
"| --- | --- | ---: |",
"| \(.status) | \(.cli_version) | \(.elapsed_ms) ms |",
(if .failure_reason != "" then "", "Failure: \(.failure_reason)" else empty end)
"| Status | Channel | CLI | GLM canary | Duration |",
"| --- | --- | --- | --- | ---: |",
"| \(.status) | \(.channel) | \(.cli_version) | \(.message_check) | \(.elapsed_ms) ms |",
(if .failure_reason != "" then "", "**Failure:** \(.failure_reason)" else empty end)
' target/harness-quickstart/result.json
else
echo "### Harness quickstart"
echo
echo "No quickstart result was produced."
fi
if [[ -f target/harness-quickstart/quickstart.mp4 ]]; then
echo
echo "Terminal recording: \`quickstart.mp4\` in the \`harness-quickstart-${{ matrix.channel }}\` artifact."
fi
jq -r '
"| Status | Channel | CLI | GLM canary | Duration |",
"| --- | --- | --- | --- | ---: |",
"| \(.status) | \(.channel) | \(.cli_version) | \(.message_check) | \(.elapsed_ms) ms |",
(if (.failure_reason // "") != "" then "", "**Failure:** \(.failure_reason)" else empty end)
' target/harness-quickstart/result.json
🧰 Tools
🪛 zizmor (1.28.0)

[warning] 171-171: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 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/harness-quickstart.yml around lines 158 - 172, Update the
jq failure condition in the result-summary block to apply the alternative
operator to failure_reason before comparing it with an empty string, preventing
missing values from rendering as null. Also expose matrix.channel through an env
variable and reference that variable in the terminal-recording message to
resolve the template-injection finding.

Source: Linters/SAST tools

Comment on lines +23 to +26
Type "harness/tests/quickstart/run-ci.sh"
Enter
Wait /__HARNESS_QUICKSTART_DONE__>/
Sleep 2s

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

VHS tape Wait command Line scope regex anchoring semantics

💡 Result:

In the VHS (Virtual Terminal Recording) tool, the Wait command is used to pause execution until a specific regular expression matches the terminal's output [1][2]. Scope Semantics: The command supports two scopes, defined by the optional modifiers +Line and +Screen [1][3]. The scope determines the source of the text against which the regular expression is matched [4]. - Line: The default scope [1]. It monitors only the current line of the terminal (as determined by the cursor's position) [4][5]. - Screen: Monitors the entire terminal buffer [4]. When used, VHS joins all lines in the buffer with newline characters and performs the regex match against the resulting string [4]. Regex Anchoring Semantics: The regular expression provided to the Wait command is used with the Go standard library's regexp.MatchString function [4]. This function does not implicitly anchor the expression to the start or end of the string [4]. Therefore, the provided regex will match if the pattern exists anywhere within the scope (the current line or the entire screen buffer, depending on the chosen scope) [5]. Additional Details: - Default Pattern: If no regex is provided, the command defaults to matching the pattern />$/ [1][6]. - Timeout: The default timeout is 15 seconds [1]. This can be adjusted using the WaitTimeout setting or the @ duration modifier in the command (e.g., Wait@10ms) [1][4]. - Regex Delimiters: The pattern must be enclosed in forward slashes (e.g., /regex/) [1][3]. Historically, this has made escaping literal forward slashes within the pattern difficult, as the parser treats them as terminators; users often use hex escape sequences like \x{2F} to represent a literal forward slash [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate tape and workflow =="
fd -a 'quickstart\.tape|.*\.(ya?ml)$' . | sed 's#^\./##' | head -200

echo "== tape content =="
cat -n harness/tests/quickstart/quickstart.tape

echo "== relevant workflow files =="
for f in $(fd '.*\.(ya?ml)$' .github workflows . | head -50); do
  if rg -q 'quickstart|run-ci|validate|ffprobe|__HARNESS_QUICKSTART_DONE__' "$f" 2>/dev/null; then
    echo "--- $f ---"
    sed -n '1,220p' "$f"
  fi
done

Repository: iii-hq/workers

Length of output: 25506


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files mentioning quickstart/run-ci/ffprobe/slack upload =="
rg -n --hidden --glob '!pnpm-lock.yaml' --glob '!*.yaml' --glob '!package-lock.json' --glob '!yarn.lock' \
  'quickstart|run-ci|__HARNESS_QUICKSTART_DONE__|ffprobe|upload|slack' . 2>/dev/null | head -300

echo "== quickstart runner =="
if [ -f harness/tests/quickstart/run-ci.sh ]; then
  cat -n harness/tests/quickstart/run-ci.sh
fi

echo "== nearby harness quickstart files =="
find harness/tests/quickstart -maxdepth 2 -type f -print | sort | while read -r f; do
  echo "--- $f ---"
  sed -n '1,220p' "$f"
done

echo "== workflow files =="
find .github harness -type d -maxdepth 4 2>/dev/null | sed 's#^\./##' | sort | head -80
find .github harness -type f -regex '.*\.\(ya?ml\)$' -print 2>/dev/null | while read -r f; do
  if rg -q 'quickstart|run-ci|__HARNESS_QUICKSTART_DONE__|ffprobe|slack|upload' "$f" 2>/dev/null; then
    echo "--- $f ---"
    sed -n '1,260p' "$f"
  fi
done

Repository: iii-hq/workers

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== exact workflow path candidates =="
find . -type f \( -path '*/.github/workflows/*' -o -path '*/harness/tests/quickstart/*' \) -print 2>/dev/null | sort

echo "== workflow quickstart references concise =="
rg -n --hidden --glob '!.git/*' --glob '!pnpm-lock.yaml' --glob '!package-lock.json' --glob '!yarn.lock' \
  'harness/tests/quickstart|quickstart-validate|quickstart\.tape|HARNESS_QUICKSTART|ffprobe|upload-artifact|slack|Notify|notifications|status' .github harness 2>/dev/null | head -300

echo "== exact workflow relevant sections candidate =="
for f in .github/workflows/_harness-e2e.yml .github/workflows/quickstart.yml harness/Makefile harness/tests/quickstart/run-ci.sh; do
  [ -f "$f" ] || continue
  echo "--- $f ---"
  rg -n -C 4 'quickstart-validate|quickstart\.tape|ffmpeg|ffprobe|upload-artifact|slack|Notify|notifications|status|HARNESS_QUICKSTART' "$f"
done

Repository: iii-hq/workers

Length of output: 41956


Anchor the VHS prompt match so Wait doesn’t match the typed command.

Wait /__HARNESS_QUICKSTART_DONE__>/ is a line-scoped substring match, and __HARNESS_QUICKSTART_DONE__> harness/tests/quickstart/run-ci.sh appears on the final line right after Enter. Anchoring to the bare prompt, e.g. Wait+Line /^__HARNESS_QUICKSTART_DONE__> ?$/, prevents the recording from stopping before the validator finishes.

🤖 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 `@harness/tests/quickstart/quickstart.tape` around lines 23 - 26, Update the
Wait assertion in the quickstart tape to use a line-anchored match for the bare
__HARNESS_QUICKSTART_DONE__ prompt, allowing an optional trailing space, so it
cannot match the typed run-ci.sh command. Keep the subsequent Sleep unchanged.

Comment on lines 386 to 395
if ! "$run_root/venv/bin/python" "$script_dir/console_send.py" \
--url "ws://127.0.0.1:$console_port/ws" \
--model "$send_model" --provider "$send_provider" \
--prompt "$send_prompt" --timeout "$turn_timeout_seconds" \
--model "$send_model" \
--provider "$send_provider" \
--prompt "$send_prompt" \
--timeout "$turn_timeout_seconds" \
>"$artifact_dir/console-send.json" \
2> >(tee -a "$log_dir/console-send.log" >&2); then
die "console message send failed (see console-send.log)"
die "GLM canary failed (see console-send.log)"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Async process substitution can drop the diagnostics die points at.

2> >(tee -a "$log_dir/console-send.log" >&2) is not waited on, so when the canary fails the shell can reach die (and then the EXIT trap) before tee flushes — leaving "see console-send.log" pointing at a truncated file. Redirect straight to the log and echo it back instead.

🐛 Proposed fix
   if ! "$run_root/venv/bin/python" "$script_dir/console_send.py" \
     --url "ws://127.0.0.1:$console_port/ws" \
     --model "$send_model" \
     --provider "$send_provider" \
     --prompt "$send_prompt" \
     --timeout "$turn_timeout_seconds" \
     >"$artifact_dir/console-send.json" \
-    2> >(tee -a "$log_dir/console-send.log" >&2); then
+    2>>"$log_dir/console-send.log"; then
+    tail -n 40 "$log_dir/console-send.log" >&2 || true
     die "GLM canary failed (see console-send.log)"
   fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ! "$run_root/venv/bin/python" "$script_dir/console_send.py" \
--url "ws://127.0.0.1:$console_port/ws" \
--model "$send_model" --provider "$send_provider" \
--prompt "$send_prompt" --timeout "$turn_timeout_seconds" \
--model "$send_model" \
--provider "$send_provider" \
--prompt "$send_prompt" \
--timeout "$turn_timeout_seconds" \
>"$artifact_dir/console-send.json" \
2> >(tee -a "$log_dir/console-send.log" >&2); then
die "console message send failed (see console-send.log)"
die "GLM canary failed (see console-send.log)"
fi
if ! "$run_root/venv/bin/python" "$script_dir/console_send.py" \
--url "ws://127.0.0.1:$console_port/ws" \
--model "$send_model" \
--provider "$send_provider" \
--prompt "$send_prompt" \
--timeout "$turn_timeout_seconds" \
>"$artifact_dir/console-send.json" \
2>>"$log_dir/console-send.log"; then
tail -n 40 "$log_dir/console-send.log" >&2 || true
die "GLM canary failed (see console-send.log)"
fi
🤖 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 `@harness/tests/quickstart/run-ci.sh` around lines 386 - 395, Update the
console_send.py failure path in run-ci.sh to redirect stderr directly to
console-send.log instead of using asynchronous process substitution with tee.
When the command fails, emit the saved log contents to stderr before calling
die, while preserving the existing failure message and artifact paths.

@ytallo
ytallo merged commit a7b2c39 into main Jul 29, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant