Documentation
¶
Overview ¶
Package scenario is the parser, validator and run.Run mapper for Chatwright's self-contained scenario document format (https://chatwright.dev/formats/scenario-document/v1) — the format spec/features/chatwright/scenario-authoring/portable-scenario-documents/ self-contained-scenario-documents/README.md in the standard repository (chatwright/chatwright) defines. A document declares a bot endpoint, an AI goal with its tasks and budgets, the cast, the declared fidelity and an independent journal verification — everything arena.GreetbotScenario() carries compiled into Go, expressed instead as one committed, language-neutral JSON file that both chatwright runtimes execute to the same verdict with no Go or TypeScript written.
Three stages, kept deliberately separate:
- Parse — pure, in-memory, no I/O of any kind. It never starts a bot, reads an environment variable, resolves a credential, opens a file beyond the document bytes it was given, or makes a network call (the spec's "parsing is inert, and refusal is explicit"). Parse returns a Report naming every structural problem by JSON pointer and rule id, and rejects (returns a non-nil error) the moment any Report issue is an error-severity one — never echoing the offending value.
- Resolve — a ScenarioProvider turns a reference (a file path today; a store id or a Cloud reference tomorrow) into document bytes, then Parse validates them. The runner (Build, run.Run) never learns where a Document came from.
- Build — the separate, explicit step that actually starts anything: it resolves declared secrets (env/credential lookup), boots an example bot or wires an HTTP bot transport, and produces a ready-to-execute run.Run plus the roster and doc-local chat-id mapping a caller needs to assemble a run bundle afterwards. A document that failed validation never reaches Build.
Index ¶
- Constants
- Variables
- func Parse(data []byte) (*Document, Report, error)
- type Bot
- type Budgets
- type BuildOptions
- type Built
- type CaseDecl
- type Cast
- type Ceiling
- type Chat
- type Condition
- type Document
- type EnvOnlyResolver
- type ExampleBotFactory
- type ExampleBotRegistry
- type ExampleBotSession
- type Fidelity
- type FileScenarioProvider
- type Goal
- type InputDecl
- type Issue
- type JournalExpectation
- type LoopConfig
- type Part
- type PlatformIdentity
- type Provider
- type RejectionError
- type Report
- type ResolvedFidelity
- type ScenarioProvider
- type Secret
- type SecretRef
- type SecretResolver
- type SecretSource
- type Severity
- type Task
- type Verify
- type VerifyResult
- type VerifySpec
Constants ¶
const ( EndpointProfilePlatformEmulated = "platform-emulated" EndpointProfileHeadlessEngine = "headless-engine" )
Endpoint profiles — see Fidelity.EndpointProfile.
const ( EnvironmentDev = "dev" EnvironmentTest = "test" EnvironmentProduction = "production" EnvironmentUnknown = "unknown" )
Environments — see Fidelity.Environment.
const ( DataSensitivitySynthetic = "synthetic" DataSensitivityRealSubject = "real-subject" )
Data sensitivities — see Fidelity.DataSensitivity.
const ( TransportHTTP = "http" TransportIframe = "iframe" )
Bot transports — see Bot.Transport.
const ( DeliveryWebhook = "webhook" DeliveryPolling = "polling" )
Bot deliveries — see Bot.Delivery.
const ( CastTypeAIAgent = "ai-agent" CastTypeHuman = "human" )
Cast types — see Cast.Type. Reused verbatim from the sdk wire vocabulary.
const ( ProviderKindModel = "model" ProviderKindCassette = "cassette" )
Provider kinds — see Provider.Kind.
const ( CassetteModeReplay = "replay" CassetteModeRecord = "record" )
Cassette provider modes — see Provider.Mode.
const ( PartKindAIGoal = "ai-goal" PartKindDeterministic = "deterministic" )
Part kinds — see Part.Kind.
const ( FailurePolicyAbort = "abort" FailurePolicyCoverageGap = "coverage-gap" )
Failure policies — see Part.FailurePolicy. Reused verbatim from run.FailurePolicy's own wire vocabulary.
const ( FieldKind = "kind" FieldDirection = "direction" FieldText = "text" FieldEdited = "edited" )
Condition fields — see Condition.Field.
const ( OpExact = "exact" OpContains = "contains" OpRegex = "regex" )
Condition operators — see Condition.Op.
const FormatV1 = "https://chatwright.dev/formats/scenario-document/v1"
FormatV1 is the scenario-document format identifier this package parses. Parse rejects any other value for Document.Format — see the format's own "unsupported format ... is named" validation rule.
const SchemaVersion1 = 1
SchemaVersion1 is the only schemaVersion this package accepts. A document declaring any other value is rejected naming it explicitly, never silently downgraded or upgraded.
const UnmetPrefix = "journal evidence incomplete: "
UnmetPrefix is the fixed runtime prefix an unmet VerifyResult's Detail always starts with — the README's "the verdict maps to VerifyResult ... the fixed runtime prefix journal evidence incomplete: " rule. It is pinned as an exported constant, not reconstructed at each call site, so this package and its tests can never drift from arena's own verifyGreetbotJournal, which produces the identical string for the identical reason.
Variables ¶
var DefaultSupportedExampleBots = map[string]bool{ "greetbot": true, }
DefaultSupportedExampleBots is the exampleBot ids this runtime (runtime-go) ships and Validate accepts a `requires: ["exampleBot:<id>"]` declaration for — see ExampleBotRegistry, which must register exactly this set (an id present here with no matching registry entry, or vice versa, is a packaging bug, not a document problem).
ErrCredentialStoreUnavailable is returned by EnvOnlyResolver.ResolveCredential for every credential name: this runtime has no credential store yet.
Functions ¶
func Parse ¶
Parse validates data as a scenario-document/v1 document. It is pure: no file is opened beyond decoding data itself, no environment variable is read, no credential store is consulted, no network call is made, and no bot — example or otherwise — is started. See the package doc comment's "Parse" stage.
The returned Report carries every Issue found, error and warning alike. The returned error is non-nil exactly when the Report has at least one SeverityError Issue, in which case the returned *Document is nil: no part of a rejected document is resolved, and the error never echoes an offending value (see Issue's own doc comment). A non-nil Document may still carry Report warnings (e.g. noRunCeiling) worth surfacing to a caller even though the document is perfectly valid.
Types ¶
type Bot ¶
type Bot struct {
ID string `json:"id"`
Name string `json:"name"`
// Transport is "http" or "iframe" — required with URL, forbidden with
// ExampleBot (the runtime wires its own example however it likes, and
// declares no transport in the document).
Transport string `json:"transport,omitempty"`
// Delivery is "webhook" or "polling" — http transport only.
Delivery string `json:"delivery,omitempty"`
// URL is the bot's webhook URL (http+webhook) or iframe src. Required
// for iframe and for http+webhook; forbidden for http+polling.
URL string `json:"url,omitempty"`
// Headers optionally carries request headers for webhook delivery; each
// value MUST be a secret reference — see SecretRef.
Headers map[string]SecretRef `json:"headers,omitempty"`
// ExampleBot names a bot the runtime itself ships (e.g. "greetbot").
// Not an extension point — see ExampleBotRegistry.
ExampleBot string `json:"exampleBot,omitempty"`
}
Bot declares how the runtime reaches the bot under test and, at the same time, its roster identity — see the README's "The bot endpoint" section. Exactly one of URL or ExampleBot is set (see Validate).
type Budgets ¶
type Budgets struct {
MaxSteps int `json:"maxSteps,omitempty"`
MaxDurationSeconds int `json:"maxDurationSeconds,omitempty"`
MaxCost *float64 `json:"maxCost,omitempty"`
}
Budgets is goal.Budgets, authored with integer seconds instead of a nanosecond duration — see the README's "Durations are integer seconds" rule. At least one of MaxSteps, MaxDurationSeconds or MaxCost must be a positive value (see Validate's ai-goal-part-without-budgets rule); goal.Budgets' own "zero means unlimited" convention is deliberately not inherited here.
type BuildOptions ¶
type BuildOptions struct {
// Now supplies the built run's clock — see run.Environment.Now. Nil
// uses time.Now.
Now func() time.Time
// Secrets resolves a declared secret's value — see SecretResolver. Nil
// uses EnvOnlyResolver{}; a document declaring no secrets never
// consults it either way.
Secrets SecretResolver
// ExampleBots is the registry Build boots a Bot.ExampleBot from. Nil
// uses DefaultExampleBots().
ExampleBots ExampleBotRegistry
// BindAddr, when set, binds the emulator to this exact address
// (telegram.NewEmulatorAt) instead of a random local port. Needed for
// "http"+"polling" delivery: an already-running external bot must be
// pre-configured with the emulator's address before Build even runs —
// see the README's "the operator points the bot's Bot API base URL at
// the emulator". Ignored for an exampleBot document (the README: "there
// is no authorable URL").
BindAddr string
// HTTPClient is the client the emulator uses to push webhook updates,
// for "http"+"webhook" delivery. Nil uses http.DefaultClient. Ignored
// otherwise.
HTTPClient *http.Client
}
BuildOptions configures Build — every seam it needs beyond doc itself. The zero value is a usable default for an exampleBot document that declares no secrets (exactly the worked-example greetbot document).
type Built ¶
type Built struct {
Run run.Run
Actors []sdk.Actor
ChatIDs map[string]int64 // doc-local chat id -> platform chat id
VerifySpec *VerifySpec
Fidelity ResolvedFidelity
Close func()
}
Built is everything Build produced from a validated Document: a ready-to-execute run.Run, the roster a caller needs to assemble a run bundle afterwards, the doc-local-to-platform chat-id mapping, this document's compiled VerifySpec (nil when it declares none), its resolved fidelity, and a Close tearing down whatever Build itself started (an example bot's own server; never anything an operator started out of band, e.g. a real bot process polling for "http"+"polling" delivery).
func Build ¶
Build is the package's separate, explicit resolution step (see the package doc comment): it resolves every declared secret doc's bot endpoint and cast providers need, boots an example bot or wires an HTTP bot transport, loads any referenced cassette file, and assembles a run.Run ready for Run.Execute. doc must already have passed Parse's validation — Build does not re-validate structure, only resolves it.
Build is where every side effect this format's parsing stage explicitly forbids is, deliberately, allowed to happen: reading an env var or consulting a credential store (via opts.Secrets), starting an example bot, reading a cassette file from disk. A caller that only wants to validate a document, never run it, should call Parse (or a ScenarioProvider's Load) and stop there.
type CaseDecl ¶
type CaseDecl struct {
Name string `json:"name"`
Inputs map[string]json.RawMessage `json:"inputs,omitempty"`
}
CaseDecl is a document-declared named input binding — see Document.Cases' own doc comment on why this shape is provisional.
type Cast ¶
type Cast struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
PlatformIdentity PlatformIdentity `json:"platformIdentity"`
// Provider is set for an "ai-agent" cast member; nil for a "human" or
// "scripted"/"replay" one (v1's document format has no way to author a
// scripted actor — see the README's "scripted is deliberately not
// expressible").
Provider *Provider `json:"provider,omitempty"`
}
Cast is one non-bot participant — see the README's "The cast and its providers" section.
type Ceiling ¶
type Ceiling struct {
MaxSteps int `json:"maxSteps,omitempty"`
MaxCost *float64 `json:"maxCost,omitempty"`
MaxDurationSeconds int `json:"maxDurationSeconds,omitempty"`
}
Ceiling is run.RunCeiling, authored with integer seconds — see Budgets' own doc comment on why. Absence is reported by Validate as noRunCeiling, never silently accepted as "no limit intended".
type Chat ¶
Chat is one declared chat — exactly one in v1 (see Validate). ID is document-local: a Part's Chat member, and Verify.Chat, reference it, so no part hard-codes a platform chat number.
type Condition ¶
type Condition struct {
Field string `json:"field"`
Op string `json:"op"`
Value json.RawMessage `json:"value"`
Negate bool `json:"negate,omitempty"`
}
Condition is a {field, op, value} triple with an optional negate — the same shape the exploration-to-regression idea fixed for action matchers.
type Document ¶
type Document struct {
Format string `json:"format"`
SchemaVersion int `json:"schemaVersion"`
ID string `json:"id"`
Version string `json:"version"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Requires []string `json:"requires,omitempty"`
Fidelity Fidelity `json:"fidelity"`
Platform string `json:"platform"`
Chats []Chat `json:"chats"`
Bot Bot `json:"bot"`
Cast []Cast `json:"cast"`
Secrets []Secret `json:"secrets,omitempty"`
// Inputs and Cases are declared, but their shape beyond "a named input"
// and "a named binding of inputs" is not pinned down by the format's
// worked example or any acceptance criterion — see InputDecl and
// CaseDecl's own doc comments. Neither is consulted by Build: a
// document executed directly (no manifest) uses no case and no input
// binding.
Inputs []InputDecl `json:"inputs,omitempty"`
Cases []CaseDecl `json:"cases,omitempty"`
Parts []Part `json:"parts"`
Ceiling *Ceiling `json:"ceiling,omitempty"`
Verify *Verify `json:"verify,omitempty"`
Verifies []string `json:"verifies,omitempty"`
// SourceURL is this Document's canonical source identity — e.g.
// "file:///abs/path/to/doc.json" for a FileScenarioProvider. Empty for
// a Document built directly from Parse without going through a
// ScenarioProvider (e.g. in a unit test).
SourceURL string `json:"-"`
// BaseDir is the directory document-relative references (a cassette
// path today) resolve against — the same directory SourceURL names,
// for a file-backed Document.
BaseDir string `json:"-"`
}
Document is the parsed, validated shape of one scenario-document/v1 file — the Go embodiment of the format the standard repository's self-contained-scenario-documents README defines. Field order mirrors that README's "Shape" table.
SourceURL and BaseDir are not part of the wire shape (no json tag): they are provenance a ScenarioProvider stamps on the Document after Parse succeeds, so Build can resolve document-relative references (e.g. a cassette path) without Parse itself ever touching the filesystem.
type EnvOnlyResolver ¶
type EnvOnlyResolver struct {
// Lookup overrides how an env var is read — defaults to os.LookupEnv.
// Tests substitute a fake here so a resolver test never actually reads
// the process environment.
Lookup func(varName string) (string, bool)
}
EnvOnlyResolver is the default SecretResolver: {"env": "VAR"} secrets resolve via os.Getenv (missing/empty is an error, never a silent empty string standing in for a credential), and {"credential": "name"} secrets always fail with ErrCredentialStoreUnavailable — no credential store exists yet in this runtime.
func (EnvOnlyResolver) ResolveCredential ¶
func (EnvOnlyResolver) ResolveCredential(credentialName string) (string, error)
ResolveCredential implements SecretResolver.
func (EnvOnlyResolver) ResolveEnv ¶
func (r EnvOnlyResolver) ResolveEnv(varName string) (string, error)
ResolveEnv implements SecretResolver.
type ExampleBotFactory ¶
type ExampleBotFactory func() (*ExampleBotSession, error)
ExampleBotFactory boots one fresh exampleBot session. Every call gets fresh state — no two Build calls, and no Build call and a warm-up, ever observe another's conversation history — matching arena's own Scenario.Setup contract.
type ExampleBotRegistry ¶
type ExampleBotRegistry map[string]ExampleBotFactory
ExampleBotRegistry maps an exampleBot id (Bot.ExampleBot) to the factory that boots it. Not an extension point (see Bot.ExampleBot's own doc comment in document.go): this registry's key set is exactly DefaultSupportedExampleBots, the set validate checks a document's `requires: ["exampleBot:<id>"]` declaration against.
func DefaultExampleBots ¶
func DefaultExampleBots() ExampleBotRegistry
DefaultExampleBots returns the registry this runtime (runtime-go) ships: "greetbot", booting chatwright.dev/runtime/examples/greetbot over a fresh telegram.Emulator — exactly arena.GreetbotScenario()'s own setupGreetbot.
func (ExampleBotRegistry) Boot ¶
func (r ExampleBotRegistry) Boot(id string) (*ExampleBotSession, error)
Boot resolves id against r, returning a clear error naming id when unregistered. validate's requires check should already have refused an undeclared exampleBot id before Build ever runs — reaching an unknown id here means the running binary's registry disagrees with DefaultSupportedExampleBots, a packaging bug rather than a document problem.
type ExampleBotSession ¶
ExampleBotSession is what booting one exampleBot produced: the emulator it is wired to, and a Close tearing both down. Mirrors arena.ScenarioSession's Setup/Close shape.
type Fidelity ¶
type Fidelity struct {
EndpointProfile string `json:"endpointProfile"`
Environment string `json:"environment,omitempty"`
DataSensitivity string `json:"dataSensitivity,omitempty"`
RedactionPolicy string `json:"redactionPolicy,omitempty"`
}
Fidelity declares a document's endpoint profile, environment and data sensitivity — see the README's "Declared fidelity and environment" section. RedactionPolicy is this package's minimal, provisional answer to that section's open question ("where does a document's redaction policy live"): a non-empty policy name, required exactly when DataSensitivity is "real-subject" (see Validate) and otherwise unused. It is not a redaction policy body — the sensitive-data-redaction idea owns that; this field only records that a document declares one applies.
type FileScenarioProvider ¶
type FileScenarioProvider struct {
// Root is prepended to a relative ref. Empty resolves a relative ref
// against the process's current working directory (os.ReadFile's own
// behaviour).
Root string
}
FileScenarioProvider is the seam's only implementation today: ref is a local filesystem path.
type Goal ¶
type Goal struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Tasks []Task `json:"tasks"`
Constraints []string `json:"constraints,omitempty"`
Budgets Budgets `json:"budgets"`
}
Goal is goal.Goal, authored — see the README's "Parts, budgets and ceilings" section.
type InputDecl ¶
type InputDecl struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Default json.RawMessage `json:"default,omitempty"`
}
InputDecl is a document-declared input — see Document.Inputs' own doc comment on why this shape is provisional.
type Issue ¶
type Issue struct {
// Code is a short, stable, machine-readable rule id, e.g.
// "inline-secret", "unsupported-capability", "ai-goal-budgets-required".
Code string `json:"code"`
// Pointer is an RFC 6901 JSON pointer into the document (e.g.
// "/cast/0/provider/apiKey"), or "" for a document-level issue with no
// single location.
Pointer string `json:"pointer"`
Message string `json:"message"`
Severity Severity `json:"severity"`
}
Issue is one machine-readable validation finding: a rule id, the JSON pointer into the document it applies to, a human-readable message that never echoes the offending value, and a Severity. See the format's own "a rejection names the JSON pointer and the rule, and never echoes the value" requirement — Message is written by this package's own code at each call site, never built by interpolating a document-supplied value, so that requirement holds by construction, not by scrubbing after the fact.
type JournalExpectation ¶
type JournalExpectation struct {
ID string `json:"id"`
UnmetDetail string `json:"unmetDetail"`
All []Condition `json:"all"`
}
JournalExpectation is one ordered journal expectation — see Verify.
type LoopConfig ¶
type LoopConfig struct {
HistoryWindow int `json:"historyWindow,omitempty"`
NonProgressLimit int `json:"nonProgressLimit,omitempty"`
ActWaitTimeoutSeconds int `json:"actWaitTimeoutSeconds,omitempty"`
RetainObservations *bool `json:"retainObservations,omitempty"`
OvershootProbe *bool `json:"overshootProbe,omitempty"`
}
LoopConfig is actor.Config's authorable tunables — see the README's "loop" paragraph for each field's default, identical to actor.Config's own zero-value defaults. RetainObservations and OvershootProbe are pointers so "omitted" (nil, defaults to true) is distinguishable from an explicit false — actor.Config itself inverts both into DisableObservationRetention/DisableOvershootProbe (see toActorConfig in build.go).
type Part ¶
type Part struct {
ID string `json:"id"`
Kind string `json:"kind"`
Title string `json:"title,omitempty"`
Chat string `json:"chat"`
ActorID string `json:"actorId"`
FailurePolicy string `json:"failurePolicy,omitempty"`
// Goal is set for kind:"ai-goal".
Goal *Goal `json:"goal,omitempty"`
// Loop optionally overrides the actor.Config tunables — see LoopConfig.
Loop *LoopConfig `json:"loop,omitempty"`
// Steps is reserved for kind:"deterministic" (action matchers — not
// built; see the README's "Reserved for action matchers" section). Its
// mere presence is never inspected beyond "this document declares a
// deterministic part", which Validate always rejects in v1.
Steps json.RawMessage `json:"steps,omitempty"`
}
Part is one ordered passage of the run — see the README's "Parts, budgets and ceilings" section. Maps onto run.Part/run.AIGoalPartInput member for member.
type PlatformIdentity ¶
type PlatformIdentity struct {
UserID int64 `json:"userId"`
FirstName string `json:"firstName,omitempty"`
}
PlatformIdentity is one cast member's platform-native identity, serving both the roster entry and the user the loop acts as.
type Provider ¶
type Provider struct {
Kind string `json:"kind"`
// Model-kind fields.
ProviderID string `json:"providerId,omitempty"`
Model string `json:"model,omitempty"`
BaseURL string `json:"baseUrl,omitempty"`
APIKey *SecretRef `json:"apiKey,omitempty"`
// Cassette-kind fields.
Mode string `json:"mode,omitempty"`
Cassette string `json:"cassette,omitempty"`
Wraps *Provider `json:"wraps,omitempty"` // mode:"record" only, a model provider
}
Provider is an ai-agent cast member's provider declaration — a discriminated union on Kind, encoded as one flat JSON object because Go's encoding/json has no native sum-type support. Exactly the fields belonging to Kind are meaningful; see Validate for which combinations are legal.
type RejectionError ¶
type RejectionError struct {
Report Report
}
RejectionError is the error Parse returns for a Report with at least one SeverityError Issue: every error-severity Issue's pointer and code, joined — never the Report's Warnings, and never any document-supplied value (see Issue's own doc comment on why that holds by construction).
func (*RejectionError) Error ¶
func (e *RejectionError) Error() string
Error renders every error-severity Issue as "<pointer>: <code>: <message>", one per line.
type Report ¶
type Report struct {
Issues []Issue `json:"issues"`
}
Report is every Issue Parse (and the validation it runs) found, in the order they were discovered.
type ResolvedFidelity ¶
type ResolvedFidelity struct {
EndpointProfile string
// Environment is doc.Fidelity.Environment when declared; otherwise the
// configured host map (localHostEnvironments, the only one this
// runtime ships) when the bot's own host is unambiguous; otherwise
// EnvironmentUnknown — never guessed beyond that.
Environment string
// DataSensitivity is doc.Fidelity.DataSensitivity when declared;
// otherwise DataSensitivityRealSubject when Environment is
// EnvironmentProduction; otherwise DataSensitivitySynthetic.
DataSensitivity string
}
ResolvedFidelity is doc.Fidelity with every default the README's "Declared fidelity and environment" section describes actually applied — what a caller (Build, a report) should show as "what this run's fidelity actually is", as opposed to Document.Fidelity, which shows only what the author explicitly wrote.
func ResolveFidelity ¶
func ResolveFidelity(doc *Document) ResolvedFidelity
ResolveFidelity applies the README's declared-then-configured-then- heuristic resolution order for environment, and the environment-defaults- sensitivity rule, to doc — a pure function (net/url parsing only; no network access) kept separate from Validate because it needs doc.Bot.URL, which is meaningless to resolve for an exampleBot document (there is no host to inspect) and because "what was declared" and "what is effective" are different questions a caller may want to show both answers to.
type ScenarioProvider ¶
type ScenarioProvider interface {
// Load resolves ref, then parses and validates the result exactly as
// Parse does — see Parse's own doc comment for what "resolves" does
// and does not do (reading ref's bytes is the only I/O Load performs;
// nothing about the document's own content — a secret, an example bot,
// a cassette file — is touched). A Report with at least one
// SeverityError Issue means Load returns a non-nil error and a nil
// Document.
Load(ctx context.Context, ref string) (*Document, Report, error)
}
ScenarioProvider resolves a reference — a file path today; a store id or a Cloud reference tomorrow — into a loaded, validated Document. It is the only seam through which a Document ever enters this package's runner side (Build): the runner never learns where a Document came from, so a future store-backed or Cloud-backed provider is a drop-in replacement for FileScenarioProvider with no change to Build, run.Run or the CLI.
type Secret ¶
type Secret struct {
Name string `json:"name"`
From SecretSource `json:"from"`
}
Secret declares a name and where the runner resolves it from — never a value. See SecretSource.
type SecretRef ¶
type SecretRef struct {
Name string `json:"secretRef"`
}
SecretRef is the only legal shape for a secret-bearing field: a JSON object carrying exactly one member, "secretRef", naming a Secret declared in Document.Secrets. Its UnmarshalJSON (see secrets.go) is deliberately strict — a JSON string, or an object with any sibling member, fails to decode — so a literal secret in one of these fields is a parse-time rejection, not something Validate discovers later.
type SecretResolver ¶
type SecretResolver interface {
ResolveEnv(varName string) (string, error)
ResolveCredential(credentialName string) (string, error)
}
SecretResolver resolves one declared Secret's actual value at Build time — the separate, explicit step Parse never reaches (see the package doc comment). Two methods, not one, so a caller can support one source without pretending to support the other: ResolveCredential returning ErrCredentialStoreUnavailable is a normal, expected outcome for a runner with no configured credential store, distinct from "this credential name does not exist in a store that does exist".
type SecretSource ¶
type SecretSource struct {
Env string `json:"env,omitempty"`
Credential string `json:"credential,omitempty"`
}
SecretSource names exactly one of an environment variable or a named credential-store entry a Secret resolves from.
type Severity ¶
type Severity string
Severity classifies one Issue — see Issue.
const ( // SeverityError means the document is rejected: Parse returns a nil // Document and a non-nil error the moment any Issue in the Report is // SeverityError. SeverityError Severity = "error" // SeverityWarning means the document is accepted but the condition is // reported rather than silently accepted — e.g. noRunCeiling, // noIndependentVerification, a declared-but-unused secret. SeverityWarning Severity = "warning" )
Severities. See Severity.
type Task ¶
type Task struct {
ID string `json:"id"`
Title string `json:"title,omitempty"`
DependsOn []string `json:"dependsOn,omitempty"`
SuccessCriteria string `json:"successCriteria"`
Milestones []string `json:"milestones,omitempty"`
}
Task is goal.Task, authored.
type Verify ¶
type Verify struct {
Chat string `json:"chat"`
MetDetail string `json:"metDetail"`
Journal []JournalExpectation `json:"journal"`
}
Verify is the declarative form of arena.Scenario.Verify — see the README's "Independent verification of what happened" section, and verify.go for its compiled evaluation.
type VerifyResult ¶
VerifyResult is one Evaluate call's deterministic verdict — the wire equivalent of arena.VerifyResult, kept as this package's own type so scenario never imports arena (a lower-level package importing a higher-level one).
type VerifySpec ¶
type VerifySpec struct {
// contains filtered or unexported fields
}
VerifySpec is a Document's Verify block, compiled once (regex patterns parsed, condition values decoded) so Evaluate can run against a journal with no further parsing.
func CompileVerify ¶
func CompileVerify(doc *Document) (*VerifySpec, error)
CompileVerify compiles doc.Verify into a VerifySpec ready to Evaluate, or (nil, nil) when doc declares no Verify block at all — see the README's "A document with no verify block is not reported as verified" rule; the caller (Build) is what turns a nil VerifySpec into the judged-not- verified outcome, this function only reports absence.
CompileVerify assumes doc has already passed validate (see Parse): Condition.Field/Op vocabulary, the regex subset restriction and Value's shape are not re-checked here — a malformed Verify block reaching this function unvalidated is a caller bug, reported as a plain Go error rather than a Report Issue.
func (*VerifySpec) ChatDocID ¶
func (s *VerifySpec) ChatDocID() string
ChatDocID is the doc-local chat id (Document.Chats[*].ID) this VerifySpec evaluates against — the caller resolves it to a platform chat id via the same Document.Chats mapping Build uses, and reads that chat's journal.
func (*VerifySpec) Evaluate ¶
func (s *VerifySpec) Evaluate(entries []platform.JournalEntry) VerifyResult
Evaluate re-derives a Verify block's verdict from entries — one chat's complete platform.JournalEntry history — independent of any actor's own task-done claim (principle 3, "evidence over claims"). Expectations are matched in declared order: expectation N matches the earliest entry strictly after the entry expectation N-1 matched (or, if N-1 never matched, after whatever the last successfully matched expectation did) — see the README's "Expectations are ordered" rule. All expectations matched yields Verified:true with Detail set to the spec's own MetDetail; otherwise Verified:false with Detail set to UnmetPrefix followed by every unmatched expectation's UnmetDetail, in declared order, joined with "; ".