feat(task-ledger): add ownership escalation and restore green checks#18
Conversation
There was a problem hiding this comment.
12 issues found across 3000 files
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/auto-response.yml">
<violation number="1" location=".github/workflows/auto-response.yml:40">
P1: Pin GitHub Actions to full commit SHAs instead of mutable major tags to prevent unreviewed upstream code from running in this write-capable workflow.</violation>
</file>
<file name=".github/workflows/labeler.yml">
<violation number="1" location=".github/workflows/labeler.yml:35">
P0: This `pull_request_target` workflow was changed from SHA-pinned actions to mutable version tags (`@v2/@v6/@v8`). In a secrets-backed, write-permission workflow, that increases supply-chain risk because action code can change without a PR in this repo.</violation>
</file>
<file name="Dockerfile.sandbox">
<violation number="1" location="Dockerfile.sandbox:10">
P2: Avoid `apt-get upgrade` in this Docker build; it breaks reproducibility against the pinned base image. Prefer updating the base image digest instead.</violation>
</file>
<file name=".gitignore">
<violation number="1" location=".gitignore:130">
P2: Remove `.gitignore` from ignore patterns; it unintentionally ignores future `.gitignore` files across the repo.</violation>
</file>
<file name="apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt">
<violation number="1" location="apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt:614">
P1: Security regression: `resolvePreferredGatewayEndpoint()` drops the TLS/fingerprint guards for manual gateways that the old inline auto-connect code enforced. The old code refused to auto-connect when `manualTls` was false or when no stored TLS fingerprint existed (the comments explicitly said "Security: autoconnect only to previously trusted gateways"). The new code returns the endpoint unconditionally, allowing auto-connect over plaintext to never-trusted gateways.</violation>
</file>
<file name=".agents/skills/openclaw-ghsa-maintainer/SKILL.md">
<violation number="1" location=".agents/skills/openclaw-ghsa-maintainer/SKILL.md:21">
P2: The refetch verification example reads `/tmp/ghsa.refetch.json` without ever writing the `gh api` output to that file.</violation>
</file>
<file name="Dockerfile.sandbox-common">
<violation number="1" location="Dockerfile.sandbox-common:27">
P2: Avoid `apt-get upgrade` in this image build; it makes builds non-deterministic and can unexpectedly mutate the base image package set.</violation>
</file>
<file name=".github/workflows/docker-release.yml">
<violation number="1" location=".github/workflows/docker-release.yml:162">
P2: Default and slim builds share the same GHA cache scope (`docker-release-amd64`), so the slim build's `cache-to: mode=max` overwrites the default build's cached layers on every run. Use distinct scopes (e.g., `docker-release-amd64` and `docker-release-amd64-slim`) so each variant retains its own layer cache across runs.</violation>
<violation number="2" location=".github/workflows/docker-release.yml:279">
P2: Same cache-scope collision as the amd64 job: the arm64 slim build overwrites the arm64 default build's cache. Use a separate scope like `docker-release-arm64-slim`.</violation>
</file>
<file name="Dockerfile.sandbox-browser">
<violation number="1" location="Dockerfile.sandbox-browser:10">
P2: Avoid `apt-get upgrade` in this image build; it breaks deterministic builds despite the pinned base digest.</violation>
</file>
<file name="apps/android/app/build.gradle.kts">
<violation number="1" location="apps/android/app/build.gradle.kts:246">
P1: The strip task targets a hardcoded non-flavor `release` intermediates path, so it won’t find the jar for `playRelease`/`thirdPartyRelease` builds.</violation>
<violation number="2" location="apps/android/app/build.gradle.kts:280">
P1: The dependency hook only matches `minifyReleaseWithR8`, so it misses flavor-specific release R8 tasks and the strip step won’t run for real release variants.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| runs-on: blacksmith-16vcpu-ubuntu-2404 | ||
| steps: | ||
| - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | ||
| - uses: actions/create-github-app-token@v2 |
There was a problem hiding this comment.
P0: This pull_request_target workflow was changed from SHA-pinned actions to mutable version tags (@v2/@v6/@v8). In a secrets-backed, write-permission workflow, that increases supply-chain risk because action code can change without a PR in this repo.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/labeler.yml, line 35:
<comment>This `pull_request_target` workflow was changed from SHA-pinned actions to mutable version tags (`@v2/@v6/@v8`). In a secrets-backed, write-permission workflow, that increases supply-chain risk because action code can change without a PR in this repo.</comment>
<file context>
@@ -25,25 +32,25 @@ jobs:
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
+ - uses: actions/create-github-app-token@v2
id: app-token
continue-on-error: true
</file context>
| private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }} | ||
| - name: Handle labeled items | ||
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | ||
| uses: actions/github-script@v8 |
There was a problem hiding this comment.
P1: Pin GitHub Actions to full commit SHAs instead of mutable major tags to prevent unreviewed upstream code from running in this write-capable workflow.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/auto-response.yml, line 40:
<comment>Pin GitHub Actions to full commit SHAs instead of mutable major tags to prevent unreviewed upstream code from running in this write-capable workflow.</comment>
<file context>
@@ -17,20 +24,20 @@ jobs:
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Handle labeled items
- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ uses: actions/github-script@v8
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
</file context>
| prefs.setLastDiscoveredStableId(list.first().stableId) | ||
| } | ||
|
|
||
| private fun resolvePreferredGatewayEndpoint(): GatewayEndpoint? { |
There was a problem hiding this comment.
P1: Security regression: resolvePreferredGatewayEndpoint() drops the TLS/fingerprint guards for manual gateways that the old inline auto-connect code enforced. The old code refused to auto-connect when manualTls was false or when no stored TLS fingerprint existed (the comments explicitly said "Security: autoconnect only to previously trusted gateways"). The new code returns the endpoint unconditionally, allowing auto-connect over plaintext to never-trusted gateways.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt, line 614:
<comment>Security regression: `resolvePreferredGatewayEndpoint()` drops the TLS/fingerprint guards for manual gateways that the old inline auto-connect code enforced. The old code refused to auto-connect when `manualTls` was false or when no stored TLS fingerprint existed (the comments explicitly said "Security: autoconnect only to previously trusted gateways"). The new code returns the endpoint unconditionally, allowing auto-connect over plaintext to never-trusted gateways.</comment>
<file context>
@@ -601,15 +592,59 @@ class NodeRuntime(context: Context) {
+ prefs.setLastDiscoveredStableId(list.first().stableId)
+ }
+
+ private fun resolvePreferredGatewayEndpoint(): GatewayEndpoint? {
+ if (manualEnabled.value) {
+ val host = manualHost.value.trim()
</file context>
| tasks.register("stripReleaseDnsjavaServiceDescriptor") { | ||
| val mergedJar = | ||
| layout.buildDirectory.file( | ||
| "intermediates/merged_java_res/release/mergeReleaseJavaResource/base.jar", |
There was a problem hiding this comment.
P1: The strip task targets a hardcoded non-flavor release intermediates path, so it won’t find the jar for playRelease/thirdPartyRelease builds.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/android/app/build.gradle.kts, line 246:
<comment>The strip task targets a hardcoded non-flavor `release` intermediates path, so it won’t find the jar for `playRelease`/`thirdPartyRelease` builds.</comment>
<file context>
@@ -211,3 +238,45 @@ dependencies {
+ tasks.register("stripReleaseDnsjavaServiceDescriptor") {
+ val mergedJar =
+ layout.buildDirectory.file(
+ "intermediates/merged_java_res/release/mergeReleaseJavaResource/base.jar",
+ )
+
</file context>
| dependsOn("mergeReleaseJavaResource") | ||
| } | ||
|
|
||
| tasks.matching { it.name == "minifyReleaseWithR8" }.configureEach { |
There was a problem hiding this comment.
P1: The dependency hook only matches minifyReleaseWithR8, so it misses flavor-specific release R8 tasks and the strip step won’t run for real release variants.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/android/app/build.gradle.kts, line 280:
<comment>The dependency hook only matches `minifyReleaseWithR8`, so it misses flavor-specific release R8 tasks and the strip step won’t run for real release variants.</comment>
<file context>
@@ -211,3 +238,45 @@ dependencies {
+ dependsOn("mergeReleaseJavaResource")
+}
+
+tasks.matching { it.name == "minifyReleaseWithR8" }.configureEach {
+ dependsOn(stripReleaseDnsjavaServiceDescriptor)
+}
</file context>
| tasks.matching { it.name == "minifyReleaseWithR8" }.configureEach { | |
| tasks.matching { it.name.endsWith("ReleaseWithR8") }.configureEach { |
| Fetch the current advisory and the latest published npm version: | ||
|
|
||
| ```bash | ||
| gh api /repos/openclaw/openclaw/security-advisories/<GHSA> |
There was a problem hiding this comment.
P2: The refetch verification example reads /tmp/ghsa.refetch.json without ever writing the gh api output to that file.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/openclaw-ghsa-maintainer/SKILL.md, line 21:
<comment>The refetch verification example reads `/tmp/ghsa.refetch.json` without ever writing the `gh api` output to that file.</comment>
<file context>
@@ -0,0 +1,87 @@
+Fetch the current advisory and the latest published npm version:
+
+```bash
+gh api /repos/openclaw/openclaw/security-advisories/<GHSA>
+npm view openclaw version --userconfig "$(mktemp)"
+```
</file context>
| && apt-get upgrade -y --no-install-recommends \ | ||
| && apt-get install -y --no-install-recommends ${PACKAGES} |
There was a problem hiding this comment.
P2: Avoid apt-get upgrade in this image build; it makes builds non-deterministic and can unexpectedly mutate the base image package set.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Dockerfile.sandbox-common, line 27:
<comment>Avoid `apt-get upgrade` in this image build; it makes builds non-deterministic and can unexpectedly mutate the base image package set.</comment>
<file context>
@@ -24,6 +24,7 @@ ENV PATH=${BUN_INSTALL_DIR}/bin:${BREW_INSTALL_DIR}/bin:${BREW_INSTALL_DIR}/sbin
RUN --mount=type=cache,id=openclaw-sandbox-common-apt-cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=openclaw-sandbox-common-apt-lists,target=/var/lib/apt,sharing=locked \
apt-get update \
+ && apt-get upgrade -y --no-install-recommends \
&& apt-get install -y --no-install-recommends ${PACKAGES}
</file context>
| && apt-get upgrade -y --no-install-recommends \ | |
| && apt-get install -y --no-install-recommends ${PACKAGES} | |
| && apt-get install -y --no-install-recommends ${PACKAGES} |
| cache-from: type=gha,scope=docker-release-arm64 | ||
| cache-to: type=gha,mode=max,scope=docker-release-arm64 |
There was a problem hiding this comment.
P2: Same cache-scope collision as the amd64 job: the arm64 slim build overwrites the arm64 default build's cache. Use a separate scope like docker-release-arm64-slim.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/docker-release.yml, line 279:
<comment>Same cache-scope collision as the amd64 job: the arm64 slim build overwrites the arm64 default build's cache. Use a separate scope like `docker-release-arm64-slim`.</comment>
<file context>
@@ -182,41 +248,50 @@ jobs:
with:
context: .
platforms: linux/arm64
+ cache-from: type=gha,scope=docker-release-arm64
+ cache-to: type=gha,mode=max,scope=docker-release-arm64
tags: ${{ steps.tags.outputs.value }}
</file context>
| cache-from: type=gha,scope=docker-release-arm64 | |
| cache-to: type=gha,mode=max,scope=docker-release-arm64 | |
| cache-from: type=gha,scope=docker-release-arm64-slim | |
| cache-to: type=gha,mode=max,scope=docker-release-arm64-slim |
| cache-from: type=gha,scope=docker-release-amd64 | ||
| cache-to: type=gha,mode=max,scope=docker-release-amd64 |
There was a problem hiding this comment.
P2: Default and slim builds share the same GHA cache scope (docker-release-amd64), so the slim build's cache-to: mode=max overwrites the default build's cached layers on every run. Use distinct scopes (e.g., docker-release-amd64 and docker-release-amd64-slim) so each variant retains its own layer cache across runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/docker-release.yml, line 162:
<comment>Default and slim builds share the same GHA cache scope (`docker-release-amd64`), so the slim build's `cache-to: mode=max` overwrites the default build's cached layers on every run. Use distinct scopes (e.g., `docker-release-amd64` and `docker-release-amd64-slim`) so each variant retains its own layer cache across runs.</comment>
<file context>
@@ -81,41 +131,50 @@ jobs:
with:
context: .
platforms: linux/amd64
+ cache-from: type=gha,scope=docker-release-amd64
+ cache-to: type=gha,mode=max,scope=docker-release-amd64
tags: ${{ steps.tags.outputs.value }}
</file context>
| cache-from: type=gha,scope=docker-release-amd64 | |
| cache-to: type=gha,mode=max,scope=docker-release-amd64 | |
| cache-from: type=gha,scope=docker-release-amd64-slim | |
| cache-to: type=gha,mode=max,scope=docker-release-amd64-slim |
| && apt-get upgrade -y --no-install-recommends \ | ||
| && apt-get install -y --no-install-recommends \ |
There was a problem hiding this comment.
P2: Avoid apt-get upgrade in this image build; it breaks deterministic builds despite the pinned base digest.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Dockerfile.sandbox-browser, line 10:
<comment>Avoid `apt-get upgrade` in this image build; it breaks deterministic builds despite the pinned base digest.</comment>
<file context>
@@ -7,6 +7,7 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN --mount=type=cache,id=openclaw-sandbox-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=openclaw-sandbox-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
apt-get update \
+ && apt-get upgrade -y --no-install-recommends \
&& apt-get install -y --no-install-recommends \
bash \
</file context>
| && apt-get upgrade -y --no-install-recommends \ | |
| && apt-get install -y --no-install-recommends \ | |
| && apt-get install -y --no-install-recommends \ |
|
@cubic-dev-ai base was corrected to |
@StreamDemon I have started the AI code review. It will take a few minutes to complete. |
|
@cubic-dev-ai Please run a full code review on this PR and check for the same issues that you previously commented on. Re-comment if those issues still exist. |
@StreamDemon I have started the AI code review. It will take a few minutes to complete. |
|
Local merge-gate note from Neko: GitHub Actions remained queued because the repo is out of Actions minutes on the free tier, so I ran the relevant gate locally on the PR head ( Validated locally:
Cubic also reran on the corrected 17-file diff and posted Proceeding with merge on the basis of local validation + current Cubic review. |
9267162
into
mm/live-ethos-task-activation-base
Summary
pnpm checkonmm/live-ethosIncluded commits
7c433f666c—style(repo): normalize formatter drift on mm/live-ethos395c1dbbec—fix(task-ledger): resolve pnpm check type drift9c06d23e1a—feat(task-ledger): add ownership escalation reassignment rules598e1af716—fix(task-ledger): reconcile landing branch integrationValidation
pnpm tsgopnpm lintpnpm vitest run src/infra/task-ledger.test.ts src/infra/task-lifecycle-publisher.test.tspnpm checkNotes
mm/live-ethosand validated as a combined branch, not just as individually green lane commits.Summary by cubic
Add ownership escalation and reassignment to the task ledger so stalled tasks move to active owners. Emit recall trace records via the ledger and restore green checks by fixing type drift, formatting, and CI routing.
New Features
ownershipEscalationmetadata and expands tests.entity: "recall", kind: "trace"records fromethos-contextandethos-ingestwith compacted headers and no PII.allowModelOverridefor embedded runs; WhatsApp internal hook payload now includesagentId,cfg, andtimestamp.CI/Infra
pnpm checkby aligning types (notably trimmingsrc/commands/agent/types.ts) and normalizing formatter drift.Written for commit 9fae969. Summary will update on new commits.