Skip to content

feat(cli): surface the trace session link to the user - #3430

Merged
migmartri merged 11 commits into
mainfrom
gh-3429-trace-push-session-link
Sep 10, 2026
Merged

feat(cli): surface the trace session link to the user#3430
migmartri merged 11 commits into
mainfrom
gh-3429-trace-push-session-link

Conversation

@migmartri

@migmartri migmartri commented Sep 10, 2026

Copy link
Copy Markdown
Member

After chainloop trace attests an AI coding session, the CLI now tells the user where to find it. Two channels, because the push happens in two quite different contexts.

Screenshot 2026-09-10 at 22 51 20

Show the session link, note that it shows two because we had an opencode session in the same branch

Screenshot 2026-09-10 at 23 13 50

Closes #3429

AI assistance: this change was produced with Claude Code. Each commit carries an Assisted-by: trailer.

🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri

After `chainloop trace` attests an AI coding session, log one line per
attested session pointing at its dashboard page. The link is only shown
when the control plane reports a ui_dashboard_url, reusing the value the
crafting state already carries from attestation init rather than making
an extra request from inside a git hook. Without a dashboard the line
still names the session.

The URL is built from the agent's own session ID, the only identifier
available at push time, which the dashboard resolves alongside the
Chainloop UUID.

Closes #3429

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
@chainloop-platform

chainloop-platform Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

AI Session Checks — 🟡 80% · ⚠️ 1 failing

Avg score Sessions Failing policies Attribution Files Lines Total Duration
🟡 80% 3 ⚠️ 1 85% AI / 15% Human 25 +1276 / -121 3h27m6s

🟡 75% — 42% AI — ✅ All policies passing

Sep 10, 2026 20:39 UTC · 8m17s · $2.86 · 88 in / 24.1k out · claude-code 2.1.268 (claude-opus-5)

View session details ↗

Change Summary

  • Inspects the dirty tree around .opencode, .cursor, and trace_banner_test.go before acting.
  • Replaces repeated dashboard URL literals in trace_banner_test.go with the shared testDashboardURL constant.
  • Leaves .opencode/plugins/chainloop-trace.ts modified and .cursor/hooks.json untracked; no commit or push happened.

AI Session Overall Score

🟡 75% — Unfinished commit flow and incomplete final verification keep this out of green.

AI Session Analysis Breakdown

🟢 88% · solution-quality

🟢 AI used the existing shared test constant instead of a quick lint-silencing workaround. · High Impact

🟢 86% · scope-discipline

No notes.

🟢 84% · context-and-planning

🟢 AI clarified whether .cursor/hooks.json should be committed before acting on it. · High Impact

🟢 82% · user-trust-signal

No notes.

🟡 63% · verification

🟢 AI ran targeted and broader go test commands and captured both pass results. · Medium Impact

🟠 Earlier tests passed, but the last edit never got a fresh test/lint rerun after the interrupted command. · Medium Severity

💡 After the final edit, rerun the exact verification command before you summarize the state.

🟡 58% · alignment

🟠 The requested commit/push never happened; .opencode and .cursor were still pending at the end. · Medium Severity

💡 When the ask is commit/push, either complete it or state early that you are pausing the flow.


File Attribution

████████░░░░░░░░░░░░ 42% AI / 58% Human

Status Attribution File Lines
modified ai app/cli/pkg/action/trace_banner_test.go +88 / -10
created human app/cli/internal/trace/providers/capabilities_test.go +74 / -0
modified human app/cli/pkg/action/trace_agent_hook.go +25 / -9
modified human app/cli/internal/trace/claude/provider.go +6 / -0
modified human app/cli/internal/trace/cursor/provider.go +6 / -0
modified human app/cli/internal/trace/opencode/provider.go +6 / -0
modified human app/cli/internal/trace/provider.go +6 / -0

Policies (4)

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

🟢 84% — 30% AI — ✅ All policies passing

Jul 6, 2026 14:22 UTC · 16m59s · $0.14 · 292.6k in / 2.6k out · opencode 1.17.13 (deepseek/deepseek-v4-pro)

View session details ↗

Change Summary

  • Amends and force-pushes the existing commit to test opencode session tracking.
  • Adds an explanatory comment above fileWritingTools in .opencode/plugins/chainloop-trace.ts.
  • Commits and pushes the comment change, then answers follow-up repository questions.

AI Session Overall Score

🟢 84% — Focused debugging session; the only visible code change was a scoped comment update.

AI Session Analysis Breakdown

🟢 91% · scope-discipline

🟢 AI kept the only edit inside the user-requested plugin file. · High Impact

🟢 88% · alignment

🟢 AI only edited the plugin after the user explicitly asked for a comment. · High Impact

🟢 82% · context-and-planning

🟢 AI inspected repo state before amending and diagnosing the hook flow. · Medium Impact

🟡 72% · user-trust-signal

🟠 The user showed mild impatience when the amend test did not immediately explain the missing linkage. · Medium Severity

💡 When debugging stalls, stop after one nudge and ask the AI for concrete evidence before continuing.

abstained · solution-quality

🟡 Solution quality was not assessed because the visible change was an explanatory comment only. · Low Severity

abstained · verification

🟡 Verification was not assessed because the visible change did not alter runtime behavior. · Low Severity

Missing criteria: solution-quality, verification


File Attribution

██████░░░░░░░░░░░░░░ 30% AI / 70% Human

Status Attribution File Lines
modified ai .opencode/plugins/chainloop-trace.ts +64 / -22
created human .cursor/hooks.json +23 / -0

Policies (4)

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

🟡 82% — 100% AI — ⚠️ 1 policies failing

Sep 10, 2026 13:19 UTC · 3h1m50s · $84.20 · 2.6k in / 431.8k out · claude-code 2.1.267 (claude-opus-5)

View session details ↗

Change Summary

  • Adds trace-push session links to CLI logging after successful attestation.
  • Reworks agent hooks to surface pending session links through Claude post-tool-use notifications.
  • Adds pending-link state handling plus provider wiring for Claude, Cursor, and OpenCode.
  • Expands Go tests around URL building, hook logging, handoff delivery, and stale-state recovery.

AI Session Overall Score

🟡 82% — Strong verification and quality, but alignment, scope, and user-trust show moderate drift.

AI Session Analysis Breakdown

🟢 90% · verification

🟢 AI repeatedly ran targeted and full Go tests after each change cluster. · High Impact

🟠 Direct terminal rendering of systemMessage stayed unconfirmed; validation relied on tests and model relay instead. · Medium Severity

💡 For hook UX, capture one real terminal render before calling the delivery path fully verified.

🟢 88% · context-and-planning

🟢 User front-loaded the dashboard-url gate and required specs before coding. · High Impact

🟢 86% · solution-quality

🟢 Reviewer-found flaws were fixed by reworking state flow, not masking symptoms. · High Impact

🟡 78% · user-trust-signal

🟠 User had to restate that the agent itself should surface the link, not just the log wording. · Medium Severity

💡 When intent shifts from phrasing to delivery channel, pause and restate the new goal before continuing.

🟡 74% · alignment

🟠 AI said the follow-up would stay off PR 3430, then folded it into that same PR. · Medium Severity

💡 When branch strategy changes, pause and confirm whether the follow-up belongs in the same PR.

🟡 70% · scope-discipline

🟠 User-expanded scope from log-line changes to agent notifications, and the same PR absorbed both features. · Medium Severity

💡 When scope grows into a second feature, confirm whether it should land in the same PR before folding it in.


File Attribution

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

Status Attribution File Lines
modified ai app/cli/internal/trace/state/pendinglinks_test.go +139 / -7
modified ai app/cli/pkg/action/trace_notify_links_test.go +137 / -6
modified ai app/cli/pkg/action/action_test.go +138 / -0
modified ai app/cli/internal/trace/state/pendinglinks.go +112 / -9
modified ai app/cli/pkg/action/trace_hook_handler_test.go +97 / -12
created ai app/cli/internal/trace/claude/announce_test.go +96 / -0
modified ai app/cli/pkg/action/trace_hook_handler.go +76 / -20
modified ai app/cli/pkg/action/trace_agent_hook.go +53 / -2
modified ai app/cli/internal/trace/claude/provider.go +33 / -0
modified ai app/cli/pkg/action/action.go +24 / -5
modified ai app/cli/internal/trace/provider.go +19 / -2
modified ai app/cli/internal/trace/opencode/provider.go +15 / -5
modified ai app/cli/pkg/action/attestation_push.go +16 / -1
modified ai app/cli/pkg/action/trace_attestation.go +9 / -7
modified ai app/cli/internal/trace/cursor/provider.go +9 / -2
modified ai app/cli/pkg/action/trace_run.go +5 / -2

Policies (4, 1 failing)

Status Policy Material Messages
✅ Passed ai-config-ai-agents-allowed ai-coding-session-2cee62 -
✅ Passed ai-config-no-dangerous-commands ai-coding-session-2cee62 -
⚠️ Failed ai-config-no-secrets ai-coding-session-2cee62
  • Secret (generic-password) was redacted from session content [turn=1756, source=tool_result, line=1]
  • Secret (generic-password) was redacted from session content [turn=1767, source=assistant-text, line=3]
  • Secret (generic-password) was redacted from session content [turn=1771, source=assistant-tool_use:Bash, line=1]
  • Secret (generic-password) was redacted from session content [turn=1772, source=tool_result, line=4]
  • Secret (generic-password) was redacted from session content [turn=1776, source=assistant-tool_use:Write, line=1]
  • Secret (generic-password) was redacted from session content [turn=1779, source=assistant-tool_use:Write, line=1]
  • Secret (generic-password) was redacted from session content [turn=1800, source=assistant-text, line=6]
  • Secret (generic-password) was redacted from session content [turn=34, source=tool_result, line=239]
✅ Passed ai-config-mcp-servers-allowed ai-coding-session-2cee62 -

Security Checks — ✅ 9 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-min-approvals -
✅ 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

@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 6 files

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 app/cli/pkg/action/trace_hook_handler_test.go Outdated
Comment thread app/cli/pkg/action/trace_hook_handler.go Outdated
Address review feedback on the session-link tests.

The logging test now uses an organization that differs from the other
fixtures and that changes under path escaping, so the asserted URL proves
the link is built from the supplied organization rather than from an
incidental value.

Add a table test for AttestationResult.GetOrganization covering the nil
links in the Status chain, which is the accessor the logging call depends
on.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
Use "Coding Session Available at <url>" so the line reads as a sentence
and the link is the last thing on it, which keeps it clickable in a
terminal. The link is inline rather than a structured field, and the
session field is dropped on that path because the URL already ends in
the session ID.

Without a dashboard the line stays a bare confirmation naming the
session, since there is no link to point at.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7

@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 2 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/cli/pkg/action/trace_hook_handler.go Outdated
When a coding agent runs the git push, the pre-push hook's session link
is captured into that agent's shell-tool output rather than shown to the
user, who is the audience for it.

A successful push now records its session links in the trace directory,
which survives the post-push state wipe. The next hook after a shell
command takes them, hands them to the agent, and clears them, so a link
is announced at most once. Claude Code's provider emits the message on
both of its delivery channels: one the client prints directly, one the
model can repeat. Cursor has no hook after a shell command; OpenCode's
payload shape is not verified yet, so both are no-ops for now.

Links expire after ten minutes, because a push the user ran in their own
terminal leaves a record no agent hook will ever consume. A push driven
by `chainloop trace run` records nothing at all: it completes after the
wrapped agent has exited, so no hook of that agent can fire again.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
@migmartri migmartri changed the title feat(cli): print the session link after a trace push feat(cli): surface the trace session link to the user Sep 10, 2026

@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 11 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/cli/pkg/action/trace_agent_hook.go Outdated
Comment thread app/cli/internal/trace/state/pendinglinks.go
Comment thread app/cli/pkg/action/trace_hook_handler.go Outdated
Address review feedback. Reading the recorded links no longer consumes
them: an agent with no way to reach the user was clearing the record it
could not act on, so the link was lost without anyone seeing it.

Providers that cannot reach the user now say so with
ErrAnnounceUnsupported rather than reporting success, and the record is
cleared only once an agent has had its go at showing it. A delivery that
was attempted and failed still consumes, because retrying on every later
shell command would nag far longer than one dropped notification costs.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
Address review feedback. The push skipped recording session links when
the trace-run sentinel was set, but that sentinel is cleared in a
deferred call, so a killed run leaves it behind. Every later pre-push
attestation in that repository would then silently stop notifying the
agent.

