metadata

package
v0.1.0-develop.2026061... Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package metadata implements structural derivation for AssemblyMeta and related types. Derivation fills in omitted fields (entrypoint, binary, deployTemplate, maxConsistencyLevel) from declared field values and peer metadata.

ref: parser.parseSlice G-7 belongsToCell auto-derive / parser.parseContract ownerCell auto-derive — same auto-derive pattern applied to assembly build fields and cross-cell consistency aggregation.

Package metadata — contract syntactic constraints (single source of truth).

JSON Schemas under kernel/metadata/schemas/ retain literal pattern/enum expressions for IDE / editor / standalone tooling consumption. The constants below are the authoritative Go-side source: TestSchemaConstants MatchSchemaLiterals (under kernel/metadata/schemas) asserts the schema literals match these constants byte-for-byte.

Adding a new syntactic constraint:

  1. add a const here;
  2. update the corresponding schema file with the same literal;
  3. wire the const into the governance validator (and into typed-identifier boundary types where applicable, e.g. GoIdentifier);
  4. extend TestSchemaConstantsMatchSchemaLiterals to compare the new pair.

Runtime is single-source: parsers do not validate values; governance is the sole gatekeeper, importing the constants here. Schema literals are the authoritative form on disk; the test guard prevents drift.

Package metadata — derived.go provides secondary projections built on top of ProjectMeta. Currently: per-cell wire summary used by /api/v1/devtools/catalog to expose listener / route / subscribe surface for ops visibility.

Derived data is stateless (no caching); callers re-derive on each request. Cost is O(cells) string copy — under 1ms for ~10 cells, no premature optimization.

Decoupling note: this file does NOT import tools/codegen/markergen. Callers that own a markergen.WireBundle (e.g. runtime/http/devtools) convert it to CellWireBundle before calling DeriveCellWireSummaries. This keeps kernel/metadata dependency-free from the tools/ layer.

Package metadata provides a parser that loads all GoCell YAML descriptors (cell.yaml, slice.yaml, contract.yaml, assembly.yaml, journey.yaml) from a project root and produces a validated ProjectMeta model.

metadata is the single source of truth for the metadata schema (see KERNEL-METADATA-NO-WIRE-01 archtest, which forbids wire-format symbol names from appearing here — those belong to runtime/devtools/catalog).

Package metadata: Locator abstracts metadata YAML discovery from parser internals. It is the single funnel through which all path-prefix knowledge about GoCell's filesystem topology must flow.

Two modes:

  • Conventional — walks the hardcoded 5-pattern layout plus 2 workspace-level singletons (actors.yaml, journeys/status-board.yaml) (cells/*/cell.yaml, cells/*/slices/*/slice.yaml, contracts/{kind}/.../contract.yaml, journeys/J-*.yaml, assemblies/*/assembly.yaml). examples/ subtree follows the same patterns with one extra prefix.
  • Manifest — reads .gocell/manifest.yaml and walks the modules: entries it declares. Single-module entry covers Operator-SDK external repo; multi-module entries cover Workspace mode aggregation.

Auto-detection: NewLocator without WithLocatorMode probes the root for .gocell/manifest.yaml; presence selects Manifest mode, absence selects Conventional. Override with WithLocatorMode(LocatorConventional) for CI determinism.

ref: bufbuild/buf buf.yaml v2 modules: schema — single manifest expressing both single-module and workspace-aggregate forms. ref: kubernetes-sigs/kubebuilder PROJECT — manifest-driven resource registration. ref: kubernetes-sigs/kustomize kustomization.yaml resources: field.

Funnel: LOCATOR-DISCOVERY-FUNNEL-01 (Hard downstream, Medium upstream). All path-prefix string comparisons against {cells/, contracts/, journeys/, assemblies/, examples/} must live inside locator_conventional.go or locator_manifest.go. fs.WalkDir / filepath.Walk / fs.ReadDir callsites in kernel/metadata/** must reside inside Locator.discoverConventional or Locator.discoverManifest. The upstream Medium ceiling is the Go language limit: cross-package sealed-interface-with-private-constructor is structurally unreachable — see SPAN-SETATTR-HOLDER-SEAL-01 (#851) won't-do for the precedent.

Package metadata defines the Go struct types for GoCell YAML metadata files and provides a file-system-based parser to load them into a unified ProjectMeta.

Index

Constants

View Source
const (
	// AssemblyIDPattern restricts assembly ids to lowercase ASCII letters
	// + digits, ≥2 chars, must start with a letter. Mirrors
	// schemas/assembly.schema.json properties.id.pattern. Reverse-aliased
	// from pkg/scaffoldid.IdentifierPattern — pkg/scaffoldid owns the
	// single-source pattern so typed-identifier funnel (ScaffoldID) and
	// YAML schema validator stay in lock-step. kernel/ may depend on pkg/
	// (architecture rule).
	AssemblyIDPattern = scaffoldid.IdentifierPattern
	// CellIDPattern is identical to AssemblyIDPattern by design — both share
	// the no-dash concatenation convention enforced by FMT-16 / FMT-C1.
	// Same single-source reverse-alias as AssemblyIDPattern.
	CellIDPattern = scaffoldid.IdentifierPattern
	// GoStructNamePattern restricts cell.GoStructName to a Go-exported
	// identifier shape (uppercase first letter, ASCII letters + digits).
	// Mirrors schemas/cell.schema.json properties.goStructName.pattern.
	GoStructNamePattern = `^[A-Z][A-Za-z0-9]*$`
	// SagaStepNamePattern restricts saga.steps[].name to a Go-identifier-safe
	// camelCase token (letters + digits, leading letter). It is STRICTER than a
	// generic SafeID because the step name flows through contractgen's
	// goPascalCase into generated Go identifiers (Run<Name> / <Name>Output); a
	// '.', ':' or '/' would emit uncompilable Go. Single source for the saga
	// step-name shape: schemas/contract.schema.json saga.steps[].name.pattern is
	// byte-locked to this const by TestSchemaConstantsMatchSchemaLiterals, and
	// kernel/governance SAGA-CONTRACT-STEP-NAME-VALID-01 compiles it (replacing
	// the prior hand-duplicated literal).
	SagaStepNamePattern = `^[a-zA-Z][a-zA-Z0-9]*$`
	// AssemblyModulePathPattern is the single source for assembly.yaml cell
	// `module` hygiene (the cross-module Go module path, #1086): a non-empty
	// string carrying no rune that could break out of the generated
	// "<module>/cellmodules/<id>" import string literal. The negated character
	// class is exactly the blocklist enforced by metadata.MatchAssemblyModulePath
	// (and consumed by AssemblyCellRef.decodeMapping): the control+space range
	// \x00-\x20, DEL \x7f, double-quote, backtick (\x60), and backslash (\\).
	// Mirrors schemas/assembly.schema.json
	// cells.items.oneOf[1].properties.module.pattern, byte-locked by
	// TestAssemblyCellRefSchemaPatternsMatchConstants. Defense-in-depth on top of
	// the %q-quoting + Go-compiler import-resolvability gate (ADR §D3).
	AssemblyModulePathPattern = "^[^\\x00-\\x20\\x7f\"\\x60\\\\]+$"
)
View Source
const AuthComboMatrixSize = 1 << HTTPAuthMetaBoolFields

AuthComboMatrixSize is the size of the auth bool combination space (2 ** HTTPAuthMetaBoolFields). Tests iterate from 0 to this value.

View Source
const DefaultManifestPath = ".gocell/manifest.yaml"

DefaultManifestPath is the forward-slash path Locator probes during auto mode and reads during Manifest mode unless WithManifestPath overrides it.

View Source
const GRPCProtoPathPrefix = "contracts/grpc/"

GRPCProtoPathPrefix is the required leading path component for endpoints.grpc.proto: every .proto reference must be a contracts-relative path rooted under contracts/grpc/. The schema literal at schemas/contract.schema.json (...grpc.properties.proto.pattern) is kept equal to "^"+GRPCProtoPathPrefix by TestSchemaConstantsMatchSchemaLiterals (the prefix contains no regex metacharacters, so the anchored pattern is a literal prefix match). Single source shared by governance FMT-37 and runtime kernel/contractspec.validateGRPC.

View Source
const HTTPAuthMetaBoolFields = 5

HTTPAuthMetaBoolFields is the count of bool fields on HTTPAuthMeta. It is the authoritative source of the matrix size used by IterateAuthBoolCombos (1 << HTTPAuthMetaBoolFields = 32 currently). TestHTTPAuthMetaFieldCount in auth_combo_test.go reflects on HTTPAuthMeta and fails CI when the actual bool field count drifts from this constant — adding a 6th bool field forces every consumer (IterateAuthBoolCombos, AuthComboLegal, the whitelist in TestAuthComboLegal_AgainstWhitelist) to be updated together.

Variables

View Source
var CapabilityEnum = []string{"postgres", "redis", "rabbitmq"}

CapabilityEnum lists the canonical values accepted for cell.requires items. Order matches the schema enum order at schemas/cell.schema.json properties.requires.items.enum; do not reorder without updating the schema in lockstep. TestSchemaConstantsMatchSchemaLiterals (kernel/metadata/schemas) asserts byte-level parity between this slice and the schema enum array. The assembly's provisioned set is the derived union of its cells' requires (Design Y, #855) — there is no assembly-level capabilities enum.

View Source
var DeployTemplateEnum = []string{"k8s", "compose", "binary"}

DeployTemplateEnum lists the canonical values accepted for assembly.build.deployTemplate. Order matches the schema enum order; do not reorder without updating schemas/assembly.schema.json in lockstep.

View Source
var LegalAuthComboNames = map[string]struct{}{
	"p-r-s-b-c": {},
	"P-r-s-b-c": {},
	"p-R-s-b-c": {},
	"p-r-S-b-c": {},
	"p-r-s-B-c": {},
	"p-r-s-b-C": {},
	"p-R-S-b-c": {},
}

LegalAuthComboNames is the hand-maintained extensional statement of FMT-27 auth bool mutex semantics (encoded by encodeAuthCombo). AuthComboLegal is the algorithmic (intensional) statement; the two must agree on all 32 combos — TestAuthComboLegal_AgainstWhitelist enforces parity between them.

Matrix tests at the schema and governance layers consume this whitelist (rather than calling AuthComboLegal) so they detect implementation divergence instead of colluding with the oracle. Single-source by design, dual-form by intent: the algorithm runs in production, the whitelist audits the algorithm.

7 legal combinations (out of 32):

"p-r-s-b-c"  default authenticated route (all flags off)
"P-r-s-b-c"  public only
"p-R-s-b-c"  passwordResetExempt only
"p-r-S-b-c"  serviceOwned only
"p-r-s-B-c"  bootstrap only
"p-r-s-b-C"  clientsOnly only
"p-R-S-b-c"  serviceOwned + passwordResetExempt

HTTP-idempotency exemption is NOT part of this matrix — it moved to the sibling endpoints.http.idempotency block (#1469 review F7), so the auth-combo space is back to 2^5 = 32 with 7 legal combos.

When a rule evolves (e.g. allowing a new pair to coexist), update both this whitelist AND AuthComboLegal in the same change; CI fails otherwise.

INVARIANT: AUTH-SCHEMA-GOVERNANCE-BOOL-SEMANTICS-01.

View Source
var ParamTypes = map[string]bool{
	"string":  true,
	"integer": true,
	"number":  true,
	"boolean": true,
}

ParamTypes lists the accepted `type` values for ParamSchema. Governance rule FMT-13 enforces membership.

View Source
var TransportEnum = transportEnumStrings()

TransportEnum lists the canonical wire transports accepted for contract.yaml `transports[]`. It is the string projection of cellvocab.AllTransports() (the typed single source); ordering matches that slice and, in lockstep, the schemas/contract.schema.json transports enum (byte-locked by TestSchemaConstantsMatchSchemaLiterals#transportEnum). Membership is enforced at the DECLARATION layer only — governance FMT-39 (via IsKnownTransport) plus the schema enum. Runtime kernel/contractspec.Validate deliberately does NOT re-check transport membership (a production ContractSpec.Transport is trusted as codegen/contractbuild-derived; declaration-layer enforcement, see ADR #1389 D6), so schema and governance — not the runtime value type — own this set.

Functions

func AssemblyGeneratedDir

func AssemblyGeneratedDir(asm *AssemblyMeta) string

AssemblyGeneratedDir returns the project-relative path of the generated/ directory for an assembly. It is derived uniformly as path.Dir(asm.File) + "/generated", which works for the conventional assemblies/<id>/ layout, the examples/<id>/ subtree, and arbitrary paths emitted by Locator manifest mode.

This is the single source of truth shared by:

  • kernel/assembly Generator.appendGeneratedFiles (boundary.yaml write path)
  • cmd/gocell/app generateOneAssembly (boundary.yaml + modules_gen cmd)
  • cmd/gocell/app generateMetricsSchema (metrics-schema.yaml write path)
  • kernel/governance validateREF16 (boundary.yaml existence check)

asm.File is the path as loaded by the parser — relative to the project root, with forward slashes (parser already normalised via filepath.ToSlash).

func AuthComboLegal

func AuthComboLegal(auth HTTPAuthMeta) bool

AuthComboLegal returns true iff the bool combination on auth is permitted by FMT-27 semantics. This function is the single source of truth shared by:

  • kernel/metadata/schemas/contract.schema.json (compile-time schema validation via if/then const:true rules — see TestContractSchemaAuthBoolMatrix)
  • kernel/governance/rules_fmt.go validateFMT27 (runtime governance check — hasFMT27AuthModeConflict delegates to !AuthComboLegal)
  • kernel/metadata/auth_combo_test.go (oracle whitelist test)

Rules:

  • Core modes {Public, PasswordResetExempt, Bootstrap, ClientsOnly}: at most one may be true.
  • ServiceOwned: may combine with PasswordResetExempt; mutually exclusive with Public, Bootstrap, and ClientsOnly.

INVARIANT: AUTH-SCHEMA-GOVERNANCE-BOOL-SEMANTICS-01 ref: kubernetes/kubernetes pkg/securitycontext/util.go (capability mode mutex)

func CellIDs

func CellIDs(refs []AssemblyCellRef) []string

CellIDs projects an []AssemblyCellRef to the bare cell-id slice, dropping module attribution. Used where only identity matters (entrypoint cell-id list, boundary cell set, devtools catalog wire shape).

func ContractDirFromMeta

func ContractDirFromMeta(c *ContractMeta) string

ContractDirFromMeta returns the contract directory relative to the project root. Returns "" when c is nil or c.Dir is unset. c.Dir is populated by the Locator funnel during ParseFS — callers that hit "" should treat it as a parsed-meta-missing condition, not as an opportunity to derive a default path from the ID. The prior ContractDirFromID-based fallback was removed when M1 (#1082) made layout discovery go through Locator: a derived "contracts/<id>" path is wrong in manifest mode and was masking real bugs when c.Dir was unexpectedly empty.

func Find

func Find(root *yaml.Node, path string) (*yaml.Node, error)

Find walks `root` (a yaml.Node, typically a *DocumentNode returned by yaml.Decoder) along the dotted path and returns the matching leaf node.

Path grammar (a restricted subset of JSONPath):

path    = segment ("." segment)*
segment = ident ("[" uint "]")*
ident   = [A-Za-z_][A-Za-z0-9_-]*

[N] on a SequenceNode selects the N-th element (0-based). [N] on a MappingNode matches the decimal string form of N as a key, which allows addressing YAML mappings with integer keys such as "endpoints.http.responses[200].schemaRef".

Examples: "id", "owner.team", "slices[0].contractUsages[1].contract", "matrix[0][1]", "endpoints.http.responses[200].schemaRef".

Unsupported (by design): leading "$", wildcards "[*]", recursive "..", quoted identifiers. We walk the minimum grammar the validator needs.

Error conditions: empty/invalid path, missing field, index out of range, mapping key not found, keying a scalar, empty document. The path prefix that reached the offending step is included in the error.

func GRPCProtoRepoRelPath

func GRPCProtoRepoRelPath(contractFile, proto string) string

GRPCProtoRepoRelPath converts a module-relative endpoints.grpc.proto path (rooted at GRPCProtoPathPrefix, as declared in contract.yaml and validated by ValidateGRPCProtoPath) into a path relative to the workspace root, by prefixing the contract's module base.

contractFile is the contract.yaml path relative to the workspace root (metadata.ContractMeta.File). The proto and the contract both live under the same module's contracts/grpc/ tree, so the module base is the portion of contractFile preceding the first GRPCProtoPathPrefix segment:

  • repo-root contract: File "contracts/grpc/d/v1/contract.yaml" → base "" → proto returned unchanged ("contracts/grpc/d/v1/x.proto").
  • satellite-module contract: File "examples/iotdevice/contracts/grpc/d/v1/contract.yaml" → base "examples/iotdevice/" → "examples/iotdevice/contracts/grpc/d/v1/x.proto".

Every filesystem reader of the proto (contractgen collision pre-pass, cellgen grpc-serve enrich) MUST resolve through this before filepath.Join with the absolute workspace root. Joining the raw module-relative proto against the workspace root silently resolves to the wrong (repo-root) location for satellite modules such as examples/iotdevice — the gap the first real example-owned grpc contract surfaced (#1151). The result is slash-form.

func GRPCServiceGoName

func GRPCServiceGoName(service string) (string, error)

GRPCServiceGoName extracts the proto service's simple name (last dotted segment of the fully-qualified service name, e.g. "device.command.v1.DeviceCommandService" → "DeviceCommandService") and validates that it is an exported Go identifier.

The protoc-gen-go-grpc generator derives Register<Name>Server from the service's Go name, which for valid proto service declarations equals the last FQN segment. An unexported or syntactically invalid segment would cause cellgen to emit a lowercase RegisterfooServer selector that the Go compiler cannot resolve. This function is the single source for that derivation: both cellgen (RegisterFunc derivation) and contractgen (service-name validation) must call it rather than duplicating the logic.

Returns an error when the name is empty, not a valid Go identifier, or not exported (uppercase first letter). It is intentionally FS-free so it can run inside governance and codegen without reading the proto file.

func IsAdminHTTPPath

func IsAdminHTTPPath(path string) bool

IsAdminHTTPPath reports whether path targets the admin HTTP listener (operator control-plane) prefix /admin/v1. This is GoCell's single admin-path predicate; the runtime router's listener-route affinity check and kernel/cell.AuthRouteMeta.IsAdmin call it exclusively, so the prefix never drifts across layers (mirrors IsInternalHTTPPath).

Accepted:

/admin/v1
/admin/v1/projection/ordercell/orders/rebuild

Rejected (fail-closed):

""                    (empty string)
"admin/v1/foo"        (missing leading /)
"/adminx/v1/foo"      (prefix must be /admin/v1 or /admin/v1/, not /adminx)
"/api/v1/foo"         (public listener, not admin)

func IsBootstrapPath

func IsBootstrapPath(path string) bool

IsBootstrapPath reports whether path matches the bootstrap admin endpoint `/api/v{N}/{cell}/setup/admin` (version-agnostic).

Matching uses exact segment comparison, not substring, to prevent false positives on paths like /foo/setup/admin/bar or /api/v1/setup/admin/foo. This is GoCell's single bootstrap-path predicate; all judgment points (FMT-28, archtest, runtime FinalizeAuth) must call this function exclusively.

Accepted:

/api/v1/access/setup/admin
/api/v2/access/setup/admin
/api/v1/anycell/setup/admin

Rejected (fail-closed):

""                               (empty string)
"api/v1/access/setup/admin"      (missing leading /)
"/api/v1/setup/admin/foo"        (substring guard: missing cell segment)
"/foo/setup/admin/bar"           (substring guard)
"/api/v1/access/setup/admin/foo" (trailing extra segment)

ref: postmortem 202605060030 §5.3 / ADR §D4

func IsConventionalAssemblyPath

func IsConventionalAssemblyPath(p string) bool

IsConventionalAssemblyPath reports whether p is an assembly YAML file under the conventional assemblies/<id>/ directory (e.g. "assemblies/corebundle/assembly.yaml"). Exposed here so the "assemblies/" path-prefix literal stays inside the Locator funnel — see LOCATOR-DISCOVERY-FUNNEL-01.A2a.

Returns false for examples/, Manifest-mode custom paths, and an empty string. Callers (e.g. deriveAssembly) check for empty file before calling and apply convention-default behavior for that case.

Intended for internal derivation logic only (deriveAssembly entrypoint heuristic); governance rules should consume AssemblyMeta.File via path.Dir instead of calling this directly.

func IsInExamplesSubtree

func IsInExamplesSubtree(p string) bool

IsInExamplesSubtree reports whether p is a YAML / Go file under the GoCell monorepo's examples/ subtree. This is a conventional-layout query exposed here (rather than open-coded in governance rules) so that the path-prefix literal "examples/" stays inside the Locator funnel — see LOCATOR-DISCOVERY-FUNNEL-01. External-repo callers under Manifest mode without an examples/ subtree always get false; this function is intended for gocell-self-repo skip-style rules.

func IsInternalHTTPPath

func IsInternalHTTPPath(path string) bool

IsInternalHTTPPath reports whether path targets the internal HTTP listener prefix where caller-cell identity can be enforced.

func IsKnownCapability

func IsKnownCapability(s string) bool

IsKnownCapability reports whether s is one of CapabilityEnum.

func IsKnownDeployTemplate

func IsKnownDeployTemplate(s string) bool

IsKnownDeployTemplate reports whether s is one of DeployTemplateEnum.

func IsKnownTransport

func IsKnownTransport(s string) bool

IsKnownTransport reports whether s is one of TransportEnum (the closed set of wire transports GoCell sanctions). The empty string is NOT a member; callers that allow an omitted transport must guard the empty case separately. The parser defaults an OMITTED contract.yaml `transports:` per kind; an explicitly present-but-empty declaration (null / []) is left empty on purpose so governance FMT-39's non-empty guard flags it rather than silently defaulting.

func IsPublicHTTPPath

func IsPublicHTTPPath(path string) bool

IsPublicHTTPPath reports whether path targets the public-facing HTTP listener (the internet-exposed API surface). The classification covers all versioned public API prefixes — /api/vN — without locking to a specific version number, because the trust-boundary concern (public internet vs. internal control-plane) is version-agnostic: any path under /api/* is reachable by external principals and subject to the public-listener auth chain.

Contrast with IsInternalHTTPPath, which is locked to /internal/v1 because internal path versioning is tightly controlled and version-specific router wiring matters. The public oracle is deliberately version-agnostic.

Accepted:

/api
/api/v1/config/{key}
/api/v2/users

Rejected (fail-closed):

""                     (empty string)
"/apix/v1/foo"        (prefix must be /api/ or exactly /api, not /apix)
"/internal/v1/foo"    (internal listener, not public)
"/healthz"            (framework probe, no trust-boundary flag)

ref: FMT-33 SLICE-HTTP-VISIBILITY-SEGREGATION-01; symmetric counterpart to IsInternalHTTPPath for the public trust boundary.

func IsValidMetadataText

func IsValidMetadataText(value string) bool

IsValidMetadataText reports whether value is free of the control characters (\n, \r, \x00, \t) that would break inline YAML scalar emission or fabricate adjacent YAML fields when interpolated into scaffold templates. All other characters — colons, dashes, unicode, punctuation — are accepted at this layer; full YAML scalar safety (quoting / escaping) is the responsibility of pkg/yamlsafe.Quote at the rendering boundary.

Tab (\t) is included because YAML 1.2 §5.1 allows tab in plain scalars, so yamlsafe.Quote would not add quotes around a tab-containing value, letting the tab pass through to the generated YAML unchanged. This matches the validateScaffoldID / validateScaffoldText tab-hardening (#8).

Predicate convention: Match* for pattern-bound checks (regex compliance); Is* for semantic free-text checks. Both return bool so callers wrap with their own errcode.

Predicate-style API mirrors MatchAssemblyID / MatchCellID — callers (kernel scaffold validation, cmd flag validation) compose their own errcode wrapping, so no errcode sentinel is introduced here.

Single-source for metadata free-text constraints; eliminates per-caller mirror copies (cf. legacy validateAssemblyTextComponent inside kernel/assembly, now deleted).

ref: kubernetes/apimachinery pkg/util/validation/validation.go — same exported-helper-only convention (pattern unexported, helper exported).

func IterateAuthBoolCombos

func IterateAuthBoolCombos(fn func(auth HTTPAuthMeta, name string))

IterateAuthBoolCombos enumerates all 2 ** HTTPAuthMetaBoolFields (= 32 today) combinations of HTTPAuthMeta's bool fields. Named-field struct literals are used here for readability — Go does NOT report a compile error when a new field is added to HTTPAuthMeta, so the safety net is a separate reflect-based guard: TestHTTPAuthMetaFieldCount in auth_combo_test.go fails CI if the bool field count drifts from HTTPAuthMetaBoolFields, forcing this helper, the matrix size constant, AuthComboLegal, and the test whitelist to be updated together.

HTTPAuthMeta.Responses ([]int) is intentionally excluded: it is not a mutex-governed flag and does not participate in FMT-27 semantics.

name encodes each field as one character: uppercase = true, lowercase = false. Order P-R-S-B-C: Public / PasswordResetExempt (R) / ServiceOwned / Bootstrap / ClientsOnly. Example: "P-r-s-b-c" = Public:true, all others false.

func MatchAssemblyID

func MatchAssemblyID(s string) bool

MatchAssemblyID reports whether s satisfies AssemblyIDPattern. Forwards to pkg/scaffoldid.Match so the regex is compiled exactly once across the codebase (CELL-ID-PATTERN-SINGLE-SOURCE-01).

func MatchAssemblyModulePath

func MatchAssemblyModulePath(s string) bool

MatchAssemblyModulePath reports whether s satisfies AssemblyModulePathPattern: a non-empty assembly cell `module` path free of runes that could break out of the generated cellmodules import string literal.

func MatchCellID

func MatchCellID(s string) bool

MatchCellID reports whether s satisfies CellIDPattern. Same single-source forwarding as MatchAssemblyID.

func MatchGoStructName

func MatchGoStructName(s string) bool

MatchGoStructName reports whether s satisfies GoStructNamePattern.

func OwnershipDeclarationRequired

func OwnershipDeclarationRequired(auth HTTPAuthMeta) bool

OwnershipDeclarationRequired reports whether the auth configuration mandates an ownership block declaration. Single-source predicate shared by kernel/governance FMT-32 and schema/metadata validation.

The ownership block is required when auth.ServiceOwned is true, regardless of other flags (e.g. PasswordResetExempt may coexist with ServiceOwned per FMT-27).

func OwnershipPathValid

func OwnershipPathValid(expr string) bool

OwnershipPathValid reports whether the ownership path expression conforms to the DSL shape. Single-source predicate shared by kernel/governance FMT-32 and schema/metadata validation.

DSL: prefix is ctx or path, followed by one or more dot-separated segments where each segment starts with a lowercase letter (camelCase enforced; snake_case and PascalCase are rejected). path.<param> referential integrity (the param must be declared in the route's pathParams) is a governance-layer concern and is not checked here.

Single-segment forms:

  • "path.<param>" alone is valid — the path param value itself is the owner key (e.g. DELETE /users/{id} where {id} directly identifies the owned resource). "path.<param>.<field>" selects an owner field on the located resource (e.g. "path.id.userID" fetches the userID field of the session record at path param "id").
  • "ctx.<seg>" alone is valid — the caller context field itself is the owner key (e.g. "ctx.userID").

Unicode chars are rejected by the ASCII character class — intentional fail-closed. No length cap is enforced by this predicate; governance callers reject absurd lengths if needed.

func ReadManifestModulePaths

func ReadManifestModulePaths(fsys fs.FS, manifestPath string) ([]string, error)

ReadManifestModulePaths reads the manifest at manifestPath from fsys and returns its declared module disk paths (ManifestModule.Path), in declaration order. It runs the same loadManifest validation (schema version, path safety, singleton uniqueness), so a malformed manifest fails closed rather than yielding a partial set.

It is the exported accessor for the workspace's metadata-module set. Tooling (the archtest workspace enumerator in tools/workspace) cross-checks this set against go.work's `use` directives — every metadata module MUST be a Go module the toolchain compiles (manifest.modules ⊆ go.work.use); a manifest module absent from go.work is a drift bug surfaced fail-closed at archtest time.

func ReadManifestModulePathsRoot

func ReadManifestModulePathsRoot(root, manifestPath string) ([]string, error)

ReadManifestModulePathsRoot is the root-confined form of ReadManifestModulePaths for disk callers: it opens an os.Root at root (Go 1.24+ traversal-resistant fs) and reads through os.Root.FS(), so a symlinked .gocell/manifest.yaml (or a symlinked module dir) that resolves outside root is rejected at the syscall layer rather than silently read from outside the workspace. Disk callers (e.g. tools/workspace cross-check) MUST use this rather than building os.DirFS themselves — confinement is not the caller's to opt out of. Mirrors NewLocator's os.OpenRoot confinement (#1592).

func ValidateGRPCProtoPath

func ValidateGRPCProtoPath(proto string) error

ValidateGRPCProtoPath validates an endpoints.grpc.proto path field. It applies five guards in order:

  1. non-empty — a proto field is required on every grpc contract.
  2. must be rooted under GRPCProtoPathPrefix ("contracts/grpc/") — prevents referencing protos outside the governed contracts tree.
  3. no control rune — a control character would corrupt generated doc comments and file path handling.
  4. filepath.IsLocal — HasPrefix alone does not stop a traversal such as "contracts/grpc/../../../etc/x" escaping the repo root on os.ReadFile. This is the single-source guard shared by contractgen and governance FMT-37 (governance never runs contractgen, so without this shared function FMT-37 missed the IsLocal check — PR #1601 finding #2 / #3).
  5. subtree escape after lexical clean — a path like "contracts/grpc/../http/x.proto" passes guard 2 (string HasPrefix) and guard 4 (stays under repo root) but cleans to "contracts/http/x.proto", which is OUTSIDE the contracts/grpc/ subtree. Guard 5 lexically cleans the path and re-asserts the prefix so that intra-subtree ".." sequences (e.g. "contracts/grpc/device/../device/x.proto" → "contracts/grpc/device/x.proto") are accepted while cross-subtree escapes are rejected.

This is a pure lexical guard — symlink resolution is intentionally out of scope. The contracts/grpc/ tree is repo-controlled and the validator must remain FS-free so governance can run it without reading the proto.

The returned error carries a plain, path-focused message. Callers are expected to wrap it with their own contract-identity context:

if err := metadata.ValidateGRPCProtoPath(proto); err != nil {
    return fmt.Errorf("contract %q: %w", id, err)
}

Types

type ActorMeta

type ActorMeta struct {
	ID                  string `yaml:"id"`
	MaxConsistencyLevel string `yaml:"maxConsistencyLevel"`
}

ActorMeta maps to a single entry in actors.yaml.

actors.yaml registers external systems that participate in contracts but are not modeled as cells. Membership in this file is itself the type declaration ("registered actor = external"); audience-aware governance rules (REF-17) reject any actor in this file from internal endpoints. No type field is needed — keeping it would create a parallel truth source against the cell-vs-actor distinction already implied by membership.

type AssemblyCellRef

type AssemblyCellRef struct {
	// yaml tags drive only marshaling (decode is the custom UnmarshalYAML below);
	// module is omitempty so a round-trip of a same-module ref emits `id: x`
	// rather than `module: ""` (which the decoder rejects as explicit-empty).
	ID     string `yaml:"id"`
	Module string `yaml:"module,omitempty"`
}

AssemblyCellRef is one entry in assembly.yaml `cells:`. It accepts two YAML forms (see UnmarshalYAML):

  • scalar shorthand `- configcore` → {ID: "configcore"}
  • object (cross-module) `- {id: payment, module: github.com/acme/pay}`

Module empty = the cell's cellmodules/ package lives in the assembly's own Go module (the common same-module case). A non-empty Module references a cell developed in an independent Go module (#1086); codegen renders the import as "<Module>/cellmodules/<ID>" instead of "<current module>/cellmodules/<ID>". The module must be a declared dependency (go.mod require / go.work use) — that is enforced by the Go compiler when the generated modules_gen.go is built, not by a metadata governance rule (a declared-but-unresolvable module fails `go build`).

A non-empty Module also requires the owning assembly to declare `build.compositionAPI: true`; otherwise `gocell generate assembly` fails with ErrMetadataInvalid (the legacy local-CellModule form has no import path and cannot express a foreign module).

func CellRefs

func CellRefs(ids ...string) []AssemblyCellRef

CellRefs builds a same-module []AssemblyCellRef from bare cell ids. It is the ergonomic constructor for the common case (every cell in the assembly's own module); cross-module entries are constructed as AssemblyCellRef{ID, Module} literals.

This function body is the single sanctioned site that embeds an AssemblyCellRef{ID: <runtime value>} composite literal; it is allowlisted by FIXTURE-CELLID-TYPED-BUILDER-01/A1 (this file). A1 does not scan CellRefs call arguments. Usage guidance by A1 scope (kernel/):

  • external kernel/ test packages with static ids → prefer explicit []AssemblyCellRef{{ID: metadatatest.CellID*}} literals (A1-checked);
  • internal `package metadata` tests (cannot import metadatatest — import cycle) and dynamic-id helpers (ids are runtime params, not literals) → use CellRefs (the only A1-compatible path);
  • non-kernel ergonomics + production synthesis from validated runtime ids.

func (*AssemblyCellRef) UnmarshalYAML

func (r *AssemblyCellRef) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML decodes the scalar-or-object union form of an assembly cell entry. The decoder's KnownFields(true) strictness does not propagate into a custom Unmarshaler, so the mapping branch enforces the {id, module} key set itself to preserve strict-decode parity with the rest of the schema.

type AssemblyMeta

type AssemblyMeta struct {
	ID    string            `yaml:"id"`
	Cells []AssemblyCellRef `yaml:"cells"`
	Owner OwnerMeta         `yaml:"owner"`
	// The assembly's provisioned capability set is NOT declared here — it is the
	// derived union of its cells' CellMeta.Requires (Design Y, #855). There is no
	// hand-authored assembly-level `capabilities` field; the single source is
	// per-cell. kernel/assembly.GenerateModulesGen computes the union when
	// rendering generatedCapabilities() in modules_gen.go.
	Build               BuildMeta `yaml:"build,omitempty"`
	MaxConsistencyLevel string    `yaml:"-"` // derived; yaml occurrence rejected by KnownFields
	Dir                 string    `yaml:"-"` // assembly directory name (parts[1]); set by parser from path, not YAML
	File                string    `yaml:"-"` // parsed assembly.yaml path relative to project root
}

AssemblyMeta maps to assemblies/{id}/assembly.yaml (platform assemblies) or examples/{id}/assembly.yaml (example assemblies). See parser.matchAssemblyYAML for the recognized path forms.

type BuildMeta

type BuildMeta struct {
	Entrypoint     string `yaml:"entrypoint,omitempty"`
	Binary         string `yaml:"binary,omitempty"`
	DeployTemplate string `yaml:"deployTemplate,omitempty"`
	// CompositionAPI when true instructs GenerateModulesGen to emit the
	// runtime/composition.CellModule form (cellmodules/ module functions) instead
	// of the legacy local-CellModule-type form used by examples/.
	// Set to true in assemblies that use cellmodules/{cell}.Module() constructors
	// (currently only assemblies/corebundle/assembly.yaml).
	CompositionAPI bool `yaml:"compositionAPI,omitempty"`
}

BuildMeta holds the build configuration for an Assembly.

type CellMeta

type CellMeta struct {
	ID               string `yaml:"id"`
	Type             string `yaml:"type"`             // "core"|"edge"|"support"
	ConsistencyLevel string `yaml:"consistencyLevel"` // "L0"-"L4"
	DurabilityMode   string `yaml:"durabilityMode"`   // "demo"|"durable" (advisory for L2+)
	// Lifecycle is the governance maturity phase
	// (experimental|candidate|asset|maintenance|retired). Optional; empty
	// defaults to experimental at BaseCell construction. Distinct from the
	// runtime cellState and from a Contract's draft/active/deprecated lifecycle
	// — see kernel/cellvocab.CellLifecycle. The parser stays lenient (membership
	// is enforced by cell.schema.json enum + governance CELL-LIFECYCLE-01 +
	// NewBaseCell's ParseCellLifecycle), matching the durabilityMode/requires
	// posture.
	Lifecycle      string         `yaml:"lifecycle,omitempty"`
	Owner          OwnerMeta      `yaml:"owner"`
	Schema         SchemaMeta     `yaml:"schema"`
	Verify         CellVerifyMeta `yaml:"verify"`
	L0Dependencies []L0DepMeta    `yaml:"l0Dependencies"`
	// GoStructName is a schema extension consumed by tools/codegen — kernel
	// itself does not interpret its value. Cells that opt into K#04 codegen
	// MUST set this; non-codegen cells leave it empty. There is no reliable
	// automatic mapping from a lowercased cell id to its conventional CamelCase
	// Go type (e.g. "ordercell" → "OrderCell"), which is why explicit
	// declaration is required.
	GoStructName GoIdentifier `yaml:"goStructName,omitempty"`
	// Requires lists the assembly-level shared infrastructure capabilities this
	// cell consumes (postgres / redis / rabbitmq). It is the authoritative single
	// source: the owning assembly's provisioned capability set is the derived
	// union of its cells' Requires (see kernel/assembly.GenerateModulesGen). The
	// closed value set is mirrored by CapabilityEnum + runtime/capability.Kind +
	// cell.schema.json `requires` enum; unknown/duplicate values are rejected by
	// `gocell validate` governance rule FMT-36 (validation-time, not by ParseFS —
	// the parser stays lenient, matching Kubernetes admission-layer validation).
	// "Requires" names what the cell consumes; it declares intent, not a uniform
	// hardness contract. Whether a listed capability is hard-required or
	// best-effort is decided per kind by the provisioner (cmd/corebundle
	// provisionCapabilities): postgres is hard-required in non-memory storage
	// modes (absence fails fast at the cell's nil-guard); redis is
	// provision-if-available (absence degrades gracefully — e.g. accesscore's
	// AUTH-CACHE-01 session cache is skipped when no redis client is configured).
	Requires []string `yaml:"requires,omitempty"`
	Dir      string   `yaml:"-"` // directory segment under cells/, set by parser
	File     string   `yaml:"-"` // parsed cell.yaml path relative to project root
}

CellMeta maps to cells/{id}/cell.yaml.

Dir captures the filesystem directory segment under cells/ as walked by the parser. It is populated from the file path, not the YAML, so strict-mode governance rules (REF-04) can compare filesystem truth against cell.id without being fooled by a path/id split.

func (*CellMeta) Clone

func (c *CellMeta) Clone() *CellMeta

Clone returns a deep copy of c, independently owning every slice and nested struct. Used by kernel/cell.BaseCell construction and by kernel/registry.CellRegistry.Get to prevent caller mutation from leaking into shared state.

nil receiver returns nil.

type CellVerifyMeta

type CellVerifyMeta struct {
	Smoke []string `yaml:"smoke"`
}

CellVerifyMeta holds structured verify refs for a Cell. Smoke refs use the format: smoke.{cellID}.{suffix}.

type CellWireBundle

type CellWireBundle struct {
	Listeners  []WireBundleListener
	Routes     []WireBundleRoute
	Subscribes []WireBundleSubscribe
}

CellWireBundle is a dependency-free mirror of markergen.WireBundle. Callers in runtime/ or cmd/ translate markergen.WireBundle → CellWireBundle so that kernel/metadata stays pure (no import of tools/codegen/*).

type CellWireSummary

type CellWireSummary struct {
	CellID     string              `json:"cellId"     yaml:"cellId"`
	Listeners  []WireListenerView  `json:"listeners"  yaml:"listeners"`
	Routes     []WireRouteView     `json:"routes"     yaml:"routes"`
	Subscribes []WireSubscribeView `json:"subscribes" yaml:"subscribes"`
}

CellWireSummary aggregates a cell's wire surface for catalog exposure. JSON / YAML tags follow the project camelCase convention.

func DeriveCellWireSummaries

func DeriveCellWireSummaries(project *ProjectMeta, bundles map[string]CellWireBundle) []CellWireSummary

DeriveCellWireSummaries converts a per-cell CellWireBundle map into an ordered []CellWireSummary sorted by CellID for deterministic catalog output.

Only cell IDs present in both project.Cells and bundles appear in the result — cells with no bundle entry are silently omitted (they contribute an empty summary only when the caller explicitly supplies an empty CellWireBundle for that cell ID).

nil or empty bundles map → returns an empty (non-nil) slice so that callers can iterate without nil checks.

type ContractMeta

type ContractMeta struct {
	ID   string `yaml:"id"`
	Kind string `yaml:"kind"` // http|event|command|projection|webhook|grpc|saga
	// Transports is the non-empty SET of wire transports this contract is
	// sanctioned to bind to (e.g. [amqp, mqtt] for an event published over both
	// brokers). When the contract.yaml omits `transports:`, parser.parseContract
	// defaults it per kind (event/command→[amqp], http/webhook→[http],
	// grpc→[grpc], projection/saga→[internal]) so every existing contract stays
	// byte-identical. Each member MUST be one of cellvocab.AllTransports();
	// kind↔transport compatibility is enforced by governance FMT-39. contractgen
	// derives the generated ContractSpec.Transport as the primary (Transports[0])
	// and emits the full exported set for multi-transport contracts.
	// ref: asyncapi/spec channel.servers (transport set); k8s SetDefaults (per-kind default).
	Transports       []string `yaml:"transports,omitempty"`
	OwnerCell        string   `yaml:"ownerCell"`
	ConsistencyLevel string   `yaml:"consistencyLevel"`
	Lifecycle        string   `yaml:"lifecycle"` // draft|active|deprecated
	// Triggers lists the outbox event topics emitted by this contract's owner
	// cell when the HTTP handler succeeds. Required for L2+ HTTP contracts.
	// Each value MUST be a string literal or named constant (dto.TopicX /
	// domain.TopicX); dynamic expressions (fmt.Sprintf, variables) are rejected
	// by CONTRACT-CONSISTENCY-EMIT-01. Validated bidirectionally against
	// outbox.Emit call sites in cells/<ownerCell>/slices/**/service.go.
	Triggers   []string       `yaml:"triggers,omitempty"`
	Endpoints  EndpointsMeta  `yaml:"endpoints"`
	SchemaRefs SchemaRefsMeta `yaml:"schemaRefs,omitempty"`
	// Saga carries the orchestration definition for kind=saga contracts (steps,
	// timeout, compensationOrder). Nil for all other kinds. Parsed under strict
	// KnownFields decode; the declarative mirror lives in
	// schemas/contract.schema.json. contractgen derives typed step I/O structs +
	// an Impl interface + BuildDefinition/Register from it (PR-07).
	Saga              *SagaMeta `yaml:"saga,omitempty"`
	Replayable        *bool     `yaml:"replayable,omitempty"`
	IdempotencyKey    string    `yaml:"idempotencyKey,omitempty"`
	DeliverySemantics string    `yaml:"deliverySemantics,omitempty"`
	// Codegen opts the contract into `gocell generate contract` output.
	// When true, contractgen produces types_gen.go / iface_gen.go (and
	// handler_gen.go for kind=http) under generated/contracts/<kind>/<...>/v<N>/.
	//
	// K#09 funnel: parser.parseContract defaults this to true when the
	// contract.yaml omits the `codegen:` key (detected via yaml AST in
	// contractYAMLHasKey). Explicit `codegen: false` is the only way to
	// opt out. Scaffold output omits the field entirely so the funnel is
	// the single source of truth (INVARIANT SCAFFOLD-BUNDLE-NO-CODEGEN-LITERAL-01).
	Codegen bool `yaml:"codegen,omitempty"`
	// Direction is the webhook flow direction: "inbound" (external → cell) or
	// "outbound" (cell → external). Only populated for kind=webhook.
	Direction string `yaml:"direction,omitempty"`
	// Signature holds the webhook signature verification metadata. Only
	// populated for kind=webhook contracts.
	Signature *WebhookSignatureMeta `yaml:"signature,omitempty"`
	// Payload holds webhook payload constraints. Only populated for
	// kind=webhook contracts.
	Payload *WebhookPayloadMeta `yaml:"payload,omitempty"`
	// Description / DeprecatedAt are documentation only — excluded from
	// structural fingerprint via fingerprint:"-".
	Description  string `yaml:"description,omitempty" fingerprint:"-"`
	DeprecatedAt string `yaml:"deprecatedAt,omitempty" fingerprint:"-"`
	// Dir / File are parsed-time fields tracking contract.yaml location relative
	// to the project root; never persisted, hence yaml:"-".
	Dir  string `yaml:"-" fingerprint:"-"`
	File string `yaml:"-" fingerprint:"-"`
}

ContractMeta maps to contracts/{kind}/{domain...}/{version}/contract.yaml.

func (*ContractMeta) ProviderEndpoint

func (c *ContractMeta) ProviderEndpoint() string

ProviderEndpoint returns the provider cell/actor ID for this contract based on its Kind. Returns "" if Kind is unknown or provider is unset.

For kind=webhook: returns the explicitly declared ownerCell. Webhook contracts must declare ownerCell explicitly (see contracts/webhook/README.md). Unlike other kinds, the webhook provider cannot be derived from Endpoints.Receivers or Endpoints.Dispatchers at parse time because those fields are populated by deriveWebhookEndpoints, which runs after parseContract calls ProviderEndpoint for the G-7 ownerCell auto-derivation. When ownerCell is set, the G-7 if-check in parseContract (if m.OwnerCell == "") is skipped correctly; when ownerCell is empty, ProviderEndpoint returns "" and ownerCell remains empty (governance rules will flag the omission).

type ContractSchemaRef

type ContractSchemaRef struct {
	Field string
	Ref   string
	Scope SchemaRefScope
}

ContractSchemaRef is one schema reference declared by a contract.

func ContractSchemaRefs

func ContractSchemaRefs(c *ContractMeta) []ContractSchemaRef

ContractSchemaRefs returns every schema reference declared by c in deterministic order. Empty refs are included so callers that need field-level completeness can still inspect them.

type ContractUsage

type ContractUsage struct {
	Contract string `yaml:"contract"`
	Role     string `yaml:"role"` // serve|call|publish|subscribe|handle|invoke|provide|read|webhook-receive|webhook-dispatch
	// Handler is the consumer handler method name on the slice's service/consumer
	// struct field; REQUIRED for role=subscribe and role=webhook-receive, forbidden
	// otherwise (including role=webhook-dispatch). Single source for the
	// reg.Subscribe / reg.RegisterWebhookReceiver handler expression that cellgen emits.
	Handler string `yaml:"handler,omitempty"`
	// Group is the broker consumer group for role=subscribe; optional, defaults to
	// the owning cell ID when empty. Forbidden for non-subscribe roles (including
	// webhook-receive and webhook-dispatch).
	Group string `yaml:"group,omitempty"`
	// Field optionally names the cell-struct field holding the consumer slice's
	// service, disambiguating slices that own more than one *sliceID.T field
	// (e.g. a route Handler plus a subscribe Consumer or a webhook receiver).
	// Optional for role=subscribe, role=webhook-receive, and role=webhook-dispatch
	// (cellgen resolves by package convention when empty); forbidden for all other
	// roles.
	Field string `yaml:"field,omitempty"`
	// SourceID is the secret-isolation key identifying the webhook source (e.g.
	// "stripe", "shopify"). REQUIRED for role=webhook-receive and
	// role=webhook-dispatch; forbidden for all other roles. For webhook-receive,
	// this value must equal contract.endpoints.inbound.sourceID.
	SourceID string `yaml:"sourceID,omitempty"`
	// TargetSelector is the target-URL selector method name used by the
	// dispatcher to resolve the outbound webhook URL at runtime. REQUIRED for
	// role=webhook-dispatch; forbidden for all other roles.
	TargetSelector string `yaml:"targetSelector,omitempty"`
	// Projection, when set on a role=subscribe CU, makes cellgen emit
	// reg.RegisterProjection (an L3 CQRS projection) instead of reg.Subscribe.
	// The value is the ProjectionID — half of the checkpoint key
	// (cellID, projectionID). MUST be a snake_case probe-name identifier:
	// ^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$. Optional; forbidden on every
	// non-subscribe role.
	Projection string `yaml:"projection,omitempty"`
	// OnReset names the optional rebuild Reset-phase hook method on the same
	// slice service field as Handler (cell.ProjectionResetHook). Only meaningful
	// when Projection is set; exported Go identifier (^[A-Z][A-Za-z0-9_]*$).
	// Optional; subscribe-only. Forbidden when Projection is empty.
	OnReset string `yaml:"onReset,omitempty"`
}

ContractUsage declares a Slice's participation in a Contract.

type EndpointsMeta

type EndpointsMeta struct {
	// HTTP (also reused by gRPC: provider=server, consumers=clients)
	Server  string             `yaml:"server,omitempty"`
	Clients []string           `yaml:"clients,omitempty"`
	HTTP    *HTTPTransportMeta `yaml:"http,omitempty"`
	// gRPC transport subtree (parallel to HTTP); server/clients above carry
	// the provider/consumer cells.
	GRPC *GRPCTransportMeta `yaml:"grpc,omitempty"`
	// Event
	Publisher        string   `yaml:"publisher,omitempty"`
	ActorSubscribers []string `yaml:"actorSubscribers,omitempty"`
	// Subscribers is derived by deriveEventSubscribers (parser post-process):
	// union of ActorSubscribers + cell IDs from slice.yaml contractUsages[role=subscribe].
	// yaml:"-" means hand-written "subscribers:" in YAML is rejected by KnownFields strict decode.
	Subscribers []string `yaml:"-"`
	// Command
	Handler  string   `yaml:"handler,omitempty"`
	Invokers []string `yaml:"invokers,omitempty"`
	// Projection
	Provider string   `yaml:"provider,omitempty"`
	Readers  []string `yaml:"readers,omitempty"`
	// Webhook (kind=webhook only)
	Inbound *WebhookInboundMeta `yaml:"inbound,omitempty"`
	// Receivers is derived by deriveWebhookEndpoints (parser post-process): cell
	// IDs from slice.yaml contractUsages[role=webhook-receive].belongsToCell,
	// deduped and sorted alphabetically. yaml:"-" means hand-written "receivers:"
	// in YAML is rejected by KnownFields strict decode — mirrors the Subscribers
	// field pattern.
	Receivers []string `yaml:"-"`
	// Dispatchers is derived by deriveWebhookEndpoints (parser post-process): cell
	// IDs from slice.yaml contractUsages[role=webhook-dispatch].belongsToCell,
	// deduped and sorted alphabetically. yaml:"-" means hand-written "dispatchers:"
	// in YAML is rejected by KnownFields strict decode — mirrors the Subscribers
	// field pattern.
	Dispatchers []string `yaml:"-"`
}

EndpointsMeta holds the kind-specific endpoint fields for a Contract. Only the fields relevant to the contract's Kind are populated.

type GRPCTransportMeta

type GRPCTransportMeta struct {
	// Service is the proto fully-qualified service name,
	// e.g. "device.command.v1.DeviceCommandService".
	Service string `yaml:"service" json:"service"`
	// Proto is the contracts-relative path to the .proto file, e.g.
	// "contracts/grpc/device/command/v1/device_command.proto".
	Proto string `yaml:"proto" json:"proto"`
}

GRPCTransportMeta declares a gRPC contract's service-level ownership. It lives under EndpointsMeta.GRPC, parallel to EndpointsMeta.HTTP; the provider/consumer cells reuse endpoints.server / endpoints.clients (gRPC mirrors http: roles serve/call). A single contract owns a whole proto service — the .proto file is the single source of truth for the RPC method set. Per-method field declarations have been removed (#1655): the contractgen ProtoRegistry enumerates all RPCs from the .proto via ReadProtoServiceInfo, so contract.yaml never lists individual method names.

No per-RPC auth overlay is declared here: a service-level public flag could not express per-method auth once a service owns multiple RPCs, and nothing consumed it (the runtime auth predicate is wired separately). The per-method auth model is deferred to #1675.

ref: grpc/grpc-go ServiceDesc; go-kratos/kratos protoc-gen-go-grpc service descriptor — the proto service name is the wire identity.

type GoIdentifier

type GoIdentifier struct {
	// contains filtered or unexported fields
}

GoIdentifier is a string statically guaranteed to satisfy GoStructNamePattern (^[A-Z][A-Za-z0-9]*$). The unexported value field forces all construction through NewGoIdentifier or UnmarshalYAML, so any GoIdentifier value reaching codegen templates has already been validated.

Raw strings cannot be coerced to GoIdentifier; downstream consumers call String() to obtain the validated text. This is the typed-identifier boundary (review §R1) preventing arbitrary cell.yaml values from being embedded into emitted Go source code.

func MustNewGoIdentifier

func MustNewGoIdentifier(s string) GoIdentifier

MustNewGoIdentifier is the panic-on-misconfig variant of NewGoIdentifier for composition-root and codegen-emitted literals. cell_gen.go embeds the validated goStructName from the parsed CellMeta — if a manual edit introduces an invalid value, the resulting panic is the expected fail-fast (C-class initialization error, see error-handling.md panic taxonomy). Runtime production code must use NewGoIdentifier.

func NewGoIdentifier

func NewGoIdentifier(s string) (GoIdentifier, error)

NewGoIdentifier validates s and returns a typed GoIdentifier. Empty input returns the zero value (cells that opt out of K#04 codegen leave goStructName empty in cell.yaml); callers requiring a non-empty value must check IsZero separately. Non-empty input that does not satisfy GoStructNamePattern is rejected with errcode.ErrMetadataInvalid.

func (GoIdentifier) IsZero

func (g GoIdentifier) IsZero() bool

IsZero reports whether the identifier is the zero value (empty input, indicating a cell that has not opted into K#04 codegen).

func (GoIdentifier) MarshalYAML

func (g GoIdentifier) MarshalYAML() (any, error)

MarshalYAML emits the underlying string so YAML round-trips preserve the original cell.yaml content.

func (GoIdentifier) String

func (g GoIdentifier) String() string

String returns the underlying identifier text. Safe to embed into Go code generation templates because every constructed value has been validated.

func (*GoIdentifier) UnmarshalYAML

func (g *GoIdentifier) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML is invoked by parser.go when CellMeta.GoStructName is decoded. Validation happens at parse time so downstream derivation, governance, and codegen all operate on validated values.

type HTTPAuthMeta

type HTTPAuthMeta struct {
	// Public marks the route as JWT-exempt. The generated NewHandler takes no
	// policy argument; auth.Route{Public: true} is emitted by RegisterRoutes.
	// Mutually exclusive with PasswordResetExempt, Bootstrap, ServiceOwned,
	// and ClientsOnly.
	Public bool `yaml:"public,omitempty" json:"public,omitempty"`
	// PasswordResetExempt allows callers whose JWT carries password_reset_required=true
	// to reach this route. The generated handler emits auth.Route{PasswordResetExempt: true}.
	// Mutually exclusive with Public, Bootstrap, and ClientsOnly. May combine
	// with ServiceOwned.
	PasswordResetExempt bool `yaml:"passwordResetExempt,omitempty" json:"passwordResetExempt,omitempty"`
	// ServiceOwned indicates that the listener must still authenticate the caller,
	// but route-level authorization is intentionally absent because the service
	// validates ownership against domain state. When true, contractgen generates a
	// single-arg NewHandler(svc Service) constructor and emits auth.Route without
	// a Policy field. May be combined with PasswordResetExempt. Mutually exclusive
	// with Public, Bootstrap, and ClientsOnly.
	ServiceOwned bool `yaml:"serviceOwned,omitempty" json:"serviceOwned,omitempty"`
	// Bootstrap marks the route as protected by HTTP Basic Auth using
	// GOCELL_BOOTSTRAP_ADMIN_USERNAME/PASSWORD env credentials. Listener-level
	// JWT middleware skips routes flagged as Bootstrap (matcher in FinalizeAuth).
	// Mutually exclusive with Public, PasswordResetExempt, ServiceOwned, and
	// ClientsOnly. FMT-28 limits this flag to contracts whose path matches
	// IsBootstrapPath.
	Bootstrap bool `yaml:"bootstrap,omitempty" json:"bootstrap,omitempty"`
	// ClientsOnly indicates that this endpoint relies solely on Contract.Clients
	// caller-cell allowlist for authorization. When true, contractgen generates a
	// single-arg NewHandler(svc Service) constructor and emits auth.Route without
	// a Policy field. auth.Mount auto-injects RequireCallerCell guard when
	// Clients is non-empty. Mutually exclusive with Public, Bootstrap,
	// PasswordResetExempt, and ServiceOwned. Requires endpoints.clients to be
	// non-empty and the path to match IsInternalHTTPPath (/internal/v1 or
	// /internal/v1/...) where caller-cell identity is verifiable via the
	// service token.
	ClientsOnly bool `yaml:"clientsOnly,omitempty" json:"clientsOnly,omitempty"`
	// Responses lists HTTP status codes injected by listener-mounted middleware,
	// NOT emitted by the handler/adapter — so they are declared here (no typed
	// response struct) rather than in the responses map. Despite the "auth" name,
	// this list already spans non-auth middleware: bootstrap auth 401, rate limiter
	// 429, and HTTP-idempotency 409 (ClaimBusy) / 422 (key-reused) — both required
	// on non-exempt mutating routes by governance rule CH-07. CH-04 treats these as
	// declared without requiring handler AST emission.
	Responses []int `yaml:"responses,omitempty" json:"responses,omitempty"`
}

HTTPAuthMeta carries route-level authentication override flags for contractgen. These map to generated auth.Route wiring and handler constructor shape.

Mutex among the 5 bool fields is enforced by metadata.AuthComboLegal (the single oracle shared by contract.schema.json if/then rules and governance validateFMT27). HTTP-idempotency behavior is NOT an auth mode — it lives on the sibling HTTPIdempotencyMeta (endpoints.http.idempotency), so HTTPAuthMeta holds exactly the 5 auth-mode flags and the auth-combo matrix stays 2^5. When adding a new bool field, see auth_combo.go for the checklist of files to update in lockstep.

ref: kubernetes-sigs/controller-tools markers/registry.go (declarative auth metadata)

type HTTPIdempotencyMeta

type HTTPIdempotencyMeta struct {
	// Exempt, when true, instructs the HTTP idempotency middleware to never
	// claim/record/replay this route's responses (credential-rotation /
	// change-password whose body returns tokens). The generated handler emits
	// auth.Route{IdempotencyExempt: true} (see runtime/auth/route.go).
	// Mandatory for any route whose response schema carries a
	// pkg/redaction.IsSensitiveKey field — codegen rejects generation without it
	// (CREDENTIAL-RESPONSE-IDEMPOTENCY-EXEMPT-FUNNEL-01).
	Exempt bool `yaml:"exempt,omitempty" json:"exempt,omitempty"`
}

HTTPIdempotencyMeta carries route-level HTTP-idempotency behavior, orthogonal to authentication. It is a first-class sibling of HTTPAuthMeta rather than a folded-in auth flag (#1469 review F7): idempotency is a middleware concern, so it does not participate in the FMT-27 auth-mode mutex matrix. Keeping it out of HTTPAuthMeta also keeps the auth-combo space at 2^5 instead of 2^6.

type HTTPOwnershipMeta

type HTTPOwnershipMeta struct {
	SubjectPath  string `yaml:"subjectPath"  json:"subjectPath"`
	ResourcePath string `yaml:"resourcePath" json:"resourcePath"`
}

HTTPOwnershipMeta declares object-level authorization subject/resource paths. Required when auth.serviceOwned=true (governance FMT-32 + schema if/then enforces this). Pointer field tri-state: nil = block absent, non-nil with empty fields = declared but incomplete; both forms are rejected by FMT-32.

type HTTPResponseMeta

type HTTPResponseMeta struct {
	Description string `yaml:"description" json:"description" fingerprint:"-"`
	SchemaRef   string `yaml:"schemaRef"   json:"schemaRef"`
}

HTTPResponseMeta describes a declared error response for a specific HTTP status code. It references a JSON Schema file, relative to the contract directory, that describes the error response body.

type HTTPTransportMeta

type HTTPTransportMeta struct {
	Method      string                 `yaml:"method"        json:"method"`
	Path        string                 `yaml:"path"          json:"path"`
	PathParams  map[string]ParamSchema `yaml:"pathParams,omitempty"  json:"pathParams,omitempty"`
	QueryParams map[string]ParamSchema `yaml:"queryParams,omitempty" json:"queryParams,omitempty"`
	// Headers declares inbound HTTP request headers this endpoint consumes
	// (keyed by canonical header name, e.g. "X-Tenant-ID"). It is the single
	// source for request-header consumption: contractgen merges each declared
	// header into the generated Request DTO as a typed field populated from
	// r.Header.Get — see archtest HTTP-REQUEST-HEADER-READ-FUNNEL-01 (cells /
	// examples may not read inbound headers raw; the generated handler is the
	// sole reader).
	//
	// Headers are POPULATE-ONLY at codegen: the generated handler emits no
	// required / length / format gate. Per-endpoint fail behavior (e.g. the
	// X-Tenant-ID anti-enumeration matrix in ADR 1160 — login→401, setup/admin→400,
	// setup/status→200 fail-soft) is owned by the cell adapter/service via
	// tenant.ParseTenantID, not derivable from a uniform codegen gate. `required`
	// is documentation / governance / client-gen metadata only. Governance FMT-40
	// rejects unenforced minLength/maxLength/minimum/maximum on header schemas so a
	// declared constraint can never silently no-op. ref: goadesign/goa v3
	// expr/http_endpoint.go Headers; go-kratos/kratos transport/http header binding.
	Headers       map[string]ParamSchema   `yaml:"headers,omitempty"     json:"headers,omitempty"`
	SuccessStatus int                      `yaml:"successStatus" json:"successStatus"`
	NoContent     bool                     `yaml:"noContent"     json:"noContent"`
	Responses     map[int]HTTPResponseMeta `yaml:"responses,omitempty" json:"responses,omitempty"`
	// Auth declares route-level authentication overrides for contractgen.
	// When set, the generated handler emits the corresponding auth.Route flags
	// instead of the default Policy-only wiring. Omit for standard authenticated routes.
	Auth HTTPAuthMeta `yaml:"auth,omitempty" json:"auth,omitempty"`
	// Ownership declares object-level authorization subject/resource paths.
	// Required when auth.serviceOwned=true (governance FMT-32 enforces presence).
	Ownership *HTTPOwnershipMeta `yaml:"ownership,omitempty" json:"ownership,omitempty"`
	// Idempotency declares route-level HTTP-idempotency behavior. It is a
	// first-class sibling of Auth (not folded into the auth-mode mutex matrix):
	// idempotency is a middleware concern, not an authentication mode (#1469
	// review F7). Omit for default idempotent-eligible routes.
	Idempotency HTTPIdempotencyMeta `yaml:"idempotency,omitempty" json:"idempotency,omitempty"`
}

HTTPTransportMeta holds transport-level details for HTTP contracts. It elevates the wire-level contract (method, path, path/query parameters, status codes, bodyless semantics) to first-class metadata so static tooling (codegen, trace span labels, contract-health) can derive the full API shape from contract.yaml alone without inspecting JSON Schema files.

ref: goadesign/goa v3 expr/http_endpoint.go - Params modeled as a typed attribute map; path params derived from the path template at parse time. ref: go-kratos/kratos cmd/protoc-gen-go-http - path params parsed via regex on the path template string.

func (*HTTPTransportMeta) IdempotencyFrameworkStatuses

func (h *HTTPTransportMeta) IdempotencyFrameworkStatuses() []int

IdempotencyFrameworkStatuses returns the HTTP status codes the idempotency middleware can emit for this route as a framework-injected set, analogous to HTTPAuthMeta.Responses (401/429). When idempotency is default-on, a mutating route (POST/PUT/PATCH/DELETE) that is not Idempotency.Exempt can return 409 on an in-flight key (ClaimBusy) or 422 on a reused key with a mismatched body fingerprint (ErrIdempotencyKeyReused, per IETF idempotency-key draft §2.7). Returns nil for GET/HEAD and exempt routes.

This is the single-source oracle for the CH-07 governance rule (#1537 review F4): CH-07 requires every contract this returns a non-empty set for to declare those statuses in auth.responses, so the declaration surface cannot drift from the middleware and a future mutating route is forced to declare 409/422 or set idempotency.exempt. These are NOT folded into declaredErrorStatuses (that would make CH-07 vacuous and has no effect on CH-04, which checks handler-emitted statuses — the middleware injects 409/422, not the handler).

The {409, 422} set is re-declared here as an integer literal because kernel/ must not import runtime/ (layering); it is bound to the single runtime source runtime/http/idempotency.FrameworkStatuses() by archtest IDEMPOTENCY-FRAMEWORK-STATUS-ORACLE-ALIGN-01, which fails if the two diverge.

Known reachability gap: this oracle keys only on method + Idempotency.Exempt, so it also requires 409/422 on mutating routes the middleware never claims for — public / bootstrap / service-token routes, whose non-PrincipalUser principals are bypassed by middleware.extractIdentity. Those routes therefore declare statuses they cannot emit (a pre-existing property of the 409 leg, not introduced by 422). Making the oracle auth-shape-aware is tracked at gh #1591.

type HeaderViolation

type HeaderViolation struct {
	// Header is the offending declared header name (the contract.yaml map key).
	Header string
	// Kind classifies the violation.
	Kind HeaderViolationKind
	// Message is a human-readable description of the problem.
	Message string
}

HeaderViolation is one endpoints.http.headers schema problem.

func ValidateHTTPHeaders

func ValidateHTTPHeaders(headers map[string]ParamSchema) []HeaderViolation

ValidateHTTPHeaders is the SINGLE SOURCE OF TRUTH for endpoints.http.headers schema validity. Headers are populate-only at codegen — the generated handler emits `req.X = r.Header.Get("<name>")` with no validation gate — so the declarable shape is restricted to what that accessor can express:

  • name: a canonical HTTP token (httpHeaderNameRe)
  • type: "string" only — r.Header.Get returns string; integer/number/boolean would generate an uncompilable typed field (#1494 review F1)
  • no minLength/maxLength/minimum/maximum — no gate is generated, so they would silently no-op
  • no case-insensitive duplicate name — HTTP header names are case-insensitive (#1494 review F3)

Both governance FMT-40 (validate-time) and contractgen buildHTTPSpec (codegen-time, fail-closed) call this so the two gates cannot drift: a header that governance would reject can never reach the generator, and the generator fails closed even if `gocell validate` is skipped (#1494 review F4).

Returns violations in a deterministic order (by header name, then kind); an empty slice means the header set is valid.

type HeaderViolationKind

type HeaderViolationKind int

HeaderViolationKind classifies an endpoints.http.headers schema violation.

const (
	// HeaderViolationName — header name is not a canonical HTTP token.
	HeaderViolationName HeaderViolationKind = iota
	// HeaderViolationType — type is missing or not "string". Headers are
	// populate-only at codegen (the generated handler emits
	// req.X = r.Header.Get(name), which yields a string); a non-string type
	// would generate an uncompilable typed field assignment (#1494 review F1).
	HeaderViolationType
	// HeaderViolationConstraint — minLength/maxLength/minimum/maximum declared.
	// Headers emit no validation gate, so a length/numeric constraint would
	// silently no-op; reject it rather than mislead.
	HeaderViolationConstraint
	// HeaderViolationDuplicate — case-insensitive duplicate header name. HTTP
	// header names are case-insensitive, so two keys folding to the same
	// canonical name collide (#1494 review F3).
	HeaderViolationDuplicate
)

type JourneyMeta

type JourneyMeta struct {
	ID           string          `yaml:"id"`
	Goal         string          `yaml:"goal"`
	Lifecycle    string          `yaml:"lifecycle"` // "active"|"experimental"
	Owner        OwnerMeta       `yaml:"owner"`
	Cells        []string        `yaml:"cells"`
	Contracts    []string        `yaml:"contracts"`
	PassCriteria []PassCriterion `yaml:"passCriteria"`
	File         string          `yaml:"-"` // parsed journey YAML path relative to project root
}

JourneyMeta maps to journeys/J-*.yaml.

type L0DepMeta

type L0DepMeta struct {
	Cell   string `yaml:"cell"`
	Reason string `yaml:"reason"`
}

L0DepMeta declares a direct dependency on an L0 (LocalOnly) Cell.

L0 Cell status: sanctioned future extension point

L0 (LocalOnly, pure-compute library cell) is a first-class member of the consistency-level model (L0-L4). An L0 Cell has no state machine, no outbox, and no contracts; it exposes shared pure-computation logic (e.g. hashing, validation algorithms, encoding helpers) that other cells in the same Assembly may import directly.

As of this writing there is no L0 Cell instance in the platform cells (accesscore / auditcore / configcore). Every platform cell.yaml therefore carries `l0Dependencies: []` — this empty slice is expected and correct; it is not a schema defect or dead-code path.

When a future L0 Cell is created (e.g. a shared pure-compute package elevated to a first-class Cell), consuming cells would add an entry here:

l0Dependencies:
  - cell: sharedcrypto
    reason: deterministic hashing for cursor tokens

Decision record: C-06-L0-CELL-DECISION — user chose doc-only (option b) over elevating an existing package to a concrete L0 instance (option a).

type Locator

type Locator struct {
	// contains filtered or unexported fields
}

Locator discovers metadata YAML files from a filesystem root and emits one MetadataSource per file, sorted by path for deterministic test fixtures.

func NewLocator

func NewLocator(root string, opts ...LocatorOption) (*Locator, error)

NewLocator constructs a Locator backed by the on-disk root directory.

The filesystem is rooted via os.OpenRoot(root).FS(), which confines EVERY access (Stat / WalkDir / ReadFile) to the directory tree at root. Per the os.Root contract, it FOLLOWS symlinks that resolve within root and REJECTS those that escape it (absolute targets, or `..`/symlink chains leaving root) with "path escapes from parent" at the syscall layer. This is the root-confinement that prevents a manifest module path (or any subpath) that is a symlink escaping the workspace from making discovery read cells/contracts outside the repo (#1592). Discovery additionally skips symlink *entries* during WalkDir (see discoverConventional / matchManifestGlob), so in practice no symlink is traversed; the GoCell layout has no symlinks at all, so neither behavior changes discovery output.

The root path itself may be a symlinked directory (e.g. macOS /var -> /private/var); os.OpenRoot follows the root's own symlink to open it, then confines all SUBSEQUENT accesses within the opened directory. Resolution uses filepath.Abs (not EvalSymlinks) — confinement comes from os.Root, not from pre-resolving root.

Callers must Close the returned Locator to release the underlying directory file descriptor (Parser.Parse does this for the Locator it constructs).

func NewLocatorFS

func NewLocatorFS(fsys fs.FS, opts ...LocatorOption) (*Locator, error)

NewLocatorFS constructs a Locator backed by an arbitrary fs.FS. Used by tests to feed fstest.MapFS fixtures and by callers that already hold an fs.FS handle (e.g., embed.FS, virtual FS).

Unlike NewLocator, NewLocatorFS does NOT add root confinement: it uses the caller's fsys verbatim. If a caller passes os.DirFS(dir) (which follows symlinks), symlink-escape confinement is the CALLER's responsibility — either use NewLocator (os.OpenRoot-confined) for untrusted disk roots, or guarantee safety another way (conventional Discover never recurses into symlink entries; tools/workspace additionally symlink-guards its go.work `use` dirs). MapFS / embed.FS have no OS symlinks, so the test/virtual path is unaffected.

func (*Locator) Close

func (l *Locator) Close() error

Close releases the os.Root directory handle held by a disk-backed Locator (NewLocator). It is a no-op for an fs.FS-backed Locator (NewLocatorFS) and is safe to call more than once. Parser.Parse / ParseFS close the Locator they construct; callers that use NewLocator directly should defer Close to release the underlying directory file descriptor.

Close errors are non-retryable (closing a read-only directory fd), so deferred callers may safely ignore them: `defer func() { _ = loc.Close() }()`.

func (*Locator) Discover

func (l *Locator) Discover() ([]MetadataSource, error)

Discover walks the locator root and emits one MetadataSource per metadata YAML file found, sorted by Path for deterministic output.

func (*Locator) FS

func (l *Locator) FS() fs.FS

FS returns the underlying fs.FS so callers that need to read file contents (parser unmarshalFile) can share the same handle.

func (*Locator) Mode

func (l *Locator) Mode() LocatorMode

Mode returns the resolved locator mode (after auto-detection).

func (*Locator) Root

func (l *Locator) Root() string

Root returns the absolute disk root used by the locator, or empty string when the locator was constructed via NewLocatorFS.

type LocatorMode

type LocatorMode int

LocatorMode selects how Locator discovers metadata.

const (
	// LocatorAuto selects Conventional unless .gocell/manifest.yaml exists at
	// root, in which case Manifest mode is used. If the manifest file is found
	// but fails to parse, Locator returns an error and does NOT fall back to
	// Conventional mode. Use WithLocatorMode(LocatorConventional) to bypass
	// manifest detection explicitly.
	LocatorAuto LocatorMode = iota
	// LocatorConventional walks the hardcoded 5-pattern layout. Used when no
	// manifest file is present, or explicitly via WithLocatorMode.
	LocatorConventional
	// LocatorManifest reads .gocell/manifest.yaml and walks the modules:
	// list it declares.
	LocatorManifest
)

func ParseLocatorMode

func ParseLocatorMode(s string) (LocatorMode, error)

ParseLocatorMode converts a CLI flag value to a LocatorMode. Empty string resolves to LocatorAuto. Unknown values return an error.

func (LocatorMode) String

func (m LocatorMode) String() string

String returns the kebab-case name of LocatorMode.

type LocatorOption

type LocatorOption func(*Locator)

LocatorOption configures NewLocator / NewLocatorFS.

func WithLocatorMode

func WithLocatorMode(m LocatorMode) LocatorOption

WithLocatorMode overrides the auto-detected mode. Use this in CI to lock the locator behavior against accidental manifest detection.

func WithManifestPath

func WithManifestPath(p string) LocatorOption

WithManifestPath sets an explicit manifest path. Defaults to .gocell/manifest.yaml.

Passing an empty string is a no-op (default path unchanged). Non-empty paths containing ".." segments or absolute paths cause NewLocator / NewLocatorFS to return an error.

type ManifestIncludes

type ManifestIncludes struct {
	// Cells corresponds to the "cells:" YAML key.
	Cells []string `yaml:"cells"`
	// Slices corresponds to the "slices:" YAML key.
	Slices []string `yaml:"slices"`
	// Contracts corresponds to the "contracts:" YAML key.
	Contracts []string `yaml:"contracts"`
	// Journeys corresponds to the "journeys:" YAML key.
	Journeys []string `yaml:"journeys"`
	// Assemblies corresponds to the "assemblies:" YAML key.
	Assemblies []string `yaml:"assemblies"`
	// Actors corresponds to the "actors:" YAML key (singular string, not a list).
	Actors string `yaml:"actors"`
	// StatusBoard corresponds to the "statusBoard:" YAML key (camelCase, not kebab-case).
	StatusBoard string `yaml:"statusBoard"`
}

ManifestIncludes maps each metadata source kind to one or more glob patterns. Globs are resolved relative to the owning ManifestModule.Path. Patterns support "*" (single segment) and "**" (zero or more segments, implemented via fs.WalkDir — not filepath.Glob which has undefined "**" semantics). Ref: bufbuild/buf v2 buf.yaml includes field.

Cells / Slices / Contracts / Journeys / Assemblies accept multiple patterns (slice of strings); Actors and StatusBoard are singletons (single string).

Actors and StatusBoard are workspace-level singletons: they may only appear in Modules[0] (validated by loadManifest). Declaring them in more than one module entry causes loadManifest to return a validation error.

type ManifestModule

type ManifestModule struct {
	Path     string           `yaml:"path"`
	Includes ManifestIncludes `yaml:"includes"`
	Excludes []string         `yaml:"excludes"`
}

ManifestModule is a single module entry inside ManifestSpec.Modules.

Path is required, must be a non-empty relative path, and must not escape outside the manifest directory (no ".." segments, no absolute paths). Use "." for the module that lives at the same level as manifest.yaml.

Includes carries per-kind glob patterns. When empty, the conventional 5-pattern defaults are used (cells/*/cell.yaml, cells/*/slices/*/slice.yaml, contracts/**/contract.yaml, journeys/J-*.yaml, assemblies/*/assembly.yaml, plus actors.yaml and journeys/status-board.yaml singletons). This lets Operator-SDK modules omit includes: entirely and still get conventional discovery.

Excludes applies path-prefix filters across all source kinds in this module. "generated/**" is always appended to the effective excludes list (additive). "vendor/**" is NOT added automatically — declare it explicitly if needed. Specifying an explicit excludes: list does not replace the generated/** guard; both the user-declared patterns and "generated/**" are applied.

type ManifestSpec

type ManifestSpec struct {
	Version string           `yaml:"version"`
	Modules []ManifestModule `yaml:"modules"`
}

ManifestSpec is the parsed .gocell/manifest.yaml file.

Corresponds to the top-level buf v2 buf.yaml modules: schema:

version: v1
modules:
  - path: .
    includes: { cells: ["cells/*/cell.yaml"], ... }
    excludes: ["generated/**", "vendor/**"]

Version must be "v1". Modules must have at least one entry.

type MetadataSource

type MetadataSource struct {
	Path   string
	Kind   SourceKind
	CellID string
}

MetadataSource is one YAML file discovered by Locator.

Path is forward-slash relative to the locator root.

CellID is populated for SourceCell / SourceSlice when the locator can derive it from layout (conventional walk path parts, or manifest includes that happen to follow conventional layout). When the locator cannot derive CellID (manifest with non-conventional layout), CellID stays empty — parser will then require slice.yaml to declare belongsToCell explicitly.

To avoid empty CellID in manifest mode, callers have two options:

(a) Read the `id` field from the corresponding cell.yaml (already parsed
    into ProjectMeta.Cells by Parser — use ProjectMeta.CellForSlice to
    derive it after a full parse pass).
(b) Declare `belongsToCell` explicitly in each slice.yaml; Parser will
    propagate that value and governance rules will validate consistency.

type OwnerMeta

type OwnerMeta struct {
	Team string `yaml:"team"`
	Role string `yaml:"role"`
}

OwnerMeta identifies the team responsible for a Cell or Journey.

type ParamSchema

type ParamSchema struct {
	// Ref is a relative path to a shared JSON Schema mixin that single-sources
	// the value shape (type/minimum/maximum/minLength/maxLength) for this param.
	// It is mutually exclusive with inline type, minimum, maximum, minLength,
	// maxLength, and format declarations; resolved at parse time so governance
	// (FMT-25) and contractgen see a fully-resolved ParamSchema transparently.
	// The Ref field is preserved after resolution for traceability.
	Ref       string `yaml:"$ref,omitempty"      json:"$ref,omitempty"`
	Type      string `yaml:"type"                json:"type"`
	Required  *bool  `yaml:"required,omitempty"  json:"required,omitempty"`
	Format    string `yaml:"format,omitempty"    json:"format,omitempty"`
	MinLength *int   `yaml:"minLength,omitempty" json:"minLength,omitempty"`
	MaxLength *int   `yaml:"maxLength,omitempty" json:"maxLength,omitempty"`
	Minimum   *int   `yaml:"minimum,omitempty"   json:"minimum,omitempty"`
	Maximum   *int   `yaml:"maximum,omitempty"   json:"maximum,omitempty"`
}

ParamSchema describes a single HTTP path or query parameter.

Type must be one of the well-known primitive names in ParamTypes. UUID path parameters use `type: "string"` with `format: "uuid"` so governance and runtime parsing rules share one convention.

Required encodes three distinct states, chosen via pointer so YAML `required: false` can be distinguished from an omitted field:

  • nil - not declared; for path parameters this is the only legal value (path placeholders are required by definition, see FMT-13); for query parameters it defaults to optional.
  • false - explicit opt-out, legal only on query parameters; FMT-13 rejects `required: false` on path parameters.
  • true - explicit required declaration, legal on query parameters.

Format is a free-form hint (e.g. "uuid", "date-time", "int64"). It does not influence FMT-13 enforcement today, but static tooling (codegen, OpenAPI export) consumes it. Governance rule FMT-25 exempts `format: "uuid"` from minLength/maxLength enforcement.

MinLength / MaxLength / Minimum / Maximum are *int (not int) for the same three-state reason as Required: nil = "not declared", non-nil = "declared, even if zero". Governance rule FMT-25 distinguishes the two: missing declarations are violations; explicit zero is accepted. Minimum / Maximum govern both integer and number parameters; use integer-valued bounds in contract.yaml until ParamSchema grows decimal bound fields.

func ResolveParamRef

func ResolveParamRef(
	paramName, contractDir string,
	param ParamSchema,
	readMixin func(contractDir, ref string) ([]byte, error),
) (ParamSchema, error)

ResolveParamRef is the single source of param $ref resolution. It enforces $ref mutual-exclusion, fetches the referenced mixin bytes via readMixin (the caller's IO layer maps contractDir+ref → bytes and owns path-traversal guarding), then back-fills value-shape fields onto param. Both the metadata parser (fs.FS IO) and the contract test harness (fixtureload IO) call this so the resolution semantics live in exactly one place.

Mutual-exclusion is checked BEFORE the read so an author error ($ref alongside inline type/min/max) is reported regardless of whether the mixin file exists.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser loads and parses all YAML metadata from a project root via a Locator.

The Locator funnel is the single source of truth for filesystem topology — see kernel/metadata/locator.go. Parser does NOT walk the filesystem directly; it consumes []MetadataSource from Locator and dispatches each entry to the matching parseXxx method by SourceKind.

func NewParser

func NewParser(root string, opts ...LocatorOption) *Parser

NewParser creates a Parser that reads from the given filesystem root. The root should point to the project root directory (containing go.mod). Pass LocatorOption to override the auto-detected layout — e.g., WithLocatorMode(LocatorConventional) to lock CI against accidental manifest detection, or WithLocatorMode(LocatorManifest) for an external-repo layout.

func (*Parser) Parse

func (p *Parser) Parse() (*ProjectMeta, error)

Parse walks the real file system via Locator and loads all metadata YAML files. Returns a fully populated ProjectMeta.

func (*Parser) ParseFS

func (p *Parser) ParseFS(fsys fs.FS) (*ProjectMeta, error)

ParseFS parses from an fs.FS (for testing with fstest.MapFS or any caller that already has an fs.FS handle).

type PassCriterion

type PassCriterion struct {
	Text     string `yaml:"text"`
	Mode     string `yaml:"mode"` // "auto"|"manual"
	CheckRef string `yaml:"checkRef,omitempty"`
}

PassCriterion is a single acceptance criterion within a Journey.

type Position

type Position struct {
	Line   int
	Column int
}

Position is a 1-based (line, column) reference into a YAML source file. A zero Position (Line==0 or Column==0) means "unknown location".

func Locate

func Locate(root *yaml.Node, path string) Position

Locate is a best-effort convenience that returns the (Line, Column) of the node at `path`, or the zero Position if the path cannot be resolved. Callers that need a precise error should use Find directly.

The caller is expected to inspect pos.Known() rather than comparing Line against 0 directly: yaml.v3 emits 1-based positions, so a zero Line here unambiguously means "unresolved", never "line 0".

func (Position) Known

func (p Position) Known() bool

Known reports whether the position carries usable line/column information. Zero values are treated as unknown because yaml.v3 reports positions as 1-based, so line 0 cannot occur for a parsed node.

type ProjectMeta

type ProjectMeta struct {
	Cells       map[string]*CellMeta     // keyed by cell ID
	Slices      map[string]*SliceMeta    // keyed by "cellID/sliceID"
	Contracts   map[string]*ContractMeta // keyed by contract ID
	Journeys    map[string]*JourneyMeta  // keyed by journey ID
	Assemblies  map[string]*AssemblyMeta // keyed by assembly ID
	StatusBoard []StatusBoardEntry
	Actors      []ActorMeta
	// contains filtered or unexported fields
}

ProjectMeta holds all parsed metadata for the entire project.

func (*ProjectMeta) HasFileNodes

func (pm *ProjectMeta) HasFileNodes() bool

HasFileNodes reports whether any file nodes have been stored.

func (*ProjectMeta) Locate

func (pm *ProjectMeta) Locate(file, path string) Position

Locate returns the Position of the YAML value at the given dotted field path inside file. Returns a zero Position when any precondition is missing (nil receiver, no file nodes, file not found, path unresolvable).

func (*ProjectMeta) PrepareFileNode

func (pm *ProjectMeta) PrepareFileNode(file string, yamlSource []byte) error

PrepareFileNode parses yamlSource into a document node and stores it for the given file path. This is the public entry point for test setup — callers never need to import yaml.v3 directly.

type ResolvedSchemaRef

type ResolvedSchemaRef struct {
	ContractSchemaRef
	AbsPath    string
	ProjectRel string
}

ResolvedSchemaRef is a schema ref resolved against an absolute project root.

func ResolveContractSchemaRef

func ResolveContractSchemaRef(projectRoot string, c *ContractMeta, ref ContractSchemaRef) (ResolvedSchemaRef, error)

ResolveContractSchemaRef resolves ref relative to c's contract directory and verifies that the target stays inside the ref's declared scope.

func ResolveContractSchemaRefs

func ResolveContractSchemaRefs(projectRoot string, c *ContractMeta) ([]ResolvedSchemaRef, error)

ResolveContractSchemaRefs resolves every non-empty schema reference for c.

type SagaMeta

type SagaMeta struct {
	// Timeout is the saga-wide deadline as a Go duration string (e.g. "30s").
	// Empty means no saga-level ceiling (per-step timeouts still apply).
	Timeout string `yaml:"timeout,omitempty"`
	// CompensationOrder selects the rollback walk. v1 limitation: only "reverse"
	// is supported (empty is treated as "reverse"); any other value is rejected
	// by governance SAGA-CONTRACT-COMPENSATION-ORDER-01. Forward/custom orders
	// are not yet modeled.
	CompensationOrder string `yaml:"compensationOrder,omitempty"`
	// Retries is the saga-wide default retry policy; each step inherits it
	// unless the step sets its own.
	Retries *SagaRetryMeta `yaml:"retries,omitempty"`
	// Steps lists the forward steps in execution order; must be non-empty.
	Steps []SagaStepMeta `yaml:"steps"`
}

SagaMeta is the orchestration block of a kind=saga contract.yaml. Steps run in slice order; each step's typed Run input is the previous step's output. The first step takes no typed input — the runtime feeds nil prevState. There is no saga-launch input payload in v1: the orchestrating cell loads the saga's initial business context by looking up domain state keyed by the enrolled Instance.ID (the correlation id).

type SagaRetryMeta

type SagaRetryMeta struct {
	MaxAttempts  int    `yaml:"maxAttempts,omitempty"`
	BaseInterval string `yaml:"baseInterval,omitempty"`
	MaxInterval  string `yaml:"maxInterval,omitempty"`
}

SagaRetryMeta is the declarative form of kernel/saga.RetryPolicy. Intervals are Go duration strings (e.g. "100ms"). Zero / empty fields inherit.

type SagaStepMeta

type SagaStepMeta struct {
	Name       string         `yaml:"name"`
	Output     string         `yaml:"output"`
	Timeout    string         `yaml:"timeout,omitempty"`
	Retries    *SagaRetryMeta `yaml:"retries,omitempty"`
	Compensate *bool          `yaml:"compensate,omitempty"`
}

SagaStepMeta is one step inside a SagaMeta. Name must be a valid idutil.SafeID and unique within the saga. Output references a JSON Schema (contract-relative) describing this step's success payload — which becomes the next step's typed input. Compensate defaults to true (the step is rolled back during the Compensating phase); set false for terminal / non-reversible steps.

type SchemaMeta

type SchemaMeta struct {
	Primary string `yaml:"primary"`
}

SchemaMeta holds the primary data schema reference for a Cell.

type SchemaRefError

type SchemaRefError struct {
	Field string
	Ref   string
	Kind  string
}

SchemaRefError reports a schema ref resolution failure.

Kind is a free-form descriptive string used only in Error(); no caller compares it. If a caller ever needs to switch on it, type it via the string-typed concept funnel pattern — tracked in #1254.

func (*SchemaRefError) Error

func (e *SchemaRefError) Error() string

type SchemaRefScope

type SchemaRefScope string

SchemaRefScope describes the filesystem boundary used to resolve a schema ref.

const (
	// SchemaRefScopeContractDir keeps schemaRefs.* inside the owning contract dir.
	SchemaRefScopeContractDir SchemaRefScope = "contractDir"
	// SchemaRefScopeProjectRoot permits HTTP response schemas to point at shared
	// schemas elsewhere in the repository.
	SchemaRefScopeProjectRoot SchemaRefScope = "projectRoot"
)

type SchemaRefsMeta

type SchemaRefsMeta struct {
	Request  string `yaml:"request,omitempty"  json:"request,omitempty"`
	Response string `yaml:"response,omitempty" json:"response,omitempty"`
	Payload  string `yaml:"payload,omitempty"  json:"payload,omitempty"`
	Headers  string `yaml:"headers,omitempty"  json:"headers,omitempty"`
	// Extra captures additional string-valued schema ref keys beyond the four
	// well-known ones, via yaml:",inline". It is excluded from JSON serialization
	// (json:"-") because Go's encoding/json does not support inline maps; callers
	// that need JSON output should implement custom MarshalJSON if needed.
	Extra map[string]string `yaml:",inline"            json:"-"`
}

SchemaRefsMeta holds JSON Schema file references relative to the contract directory. Known keys are request, response, payload, headers; additional string-valued keys are captured in Extra to stay compatible with contract.schema.json's additionalProperties: {"type":"string"}.

type SliceMeta

type SliceMeta struct {
	ID            string `yaml:"id"`
	BelongsToCell string `yaml:"belongsToCell"`
	// ConsistencyLevel: "L0"-"L4"; required (parser rejects empty);
	// MUST be ≤ cell.ConsistencyLevel. slice.yaml is the SoR for slice
	// level (codegen funnel projects to slice_gen.go.sliceMeta);
	// inheritance from cell.consistencyLevel is no longer supported.
	ConsistencyLevel string `yaml:"consistencyLevel"`
	// Lifecycle is the slice's governance maturity phase
	// (experimental|candidate|asset|maintenance|retired). Optional; MUST be ≤ the
	// parent cell's lifecycle (governance CELL-LIFECYCLE-01). See
	// kernel/cellvocab.CellLifecycle.
	Lifecycle      string          `yaml:"lifecycle,omitempty"`
	ContractUsages []ContractUsage `yaml:"contractUsages"`
	Verify         SliceVerifyMeta `yaml:"verify"`
	AllowedFiles   []string        `yaml:"allowedFiles,omitempty"`
	Dir            string          `yaml:"-"` // slice directory segment, set by parser
	CellDir        string          `yaml:"-"` // parent cell directory segment, set by parser
	File           string          `yaml:"-"` // parsed slice.yaml path relative to project root
}

SliceMeta maps to cells/{cell-id}/slices/{slice-id}/slice.yaml.

Dir / CellDir record the filesystem directory segments as walked by the parser — they are the ground truth for strict-mode governance rules (FMT-16, FMT-17, REF-05). Reading these instead of rederiving from the map key prevents a path-vs-id split from fooling the validator (e.g. a kebab directory paired with a no-dash id in slice.yaml).

func (*SliceMeta) Clone

func (s *SliceMeta) Clone() *SliceMeta

Clone returns a deep copy of s, independently owning every slice and nested struct. Used by tools/codegen-generated SliceMetadata() accessors and by kernel/cell.NewBaseSliceFromMeta to prevent caller mutation from leaking into shared state — mirroring the K8s zz_generated.deepcopy.go pattern.

nil receiver returns nil.

type SliceVerifyMeta

type SliceVerifyMeta struct {
	Unit     []string     `yaml:"unit"`
	Contract []string     `yaml:"contract"`
	Waivers  []WaiverMeta `yaml:"waivers"`
}

SliceVerifyMeta holds verification requirements for a Slice.

type SourceKind

type SourceKind int

SourceKind classifies a metadata YAML file by its semantic bucket.

const (
	// SourceUnknown is the zero value; never emitted by Locator.
	SourceUnknown SourceKind = iota
	// SourceCell marks a cell.yaml file.
	SourceCell
	// SourceSlice marks a slice.yaml file.
	SourceSlice
	// SourceContract marks a contract.yaml file.
	SourceContract
	// SourceJourney marks a journeys/J-*.yaml file.
	SourceJourney
	// SourceAssembly marks an assembly.yaml file.
	SourceAssembly
	// SourceActors marks the workspace-level actors.yaml singleton.
	SourceActors
	// SourceStatusBoard marks the workspace-level journeys/status-board.yaml
	// singleton.
	SourceStatusBoard
)

func (SourceKind) String

func (k SourceKind) String() string

String returns the kebab-case name of the SourceKind, used in errors and diagnostics.

type StatusBoardEntry

type StatusBoardEntry struct {
	JourneyID string `json:"journeyId" yaml:"journeyId"`
	State     string `json:"state"     yaml:"state"`
	Risk      string `json:"risk"      yaml:"risk"`
	Blocker   string `json:"blocker"   yaml:"blocker"`
	UpdatedAt string `json:"updatedAt" yaml:"updatedAt"`
}

StatusBoardEntry maps to a single entry in journeys/status-board.yaml.

type WaiverMeta

type WaiverMeta struct {
	Contract  string `yaml:"contract"`
	Owner     string `yaml:"owner"`
	Reason    string `yaml:"reason"`
	ExpiresAt string `yaml:"expiresAt"`
}

WaiverMeta records a temporary exemption from a contract verification.

type WebhookInboundMeta

type WebhookInboundMeta struct {
	// PathPattern is the HTTP path the platform exposes for receiving webhook
	// callbacks from the external source (e.g. "/webhooks/stripe/events").
	PathPattern string `yaml:"pathPattern"`
	// SourceID is the secret-isolation key identifying the webhook source
	// (e.g. "stripe"). Must match the SourceID declared in every
	// webhook-receive ContractUsage for this contract. Must satisfy
	// ^[a-z][a-z0-9_-]{0,63}$ (aligns with runtime kernel/webhook
	// sourceIDPattern, maxLen 64).
	SourceID string `yaml:"sourceID"`
}

WebhookInboundMeta holds inbound-specific routing fields for a webhook contract (direction=inbound).

type WebhookPayloadMeta

type WebhookPayloadMeta struct {
	// SchemaRef is an optional path to a JSON Schema file (relative to the
	// contract.yaml directory) that validates the webhook payload body.
	SchemaRef string `yaml:"schemaRef,omitempty"`
	// ContentType is the expected MIME type of the webhook payload
	// (e.g. "application/json"). Runtime rejects requests with a mismatched
	// Content-Type header.
	ContentType string `yaml:"contentType,omitempty"`
	// MaxBodyBytes is the maximum allowed size (in bytes) of the webhook
	// request body. Runtime rejects requests exceeding this limit with 413.
	// A value of 0 means no limit (not recommended for production).
	MaxBodyBytes int64 `yaml:"maxBodyBytes,omitempty"`
}

WebhookPayloadMeta holds payload constraints for a webhook contract.

type WebhookSignatureMeta

type WebhookSignatureMeta struct {
	// Algorithm is the HMAC algorithm used to verify the webhook signature
	// (e.g. "hmac-sha256").
	Algorithm string `yaml:"algorithm"`
	// ToleranceSeconds is the bidirectional time window (in seconds) for
	// replay-attack prevention. Requests whose timestamp differs from server
	// time by more than this value in either direction are rejected. A value
	// of 0 disables time-window checking (not recommended for production).
	ToleranceSeconds int `yaml:"toleranceSeconds,omitempty"`
	// DeliveryIDHeader is the HTTP header carrying the unique delivery ID
	// (e.g. "svix-id"). Used as part of the signed string to prevent
	// cross-delivery replay attacks.
	DeliveryIDHeader string `yaml:"deliveryIDHeader"`
	// TimestampHeader is the HTTP header carrying the Unix timestamp of the
	// delivery (e.g. "svix-timestamp"). Must be within ToleranceSeconds of
	// server time.
	TimestampHeader string `yaml:"timestampHeader"`
	// SignatureHeader is the HTTP header carrying the HMAC signature value
	// (e.g. "svix-signature").
	SignatureHeader string `yaml:"signatureHeader"`
	// SignedStringForm is a template for constructing the signed string from
	// delivery fields. Template variables: {deliveryID} (value of
	// DeliveryIDHeader), {timestamp} (value of TimestampHeader), {body}
	// (raw request body). Example: "{deliveryID}.{timestamp}.{body}".
	SignedStringForm string `yaml:"signedStringForm"`
}

WebhookSignatureMeta holds HMAC signature verification parameters for an inbound webhook contract.

type WireBundleListener

type WireBundleListener struct {
	Ref    string
	Prefix string
}

WireBundleListener mirrors markergen.ListenerSpec.

type WireBundleRoute

type WireBundleRoute struct {
	Slice    string
	Listener string
	SubPath  string
	Method   string // empty when marker omitted (defaults to "RegisterRoutes")
}

WireBundleRoute mirrors markergen.RouteSpec (fields relevant to catalog exposure).

type WireBundleSubscribe

type WireBundleSubscribe struct {
	Slice   string
	Topic   string
	Handler string
	Group   string
}

WireBundleSubscribe mirrors markergen.SubscribeSpec (fields relevant to catalog).

type WireListenerView

type WireListenerView struct {
	Ref    string `json:"ref"              yaml:"ref"`
	Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
}

WireListenerView is the catalog wire view of a single listener declaration.

type WireRouteView

type WireRouteView struct {
	Slice    string `json:"slice"            yaml:"slice"`
	Listener string `json:"listener"         yaml:"listener"`
	SubPath  string `json:"subPath,omitempty" yaml:"subPath,omitempty"`
	Method   string `json:"method,omitempty"  yaml:"method,omitempty"`
}

WireRouteView is the catalog wire view of a single route mount.

type WireSubscribeView

type WireSubscribeView struct {
	Slice   string `json:"slice"   yaml:"slice"`
	Topic   string `json:"topic"   yaml:"topic"`
	Handler string `json:"handler" yaml:"handler"`
	Group   string `json:"group"   yaml:"group"`
}

WireSubscribeView is the catalog wire view of a single event subscription.

Directories

Path Synopsis
Package metadatatest provides typed builders for constructing kernel/metadata fixtures in *_test.go files.
Package metadatatest provides typed builders for constructing kernel/metadata fixtures in *_test.go files.

Jump to

Keyboard shortcuts

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