feat(controlplane): size the policy-evaluation bundle from the attestation and always surface it - #3413
Conversation
…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
AI Session Checks — 🟡 83% · ✅ 0 failing
|
| 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 |
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
There was a problem hiding this comment.
All reported issues were addressed across 25 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
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
There was a problem hiding this comment.
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


Context
Follow-up to #3408, which capped the policy evaluations the workflow-run
ViewAPI 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
policyEvaluationsBundleSizealongside the bundle reference. TheViewAPI 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_refis populated whenever the attestation carries a bundle, not only when the evaluations were left out. A newinlinedfield says whether the evaluations travel alongside the reference;reasonstays about failures and is unset when they do.In the CLI,
workflow run describerenders thechainloop artifact downloadcommand 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
Describeplumbing added for the previous size lookup is removed from the control plane's CAS client and thecasclientdownloader 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 Codetrailer.