feat(runtime): mounted-resource manifest + caller selection receipts on LoopResult#362
Merged
Merged
Conversation
…on LoopResult Add a domain-free observability primitive to the driven-loop run output: `LoopResult.provenance` carries a manifest of what was mounted into the run's boxes (each entry path/sha256/bytes/source) and one selection receipt per scored candidate (which iteration the selector chose + score + why + selector identity). Pure provenance for run auditability — nothing in the kernel branches on it. Mounts are populated from the existing prepareBox seam: `prepareBox`'s ctx gains a `recordMount` recorder so the caller (which owns the bytes it writes) declares each mount; the kernel never reads box contents. The recorder is threaded through both box-acquisition paths — the fresh-box path (createSandboxForSpec) and the lineage path (createSandboxLineage). Receipts are assembled at finalize from the iterations and the resolved winner, attributing the caller/driver/default selector. Additive and optional: when nothing is recorded the manifest is an empty array, and shared box helpers used outside runLoop (sandbox-act, detached-turn) pass no recorder and get a no-op.
tangletools
approved these changes
Jun 22, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved PR — 969e8cb8
Blanket team auto-approval is enabled for this reviewer service.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: blanket_auto_approve · 2026-06-22T13:38:28Z
Merged
drewstone
added a commit
that referenced
this pull request
Jun 22, 2026
…provenance + artifact lifecycle (#363) Bundles the build-phase PRs landed since 0.72.0: - #360 max-live-workers concurrency cap + explicit worker metering opt-in - #362 mounted-resource manifest + caller selection receipts on LoopResult - #361 artifact registry + marginal-lift ablation (rt#267 phase 1, ./lifecycle) - #359 preserve partial events on abort via typed SandboxRunAbortError Bumps package.json to 0.73.0, pins docs/canonical-api.md to 0.73.0, and adds decision-table rows for run provenance (result.provenance.mounts/selectionReceipts) and the artifact-lifecycle ablation (measureMarginalLift / ArtifactRegistry, /lifecycle).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a generic, domain-free observability primitive to the driven-loop run output.
LoopResultgainsprovenance:mounts— a manifest of what was mounted into the run's boxes. Each entry:path,sha256,bytes,source. Pure provenance — no domain semantics.selectionReceipts— one receipt per scored candidate:candidateIndex,selected,score?,reason?, andselector('caller' | 'driver' | 'default'). Answers "why did THIS one win?".This makes a run auditable after the fact ("what exactly was this agent given, and why did the kernel pick this output?") without the kernel attaching any meaning to the data.
How
Mounts ride the existing
prepareBoxseam (src/runtime/types.ts).prepareBox's ctx gains arecordMountrecorder so the caller — which owns the bytes it writes into the box — declares each mount. The kernel never reads box contents; it only collects what the caller records. The recorder is threaded through both box-acquisition paths:createSandboxForSpecinrun-loop.ts)createSandboxLineageinsandbox-lineage.ts, covering fresh start + fork branches)Receipts are assembled at
finalizefrom the iterations and the resolved winner, attributing the selector that ran (caller-suppliedselectWinner, driver-authoredselectWinner, or the kernel's default argmax). Only the default selector gets a kernel-authoredreason; caller/driver winners run by their own rationale, so the kernel leavesreasonunset rather than inventing one. Errored or output-less iterations were never selectable, so they get no receipt.Safety / scope
Additive and optional. When nothing is recorded the manifest is an empty array. Shared box helpers used outside
runLoop(sandbox-act,detached-turn) pass no recorder and get a no-op, so the prepareBox ctx shape is always satisfied and no behavior changes on those paths.Verification
pnpm run buildgreen (dist regenerated)pnpm run typecheckgreen (incl.typecheck:examples)pnpm testgreen — 107 files, 1049 passed / 1 skippedpnpm run lintgreenpnpm run docs:freshnessOK;docs/api/*regenerated via TypeDoc and committed so the freshness gate stays greensrc/runtime/run-loop.provenance.test.ts(4 cases): mount manifest surfaced, empty manifest when nothing mounted, default-selector receipts with scores + winner flag across a fanout, and an errored iteration omitted with a caller-selector attribution.Closes rt#353.