Repository navigation
feat(trace): local storage mode for AI coding sessions - #3617
matiasinsaurralde wants to merge 12 commits into
Conversation
AI Session Checks — 🟢 91% · ✅ 0 failing
|
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-ac2618 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-ac2618 |
- |
| ✅ Passed | ai-config-no-secrets |
ai-coding-session-ac2618 |
- |
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-ac2618 |
- |
🟢 93% — 100% AI — ✅ All policies passing
-
Oct 14, 2026 02:52 UTC · 2h3m24s · $118.09 · 1.2k in / 768.3k out · claude-code 2.1.296 (claude-opus-4-8)
Change Summary
-
- Adds
traceStorage,tracePath, andtraceRequiredplumbing for local session storage. - Implements local-mode storage across
trace init, pre-push hooks,trace run, and session-start banners. - Adds focused tests, updates CLI docs/Trace README, and preserves stored sessions across cleanup.
- Adds
AI Session Overall Score
-
🟢 93% — Clean, well-planned feature work with strong verification and no visible trust erosion.
AI Session Analysis Breakdown
-
🟢 95% · alignment
-
🟢 The AI resolved spec ambiguities with the user before coding and then stayed on that path. · High Impact
🟢 95% · context-and-planning
-
🟢 A detailed plan landed before editing and framed the whole implementation. · High Impact
🟢 95% · verification
-
🟢 Focused tests and two manual binary checks covered the new local-mode paths. · High Impact
🟢 93% · user-trust-signal
-
No notes.
🟢 90% · scope-discipline
-
No notes.
🟢 89% · solution-quality
-
No notes.
-
File Attribution
████████████████████100% AI / 0% Human
Policies (4)
Status Policy Material Messages ✅ Passed ai-config-ai-agents-allowedai-coding-session-7492b7- ✅ Passed ai-config-no-dangerous-commandsai-coding-session-7492b7- ✅ Passed ai-config-no-secretsai-coding-session-7492b7- ✅ Passed ai-config-mcp-servers-allowedai-coding-session-7492b7- -
Security Checks — evaluating
Policies are still being evaluated. Results will update once available.
Scans not applied (3)
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
iac-scan |
no IaC files changed |
Security context
This change touches code with 6 recorded security-fix advisories. These are pointers to what past fixes established, not findings in this diff, and they never fail the check.
View in Chainloop ↗ · How this works ↗
Powered by Chainloop and Chainloop Trace
…ontrol plane Chainloop Trace records an AI coding session and, on git push, uploads a signed attestation to a control plane. This adds a local mode where a repository records its sessions and stores the assembled evidence set on disk, with no control plane, login, or network. The storage mode is a committed repository policy in .chainloop.yml: traceStorage (CONTROL_PLANE default, or LOCAL), an optional tracePath, and traceRequired (replacing the deprecated requireTrace). `trace init --local` sets a repository up with no control plane, and the pre-push hook then runs the same push-time assembly and writes each material under its content digest, with a per-session manifest that declares the output unsigned and not an attestation. `trace run` honors the mode as well. The stored evidence equals what a push would upload apart from the signature, and secret redaction stays on by default with a CHAINLOOP_TRACE_NO_REDACT opt-out for a trusted local store. In local mode no control-plane connection is opened: the trace commands build it lazily and only on the attestation path. The stored sessions are kept across a trace uninstall. Refs #3582 Chainloop-Trace-Sessions: 7492b77a-1dc6-4352-823f-f87c42672452 Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
7be3fc4 to
49b0b6c
Compare
The local store sized the merged annotations map from len(crafted)+len(extra), which a static analyzer flags as an allocation size that may overflow. Both maps hold only a handful of annotation keys, so the sum cannot overflow in practice; size the hint from one length and let the map grow instead. Refs #3582 Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
There was a problem hiding this comment.
All reported issues were addressed across 23 files
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
The local-mode session-start banner now says the session is recorded in local mode, so a developer can tell at a glance which storage mode is active without inferring it from the destination line (R-012). Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
A config read or parse error no longer selects CONTROL_PLANE silently, so a LOCAL repository with a broken .chainloop.yml cannot have its pre-push hook upload a local-only session. Local evidence is finalized only when every gathered session is stored, so a partial failure no longer strands a session's retry state. A control-plane trace run is reported in telemetry again, while a local run stays excluded. A trace run that stores its evidence surfaces a store failure when the wrapped command succeeded. Uninstall preserves a configured tracePath that resolves inside the trace directory. The local session-start banner states the storage timing that matches how the session is driven. Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
…clone guidance A control-plane trace init over a repository previously set to LOCAL now writes traceStorage: CONTROL_PLANE, so the pre-push hook stops storing on disk and the init no longer disagrees with how the repository behaves. The local-mode next steps now tell each teammate to run chainloop trace init --local after cloning, since the managed git hooks live under .git and are not cloned, so a commit alone does not set a clone up to record. Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
A control-plane trace init now surfaces an invalid traceStorage in an existing .chainloop.yml before installing hooks, instead of discarding the error and leaving a repository whose every pre-push fails on the same config. An explicit --local run still overrides the bad value. Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
…ent it A control-plane trace init that clears a previous LOCAL policy now tells the user the repository switched and to commit .chainloop.yml, instead of changing the mode with only a debug log. The trace README documents how to switch storage modes in both directions and that each clone installs its own hooks. Signed-off-by: Matías Insaurralde <matias@chainloop.dev>
I'd NOT make it imply local since in the future we might allow more than one mode I'd also like to discuss the right default location, would it be or should it be |
Maybe here we can add, if in the future you wna to connect this repo to a chainloop control plane, run trace init again |
migmartri
left a comment
There was a problem hiding this comment.
@matiasinsaurralde ptal at my comments, I can't make it work as well as I added some additional feedback
I think |
Replace the single traceStorage value and the --local flag with a traceModes list in .chainloop.yml and the --online and --offline flags of trace init. A repository can attest to the control plane, store sessions on disk, or do both. With both modes, the pre-push hook stores the offline copy first, then attests. A failed mode keeps the sessions for the next push. - trace init with no mode flag keeps the modes in .chainloop.yml. - --trace-path no longer turns on the offline mode. - An online init that fails on authentication, or cannot reach the control plane, suggests "chainloop trace init --offline". - The session-start banner names the active trace modes. - A clone of an offline repository needs no trace init: the first traced session installs the git hooks, as in the online mode. Assisted-by: Claude Code Signed-off-by: Jose I. Paris <jiparis@chainloop.dev> Chainloop-Trace-Sessions: ac261808-7f67-40a0-b901-d1d43b9dd863
There was a problem hiding this comment.
All reported issues were addressed across 23 files (changes from recent commits).
Tip: Get a Slack DM when cubic finishes reviewing your PRs. Connect Slack
View guided diff | Re-trigger cubic
…ther" This reverts commit db6b419. Signed-off-by: Jose I. Paris <jiparis@chainloop.dev> Assisted-by: Claude Code
There was a problem hiding this comment.
All reported issues were addressed across 23 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
A clone of a local-mode repository needs no trace init: the first traced session installs the git hooks, as in control-plane mode. Remove the next-steps text that told each teammate to run init after cloning, fix the README to match, and test the auto-install for a local-mode clone with no project name. Assisted-by: Claude Code Signed-off-by: Jose I. Paris <jiparis@chainloop.dev> Chainloop-Trace-Sessions: ac261808-7f67-40a0-b901-d1d43b9dd863
Rewrite the help, flag descriptions, init output, errors, logs and the session-start banner of the local mode with short, active sentences and no semicolons. Remove "the push tells you where" from the next steps. Assisted-by: Claude Code Signed-off-by: Jose I. Paris <jiparis@chainloop.dev> Chainloop-Trace-Sessions: ac261808-7f67-40a0-b901-d1d43b9dd863
There was a problem hiding this comment.
2 issues found across 11 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/cli/cmd/trace_run.go">
<violation number="1" location="app/cli/cmd/trace_run.go:35">
P2: This says the identity flags are mandatory for all runs, then says they are optional in local mode. Qualify the requirement as applying only to control-plane runs.</violation>
</file>
<file name="app/cli/documentation/cli-reference.md">
<violation number="1" location="app/cli/documentation/cli-reference.md:3385">
P2: `trace run` also reads `tracePath` to choose the local evidence directory, so this says it reads only one config value incorrectly. Name both settings here.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| .chainloop.yml, and removes every hook and local trace artifact when the | ||
| wrapped command exits, so a session never leaks setup into the next one. | ||
| trace run does not read the attestation identity from .chainloop.yml. The | ||
| --org, --project and --workflow flags (mandatory) and the optional --version |
There was a problem hiding this comment.
P2: This says the identity flags are mandatory for all runs, then says they are optional in local mode. Qualify the requirement as applying only to control-plane runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/cmd/trace_run.go, line 35:
<comment>This says the identity flags are mandatory for all runs, then says they are optional in local mode. Qualify the requirement as applying only to control-plane runs.</comment>
<file context>
@@ -31,15 +31,18 @@ const traceRunLong = `Wrap an external command (typically an AI coding agent) an
-preserving stored local session evidence, so a session never leaks setup into
-the next one.
+trace run does not read the attestation identity from .chainloop.yml. The
+--org, --project and --workflow flags (mandatory) and the optional --version
+flag set it.
+
</file context>
| --org, --project and --workflow flags (mandatory) and the optional --version | |
| --org, --project and --workflow flags are required for control-plane runs. The | |
| optional --version flag sets it. |
| trace run reads one value from .chainloop.yml: traceStorage. In a local-mode | ||
| repository (traceStorage: LOCAL), trace run writes the session to disk when the |
There was a problem hiding this comment.
P2: trace run also reads tracePath to choose the local evidence directory, so this says it reads only one config value incorrectly. Name both settings here.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/documentation/cli-reference.md, line 3385:
<comment>`trace run` also reads `tracePath` to choose the local evidence directory, so this says it reads only one config value incorrectly. Name both settings here.</comment>
<file context>
@@ -3376,15 +3378,18 @@ Wrap an external command (typically an AI coding agent) and emit a
+--org, --project and --workflow flags (mandatory) and the optional --version
+flag set it.
+
+trace run reads one value from .chainloop.yml: traceStorage. In a local-mode
+repository (traceStorage: LOCAL), trace run writes the session to disk when the
+command exits. It does not connect to a control plane, and the --org, --project
</file context>
| trace run reads one value from .chainloop.yml: traceStorage. In a local-mode | |
| repository (traceStorage: LOCAL), trace run writes the session to disk when the | |
| trace run reads traceStorage and the optional tracePath from .chainloop.yml. | |
| In a local-mode repository (traceStorage: LOCAL), trace run writes the session to disk when the |
When a control-plane trace init fails because the user is not authenticated, add a tip to the error: run "chainloop trace init --local" to configure the repository in local mode. Assisted-by: Claude Code Signed-off-by: Jose I. Paris <jiparis@chainloop.dev> Chainloop-Trace-Sessions: ac261808-7f67-40a0-b901-d1d43b9dd863



Refs PFM-7675
Summary
Chainloop Trace records an AI coding session and attests it to a control plane on
git push. This adds a local storage mode: a repository records its sessions and stores the assembled evidence set on disk, with no control plane, login, or network.The mode is a committed repository policy in
.chainloop.yml:traceStorage-CONTROL_PLANE(default) orLOCALtracePath- optional location for stored sessions (defaults under the per-clone trace state)traceRequired- replaces the deprecatedrequireTracechainloop trace init --localsets a repository up with no control plane (R-008). In local mode the pre-push hook runs the same push-time assembly and writes each material under its content digest, with a per-session manifest that declares the output unsigned and not an attestation (R-002, R-003, R-006, R-009).trace runhonors the mode as well. Secret redaction stays on by default, with aCHAINLOOP_TRACE_NO_REDACTopt-out for a trusted local store (R-004). No control-plane connection is opened in local mode (R-005), and stored sessions are preserved across atrace uninstall.Implements the design in
docs/specs/issue-3582-trace-evidence-export.md.Refs #3582
Flow
trace init --local: