Skip to content

Add schema evidence, resumable AI interpreter sessions, and Screenplay generation - #1

Merged
einari merged 24 commits into
mainfrom
feature/schema-evidence
Jul 23, 2026
Merged

Add schema evidence, resumable AI interpreter sessions, and Screenplay generation#1
einari merged 24 commits into
mainfrom
feature/schema-evidence

Conversation

@einari

@einari einari commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Added

  • Database schema captured as evidence: the Extractor reads the full schema (column types, sizes, nullability, primary keys, foreign keys, unique constraints) from SQL Server and PostgreSQL sources and emits it as a database-schema observation
  • Schema evidence drives interpretation: property types from column types, required/max-length validation rules, and unique constraints on the extracted model
  • Cratis.Prologue.Interpretation package — a resumable interpreter session engine with checkpoint questions: the language model can ask the user multiple-choice questions (always with a free-text option) when uncertain, one at a time, and sessions park, persist, and resume statelessly at any time
  • Structured LLM refinement producing intentful names, a derived system name, and descriptions for every module, feature, slice, and command
  • Cratis.Prologue.Screenplay package — generates a Screenplay .play document from an extraction result using the Screenplay syntax tree and printer
  • Interpreter service mode: an Orleans-backed session per Prologue behind an HTTP API, with MongoDB-persisted state, a configurable grace period for unanswered questions, and clean self-exit so orchestrators can restart-to-resume
  • Interpreter batch mode now also writes the generated .play file next to the extraction result
  • Extraction contract carries the system name, descriptions, validation rules, and constraints

🤖 Generated with Claude Code

einari and others added 24 commits July 23, 2026 10:37
Introduce DatabaseSchemaObserved with the database-schema discriminator —
the structural evidence of a watched database captured when capture
starts: tables with columns (data type, max length, nullability,
identity, default expression), primary keys, foreign-key relationships,
and unique constraints. Register it in the JSON polymorphic payload
hierarchy and in the MongoDB class maps alongside the other payloads.
Metadata only, never data values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The correlator only let evidence payloads settle standalone, so an
observation that is neither a command nor evidence — such as the
database schema captured at startup — would sit in the pending list
forever. Settle everything that is not a command on its own instead,
while keeping schema observations out of a command's evidence: the
schema describes the system, not what a command did.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Schema readers materialize a database's catalog into flat, engine-neutral
rows (columns, primary keys, foreign keys, unique constraints) so the
row-to-contract assembly is a pure function that can be specified with
fake row data — the same split CdcChangeDecoder uses for CDC rows.
SchemaRows.OnlyTables narrows the rows to a configured table allowlist
before they become an observation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each SQL Server and PostgreSQL source now reads its database's
structure — columns with sizes and nullability, primary keys, foreign
keys, and unique constraints — once when it starts watching, and
publishes it as a database-schema observation through the same channel
as the change observations. SQL Server honors the configured table
allowlist with the same semantics as the CDC preparer; Postgres covers
every user table, matching its FOR ALL TABLES publication. Failing to
read the schema is logged and never stops the source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The heuristics, refiners, LLM clients, prompts, and their specs move from
the Interpreter exe into a new Cratis.Prologue.Interpretation package so
the CLI and Studio can host the same engine. The exe stays a thin batch
job over the new project and builds the same container image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g data

The extracted model can now carry what the schema evidence and the
refinement pass will derive: a system name on the result, descriptions
on modules, features, and slices, required/max-length facts on
properties, validation rules on commands, and uniqueness constraints
on slices. Construction sites pass empty values until the heuristics
start filling them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DatabaseSchemaObserved captures now shape the extracted model: column
types decide property types over name conventions, non-nullable columns
mark properties required and yield Required rules on the command (except
primary-key and identity columns, which the database generates), bounded
string columns yield max lengths and MaxLength rules, and single-column
unique constraints become uniqueness constraints on the slice producing
the covering event. Schema joins transactions on database name plus
schema-qualified table name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The session engine's vocabulary: a status enum covering every stage a
host can observe, a formalized question/answer object model with
multiple-choice support, and a fully serializable session state whose
transcript carries the language-model conversation — the unit any host
persists at a checkpoint and resumes later by replaying it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One JSON exchange per round replaces the names-only rename map: the
language model returns a system name, renames, descriptions (keyed by
the renamed module/feature/slice paths), and the questions it needs
answered. Defensive parsing takes the first balanced object and yields
nothing on any malformed response so callers fall back to the unrefined
model. A deterministic system-name deriver covers the no-LLM path, the
renamer now applies full refinements (renames, then descriptions, then
the system name), and a chat-client factory seam makes the language
model substitutable in specs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The session runs heuristics to a structural model, then converses with
the language model in structured rounds — parking at AwaitingAnswers
when it asked questions and continuing when every pending question is
answered, up to three rounds before finalizing with whatever the model
last returned. All conversation state lives in the serializable session
state and the transcript is replayed on resume, so a CLI loops in place
while Studio can persist and come back any time. Language-model
failures, timeouts, and unusable responses always fall back to the
deterministic heuristic model — the session completes regardless. The
runner drives a session to its end for synchronous hosts, surfacing
pending questions through callbacks one at a time. Zero question rounds
makes a session non-interactive: the prompt tells the model not to ask
and the session finalizes instead of parking.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The exe now creates a session with zero question rounds — the prompt
tells the language model not to ask and the session finalizes with its
best judgment, so a batch run can never stall on questions — and drives
it with the runner, logging every status transition as a console line.
The superseded names-only refinement flow (extractor, refiner, rename
prompt) is removed; the session engine with the structured refinement
exchange is the one path. The extraction result now carries the system
name, descriptions, validations, and constraints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion results

Introduce a packable Source/Screenplay project that bridges the extracted
event model to the Cratis Screenplay language:

- IScreenplayGenerator/ScreenplayGenerator maps the module → feature →
  slice tree onto Cratis.Screenplay's ApplicationSyntax and prints it
  with IScreenplayPrinter — descriptions carry through; commands declare
  camelCase properties, validation rules (not empty/max/min/matches) and
  produces mappings; events, unique-property constraints, an
  all-instances query per read model and the projection with from blocks
  and identity set-mappings complete the slices.
- ScreenplayTypeMapping turns the inferred C# property types into the
  built-in Screenplay type names (Guid→Uuid, DateTimeOffset→DateTime,
  DateOnly→Date, int/long→Int, ...).
- ScreenplayFileName derives <PascalCaseSystemName>.play with a
  CapturedSystem fallback.

References the Cratis.Screenplay 1.3.0 package from the local feed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A representative extraction result — descriptions, schema-derived
validations, a unique constraint, a read model with its projection —
asserts the generated .play text declares every construct and
round-trips through ScreenplayCompiler.Compile with zero error and zero
warning diagnostics. File-name specs cover PascalCase pass-through,
sanitizing separators and invalid characters, and the CapturedSystem
fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After the session completes, the interpreter exe reports
GeneratingScreenplay and writes the generated Screenplay document. The
destination comes from --play-output / PROLOGUE_PLAY_OUTPUT and defaults
to the extraction result's folder combined with the file name derived
from the system name (for example /output/Library.play).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Service mode is selected with --serve or PROLOGUE_MODE=service, and the
service's port, grace period and idle timeout come from
PROLOGUE_SERVICE_PORT, PROLOGUE_GRACE_PERIOD and PROLOGUE_IDLE_TIMEOUT
(seconds), with defaults 5004/300/600. Batch behavior is unchanged when
none of them are present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The interpreter exe becomes a web project referencing Orleans so its
service mode can host sessions as grains. Session state persists as a
System.Text.Json-serialized wrapper document keyed by the prologue id in
the interpreter-sessions collection, through a MongoDbGrainStorage<TState>
whose only dependency is the IMongoCollection<TState>, per the Orleans
conventions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The InterpreterSessionHost carries the grain's decision logic without
Orleans: idempotent start from stored captures (resuming persisted
state), background Proceed with every checkpoint persisted, answers that
continue the session once all pending questions are answered, and the
result with its generated Screenplay once completed.

The SessionActivityTracker owns the clean-exit decisions against an
injected TimeProvider: a grace period while awaiting answers that every
answer resets, an idle timeout once a finished session's result has been
fetched, and an idle timeout when no session activity happens at all.
The ServiceLifecycleWatcher polls it and stops the host cleanly — state
is persisted, so the orchestrator can restart the container to resume.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The --serve / PROLOGUE_MODE=service mode runs a localhost-clustered
Orleans silo and the session HTTP API in one process on
PROLOGUE_SERVICE_PORT (default 5004):

- POST /sessions/{prologueId} starts or continues a session (202)
- GET  /sessions/{prologueId} returns the snapshot
- POST /sessions/{prologueId}/answers records an answer (202; a
  not-pending question maps to a 404 problem, never a 500)
- GET  /sessions/{prologueId}/result returns the extraction result and
  Screenplay once completed, 409 before
- GET  /healthz answers reachability probes

The reentrant session grain is a thin shell over the session host, keyed
by prologue id, restoring persisted state on activation so a restarted
container resumes mid-conversation. Prologue types crossing grain calls
serialize with System.Text.Json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy the Screenplay and Storage projects into the build stage (the
Screenplay reference was missing since the .play output landed), switch
the runtime image to aspnet since the exe now hosts an HTTP API, and
document the service-mode environment with the exposed default port.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brings CommandSyntax.Description and the printer's fenced multi-line
description blocks that the interpreter now emits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A trailing defaulted parameter matching how descriptions were added to
the module, feature, and slice records, so existing construction sites
and stored session state stay compatible. The JSON round-trip spec now
proves the description survives persistence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The refinement prompt now instructs the model to describe every
command's logic and intent in business language under
command:<module>/<feature>/<slice>/<command> keys, and ModelRenamer
applies them onto the renamed model alongside the module, feature, and
slice descriptions — including commands in sub-feature slices.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ScreenplayGenerator maps ExtractedCommand.Description onto
CommandSyntax.Description so the printer renders it as the first
command body line, and multi-line descriptions on modules, slices, and
commands round-trip through ScreenplayCompiler via the printer's
fenced blocks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@einari einari added the minor New features, non-breaking additions label Jul 23, 2026
@einari
einari merged commit a0f53b5 into main Jul 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features, non-breaking additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant