Skip to content

feat(controlplane): size the policy-evaluation bundle from the attestation and always surface it - #3413

Merged
javirln merged 4 commits into
chainloop-dev:mainfrom
javirln:javirln/marlin
Sep 9, 2026
Merged

feat(controlplane): size the policy-evaluation bundle from the attestation and always surface it#3413
javirln merged 4 commits into
chainloop-dev:mainfrom
javirln:javirln/marlin

Conversation

@javirln

@javirln javirln commented Sep 8, 2026

Copy link
Copy Markdown
Member

Context

Follow-up to #3408, which capped the policy evaluations the workflow-run View API inlines.

Two things were left over. The API sized the policy-evaluation bundle by asking the CAS for its metadata on every view, and it returned a reference to the bundle only when it declined to inline it — so on the happy path a caller was never told the bundle existed or how to fetch it.

What changed

The attestation predicate now records policyEvaluationsBundleSize alongside the bundle reference. The View API reads that size rather than paying a CAS round trip for it: a bundle above the cap is turned away without touching the CAS at all, and a bundle whose size the attestation does not record is read in full.

AttestationItem.policy_evaluations_ref is populated whenever the attestation carries a bundle, not only when the evaluations were left out. A new inlined field says whether the evaluations travel alongside the reference; reason stays about failures and is unset when they do.

In the CLI, workflow run describe renders the chainloop artifact download command for the bundle wherever a digest is known — next to the policy table, or next to the notice that stands in for it when the evaluations were not inlined — and exposes the reference in its JSON output.

The CAS Describe plumbing added for the previous size lookup is removed from the control plane's CAS client and the casclient downloader interface.

Scope

Attestations that do not record a bundle size carry nothing to gate on and remain uncapped, as do attestations whose evaluations live inline in the predicate. Attestation crafting and push are otherwise unchanged: the bundle format and the CAS upload are untouched, and the predicate gains one additive field.

AI disclosure

This contribution was produced with AI assistance (Claude Code). The commit carries an Assisted-by: Claude Code trailer.

Review in cubic

…ation and always surface it

The workflow-run View API sized the policy-evaluation bundle by asking the CAS
for its metadata before deciding whether to inline it, and returned a reference
to the bundle only when it declined. Callers on the happy path were never told
the bundle existed.

The attestation predicate now records policyEvaluationsBundleSize alongside the
bundle reference, and View reads the size it was handed rather than paying a CAS
round trip for it. Bundles above the cap are turned away without touching the
CAS at all; bundles whose size the attestation does not record are read in full.

AttestationItem.policy_evaluations_ref is populated whenever the attestation
carries a bundle, with an "inlined" field saying whether the evaluations travel
alongside it. In the CLI, "workflow run describe" renders the artifact download
command for the bundle wherever a digest is known, next to the policy table or
the notice that stands in for it, and exposes the reference in its JSON output.

The CAS Describe plumbing that backed the previous size lookup is removed.

Assisted-by: Claude Code
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>

Chainloop-Trace-Sessions: a101b7eb-b58c-45c3-8fd7-d7f5240ac260
@chainloop-platform

chainloop-platform Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

AI Session Checks — 🟡 83% · ✅ 0 failing

Avg score Sessions Failing policies Attribution Files Lines Total Duration
🟡 83% 1 ✅ 0 100% AI / 0% Human 19 +397 / -569 1h36m26s

🟡 83% — 100% AI — ✅ All policies passing

Sep 8, 2026 11:38 UTC · 1h36m26s · $32.24 · 560 in / 209.6k out · claude-code 2.1.263 (claude-opus-5)

View session details ↗

Change Summary

  • Adds policyEvaluationsBundleSize to the attestation path and uses it in workflow-run view logic.
  • Always returns and renders the policy-evaluation bundle reference, including the CLI download row.
  • Removes the CAS Describe size-plumbing, updates renderer and CLI tests, and regenerates related API artifacts.
  • Finishes with commit, draft PR, and a follow-up lint-fix commit on the same branch.

AI Session Overall Score

🟡 83% — Strong execution, but alignment and verification left reviewer-visible gaps.

AI Session Analysis Breakdown

🟢 94% · solution-quality

🟢 AI removed the CAS Describe plumbing instead of layering another lookup. · High Impact

🟢 90% · context-and-planning

🟢 AI wrote a detailed multi-section plan before substantive edits began. · High Impact

🟢 90% · scope-discipline

No notes.

🟢 86% · user-trust-signal

No notes.

🟡 74% · verification

🟢 AI updated tests first, observed failures, then implemented the fixes. · High Impact

🟠 The planned CAS-backed end-to-end check never ran before the AI declared completion. · Medium Severity

💡 When you plan an end-to-end validation, either run it or narrow the completion claim to the checks you actually executed.

🟠 The interactive session never included explicit user confirmation that the changed behavior worked. · Medium Severity

💡 When the user stays present, ask for a quick runtime confirmation before shipping multi-path CLI behavior changes.

🟡 72% · alignment

🟠 AI kept bounded downloads until the user explicitly told it to remove them. · Medium Severity

💡 When a prior safeguard may survive a design change, restate it and confirm the user still wants it before coding.


File Attribution

████████████████████ 100% AI / 0% Human

Status Attribution File Lines
modified ai app/controlplane/internal/service/workflowrun_test.go +76 / -76
modified ai app/controlplane/internal/service/workflowrun.go +48 / -85
modified ai app/controlplane/pkg/biz/mocks/CASClient.go +0 / -87
modified ai app/cli/pkg/action/workflow_run_describe_test.go +84 / -1
modified ai app/cli/cmd/workflow_workflow_run_describe_test.go +65 / -17
modified ai app/controlplane/pkg/biz/casclient_test.go +0 / -71
modified ai pkg/casclient/mocks/Downloader.go +0 / -69
modified ai pkg/casclient/mocks/DownloaderUploader.go +0 / -68
modified ai app/cli/cmd/workflow_workflow_run_describe.go +30 / -26
modified ai app/cli/pkg/action/workflow_run_describe.go +27 / -12
modified ai app/controlplane/pkg/biz/casclient.go +0 / -23
modified ai pkg/attestation/renderer/chainloop/v02.go +18 / -5
modified ai pkg/attestation/renderer/chainloop/v02_test.go +15 / -6
modified ai app/cli/pkg/action/attestation_push.go +10 / -9
modified ai app/controlplane/api/controlplane/v1/response_messages.proto +10 / -6
modified ai pkg/attestation/renderer/renderer.go +5 / -4
modified ai app/cli/pkg/action/attestation_push_test.go +6 / -1
modified ai pkg/attestation/renderer/chainloop/chainloop.go +3 / -0
modified ai pkg/casclient/casclient.go +0 / -3

Policies (4)

Status Policy Material Messages
✅ Passed ai-config-ai-agents-allowed ai-coding-session-a101b7 -
✅ Passed ai-config-no-dangerous-commands ai-coding-session-a101b7 -
✅ Passed ai-config-no-secrets ai-coding-session-a101b7 -
✅ Passed ai-config-mcp-servers-allowed ai-coding-session-a101b7 -

Security Checks — ✅ 8 passing

secret-scan

Status Policy Messages
✅ Passed secrets-detection -

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-description-required -
✅ Passed pr-user-story-linked -

⏭️ 2 scans not applied

Scan Reason
vulnerability-scan no manifest/lockfile changed
github-actions-scan no workflow files changed

View attestation ↗


Powered by Chainloop and Chainloop Trace

… switch

Behaviour is unchanged; it clears a revive indent-error-flow warning in a file
this change already touches.

Assisted-by: Claude Code
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>

Chainloop-Trace-Sessions: a101b7eb-b58c-45c3-8fd7-d7f5240ac260
The added rendering cases pushed two literals past the goconst threshold.

Assisted-by: Claude Code
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>

Chainloop-Trace-Sessions: a101b7eb-b58c-45c3-8fd7-d7f5240ac260
@javirln
javirln marked this pull request as ready for review September 8, 2026 13:17

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 25 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/controlplane/internal/service/workflowrun.go
@migmartri

Copy link
Copy Markdown
Member

The size is recorded in the attestation. It went in as a dedicated predicate field, policyEvaluationsBundleSize, rather than a chainloop.dev/size descriptor annotation — the proposal allowed either. It sits alongside policyEvaluationsRef in the signed statement, so it is equally tamper-proof, and uploadPolicyEvaluationsBundle fills it from the bytes it has just marshalled.

can you elaborate on why it should be outside of the object? resourceDescriptors contain inside the size, why not this one?

image

do you have an example of final attestation with this annotation?

The size the workflow-run View API gates the inlining cap on was recorded as a
predicate field of its own, next to the reference rather than in it.

