Add schema evidence, resumable AI interpreter sessions, and Screenplay generation - #1
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Added
database-schemaobservationCratis.Prologue.Interpretationpackage — 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 timeCratis.Prologue.Screenplaypackage — generates a Screenplay.playdocument from an extraction result using the Screenplay syntax tree and printer.playfile next to the extraction result🤖 Generated with Claude Code