Skip to content

feat: meter kind-as-option + platform-infra price override#123

Merged
I-am-nothing merged 1 commit into
mainfrom
feat/meter-kind-option-infra-price
Jun 17, 2026
Merged

feat: meter kind-as-option + platform-infra price override#123
I-am-nothing merged 1 commit into
mainfrom
feat/meter-kind-option-infra-price

Conversation

@I-am-nothing

Copy link
Copy Markdown
Contributor

Milestone D SDK follow-up (PR #1b), stacked logically on the merged declaration-first metering work (#1 / v0.2.2). Two changes to ms.Meter, plus the validation that makes them safe.

What changed

1. Kind is now an OPTION, not a positional argument.
ms.Meter(name string, opts ...ms.MetricOption). ms.Counter / ms.Gauge become ms.MetricOptions that set the kind, so the call site reads exactly the same:

ms.Meter("orders.placed", ms.Counter, ms.Unit("order"), ms.Price(50_000))

ms.Unit / ms.Price are unchanged.

2. Platform-infra customer-price override.
A reserved infra.* / platform.* name — previously rejected at declaration — may now be declared with ms.Price only, to override what the module's customer is billed for that platform-measured infra:

ms.Meter("infra.compute.ms", ms.Price(0)) // absorb platform compute into your own pricing

This is a pure Plane-2 (customer-facing) choice. Kind/unit on these names are platform-owned, and the developer still owes the platform the measured COGS regardless.

Validation (panic at declaration, like ValidateName)

  • a custom (non-reserved) name MUST pass exactly one kind option → panic if missing, or if both ms.Counter and ms.Gauge are given;
  • a reserved name may carry ms.Price ONLY → panic if a kind or unit option is passed on it, and panic if NO price is given (a price-less reserved declaration is a meaningless no-op that would pollute the manifest);
  • duplicate metric name → panic (unchanged).

ms.Record(ctx, name, value) continues to REJECT (return an error for) a reserved infra.*/platform.* name: a module may declare its price but never self-report the platform-measured quantity.

BREAKING

The metric kind moves from a positional argument to an option. The exported ms.Kind type and the ms.Counter/ms.Gauge Kind constants are gone; ms.Counter/ms.Gauge are now ms.MetricOptions. VERSION bumped 0.2.2 → 0.2.3.

Deferred

Platform-side enforcement of the override + default cost × 1.2 for platform-infra metrics lands with PR #10.

Design ref

docs-temp/milestone-d-meter/design.md §3a (build rule 3), §4 Axis 1, §5 ("app-module-sdk") — PR #1b.

Review follow-ups (non-blocking, from go review)

  • Decl carries an unexported kindDup field; added a doc note that it must be constructed via DeclFromOptions, not a struct literal (a hand-built Decl would skip the conflicting-kind guard). Addressed in this PR.
  • meter/meter_test.go reserved-with-kind/unit subtests spread a local []MetricOption into DeclFromOptions — correct, slightly unusual; no change.
  • internal/core/resources.go manifest write sets Kind: string(d.Kind); for a reserved override d.Kind == "" and the MetricDecl.Kind json tag is omitempty, so it is omitted from the wire (correct per spec). No change.

🤖 Generated with Claude Code

Move the metric KIND from a required positional argument to a functional
option: ms.Meter(name string, opts ...MetricOption). ms.Counter / ms.Gauge
are now MetricOptions, so the call site reads unchanged —
ms.Meter("orders.placed", ms.Counter, ms.Unit("order"), ms.Price(50_000)).

Allow a reserved infra.*/platform.* name to be declared with ms.Price ONLY,
to override the customer passthrough (e.g. ms.Meter("infra.compute.ms",
ms.Price(0)) to absorb). Kind/unit on these is platform-owned.

Validation (panic at declaration, like ValidateName):
- a custom (non-reserved) name MUST pass exactly one kind — panic if missing
  or if both ms.Counter and ms.Gauge are given;
- a reserved name may carry ms.Price ONLY — panic if a kind or unit option is
  passed, AND panic if no ms.Price is given (a price-less reserved declaration
  is a meaningless no-op that would pollute the manifest);
- duplicate metric name panics (unchanged).
ms.Record still rejects self-reporting a reserved infra.*/platform.* name —
a module may set its price but never fabricate the platform-measured quantity.

MetricOption is an interface (not a func type) so ms.Counter / ms.Gauge are
const-backed and cannot be reassigned from a third-party module (ms.Counter =
nil would otherwise silently break all later Meter calls — an API footgun on a
security boundary).

BREAKING: kind moves from positional arg to option. Deferred: platform-side
enforcement of the override + default cost×1.2 (PR #10).

Design ref: docs-temp/milestone-d-meter/design.md §3a (build rule 3), §4 Axis 1,
§5 (PR #1b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@I-am-nothing I-am-nothing merged commit 6e044ab into main Jun 17, 2026
1 check passed
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.

1 participant