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
- func AwaitJobs(timeout time.Duration) (finished bool)
- func HasRunningJobs() bool
- func LoadSeed(ref *SeedRef) (*Seed, *SeedResolution, error)
- func RequireBinary(bins map[string]string, name string) (string, error)
- func ValidDigest(s string) bool
- func ValidateBinaries(bins map[string]string) error
- func ValidateEnvelopeAgainst(raw []byte, exp Expectation) error
- func ValidateEnvelopeBytes(raw []byte) error
- func ValidateEnvelopeSized(raw []byte, maxBytes int) error
- type Artifact
- type ArtifactManifest
- type Capability
- type Consent
- type Descriptor
- type Effects
- type Envelope
- func Describe(version string) Envelope
- func EnforceOutputBudget(env Envelope, maxBytes int) Envelope
- func Estimate(capability string, raw []byte) Envelope
- func InputError(op, path string, err error) Envelope
- func Invoke(capability string, raw []byte) Envelope
- func JobStatus(raw []byte) Envelope
- func Usage(message string) Envelope
- type Error
- type Execution
- type Expectation
- type Grants
- type Job
- type JobState
- type JobStatusRequest
- type Overlay
- type Permissions
- type Request
- type Requirement
- type ReviewState
- type Root
- type Seed
- type SeedAttachment
- type SeedRef
- type SeedResolution
- type Skill
Constants ¶
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.
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.
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.
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.
const ManifestSchemaID = "xibodev.facet.artifact/v1"
ManifestSchemaID identifies Facet's render/artifact manifest contract.
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.
const SeedManifestFile = "manifest.json"
SeedManifestFile is the entry file inside a seed bundle directory.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 facet-studio 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 ¶
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. facet-studio'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 ¶
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 facet-studio 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 EnforceOutputBudget ¶
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 InputError ¶
InputError reports an unreadable --input argument.
func Invoke ¶
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.
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.
//
// facet-studio 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 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 asks a long-running capability to return a job handle immediately
// rather than blocking until the work completes. Opt-in: existing
// consumers, including the human-facing CLI, expect a finished result.
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 ¶
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.