Skip to contentScreenplay.Company

SSL 0.1 language contract

1. Status and normative words

This is the proposed 0.1 contract. MUST, MUST NOT, SHOULD and MAY describe conformance to this proposal. They do not claim current product implementation. Normative core syntax is JSON as defined by RFC 8259, subject to the restrictions below. File suffix: .story.json. Protocol and query documents use JSON with the same value rules. No executable JavaScript, imports, macros, template interpolation or implicit network fetch is part of the language.

There are four distinct outcomes: syntactically readable, structurally valid, semantically understood for a selected capability, and authorised for a requested effect. The first never implies the last. Unknown vocabulary does not make a structurally valid document invalid.

2. Document grammar

A document is a JSON object with these members. ? means optional, not a syntax character. Additional top-level members MUST be retained by readers but have no core effect.

language:     "ssl/0.1"
work:         ID
snapshot:     string                         opaque owner-assigned identity
coverage:     {mode: "complete"|"partial", description?: string}
namespaces:   object<string, absolute-URI>    namespace prefix → immutable identity
heads:        object<ID, positive-integer>   selected revisions in this snapshot
records:      Record[]
requires?:    string[]                       optional semantic capabilities
extensions?: object<string, Value>

Record = {
  id: ID,
  rev: positive-integer,
  kind: Term,
  definition?: ExactRef,                    definition describing this revision
  fields: object<string, Value>,
  extensions?: object<string, Value>
}

work is a security/routing scope, not an access credential. snapshot is immutable within that owner/work. A snapshot identifies the underlying immutable owner state/head manifest. Partial, paged and differently authorised projections of the same snapshot can contain different content. A separately named projection identity, when supplied in extensions, binds its own content digest; a host MUST reject conflicting content for that projection identity. A partial snapshot may omit records, heads, sources and definitions; it cannot make negative claims about omitted material. Completeness refers to accessible selected state under an explicit export contract, not the existence of every historic revision or external asset. Receivers cannot authenticate completeness solely from the field.

An ID is a nonempty ASCII absolute URI up to 512 bytes. Examples use urn:ssl:demo:person:a. Identity is scoped by (authority, work, id); an exported ID retains origin, and a fork records its new owner/work and lineage. A copy into another work cannot silently acquire authority over the original. Display names are never identifiers. Case-sensitive exact string comparison applies to IDs; no implicit URL normalisation, percent decoding or redirects.

rev and integer numbers range from 0 through 9,007,199,254,740,991; revisions start at 1. Duplicate (id, rev) and duplicate JSON object keys are invalid, even if their values agree. A new revision is immutable once accepted by the owning host. A package may include historical revisions. heads chooses the visible revision for each included live identity; it is not inferred by maximum revision. Every selected head in a complete package resolves to a supplied record; in a partial package an unresolved head is unavailable.

Every record MUST have a kind and fields, but fields may be empty. The minimal document can have no records. A fragment may consist only of kind: "story.fragment" and fields: {"text": "A thought"}. No character, conflict, plot structure, calendar, genre or production profile is mandatory.

3. Lexical and value rules

Input MUST be valid UTF-8 without a BOM. Escaped JSON Unicode MUST decode to scalar values: lone UTF-16 surrogates are invalid. Do not normalise Unicode, case, punctuation or line endings inside strings. A decoded CRLF pair remains two scalar values. JSON whitespace outside strings is presentation; preserve original file bytes separately if byte-for-byte source reproduction is required. All noninteger quantities use tagged values; NaN, infinity, exponents and floating-point tokens are outside core numeric syntax. Negative integers are allowed down to -9,007,199,254,740,991. Integer lexical spelling is canonical (0, never -0, no leading zeros).

Value is a string, integer, boolean, null, an ordered array of Values, an object with nonreserved member names, or one of the following exact tagged objects. Core tags use $; user fields beginning with $ are forbidden outside $literal.

FormMeaning
{"$ref": ID, "rev": 3}Exact reference to an immutable revision
{"$ref": ID}Reference resolved only through the supplied snapshot's heads
{"$unknown": "unspecified"}No assertion has been supplied
{"$unknown": "unresolved", "reason": "..."}A question or conflict remains
{"$unknown": "unavailable"}Data cannot be obtained in this read
{"$unknown": "withheld"}Deliberately hidden in the relevant story perspective; not a permissions bypass
{"$decimal": "-12.5", "unit": "kg"}Exact decimal and optional opaque unit
{"$rational": [24000, 1001], "unit": "frames/second"}Exact rational; denominator positive, reduced by gcd
{"$literal": JSON}Uninterpreted JSON including reserved-looking keys, still subject to UTF-8/scalar/numeric lexical limits; queries expose it only via explicit unwrap

Tagged objects have exactly their documented keys. $unknown.reason is optional for every unknown kind. $decimal.unit and $rational.unit are optional; absence means dimensionless. Decimal spelling matches -?(0|[1-9][0-9]*)(\.[0-9]*[1-9])?, excluding -0; precision is arbitrary within declared implementation limits. Unknown $ tags are preserved as opaque values and produce unsupported only when a requested computation needs their meaning. Malformed recognised tags are a structural error for that value; lossless opening still retains the original document with diagnostics.

null means explicitly no value. A missing field means no statement. An empty array means a known empty collection within its declared scope. false means false. None is interchangeable with unknown, redacted or absent. A query API reports missing and access-filtered values without revealing which private records exist.

An array is ordered. An object is not. A record's physical position in a file has no story meaning. Reordering records cannot reorder the film.

4. Terms and definitions

A Term matches [a-z][a-z0-9_-]*\.[A-Za-z][A-Za-z0-9_.-]*. Its first component is a namespace prefix. The namespace map determines meaning by URI; different prefixes bound to the same URI are aliases. A document MUST bind every used namespace. Local invention uses a work-owned URI, for example "local": "urn:ssl:demo:vocabulary". Defining that map entry is enough to use a new kind. A declaration record is optional.

Core draft namespace identities are urn:ssl:0.1:core, urn:ssl:0.1:story, urn:ssl:0.1:film, urn:ssl:0.1:prep, urn:ssl:0.1:shoot, urn:ssl:0.1:post, and urn:ssl:0.1:delivery. Namespaces do not fetch documents. Field names may be ordinary local names or qualified terms. Changing a prefix binding changes meaning and MUST be treated as an explicit semantic migration, never a formatting change.

A definition is an ordinary core.definition record with fields:

term: Term                         required
meaning: string                    required; natural-language explanation
maturity: "observed"|"draft"|"stable"  default "observed"
properties?: object<fieldName, {
  type?: "any"|"text"|"integer"|"boolean"|"decimal"|"rational"|
         "ref"|"object"|"array",
  targetKinds?: Term[],
  required?: boolean,              default false
  nullable?: boolean,              default true
  items?: same-property-shape,
  enum?: Value[],
  meaning?: string,
  unit?: string
}>
parents?: ExactRef[]
examples?: Ref[]
supersedes?: ExactRef
constraints?: Ref[]

All definition expectations are advisory by default. Unlisted properties are ALWAYS allowed. Required properties mean "needed to satisfy this profile", not "needed to save or open this idea". Definitions cannot erase fields, supply implicit defaults, coerce values or authorise changes. Definitions describe a record; they do not execute code. Parent definitions compose descriptions and checks; contradictory expectations produce a definition finding. Parent cycles are reported and traversal stops at visited revisions.

A record may pin a definition revision. An unpinned or missing definition leaves its meaning informal and never resolves automatically to the latest definition. A tool can suggest an available definition and explicitly bind it in a subsequent revision. An incompatible meaning gets a new term; an additive refinement gets a new definition revision. Existing uses keep their old meaning. Maturity is a recommendation from the author, not proof of standardisation.

Agent workflow: observe a repeated pattern → create a local term immediately → record examples and a meaning → propose optional properties → run advisory checks on existing uses → offer explicit migrations where useful → share/promote the definition through an approved registry process. Discovery never waits for registry approval.

5. Exact expression and anchors

story.expression fields include format, body, optional language, originalAsset, derivedFrom and attribution. format is a string such as text/plain, text/markdown or text/fountain; unrecognised formats retain their body and show a safe literal fallback. body is the exact decoded string. Renderers MAY style it, but an editor MUST be able to recover it unchanged. A derived rewrite is a new expression revision or identity, never an invisible rendering step.

An anchor is a Value object:

{target: ExactRef, field: [string|integer, ...],
 selector: {kind: "whole"}
        | {kind: "text", start: integer, end: integer, quote: string}
        | {kind: "time", start: Rational, duration: Rational}
        | {kind: "region", x: Decimal, y: Decimal, width: Decimal, height: Decimal}}

field selects a stored value, never a rendered DOM offset. Text intervals are half-open Unicode scalar offsets with 0 <= start < end <= length; the exact substring MUST equal quote. Client UTF-16/grapheme positions must be converted. Region coordinates use a normalised image rectangle in [0,1], positive width/height, wholly within bounds. Time uses tagged Rationals with unit seconds, nonnegative start and positive duration. Regions use dimensionless tagged Decimals. Whole anchors have no offsets. A new target revision never silently retargets an old anchor; a suggested reanchor is a separate operation with evidence.

Screenplay body content retains Fountain syntax exactly. A Fountain parser is a versioned projection, whose dialect and implementation version are recorded with its output. Parsed elements are derived anchors, not a second editable canonical copy. Edits initiated on a rendered element must become an exact body patch against the pinned expression. Original source bytes may be retained in an asset for byte fidelity; source formatting outside JSON strings is not semantically significant.

6. Identity, history and references

Exact references never follow renames, aliases or merges. Snapshot references resolve via heads at that snapshot and remain stable for the duration of a read. Resolved query output includes exact references. A head reference to a tombstone resolves to that tombstone record; consumers must inspect kind and cannot treat it as the deleted entity. A malformed recognised ref is invalid-reference, distinct from a well-formed unavailable target. A missing reference in a partial read returns unavailable. In a complete snapshot it produces a dangling-reference finding; it does not destroy the surrounding material or block basic rendering. A requested effect requiring that target cannot proceed until resolved.

A core.tombstone revision has previous: ExactRef and reason: string; it becomes the head of the deleted identity. Restore creates a new revision carrying the restored data and a provenance link. Referencing an old revision remains valid while retention permits. Aliases and entity merges are explicit core.alias records with from, to, reason; applications MAY offer navigation but MUST NOT redirect historical facts or rewrite expression text. Merging two people who share a name requires an explicit identity decision.

Revisions belong to records; a snapshot binds a whole read. Host commit sequence, story chronology, film order and media time are separate dimensions. SSL does not make the number on a scene into its identity.

Historical retention is subject to the owning account's deletion policy. Erasure can remove original content and encryption keys; receipts may retain a minimal non-content audit entry where allowed. The system must mark unavailable evidence and invalidate dependent views, not promise permanent recovery of erased personal material.

7. Claims, perspective and uncertainty

A story.claim identifies a proposition in prose and optionally a computable subject/predicate/object triple. It carries separate fields for mode, stance, context, perspective, validDuring and evidence (see vocabulary). These separate dimensions prevent an accepted character belief from becoming an objective world fact.

A writer can choose that a character believes a lie. That choice validates the intended portrayal of that belief; it does not validate the lie. An agent may read a trait from a draft; that is evidence for a reading about that draft, not permission to add settled character canon. Research assertions about the real world do not automatically become facts of a fictional world.

Contradictions are material to explore. Formal validators can identify conflicting explicit propositions only within matching predicate definitions, context, perspective, time and scope. Two prose passages that appear to contradict each other require an attributed reading. Open-world semantics apply: absence of a claim cannot prove its negation.

World chronology uses events and explicit temporal constraints. A flashback occurrence references an event without moving that event in world time. Nested dreams, alternate histories, testimony and unreliable narration use context records. Development alternatives are contexts too, but their frame explicitly differs from fictional reality. No global boolean true is sufficient to encode these distinctions.

8. Extensibility and graceful handling

Readers MUST retain unknown fields, kinds and tagged values. Generic views show a title or kind, literal expression and accessible properties. Unknown material remains available to generic queries. Unknown content is never dropped on save or export without a specific loss report acknowledged by the caller. Binary assets can remain unresolved while metadata is read.

An explicit selected profile may require understood semantics for a particular output or operation. If that requirement is unmet, that operation returns unsupported; unrelated work remains usable. requires advertises those capabilities, but is not an instruction to install or execute anything. A reader MAY open the whole document in preservation mode while an edit or delivery is unsupported.

For an invalid JSON file, a reader SHOULD retain raw bytes, report offsets and offer repair as a proposed new file. It MUST NOT pretend malformed text parsed successfully. In a streamed incomplete document, do not commit until a complete envelope is received; preserve recoverable local draft bytes. Flexibility is achieved through open semantics and reversible repair, not silent reinterpretation of broken syntax.

9. Resource limits, security and compatibility

Conforming implementations publish size/depth/record/traversal limits and return limit-exceeded without silent truncation. Minimum portable exchange support: 10 MiB document, 1 MiB decoded string, 10,000 records, nesting depth 32 and 512-byte IDs. Larger material uses separately addressed assets or paged snapshots. These are minimum supported capacities, not story limits; an implementation may support more. Untrusted strings and assets are inert; HTML/Markdown renders must sanitise active content. URL-looking text cannot trigger network access.

Language version negotiation is exact major/minor for 0.x. A future unknown version can be preserved but cannot claim semantic conformance. Namespace definitions evolve independently through explicit revisions. Unknown optional fields remain round-trippable across minor changes. A capability response advertises language versions, profiles, query operators, function versions and limits. A missing capability restricts only dependent operations.

Canonical document-value hashing uses RFC 8785 canonical JSON on core-valid data, UTF-8 encoded, with SHA-256 and sha256: plus lowercase hex. Restricting plain numbers to safe integers avoids numeric spelling/rounding disagreements; exact decimal/rational objects stay explicit. This is a document-value digest, not semantic-equivalence hashing: prefix aliases or equivalent definitions can produce different digests. Hashes do not confer authority. Original-byte asset hashes and canonical document hashes are different and MUST be labelled. Signatures and identity credentials belong to the host envelope, not user-authored fields. An optional document digest excludes only its own explicitly defined envelope field; the 0.1 file grammar has no self-digest field.