fix(ci): make the sandbox kit workflow runnable - #3459
Conversation
A shell comment in the "Add Attestation and Push Kit" step contained the literal empty expression syntax. GitHub expands template expressions across the entire workflow file, including inside run bodies and their comments, so the empty expression failed expression parsing and the whole file was rejected. The workflow never scheduled a job on any release bump, which means no sandbox kit has been published since it was added. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 1e55d1fc-a673-481e-a278-4cdd9eecb47f
AI Session Checks — 🟡 82% ·
|
| Avg score | Sessions | Failing policies | Attribution | Files | Lines | Total Duration |
|---|---|---|---|---|---|---|
| 🟡 82% | 1 | 100% AI / 0% Human | 1 | +9 / -3 | 10m16s |
🟡 82% — 100% AI — ⚠️ 1 policies failing
-
Sep 21, 2026 13:42 UTC · 10m16s · $6.94 · 192 in / 50.5k out · claude-code 2.1.278 (claude-opus-5)
Change Summary
-
- Rewords the
package_sandbox_kitworkflow comment that contained an empty${{ }}expression. - Bumps
docker/login-actionfrom v4.4.0 to v4.6.0 for Docker Hub OIDC support. - Adds workflow comments documenting the OIDC version requirement.
- Rewords the
AI Session Overall Score
-
🟡 82% — Solid fix, but planning was thin and workflow success was never end-to-end verified.
AI Session Analysis Breakdown
-
🟢 94% · scope-discipline
-
🟢 Both fixes stayed in the same workflow file the investigation centered on. · High Impact
🟢 92% · solution-quality
-
🟢 The AI found two concrete root causes instead of papering over the failure. · High Impact
🟢 90% · alignment
-
🟢 AI explicitly warned the first fix only restored parsing, not full execution. · Medium Impact
🟢 90% · user-trust-signal
-
No notes.
🟡 65% · context-and-planning
-
🟠 The session started from a thin prompt and never produced a visible plan before editing. · Medium Severity
💡 For workflow investigations, write a short plan before editing so validation steps are explicit.
🟡 65% · verification
-
🟠 Workflow changes were locally probed, but the GitHub Actions path was never rerun after the fixes. · Medium Severity
💡 For workflow fixes, rerun the job or trigger the path before saying it will work.
-
File Attribution
████████████████████100% AI / 0% HumanStatus Attribution File Lines modified ai .github/workflows/package_sandbox_kit.yaml+9 / -3
Policies (4, 1 failing)
Status Policy Material Messages ✅ Passed ai-config-ai-agents-allowedai-coding-session-1e55d1- ✅ Passed ai-config-no-dangerous-commandsai-coding-session-1e55d1- ⚠️ Failedai-config-no-secretsai-coding-session-1e55d1- Secret (generic-password) detected in session content [turn=161, source=assistant-text, line=7]: One caveat: this only makes the workflow parse. It has never actually executed a single step, so the rest of it — the Docker Hub OIDC login (which passes
usernamewith no[REDACTED:generic-passwo...</li><li>Secret (generic-password) detected in session content [turn=185, source=tool_result, line=33]: [REDACTED:generic-password]: ${{ secrets.GITHUB_TOKEN }}</li><li>Secret (generic-password) detected in session content [turn=230, source=tool_result, line=13]: 155-action as the [REDACTED:generic-password].</li><li>Secret (generic-password) detected in session content [turn=230, source=tool_result, line=2]: 144- [REDACTED:generic-password]: ${{ secrets.AZURE_CLIENT_SECRET }}</li><li>Secret (generic-password) detected in session content [turn=233, source=tool_result, line=17]: [REDACTED:generic-password]:</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=111]: await Docker.getExecOutput(['login', '--[REDACTED:generic-password]-stdin', '--username', username, registry], {</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=114]: input: Buffer.from([REDACTED:generic-password]),</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=51]: await loginECR(auth.registry, auth.username, auth.[REDACTED:generic-password], auth.scope);</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=53]: await loginStandard(auth.registry, auth.username, auth.[REDACTED:generic-password], auth.scope);</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=77]: export async function loginStandard(registry: string, username: string, [REDACTED:generic-password]: string, scope?: string): Promise<void> {</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=78]: if (!username && ![REDACTED:generic-password]) {</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=79]: throw new Error('Username and [REDACTED:generic-password] required');</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=84]: if (![REDACTED:generic-password]) {</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=87]: await loginExec(registry, username, [REDACTED:generic-password], scope);</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=90]: export async function loginECR(registry: string, username: string, [REDACTED:generic-password]: string, scope?: string): Promise<void> {</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=92]: const regDatas = await aws.getRegistriesData(registry, username, [REDACTED:generic-password]);</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=94]: await loginExec(regData.registry, regData.username, regData.[REDACTED:generic-password], scope);</li><li>Secret (generic-password) detected in session content [turn=248, source=tool_result, line=98]: async function loginExec(registry: string, username: string, [REDACTED:generic-password]: string, scope?: string): Promise<void> {</li><li>Secret (generic-password) detected in session content [turn=252, source=tool_result, line=14]: 64:organization name asusername, omit[REDACTED:generic-password], and set the OIDC connection</li><li>Secret (generic-password) detected in session content [turn=336, source=tool_result, line=10]: organization name asusername, omit[REDACTED:generic-password]`, and set the OIDC connection - … and 3 more — view all ↗
✅ Passed ai-config-mcp-servers-allowedai-coding-session-1e55d1- -
Security Checks — ✅ 10 passing
✅ secret-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ github-actions-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | ci-pipeline-security |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
✅ iac-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | iac-misconfiguration |
- |
PR info
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | pr-min-approvals |
- |
| ✅ Passed | pr-description-required |
- |
| ✅ Passed | pr-user-story-linked |
- |
⏭️ 1 scan not applied
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
View attestation ↗
View attestation ↗
Powered by Chainloop and Chainloop Trace
The Docker Hub login step passes the organization as `username`, omits `password` and sets DOCKERHUB_OIDC_CONNECTIONID, which is the documented Docker Hub OIDC shape. That support landed in docker/login-action v4.5.0; the step pinned v4.4.0, which ignores the variable entirely and rejects a password-less login with "Password required". Pin v4.6.0. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 1e55d1fc-a673-481e-a278-4cdd9eecb47f
Summary
The Package Sandbox Kit workflow has never run successfully. Two independent blockers.
1. The workflow file does not parse. A shell comment inside the "Add Attestation (Sandbox Kit) and Push Kit" step contained the literal empty expression syntax. GitHub expands template expressions across the whole workflow file, including inside
run:bodies and the shell comments within them, so the empty expression failed expression parsing and the entire file was rejected. Runs surfaced only as "This run likely failed because of a workflow file issue", with no jobs and no check runs. The comment is reworded and now states the constraint so it is not reintroduced.2. The Docker Hub login could not succeed. The step passes the organization as
username, omitspasswordand setsDOCKERHUB_OIDC_CONNECTIONID, which is the documented Docker Hub OIDC shape. That support landed indocker/login-actionv4.5.0, but the step pinned v4.4.0, which ignores the variable and rejects a password-less login with "Password required". Pinned to v4.6.0.Since the workflow triggers on
devel/sandbox-kit/*/spec.yaml, which the release bump script rewrites, every release bump produced a failed run and no sandbox kit has been published.Verification
actionlintis clean on the file.kits=["claude"]) and against a no-op range (kits=[]).docker/login-actionv4.6.0 confirmed to implementDOCKERHUB_OIDC_CONNECTIONID; v4.4.0 confirmed not to.DockerSandboxes-linux-amd64-ubuntu2404.debandubuntu-latestis ubuntu-24.04.sbx kit validate REFERENCEandsbx kit push DIRECTORY REFERENCE --signmatch the invocations, andsbx v0.43.0reportsVALIDfordevel/sandbox-kit/claude.dl.chainloop.dev/cli/install.sh.Still unexercised until this actually runs: the Docker Hub OIDC connection ruleset, the push to
docker.io/chainloop/sbx-kit-claude, and the material kind auto-detection forchainloop attestation add --name sandbox-kit, which has no entry in.github/workflows/contracts/unlike every other attesting workflow.AI assistance
This change was produced with the assistance of Claude Code. Both commits carry an
Assisted-by: Claude Codetrailer.🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri