module

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package module projects Facet's existing mechanical toolbox onto the shared host module protocol. It is a strict adapter: it adds no execution system, duplicates no tool logic, and reaches the toolbox only through toolbox.CLI, the same entry point the human-facing `facet tools` command uses.

Index

Constants

View Source
const (
	ModuleID   = "xibodev.facet"
	ModuleName = "Facet"
	Protocol   = "xibodev.module/v1"

	// ContractVersion is the BEHAVIOURAL contract Facet implements, distinct
	// from Protocol, which versions only the wire format.
	//
	// Everything Facet believed about roots, deadline semantics, grant
	// exhaustiveness and confinement authority was inferred from what a host's
	// validator happened to accept. Nothing versioned those behaviours, so an
	// inference could not be pinned and could not fail a test when it drifted.
	// That is the defect this closes.
	//
	// EXACT PINNING, NOT NEGOTIATION (operator ruling). A module and a host
	// either agree on this identity or they do not interoperate. There are no
	// ranges, no highest-common selection, no downgrade and no fallback —
	// those are negotiation, and negotiation is deferred until several
	// behavioural versions genuinely coexist. A single-valued "negotiation" is
	// the shape that let ProtocolVersions look like a choice while being a
	// membership test against one constant.
	ContractVersion = "xibodev.module/v2"

	// Operation is the VERB, exactly one of these two — never a capability ID.
	// The capability travels in the request and is correlated by request_id.
	// Pinned to the host's modproto.OperationDescribe / OperationInvoke.
	OpDescribe = "describe"
	OpInvoke   = "invoke"
)

Identity. ModuleID was chosen by the operator; Protocol is pending host ack.

View Source
const (
	CapToolsList       = "creative.tools.list"
	CapToolsDescribe   = "creative.tools.describe"
	CapToolsEstimate   = "creative.tools.estimate"
	CapToolsRun        = "creative.tools.run"
	CapOutputReview    = "creative.output.review"
	CapArtifactInspect = "creative.artifact.inspect"
	CapJobsStatus      = "creative.jobs.status"
)

Capability IDs. The host addresses these; it never names a Facet tool.

View Source
const ArtifactKindOutput = "output"

ArtifactKindOutput is the kind every Facet artifact carries.

Facet emits ONE kind: a file a tool wrote. The host validates an artifact's kind against the producing capability's artifact_schemas, so this must appear there or every artifact-producing run is refused.

View Source
const DefaultMaxOutputBytes = 256 * 1024

DefaultMaxOutputBytes bounds a module response. A large payload belongs in an artifact pointer, not inlined in result: inlining floods the agent's context and defeats the artifact model. The host enforces its own limit from the request; this is Facet's own default when none is supplied.

View Source
const ManifestSchemaID = "xibodev.facet.artifact/v1"

ManifestSchemaID identifies Facet's render/artifact manifest contract.

View Source
const ProviderVaries = "varies"

ProviderVaries is declared by a capability that dispatches any tool, so the provider is not knowable until one is selected. It is a DECLARATION value, and an invocation replaces it with the provider actually used.

View Source
const SeedManifestFile = "manifest.json"

SeedManifestFile is the entry file inside a seed bundle directory.

View Source
const SeedSchemaID = "xibodev.midden.seed/v1"

SeedSchemaID is the Midden content-seed contract Facet consumes.

Facet consumes a seed by path plus digest only. It never invokes Midden, never imports a Midden client, and never reads Midden's database. The host agent decides when recovery is needed and hands the resulting seed to Facet.

Variables

This section is empty.

Functions

func AwaitJobs

func AwaitJobs(timeout time.Duration) (finished bool)

AwaitJobs blocks until every started job reaches a terminal state, or the deadline passes.

A one-shot CLI writes its envelope and exits, which would abandon async work mid-render: the handle would be returned, the goroutine killed, and the render silently discarded. A handle that loses the work it represents is worse than blocking, so the process waits for its own jobs before leaving.

It reports whether everything finished, so a caller can say plainly that work was abandoned rather than implying success.

func HasRunningJobs

func HasRunningJobs() bool

HasRunningJobs reports whether this process started work that has not finished.

func LoadSeed

func LoadSeed(ref *SeedRef) (*Seed, *SeedResolution, error)

LoadSeed reads a seed and verifies its digest.

A seed is a DIRECTORY bundle whose entry file is manifest.json:

<seed-root>/manifest.json     entry, the shape Seed reads
<seed-root>/brief.md          prose for the host agent
<seed-root>/evidence.jsonl    the evidence set
<seed-root>/provenance.json   source identities and revisions
<seed-root>/attachments/      referenced files

Path may point either at the seed root or directly at manifest.json; a directory is resolved to its manifest. Facet reads only manifest.json and the attachments it names — brief.md, evidence.jsonl, and provenance.json are for the host agent, not for the mechanical toolbox.

Digest verification is mandatory when the caller supplies one: an unverified seed is refused rather than consumed, so a Facet artifact can never claim provenance from bytes it did not actually read. Digests are sha256 hex, lowercase, over raw bytes; an optional "sha256:" prefix is accepted.

The digest supplied in a SeedRef covers the file that is read. Evidence identity is carried separately by the manifest's own evidence_digest, so regenerating brief.md prose does not invalidate an evidence reference.

func RequireBinary

func RequireBinary(bins map[string]string, name string) (string, error)

RequireBinary reports the absolute path for a declared binary, or a dependency_missing failure naming it.

This fails CLOSED: an ungranted binary is an error, never an invitation to look it up.

func ValidDigest

func ValidDigest(s string) bool

ValidDigest reports whether s is "sha256:" + exactly 64 lowercase hex digits. Bare hex, uppercase hex, a truncated value, and a wrong algorithm are all rejected rather than repaired.

func ValidateBinaries

func ValidateBinaries(bins map[string]string) error

ValidateBinaries checks a host-supplied binary map before any of it is used.

The rules exist because a module running under the host inherits NO environment: this map is the module's entire execution authority, so a bad entry is an authority problem rather than a convenience problem.

  • A name Facet never declared is REFUSED rather than ignored. Silently dropping it would let a host believe it granted something Facet honours.
  • A relative path is REFUSED. Relative means "search", and a search can resolve to a binary the host never authorized.
  • An empty value is REFUSED. "Could not resolve" must arrive as absence, not as an empty string, so the two stay distinguishable — the same null-versus-zero rule the cost model rests on.

Facet never falls back to PATH, a home directory, or its own lookup when a binary is missing. A fallback would quietly restore the ambient authority the host removed and make its enforcement decorative.

func ValidateEnvelopeAgainst

func ValidateEnvelopeAgainst(raw []byte, exp Expectation) error

ValidateEnvelopeAgainst applies the structural rules and then the contextual ones that need the declared contract.

Both extra checks catch under-reporting rather than malformation, which is why they cannot live in the structural pass: the bytes are well-formed and only a comparison reveals the lie.

func ValidateEnvelopeBytes

func ValidateEnvelopeBytes(raw []byte) error

ValidateEnvelopeBytes enforces the module-boundary rules on raw stdout bytes.

It mirrors the host's modproto.ValidateEnvelope. Facet cannot import that package (Go forbids importing another module's internal/, and compa is unpublished), so this is a second independent implementation of one contract. That is exactly why both lanes cross-parse each other's fixtures: a rule that drifts here breaks a test rather than production.

Facet validates its OWN output with this, not just the host's fixtures. A validator only used on someone else's data proves nothing about your own.

func ValidateEnvelopeSized

func ValidateEnvelopeSized(raw []byte, maxBytes int) error

ValidateEnvelopeSized applies the structural rules plus an output-size bound.

Size is deliberately separate from ValidateEnvelopeBytes: an oversized payload is not malformed, so it cannot be caught by shape checks. It is a read-time bound, which is why the host enforces it via max_output_bytes rather than in the validator.

Types

type Artifact

type Artifact struct {
	ID        string `json:"id,omitempty"`
	Kind      string `json:"kind"`
	Path      string `json:"path"`
	Root      string `json:"root,omitempty"`
	MediaType string `json:"media_type,omitempty"`
	Bytes     int64  `json:"bytes,omitempty"`
	Digest    string `json:"digest,omitempty"`
	Title     string `json:"title,omitempty"`
	// Presentation names the host rendering primitive for this artefact.
	//
	// It exists for what a media type cannot express: a scene plan is
	// application/json and also a timeline, and only the module knows that.
	// The host honours a known value and ignores an unknown one, so a module
	// names a primitive the host owns rather than inventing one.
	Presentation string `json:"presentation,omitempty"`
}