The caller knows the truth without consulting disk, so `trace run` now
says so through a push option instead. Nothing infers it from state that
can go stale.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7

@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 9 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/cli/pkg/action/trace_agent_hook.go Outdated
Comment thread app/cli/pkg/action/trace_agent_hook.go
Address review feedback on the two preceding fixes.

ClearPendingLinks no longer returns an error its only caller could act
on, matching DeleteFileSnapshot and DeleteShellPreSignature, which are
the same kind of single-use payload consumed by a best-effort hook. The
notifier then stops inspecting one error either side of a mutation and
clears in one place.

Record why announcing before clearing is at-least-once by choice: the
agent never acknowledges what it rendered, so exactly-once is not
available, and the other ordering trades a repeated line for a link
nobody sees.

Split the keep-the-links case into its own test rather than a table flag
that gave one subtest two endings, drop a doc claim about corrupt records
that a truncating write already prevents, and remove three restatements
of rationale that lives with the field it describes.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7

@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 7 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/cli/pkg/action/trace_notify_links_test.go Outdated
Address review feedback. The test said a capable agent still gets its
turn later, but reused the unsupported provider for the second call, so
it only proved an unsupported agent is retried and never covered the
transition it described.

The second call now uses a capable provider and asserts it is offered
the links, announces them, and consumes them.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
The session-start banner read "This session will be attested by
Chainloop". "Attested" is Chainloop vocabulary, not the developer's: a
teammate who cloned the repository and never ran init cannot tell
whether that means recorded, uploaded or signed. It also never said
where the data goes, which is a poor showing for a tool whose pitch is
transparency about what leaves your machine.

It now reads:

    Chainloop Trace is recording this session.
    Evidence will be sent to <dashboard URL>
    organization: <org>  project: <project>

The destination line appears only when the control plane reports a web
dashboard, matching the gate the session link already uses. Finding out
costs one Infoz call, which needs no credentials and is abandoned after
two seconds, because a developer waiting to type is a worse cost than a
missing line. Organization and project come from .chainloop.yml at no
cost. Each line is dropped when its fact is unknown, so the banner never
names a destination it did not confirm.

fetchUIDashboardURL now takes the timeout from its caller, since a
person waiting on a command they ran themselves will tolerate far more
than an agent hook holding up a first prompt.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7
@migmartri
migmartri requested a review from a team September 10, 2026 20:32

@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 5 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/cli/pkg/action/trace_agent_hook.go
The session-start banner spent a line on the destination and another on
the organization and project, though between them they answer a single
question: where this is going. They now share a line:

    Chainloop Trace is recording this session.
    Evidence will be sent to <url> (organization: <org>, project: <p>)

The space before the parenthesis is load-bearing, since it is what lets
a terminal linkify the URL without swallowing the punctuation after it.
Either half still stands alone when the other is unknown.

Building that banner costs a control-plane round trip, and Cursor and
opencode discard system messages entirely, so every session start on
those agents paid up to two seconds for a string nobody would read.
Providers now report whether the channel reaches the user, and the
lookup is skipped when it does not.

The capability is a predicate rather than a sentinel error because the
cost being avoided is in preparing the argument, which a post-hoc error
cannot save. A registry test fails when a new provider arrives without
a recorded decision, since the zero value would quietly claim no
support.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: 2cee6265-17c4-487a-b31d-072037bd3bc7, 82030869-b7f3-4352-bd95-cea2eb75abab
The opencode plugin now traces bash tool invocations in addition to
file-writing tools, and resolves every file touched by an apply_patch
call instead of only a single filePath/path argument. Hook invocations
are fire-and-forget so a missing or failing chainloop binary never
blocks tool execution.

Also adds .cursor/hooks.json so Cursor sessions emit session-start,
session-end, and after-file-edit trace events.

Assisted-by: Claude Code
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: ses_0c830ab01ffeGQhnPohmihWjP5

@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 2 files (changes from recent commits).

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

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread .opencode/plugins/chainloop-trace.ts
Comment thread .cursor/hooks.json
@migmartri
migmartri merged commit 35cd46c into main Sep 10, 2026
16 of 17 checks passed
@migmartri
migmartri deleted the gh-3429-trace-push-session-link branch September 10, 2026 21:57
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.

chainloop trace: print the session link after a successful push

2 participants