contractbuild

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package contractbuild is the sole sanctioned runtime-side construction funnel for framework-owned kernel/contractspec.ContractSpec values.

Usage

Framework HTTP infra endpoint (health probe, devtools, etc.):

spec := contractbuild.NewFrameworkHTTP("http.framework.<subsystem>.<endpoint>.v1", "GET", "/path")

(the ID MUST start with "http.framework." — a missing prefix panics at startup.) Event-tracing projection of a validated outbox.Subscription:

spec, err := contractbuild.NewEventDerivation(sub) // sub passes sub.Validate()

Outbound webhook dispatcher subscription spec from a validated DispatchSpec:

spec, err := contractbuild.NewWebhookDispatch(dispatchSpec) // dispatchSpec passes dispatchSpec.Validate()

Why this package exists (compiler-Hard upstream)

ContractSpec values come from exactly two homes:

  • generated/contracts/**/spec_gen.go — business contracts (contractgen codegen output).
  • this package — framework-owned HTTP infra (health probes, devtools catalog), event-tracing derivations, and webhook-dispatch subscription specs.

Hand-written ContractSpec{…} composite literals are forbidden under cells/, examples/**/cells/, and runtime/ by archtest NO-MANUAL-CONTRACTSPEC-LITERAL-01 (downstream Hard). The three funnels here — NewFrameworkHTTP, NewEventDerivation, and NewWebhookDispatch — are the only legitimate runtime-side construction paths.

Location: this package lives under runtime/internal/, so the Go compiler itself refuses imports from outside the runtime/ subtree (cells/, examples/, kernel/, cmd/, adapters/, tools/, tests/). Business code therefore *cannot* construct a framework ContractSpec — the violation is unrepresentable, not merely archtest-detected.

Before issue #1038 the funnels lived in kernel/contractspec. NewFrameworkHTTP had an open caller (any package could call it; only the FrameworkHTTPIDPrefix panic gated content), and NewEventDerivation relied on a single-file path-string allowlist in archtest NO-MANUAL-CONTRACTSPEC-LITERAL-01 (upstream Medium). A token-in-kernel design could not reach Hard: the callers live in runtime/ (a different package from kernel/contractspec), so kernel would have to export a mint function reachable by everyone — the permanent Go ceiling recorded at #851 / #893 / #1282 ("Go package visibility cannot express 'only these packages may call this exported symbol'").

The internal/ move resolves it because *all* callers (runtime/bootstrap, runtime/http/devtools, runtime/eventrouter) sit under runtime/. This is the same Medium→Hard upgrade issue #638 applied to runtime/internal/authtest; see ai-robust.md §Hard 范本目录 → "internal/ wrap 包".

AI-robust grading (single source; not duplicated in rule docs)