It now sits inside policyEvaluationsRef, alongside the digest and media type it
describes, as a "size" key of that object. in-toto resource descriptors have no
size field, so the reference becomes a Chainloop type embedding the descriptor
and adding the size, which keeps the rendered predicate a single object:

  "policyEvaluationsRef": {
    "digest": {"sha256": "4c2497..."},
    "media_type": "application/vnd.chainloop.policy-evaluations.v1+json",
    "name": "policy-evaluations",
    "size": 325081
  }

Reading the size from the reference the resolver already receives removes the
separate predicate field, the extra interface method and the extra argument
threaded through the renderer and the View path. A negative size reads as
unrecorded rather than being trusted.

Assisted-by: Claude Code
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>

Chainloop-Trace-Sessions: a101b7eb-b58c-45c3-8fd7-d7f5240ac260
@javirln

javirln commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

The size is recorded in the attestation. It went in as a dedicated predicate field, policyEvaluationsBundleSize, rather than a chainloop.dev/size descriptor annotation — the proposal allowed either. It sits alongside policyEvaluationsRef in the signed statement, so it is equally tamper-proof, and uploadPolicyEvaluationsBundle fills it from the bytes it has just marshalled.

can you elaborate on why it should be outside of the object? resourceDescriptors contain inside the size, why not this one?
image

do you have an example of final attestation with this annotation?

No big reason and maybe I just placed them in the wrong place because I intended to place it under the bundle ref. It's now fixed, here's an example:

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "chainloop.workflow.demo",
      "digest": {
        "sha256": "259dffe5ebf83d7fc2e216174f399e68efa23fb6ae89d90b80cea75ec0213ee0"
      }
    }
  ],
  "predicateType": "chainloop.dev/attestation/v0.2",
  "predicate": {
    "auth": {
      "id": "2a8d0ef9-e4e9-4106-924e-d931ce6af838",
      "type": "AUTH_TYPE_USER"
    },
    "buildType": "chainloop.dev/workflowrun/v0.1",
    "builder": {
      "id": "chainloop.dev/cli/dev@sha256:67289b76b523b00f7443c3f79d13271f7a8385e858cfea09d5fff31b9172eadd"
    },
    "casBackend": {
      "casBackendID": "27d9c085-2b9a-48a8-b8e2-4a16bf3bdcec",
      "casBackendName": "minio-e2e",
      "fallback": false
    },
    "materials": [
      {
        "annotations": {
          "chainloop.material.cas": true,
          "chainloop.material.name": "sbom",
          "chainloop.material.sbom.main_component.name": "e2e-demo",
          "chainloop.material.sbom.main_component.type": "application",
          "chainloop.material.sbom.main_component.version": "",
          "chainloop.material.size": "404150",
          "chainloop.material.type": "SBOM_CYCLONEDX_JSON"
        },
        "digest": {
          "sha256": "9bf2cc6a3d22ab5630940e7007eaac4a388d357e2b371c9c022302b889e1c7e3"
        },
        "name": "sbom.json"
      }
    ],
    "metadata": {
      "contractName": "demo-proj-demo",
      "contractVersion": "1",
      "finishedAt": "2026-09-09T04:28:49.451710639Z",
      "initializedAt": "2026-09-09T04:28:49.156841241Z",
      "name": "demo",
      "organization": "e2e-demo",
      "project": "demo-proj",
      "projectVersion": "",
      "projectVersionPrerelease": false,
      "team": "",
      "workflowID": "c1331928-5185-4980-8aea-8660c2677997",
      "workflowName": "demo",
      "workflowRunID": "343b2c29-be80-4b2b-81c0-fff50ecc28b6"
    },
    "policyAttBlocked": false,
    "policyBlockBypassEnabled": false,
    "policyCheckBlockingStrategy": "ADVISORY",
    "policyEvaluationsCount": 1,
    "policyEvaluationsRef": {
      "digest": {
        "sha256": "4c2497ddd8a0d4fc4b8022d0a8911efbe1a792d0231d3832d2ea2afdb314e0e5"
      },
      "media_type": "application/vnd.chainloop.policy-evaluations.v1+json",
      "name": "policy-evaluations",
      "size": 325081
    },
    "policyHasViolations": true,
    "policyViolationsCount": 4000,
    "runnerEnvironment": "unknown",
    "runnerType": "RUNNER_TYPE_UNSPECIFIED",
    "signingCA": "fileCA"
  }
}

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 8 files (changes from recent commits).

Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread pkg/attestation/renderer/chainloop/v02.go
@javirln
javirln merged commit 5c634eb into chainloop-dev:main Sep 9, 2026
17 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.

2 participants