Artifact is a pointer to a file a run produced, never its contents.

Path is relative to Root, a logical root the host named in the request. An absolute path is a protocol violation: it makes root confinement uncheckable. Digest is "sha256:" + lowercase hex.

type ArtifactManifest

type ArtifactManifest struct {
	Schema     string          `json:"schema"`
	Module     string          `json:"module"`
	Capability string          `json:"capability"`
	RequestID  string          `json:"request_id"`
	Source     *SeedResolution `json:"source,omitempty"`
	Artifacts  []Artifact      `json:"artifacts"`
	Execution  Execution       `json:"execution"`
	Review     ReviewState     `json:"review"`
}

ArtifactManifest is what Facet emits after producing creative output.

It retains the seed reference verbatim so a finished asset can be traced back to the exact bytes that produced it.

func NewManifest

func NewManifest(capability string, env Envelope, source *SeedResolution) ArtifactManifest

NewManifest builds a manifest from a completed module envelope.

type Capability

type Capability struct {
	ID              string   `json:"id"`
	Title           string   `json:"title"`
	Summary         string   `json:"summary"`
	RequestSchema   string   `json:"request_schema"`
	ResultSchema    string   `json:"result_schema"`
	ArtifactSchemas []string `json:"artifact_schemas"`
	Effects         Effects  `json:"effects"`
	Skills          []string `json:"skills"`
	LongRunning     bool     `json:"long_running"`
	PollCapability  string   `json:"poll_capability,omitempty"`

	// Projects names the v2 Operations this capability can reach.
	//
	// WITHOUT IT THE NO-WEAKENING CHECK COMPARES NOTHING. compa's
	// CheckNoWeakening iterates a capability's projections; an absent list
	// means zero iterations, so a capability declaring effects weaker than the
	// Operations it dispatches passes identically to an honest one. Measured:
	// their host performed 0 comparisons against a descriptor with 35
	// Operations and reported zero findings — a check with no input and a
	// check that passed produce the same observable.
	//
	// EMPTY IS LEGAL and means something different from absent: a registry
	// read transforms no product material and projects nothing, which the
	// frozen shape states explicitly. Five of Facet's seven capabilities are
	// exactly that.
	Projects []string `json:"projects"`
}

Capability describes one addressable operation and its honest effects.

RequestSchema and ResultSchema are IDs INTO the descriptor's schema maps. A reference that resolves to nothing means the host cannot validate anything the capability sends or returns, so every ID here must be a present key.

type Consent struct {
	PaidGenerationApproved bool   `json:"paid_generation_approved"`
	ApprovedBy             string `json:"approved_by"`
	Note                   string `json:"note,omitempty"`
}

Consent records that a human approved a paid or billable operation. The module never fabricates this; the host agent must obtain it from a person.

type Descriptor

type Descriptor struct {
	Module           string   `json:"module"`
	Name             string   `json:"name"`
	Version          string   `json:"version"`
	ProtocolVersions []string `json:"protocol_versions"`
	// ContractVersion is the single behavioural contract this module
	// implements. Singular by design: a list would read as negotiable, and
	// ProtocolVersions already proved that a list validated by a membership
	// test looks like a choice while being a constant.
	ContractVersion string         `json:"contract_version"`
	Capabilities    []Capability   `json:"capabilities"`
	RequestSchemas  map[string]any `json:"request_schemas"`
	ResultSchemas   map[string]any `json:"result_schemas"`
	ArtifactSchemas map[string]any `json:"artifact_schemas"`
	AgentOverlays   []Overlay      `json:"agent_overlays"`
	Skills          []Skill        `json:"skills"`
	Permissions     Permissions    `json:"permissions"`
	Requirements    []Requirement  `json:"requirements"`

	// --- xibodev.module/v2 semantic layer ---
	//
	// These ride in the SAME document as the v1 fields above. §10 requires a
	// v1 host to ignore fields it does not recognise, and compa pinned
	// that direction by test (37835f6) precisely because "works because the
	// decoder is tolerant" and "works because the contract requires it"
	// produce the same observable.
	//
	// A v1 host therefore sees exactly the v1 descriptor it always saw; a v2
	// host reads contract_version, passes the gate, and finds the semantics.
	//
	// omitempty is deliberate: a module that has not built its v2 payload must
	// publish NOTHING rather than an empty list, because an empty `operations`
	// is an affirmative claim of having none.
	Operations    []toolbox.V2Operation             `json:"operations,omitempty"`
	ArtifactKinds map[string]toolbox.V2ArtifactKind `json:"artifact_kinds,omitempty"`
}

Descriptor declares exactly the twelve agreed fields, in order.

The schema maps carry JSON Schema documents keyed by schema ID, so a capability can reference a schema by ID rather than inlining it.

type Effects

type Effects struct {
	Local          bool   `json:"local"`
	Network        bool   `json:"network"`
	ExternalWrites bool   `json:"external_writes"`
	Provider       string `json:"provider"`
	CostKnown      bool   `json:"cost_known"`

	// --- v2 ---
	//
	// The host decodes a CAPABILITY's effects into the SAME type as an
	// Operation's, so no-weakening is a field comparison rather than a
	// translation. A field absent here decodes to FALSE, which is an
	// affirmative claim the module never made.
	//
	// Measured: without may_charge, creative.tools.run declared false while
	// reaching eight chargeable Operations. That is a real weakening and the
	// host is right to refuse it -- it was invisible only because the
	// capability declared no projections, so nothing was ever compared.
	//
	// Additive to v1: these names did not exist in the v1 effects object, so a
	// v1 host ignores them exactly as it ignores `operations`.
	MayCharge     bool `json:"may_charge"`
	Deterministic bool `json:"deterministic"`
}

Effects declares what invoking a capability does, BEFORE it runs. The host uses it to decide whether approval is required, so it must be honest even when the answer is inconvenient.

CostKnown distinguishes "free" from "unknown". When false the host treats the capability as unpriced and requires approval regardless of any number present.

type Envelope

type Envelope struct {
	Protocol  string    `json:"protocol"`
	Module    string    `json:"module"`
	Operation string    `json:"operation"`
	RequestID string    `json:"request_id"`
	OK        bool      `json:"ok"`
	Result    any       `json:"result,omitempty"`
	Error     *Error    `json:"error,omitempty"`
	Warnings  []string  `json:"warnings"`
	Execution Execution `json:"execution"`
}

Envelope is the only thing written to stdout. Field order matches the cross-repo protocol exactly: protocol, module, operation, request_id, ok, result|error, warnings, execution.

func Describe

func Describe(version string) Envelope

Describe builds the descriptor from live toolbox state.

func EnforceOutputBudget

func EnforceOutputBudget(env Envelope, maxBytes int) Envelope

EnforceOutputBudget replaces a response too large for the host with an error that fits.

The host truncates at max_output_bytes and treats a truncated envelope as unusable, because a partial JSON document cannot be trusted even when it happens to parse. A module that ignores the budget therefore turns a successful call into a corrupt response, and the host cannot tell whether the work succeeded.

Refusing is strictly better: the caller learns the response was too large, which is actionable, rather than receiving JSON that stops mid-object. The replacement is deliberately small — no result, bounded details — so it cannot itself exceed the budget.

A zero or absent budget means the caller set none, and the envelope passes through untouched.

func Estimate

func Estimate(capability string, raw []byte) Envelope

func InputError

func InputError(op, path string, err error) Envelope

InputError reports an unreadable --input argument.

func Invoke

func Invoke(capability string, raw []byte) Envelope

Invoke executes one capability through the existing toolbox.

It is a strict adapter. It does not implement tool behavior, does not retry, and does not soften an error into a success. Its only additions are protocol framing, consent enforcement, and honest execution facts.

creative.tools.estimate is dispatched here like any other capability. It remains a separate, auditable operation that never bills and never generates; it is simply not a separate verb.

func JobStatus

func JobStatus(raw []byte) Envelope

JobStatus implements creative.jobs.status.

It is deterministic, local, free and side-effect free: polling never runs work, never bills, and never writes.

func Usage

func Usage(message string) Envelope

Usage reports a malformed command as a protocol envelope rather than as bare text, so the host never has to parse stderr to learn that a call was wrong.

type Error

type Error struct {
	Code      string         `json:"code"`
	Message   string         `json:"message"`
	Retryable bool           `json:"retryable"`
	Details   map[string]any `json:"details"`
}

Error carries a stable code, a human message, a retryable flag, and bounded details. Codes pass through from the toolbox unchanged where one exists.

type Execution

type Execution struct {
	Local          bool       `json:"local"`
	Network        bool       `json:"network"`
	ExternalWrites bool       `json:"external_writes"`
	Provider       string     `json:"provider"`
	EstimatedCost  *float64   `json:"estimated_cost"`
	ActualCost     *float64   `json:"actual_cost"`
	Artifacts      []Artifact `json:"artifacts"`
	// ContractVersion names the behavioural contract that GOVERNED this run,
	// omitted when the caller named none.
	//
	// Without it, "the host pinned v2" and "the host said nothing and was
	// served under v1" are both ok:true and indistinguishable to a reader of
	// the envelope. That does not lie today, because no v2 guarantee yet
	// differs from v1 here — it becomes a lie the moment one does, and a
	// consumer cannot then tell a v2 guarantee from a v1 coincidence.
	//
	// compa found the same two-states-one-value collapse in their own
	// gate (absent and wrong both reported not-ok) and split it into three
	// outcomes. This is the mirror on the module side: they report which
	// contract they may RELY on, this reports which contract a run was SERVED
	// under.
	//
	// Additive and omitted when absent, per §10: a v1 caller sees exactly what
	// it saw before.
	ContractVersion string `json:"contract_version,omitempty"`
}

Execution declares honest effect and cost facts.

EstimatedCost and ActualCost are nullable on purpose: null means the cost is genuinely unknown, 0 means it is genuinely free. Never collapse the two.

type Expectation

type Expectation struct {
	// RequestID the host generated. A module must echo it verbatim.
	RequestID string
	// CostKnown is the capability's declared Effects.CostKnown. When false, a
	// numeric cost in the response is under-reporting, not information.
	CostKnown bool
	// CostKnownSet distinguishes "declared false" from "not supplied", so an
	// absent expectation never silently asserts one.
	CostKnownSet bool
}

Expectation carries what the DECLARED contract said, so a response can be checked against it. Two of the protocol's violations are not structurally detectable from a single envelope — they need this comparison.

type Grants

type Grants struct {
	Network       []string `json:"network"`
	Credentials   []string `json:"credentials"`
	PaidProviders []string `json:"paid_providers"`
	Publish       bool     `json:"publish"`
	Subprocess    []string `json:"subprocess"`
}

Grants is what the host authorized for one invocation.

type Job

type Job struct {
	JobID      string     `json:"job_id"`
	State      JobState   `json:"state"`
	Capability string     `json:"capability,omitempty"`
	Tool       string     `json:"tool,omitempty"`
	Percent    *float64   `json:"percent"`
	Detail     string     `json:"detail,omitempty"`
	StartedAt  time.Time  `json:"started_at"`
	FinishedAt *time.Time `json:"finished_at,omitempty"`

	// Result and Execution are populated once the job reaches a terminal
	// state, so the terminal poll can carry the authoritative outcome.
	Result    any        `json:"result,omitempty"`
	Error     *Error     `json:"error,omitempty"`
	Execution *Execution `json:"execution,omitempty"`
}

Job is the status of one long-running invocation.

Progress is reported ONLY when it is genuinely known. A renderer that does not report progress yields a null percent rather than a fabricated one: an invented number is worse than no number, because a stalled render showing a confident 40% reads as healthy.

type JobState

type JobState string

JobState is the lifecycle of a long-running invocation.

const (
	JobRunning   JobState = "running"
	JobSucceeded JobState = "succeeded"
	JobFailed    JobState = "failed"
)

type JobStatusRequest

type JobStatusRequest struct {
	RequestID string `json:"request_id"`
	JobID     string `json:"job_id"`

	// Protocol fields the host sends on EVERY invocation. They are modelled
	// rather than acted on here, because decoding is strict: an unmodelled
	// field fails the decode, and the failure path minted a fresh request_id —
	// so the host's id was never echoed and it could not correlate the
	// response.
	//
	// Verified: {"request_id":"req_host_abc","job_id":...} echoed correctly,
	// but the same request carrying protocol and capability did not. This is
	// the identical mistake already fixed on the main request path; polling
	// has its own decoder and never got the fix.
	Protocol       string `json:"protocol,omitempty"`
	Capability     string `json:"capability,omitempty"`
	DeadlineMS     int    `json:"deadline_ms,omitempty"`
	MaxOutputBytes int    `json:"max_output_bytes,omitempty"`
}

JobStatusRequest is the body of a poll.

type Overlay

type Overlay struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Path   string `json:"path"`
	Digest string `json:"digest"`
	Tokens int    `json:"tokens"`
}

Overlay points at agent guidance the host may load. The host decides.

Digest is "sha256:<lowercase-hex>" over the file contents. The host refuses to fold content into agent context without verifiable provenance, so an empty digest is a hard rejection rather than a missing nicety.

type Permissions

type Permissions struct {
	FilesystemRead  []string `json:"filesystem_read"`
	FilesystemWrite []string `json:"filesystem_write"`
	Network         []string `json:"network"`
	Credentials     []string `json:"credentials"`
	PaidProviders   []string `json:"paid_providers"`
	Publish         bool     `json:"publish"`
	Subprocess      []string `json:"subprocess"`
}

Permissions states what the module needs to do its job.

Every field except Publish is a LIST of named grants, not a boolean. The host intersects a declaration with policy and grants per invocation, which a boolean cannot express: "network: true" says nothing about which hosts, and "subprocess: true" nothing about which binaries.

type Request

type Request struct {
	RequestID string          `json:"request_id"`
	Tool      string          `json:"tool"`
	Input     json.RawMessage `json:"input"`
	Consent   *Consent        `json:"consent,omitempty"`
	Seed      *SeedRef        `json:"seed,omitempty"`
	// Binaries maps a declared subprocess name to the ABSOLUTE path the host
	// resolved for it. A module inherits no environment, so this is the only
	// way a binary becomes reachable.
	//
	// An absolute path is an identity; a PATH is a search, and a search can
	// resolve to something the host never authorized. A binary the host could
	// not resolve must be ABSENT rather than empty, so "not supplied" and
	// "supplied as nothing" stay distinguishable.
	Binaries map[string]string `json:"binaries,omitempty"`
	// Async is accepted for compatibility with earlier descriptors. Facet is
	// hosted per invocation, so process-local jobs cannot be polled by the next
	// call and execution remains synchronous.
	Async bool `json:"async,omitempty"`
	// Roots maps a logical root name declared in permissions.filesystem_* to a
	// canonicalized absolute path the host supplies per invocation. A module
	// resolves nothing itself: `facet_bundle` is how the Remotion composer is
	// found, and resolving it from the working directory made the renderer
	// depend on where the process happened to be launched.
	Roots map[string]Root `json:"roots,omitempty"`

	// The host sets these on every invocation. They are modelled so a strict
	// decoder accepts a real host request: rejecting a field the host always
	// sends would refuse every genuine call, and tolerating unknown fields
	// would silently discard a misspelled `consent`. Both are unacceptable, so
	// the protocol's own fields are named explicitly.
	Protocol string `json:"protocol,omitempty"`
	// ContractVersion is the BEHAVIOURAL contract the host expects, echoed
	// from what it read in the descriptor. Exact match or refusal — see
	// module.ContractVersion.
	ContractVersion string `json:"contract_version,omitempty"`
	Capability      string `json:"capability,omitempty"`
	// Grants are the permissions actually authorized for THIS invocation. A
	// module must assume it has nothing that is not listed.
	Grants *Grants `json:"grants,omitempty"`
	// DeadlineMS is the host's wall-clock budget; it enforces it by killing the
	// process tree regardless, so this is a courtesy rather than a promise.
	DeadlineMS int `json:"deadline_ms,omitempty"`
	// MaxOutputBytes is the stdout ceiling. Facet returns artifact pointers
	// rather than inline payloads, so it is recorded rather than acted on.
	MaxOutputBytes int `json:"max_output_bytes,omitempty"`
}

Request is the input body for estimate and invoke.

Tool names the underlying Facet tool. Input is the tool-specific request body and is passed through to the toolbox unmodified: the module does not rewrite, enrich, or reinterpret a creative request.

type Requirement

type Requirement struct {
	Name     string `json:"name"`
	Kind     string `json:"kind"`
	Required bool   `json:"required"`
	For      string `json:"for"`
}

Requirement is an external dependency the module cannot install itself.

type ReviewState

type ReviewState struct {
	TechnicalQA   string `json:"technical_qa"`
	HumanApproved bool   `json:"human_approved"`
	Note          string `json:"note"`
}

ReviewState separates technical QA from creative acceptance.

A passing technical check is never editorial approval. HumanApproved stays false until a person says otherwise; no agent may set it.

type Root

type Root struct {
	Path string `json:"path"`
	// Mode is "ro" or "rw"; facet_bundle is always read-only.
	Mode string `json:"mode"`
}

Root is one host-supplied filesystem grant.

type Seed

type Seed struct {
	Schema               string   `json:"schema"`
	Goal                 string   `json:"goal,omitempty"`
	Title                string   `json:"title,omitempty"`
	Summary              string   `json:"summary,omitempty"`
	KeyPoints            []string `json:"key_points,omitempty"`
	Attachments          []string `json:"attachments,omitempty"`
	SuggestedOutputTypes []string `json:"suggested_output_types,omitempty"`
	EvidenceDigest       string   `json:"evidence_digest,omitempty"`
}

Seed is Facet's read model of a Midden content seed's manifest.json.

It is deliberately permissive: Midden owns this contract, so Facet validates only what it must consume. Every field is optional except the schema — a seed carrying only a goal is valid.

SuggestedOutputTypes is advisory. Facet owns the vocabulary (explainer, screen-demo, social-clip, talking-head, cinematic, localization) and an unrecognised value degrades to "no suggestion" with a warning rather than failing the seed, so a new Facet output type is never a breaking change in Midden's schema.

func (*Seed) OutputTypes

func (s *Seed) OutputTypes() (accepted []string, warnings []string)

OutputTypes returns the recognised suggestions and warnings for the rest. An unknown suggestion is never fatal.

type SeedAttachment

type SeedAttachment struct {
	Path    string `json:"path"`
	Digest  string `json:"digest,omitempty"`
	Bytes   int    `json:"bytes,omitempty"`
	Missing bool   `json:"missing,omitempty"`
}

SeedAttachment is one file the seed named, resolved against the bundle.

Path stays RELATIVE to the seed bundle for the same reason SeedResolution's does: the staged location is the host's and differs between machines. Missing is set rather than failing the load — a seed naming a file that did not survive staging is worth reporting, not worth refusing, because the evidence and manifest may still be exactly what the caller wanted.

type SeedRef

type SeedRef struct {
	Schema string `json:"schema"`
	Path   string `json:"path"`
	Digest string `json:"digest"`
}

SeedRef references a Midden content seed by path and digest only. Facet never invokes Midden and never reads its database.

type SeedResolution

type SeedResolution struct {
	Schema         string `json:"schema"`
	Path           string `json:"path"`
	DigestExpected string `json:"digest_expected"`
	DigestActual   string `json:"digest_actual"`
	EvidenceDigest string `json:"evidence_digest,omitempty"`
	Verified       bool   `json:"verified"`
	// Attachments the seed named and Facet actually resolved, each with the
	// digest of the bytes on disk.
	//
	// The manifest names attachments relative to the bundle, and the field was
	// parsed and never read: Midden's video_brief — the document written
	// specifically to brief this render — was dropped, and nothing downstream
	// could tell it had existed. An artifact claiming provenance from a seed
	// should say which of the seed's documents it actually had.
	Attachments []SeedAttachment `json:"attachments,omitempty"`
}

SeedResolution is the verified outcome of loading a seed.

EvidenceDigest is the identity of the evidence set, distinct from the digest of the manifest bytes: it stays stable when brief.md prose is regenerated.

type Skill

type Skill struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Summary string `json:"summary"`
	Path    string `json:"path"`
	Digest  string `json:"digest"`
	Tokens  int    `json:"tokens"`
}

Skill is progressively selected; the host must not load them all every turn.

Jump to

Keyboard shortcuts

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