The "upstream Hard" claim is precisely scoped to the threat class — business code constructing framework specs — which is exactly the set of non-runtime packages (cells/, examples/, cmd/, adapters/, kernel/). Two orthogonal vectors:

  • Non-runtime construction: Hard. Those packages can neither import this package (Go internal/ rule — compiler Hard) nor write a contractspec.ContractSpec{…} literal: NO-MANUAL-CONTRACTSPEC-LITERAL-01 scans EVERY production tree that can import kernel/contractspec — cells/, examples/, runtime/, kernel/, cmd/, adapters/, cellmodules/ (#1038 review C1/F2 widened the scan beyond the original cells/+examples-cells/+runtime/ so this claim is repo-wide true, not just for cells/). Both forms are unrepresentable. (The literal-ban is archtest-enforced, not a compiler gate; per the established grading it is the funnel's downstream Hard.)
  • Within-runtime construction: open by design for the funnel CALL (any runtime/ framework infra may call NewFrameworkHTTP / NewEventDerivation / NewWebhookDispatch — these are framework-owned infra, not business contracts), while a raw contractspec.ContractSpec{…} literal inside runtime/ is still caught by NO-MANUAL-CONTRACTSPEC-LITERAL-01. There is no within-runtime caller allowlist to Hard-ify (it was retired, see below) — the openness is intentional, not a Medium funnel awaiting upgrade, so no tracking issue.

Per-funnel content invariants:

  • NewFrameworkHTTP content: the ID field is Hard (frameworkHTTPIDPrefix A-class panic asserts framework ownership). Method/Path are NOT validated here — their structural validity is enforced downstream at route registration (runtime/auth.Route.validateContractShape via auth.Mount). The panic fires at package-initialization time (call sites are package-level var assignments with static-literal IDs), so it is not recoverable by the HTTP middleware layer — a malformed prefix fails the process at startup with a Go stack trace, by design.
  • NewEventDerivation content: shape-Hard, NOT provenance-Hard. The funnel runs sub.Validate() before deriving and the derived spec additionally passes ContractSpec.Validate() (defense in depth), so it never emits a structurally-invalid spec. But outbox.Subscription has EXPORTED fields — a runtime/ caller can construct one from arbitrary strings; the typed param only forces values to be wrapped in the named type, it does NOT verify they originated from cellgen/registry. Value provenance is a runtime data-flow property (eventrouter fills these from contract-bound registrations), not a type-system guarantee. A sealed Subscription constructor would Hard-ify it — tracked at gh #1532. (The former single-file "only eventrouter" allowlist + drift guard are RETIRED, #1038 / #1445; the primitive signature was a vestige of the old kernel/contractspec placement which could not import kernel/outbox.)
  • NewWebhookDispatch content: shape-Hard, NOT provenance-Hard — identical situation to NewEventDerivation. spec.Validate() + ContractSpec.Validate() guarantee structural validity (ID/Topic non-empty; Kind/Transport hardcoded valid), but webhook.DispatchSpec has EXPORTED fields (ContractID/SourceID/ CellID), so a runtime/ caller can fabricate one. Value provenance comes from the cellgen→RegistrySnapshot.WebhookDispatchers data flow, not the type system. Sealed-constructor Hard path: gh #1532.

The upstream caller-set Hard (runtime/internal/ placement) and downstream literal-ban Hard (NO-MANUAL-CONTRACTSPEC-LITERAL-01) hold uniformly for every funnel here regardless of the content-axis caveat above; #1532 concerns only the content value-provenance axis (same permanent-ceiling family as #851 / #893 / #1282).

  • ContractSpec{…} literal ban: downstream Hard (unchanged, NO-MANUAL-CONTRACTSPEC-LITERAL-01). runtime/internal/contractbuild is the sanctioned funnel home and is excluded from that scan, analogous to the former kernel/contractspec/** exclusion.

ref: runtime/internal/authtest (#638 internal/ Medium→Hard precedent); docs/reviews/202605181109-042-archtest-six-agent-audit.md §3b/§4 row 9.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewEventDerivation

func NewEventDerivation(sub outbox.Subscription) (contractspec.ContractSpec, error)

NewEventDerivation projects a validated outbox.Subscription into a ContractSpec shape for tracing / observability consumers. It is a derivation funnel, NOT a declaration funnel — the spec is derived from the subscription's already-bound contract identity, never fabricated.

The funnel validates SHAPE, not value provenance: the parameter is a typed outbox.Subscription (not loose primitives) and the funnel runs sub.Validate() before deriving, plus the derived spec passes ContractSpec.Validate() (defense in depth: e.g. ContractID need not be a valid contract ID just because the subscription validated). But outbox.Subscription has exported fields, so a runtime/ caller can construct one from arbitrary strings — the typed param forces values to be wrapped in the named type, it does NOT prove they came from cellgen/registry. Real value provenance is the eventrouter data flow (AddContractHandler fills these from contract-bound registrations), not a type-system guarantee; a sealed Subscription constructor would Hard-ify it (gh #1532). The single-file caller allowlist was retired (#1038 / #1445); the typed parameter is now natural because this package lives in runtime/ and may import kernel/outbox (the former primitive signature was a vestige of the old kernel/contractspec placement). Callers MUST handle the returned error — content shape invariants are funnel-owned, not caller discipline. See doc.go for the full AI-robust grading.

func NewFrameworkHTTP

func NewFrameworkHTTP(id, method, path string, clients ...string) contractspec.ContractSpec

NewFrameworkHTTP constructs a ContractSpec for runtime-owned HTTP infrastructure endpoints (health probes, devtools catalog, projection rebuild control-plane, etc.). Kind is fixed as cellvocab.ContractHTTP; Transport is fixed as "http".

The optional clients carry the caller-cell allowlist. An /internal/ path REQUIRES a non-empty allowlist (ContractSpec.validateHTTP fails closed otherwise — every internal API must name its callers); public framework endpoints (/healthz, /readyz, /metrics, devtools) pass no clients. Validation of the path↔clients pairing is deferred to ContractSpec.Validate() at the mount site, so the funnel itself stays a pure constructor.

The id MUST start with "http.framework.". This constraint is enforced at construction time with a panic (A-class assertion), not merely by code review. All legitimate call sites use static string literals so the panic fires at process initialization.

This is the ONLY legitimate construction path for ContractSpec values in runtime/ HTTP infrastructure code. Composite literal `contractspec.ContractSpec{...}` is forbidden under cells/, examples/*/cells/, and runtime/ by archtest NO-MANUAL-CONTRACTSPEC-LITERAL-01. The package lives under runtime/internal/ so the Go compiler refuses imports from outside the runtime/ subtree — business code physically cannot call this funnel (see doc.go for the compiler-Hard upstream rationale).

func NewWebhookDispatch

func NewWebhookDispatch(spec webhook.DispatchSpec) (contractspec.ContractSpec, error)

NewWebhookDispatch projects a validated webhook.DispatchSpec into the event-kind ContractSpec the event router subscribes on for an outbound webhook dispatcher. Like NewEventDerivation it is a derivation funnel, not a declaration funnel: Topic == spec.ContractID (the producing cell emits to that topic; the dispatcher signs and POSTs each delivered entry).

Transport is fixed to "amqp" (webhookDispatchTransport): outbound webhook dispatchers are exclusively broker-consumed, so — unlike NewEventDerivation, which derives Transport from sub.ContractTransport — there is nothing to derive; the constant is the whole truth.

The funnel validates SHAPE, not value provenance: spec.Validate() runs before deriving and the derived spec passes ContractSpec.Validate(), so the funnel never emits a structurally-invalid spec. But webhook.DispatchSpec has exported fields, so a runtime/ caller can construct one from arbitrary strings — the typed param does NOT prove the values came from cellgen/registry. Real value provenance is the cellgen→RegistrySnapshot.WebhookDispatchers data flow, not a type-system guarantee; a sealed DispatchSpec constructor would Hard-ify it (gh #1532). The second ContractSpec.Validate() is belt-and-suspenders here (Kind/Transport hardcoded valid, ID/Topic from the non-empty ContractID, so it cannot fail today) — kept for parity with NewEventDerivation and so a future change to the hardcoded values still fails closed. Callers MUST handle the returned error. See doc.go for the full AI-robust grading.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL