feat(lifecycle): artifact registry + marginal-lift ablation (phase 1)#361
Merged
Conversation
…hase 1) Foundation for the artifact-lifecycle. Two primitives the rest hangs off: - ArtifactRegistry — a typed catalog of profile artifacts (skill|tool|mcp|hook|subagent|prompt) with stable ids and register/list/get/promote/compose. applyArtifact is the one bridge from an ArtifactKind to an AgentProfile field (§1.5 profile law). - measureMarginalLift — the with-vs-without ablation: given a baseline profile + a candidate artifact + an EvalRunner, returns the score/cost delta the artifact adds on its own. Exposed as the new ./lifecycle subpath. Per-surface lifecycles and the buildable-surface author contract are deferred to later phases.
tangletools
approved these changes
Jun 22, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved PR — f4ad2a17
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:35:32Z
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
rt#267 phase 1 — the artifact-lifecycle foundation. Two primitives the rest of the lifecycle hangs off, exposed as a new
./lifecyclesubpath.1.
ArtifactRegistry— typed catalog of profile artifacts with stable idsA pure in-memory store of
ProfileArtifacts. An artifact is a discrete, individually-promotable piece of anAgentProfile— one ofskill | tool | mcp | hook | subagent | prompt(the §1.5 profile surface, one-to-one with anAgentProfilefield). API:register(input)— assigns a stable id (<kind>-<n>, or honors a caller-supplied id idempotently; re-registering replaces under the same id). Fails loud on a malformed explicit id.get(id)/list({ kind?, status? })/promote(id)(idempotent; fails loud on unknown id).compose(base, ids?)— applies a set of artifacts onto a baseline profile (no ids = allpromoted; explicit ids = exactly those, in order, fail-loud on unknown).applyArtifact/applyArtifacts— the one bridge from anArtifactKindto anAgentProfilefield (shallow-immutable; never mutates the base). Both the registry'scomposeand the marginal-lift ablation share it, so there is a single source of truth for how a kind lands on the profile.2.
measureMarginalLift— the with-vs-without ablationRuns the caller's
EvalRunneronbaseline(the "without" arm) and onapplyArtifact(baseline, candidate)(the "with" arm), and subtracts:scoreDelta = with.composite − without.composite,costDelta = with.costUsd − without.costUsd. Selector-agnostic — it quantifies the ablation, it does not judge or gate (selector≠judge). Supports a pre-computedbaselineResultto skip the baseline arm when ranking several candidates against one baseline, and forwards anAbortSignal.EvalResultmirrors the project's score/cost convention (compositefromOutcomeMeasurement,costUsdfromLoopResult), so a caller wires a thin wrapper overrunLoop/runBenchmark/runAgentEval.Why this shape
AgentProfilecontract (@tangle-network/agent-interface) and the §1.5 "an agent IS its profile" law — no new profile model, no parallel surface type.ValidationError) — fails loud, no silent zeros../lifecyclesubpath + tsup entry + typedoc entryPoint. No existing surface changed.Tests
src/lifecycle/lifecycle.test.ts— 21 tests:applyArtifactfor all six kinds (immutability, key resolution, append-vs-overwrite), the registry (stable/explicit ids, idempotent re-register, fail-loud guards, filtered list, promote, compose with/without ids), andmeasureMarginalLift(delta math, exactly-two-arm runs, baseline-skip, negative-delta drop signal, signal forwarding, and an end-to-end rank-then-promote-then-compose flow).Gates (all green, run locally)
pnpm run buildOK (emitsdist/lifecycle.{js,d.ts})pnpm run typecheckOK (incl.typecheck:examples)pnpm test— 1066 passed, 1 skippedpnpm run lint(Biome) OKpnpm run docs:checkOK (regenerateddocs/api/lifecycle.md+ index, freshness gate green)Deferred to later phases (NOT in this PR)
ArtifactKind(e.g. how askillcandidate is generated/validated vs anmcpcandidate).BuildableSurfaceauthor contract — the typed interface a profile author implements to enumerate which artifacts a surface can produce, so the registry can be populated by an author rather than hand-registered.measureMarginalLiftoutput intodefaultProductionGate/heldOutGateso promotion is gated on held-out significance, not a raw point delta. Phase 1 deliberately stops at "produce the number"; the gate consumes it later.This was the largest of the five; it is fully green, so opening as a normal (non-draft) PR.