Repository navigation
Billing: Contract + Emit Values - #2428
Conversation
…L_CAPABILITY_USAGE_ENABLED Groundwork for capability-centric workflow billing (CRE-6778): - ResourceManager.EmitUsageValue emits a METER_ACTION_USAGE record from a *big.Int so chain-write capabilities can report gas fees in wei without truncation. - resourcemanager/workflow_usage.go pins the producer side of the billing contract: record type cre:workflow, resource types cre:workflow:compute and cre:workflow:gas:<chain_selector>, ResourceId "<workflow_id>:<execution_id>", emitting service names and resource pool. Matches the consumer in billing-platform-service (ConstructCapabilityUsageEventID). - loop.EnvConfig.CapabilityUsageEnabled (CL_CAPABILITY_USAGE_ENABLED) carries the new [Metering].CapabilityUsageEnabled node setting to LOOP capability plugins, independent of MeterRecordsEnabled. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
✅ API Diff Results -
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Capability usage remains blocked by MeterRecordsEnabled, contradicting the new setting’s documented independence.
Review effort: Balanced
Findings: 1
What changed in this PR
Adds workflow capability billing primitives and propagates capability usage configuration to LOOP plugins.
Changes:
- Adds arbitrary-precision usage emission.
- Defines workflow compute/gas billing identifiers.
- Adds
CL_CAPABILITY_USAGE_ENABLEDconfiguration transport.
| File | Description |
|---|---|
pkg/resourcemanager/workflow_usage.go |
Defines workflow billing identifiers and helpers. |
pkg/resourcemanager/workflow_usage_test.go |
Tests workflow billing helpers. |
pkg/resourcemanager/resourcemanager.go |
Adds arbitrary-precision usage emission. |
pkg/resourcemanager/resourcemanager_test.go |
Tests large and nil usage values. |
pkg/loop/config.go |
Adds capability usage configuration. |
pkg/loop/config_test.go |
Tests configuration parsing and propagation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…y by CapabilityUsageEnabled Review follow-up. ResourceManager drops every emit when its own MeterRecordsEnabled is false, so a usage manager built from MeteringConfig would be silently gated by the durable-metering flag. CapabilityUsageConfig returns a Config for a dedicated usage ResourceManager: records enabled iff CapabilityUsageEnabled, snapshots off, same emitter and deployment identity. Producers run that manager next to the durable one, so the two settings never share a gate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d contract Identity.ResourcePool / ResourcePoolID follow the Billing <-> Decentralized stack contract: pool "cre:workflow:gas" with pool id "cre:workflow:gas:<chain_selector>" for gas, "cre:workflow:compute" for both on compute. WithWorkflowUsagePool derives them from the resource type so producers cannot drift. Replaces the single "workflow_usage" pool. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| // WorkflowGasResourceType returns the gas resource type for a chain selector, | ||
| // e.g. "cre:workflow:gas:421614". | ||
| func WorkflowGasResourceType(chainSelector uint64) string { | ||
| return ResourceTypeWorkflowGasPrefix + strconv.FormatUint(chainSelector, 10) |
There was a problem hiding this comment.
cc @seanwangsmartcontract - calling for viz, but this matches our expectations in https://docs.google.com/document/d/1EQSjpfxNHsBWFikfdVoaIh_KtaPKJQlqaKS8SnpQqfM/edit?tab=t.jlozzrlh0lnu#heading=h.6mpcocqx8ink
There was a problem hiding this comment.
Matches that section: ResourcePool is cre:workflow:gas / cre:workflow:compute, ResourcePoolId is the fully qualified resource type, ResourceId is <wf>:<exec>.
…abled Per review, capability usage records (compute, gas) do not need their own gate. The existing MeterRecordsEnabled / MeterSnapshotsEnabled knobs already control emission per node and are sufficient for local-cre tuning, so producers reuse EnvConfig.MeteringConfig for the usage ResourceManager. Removes CL_CAPABILITY_USAGE_ENABLED, EnvConfig.CapabilityUsageEnabled and EnvConfig.CapabilityUsageConfig with their tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| // EmitUsageValue is EmitUsage for quantities that do not fit an int64, such as a | ||
| // transaction fee in wei. A nil value emits "0". Same event_id contract and | ||
| // fail-open semantics as EmitUsage. | ||
| func (rm *ResourceManager) EmitUsageValue(ctx context.Context, identity ResourceIdentity, eventID string, value *big.Int, fields UtilizationFields) { |
There was a problem hiding this comment.
one note is that I'm not sure how the *big.Int will go across the wire in the message payload. it will need to be serialized to string, we should double check that the emitRecord call will handle this properly. we use a cloudEvent packaging, which I believe wraps OTEL
| // WorkflowUsageResourceID returns the Utilization.ResourceId for a workflow | ||
| // execution: "<workflow_id>:<execution_id>". Neither component may be empty or | ||
| // contain ':', since the consumer splits on the first ':'. | ||
| func WorkflowUsageResourceID(workflowID, executionID string) (string, error) { |
There was a problem hiding this comment.
nit: is this shared with any other components? maybe deserves its own file or even a "utils" package?

Groundwork for capability-centric workflow billing smartcontract-it.atlassian.net/browse/CRE-6917. No behaviour change for existing producers.
What
ResourceManager.EmitUsageValue(ctx, identity, eventID, *big.Int, fields):METER_ACTION_USAGEfrom an arbitrary-precision value, so chain-write capabilities can report gas fees in wei/lamports without int64 truncation. Same event-id contract and fail-open semantics asEmitUsage.resourcemanager/workflow_usage.go: the producer side of the billing contract, shared by the engine (compute) and the chain-write plugins (gas):cre:workflow; resource typescre:workflow:compute(ms) andcre:workflow:gas:<chain_selector>(WorkflowGasResourceType)Utilization.ResourceId = "<workflow_id>:<execution_id>"(WorkflowUsageResourceID, rejects:in either part)chain-write/workflow-engine;Identity.ResourcePoolcre:workflow:gas/cre:workflow:computewithResourcePoolId= fully qualified resource type (WithWorkflowUsagePool)Matches the consumer in billing-platform-service (#963,
ConstructCapabilityUsageEventID:cre:workflow:<wf>:<exec>:<EventId>).Usage records are gated by the existing
MeterRecordsEnabled; no separate flag.