Compare commits

4 Commits

16 changed files with 715 additions and 325 deletions

View File

@@ -2,7 +2,8 @@
## Status ## Status
Ready for implementation. No implementation work described here has started. Completed on 2026-07-27. The framework characterization baseline is complete,
and Migration Step 4 is the next planned work.
## Objective ## Objective

View File

@@ -2,8 +2,8 @@
## Status ## Status
Accepted plan. Steps 1 and 2 are complete. Steps 3 through 9 remain proposed Accepted plan. Steps 1 through 3 are complete. Steps 4 through 9 remain
and are not yet implemented. proposed and are not yet implemented.
## Objective ## Objective
@@ -145,6 +145,11 @@ Preserve coverage of:
**Gate:** Current framework and adapter contracts are represented by passing **Gate:** Current framework and adapter contracts are represented by passing
tests sufficient to detect behavioral regressions during the split. tests sufficient to detect behavioral regressions during the split.
**Gate status:** Complete as of 2026-07-27. The framework contract corpus,
public `Engine` characterization, ownership audit, full test and vet suites,
temporary executable build, and maintained offline examples passed. Step 4 is
next.
### Step 4: Make Scriptorium Adapters Consume The Public Facade ### Step 4: Make Scriptorium Adapters Consume The Public Facade
Within the current repository, refactor the CLI and HTTP adapters to use the Within the current repository, refactor the CLI and HTTP adapters to use the

View File

@@ -2,8 +2,9 @@
## Status ## Status
Accepted scope. Implementation has not started. The ordered execution plan is Completed on 2026-07-27. The target state below is characterized by the
in [implementation.md](implementation.md). passing baseline; its ordered implementation record is in
[implementation.md](implementation.md).
## Purpose ## Purpose
@@ -38,7 +39,7 @@ injected-client behavior, and reserved provider parameters.
## Target State ## Target State
At completion: The completed baseline provides:
- tests destined for Promptkit use only Promptkit-destined testdata or - tests destined for Promptkit use only Promptkit-destined testdata or
fixtures generated within the test; fixtures generated within the test;
@@ -165,13 +166,13 @@ Step 3 is complete when:
- the full roadmap behavior list has a clear, non-duplicative test owner; - the full roadmap behavior list has a clear, non-duplicative test owner;
- tests that require Step 4 rewrites are explicitly identified; - tests that require Step 4 rewrites are explicitly identified;
- no production behavior or public API changed; - no production behavior or public API changed;
- all validation in Stage 5 passes; and - all required validation passes; and
- the main migration roadmap records the Step 3 gate as complete. - the main migration roadmap records the Step 3 gate as complete.
Migration Step 4 must not begin until these criteria are satisfied. Migration Step 4 must not begin until these criteria are satisfied.
## Lifecycle ## Lifecycle
This is a temporary implementation roadmap. Once Step 3 is complete and its This completed implementation roadmap remains the concise characterization
gate status is recorded in the main migration roadmap, this file may be removed; record for the migration. Repository history retains the detailed implementation
repository history retains the detailed implementation record. record.

File diff suppressed because it is too large Load Diff

View File

@@ -1,127 +0,0 @@
package usecase
import (
"context"
"path/filepath"
"testing"
"gitea.maximumdirect.net/eric/scriptorium/internal/artifact"
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
"gitea.maximumdirect.net/eric/scriptorium/internal/profile"
"gitea.maximumdirect.net/eric/scriptorium/internal/prompt"
"gitea.maximumdirect.net/eric/scriptorium/internal/promptdef"
"gitea.maximumdirect.net/eric/scriptorium/internal/validate"
)
type integrationLLM struct{}
func (f *integrationLLM) Generate(ctx context.Context, req domain.GenerateRequest) (*domain.GenerateResponse, error) {
lastIntegrationRequest = req
return &domain.GenerateResponse{
Content: `{"summary":"Party discovered a captive scout beneath the tower.","events":[{"title":"Scout found in cellar","type":"discovery","notes":"Scout requested rescue from goblin raiders."}]}`,
Usage: domain.TokenUsage{
PromptTokens: 42,
CompletionTokens: 36,
TotalTokens: 78,
},
}, nil
}
var lastIntegrationRequest domain.GenerateRequest
func TestRunnerIntegrationWithPromptAndProfileFixturesAndValidation(t *testing.T) {
root, err := filepath.Abs(filepath.Join("..", ".."))
if err != nil {
t.Fatalf("failed to resolve repo root: %v", err)
}
promptsDir := filepath.Join(root, "examples", "prompts")
profilesDir := filepath.Join(root, "examples", "profiles")
schemasDir := filepath.Join(root, "examples", "schemas")
fixturesDir := filepath.Join(root, "examples", "fixtures")
t.Setenv("SCRIPTORIUM_API_KEY", "test-key")
runner := NewRunner(
promptdef.NewFilesystemRepository(promptsDir),
profile.NewFilesystemRepository(profilesDir),
artifact.NewCompositeReader(),
prompt.NewGoRenderer(),
&integrationLLM{},
validate.NewStandardValidator(schemasDir),
)
res, err := runner.Run(context.Background(), domain.RunRequest{
PromptID: "generic.structured_events",
Inputs: map[string]domain.ArtifactRef{
"transcript": {
Type: domain.ArtifactRefFile,
URI: filepath.Join(fixturesDir, "transcript.md"),
},
"glossary": {
Type: domain.ArtifactRefFile,
URI: filepath.Join(fixturesDir, "glossary.yml"),
},
},
})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if res.PromptID != "generic.structured_events" {
t.Fatalf("unexpected prompt id: %q", res.PromptID)
}
if res.SelectedProfileID != "local-quality" {
t.Fatalf("expected selected profile local-quality from prompt default, got %q", res.SelectedProfileID)
}
if res.RunID == "" {
t.Fatal("expected run id")
}
if res.PromptHash == "" {
t.Fatal("expected prompt hash")
}
if res.PromptVersion != "1.0.0" {
t.Fatalf("unexpected prompt version: %q", res.PromptVersion)
}
if res.Validation.Status != domain.ValidationPassed {
t.Fatalf("expected passed validation, got %q", res.Validation.Status)
}
if res.Validation.Mode != domain.ValidationJSONSchema {
t.Fatalf("expected json_schema mode, got %q", res.Validation.Mode)
}
if lastIntegrationRequest.StructuredOutput == nil {
t.Fatal("expected provider-level structured output request for json_schema prompt")
}
if lastIntegrationRequest.StructuredOutput.Type != domain.StructuredOutputJSONSchema {
t.Fatalf("expected structured output type json_schema, got %q", lastIntegrationRequest.StructuredOutput.Type)
}
if lastIntegrationRequest.StructuredOutput.JSONSchema == nil || lastIntegrationRequest.StructuredOutput.JSONSchema.Schema == nil {
t.Fatalf("expected structured output json_schema payload, got %+v", lastIntegrationRequest.StructuredOutput.JSONSchema)
}
if res.Artifact.ContentType != "application/json" {
t.Fatalf("expected application/json output, got %q", res.Artifact.ContentType)
}
if len(res.RawOutput) == 0 {
t.Fatal("expected raw output to be preserved")
}
if res.PromptHash == "" {
t.Fatal("expected non-empty prompt hash")
}
if len(res.InputHashes) != 2 {
t.Fatalf("expected two input hashes, got %d", len(res.InputHashes))
}
if res.InputHashes["transcript"] == "" || res.InputHashes["glossary"] == "" {
t.Fatalf("expected both input hashes to be set, got %#v", res.InputHashes)
}
if res.Usage.TotalTokens != 78 {
t.Fatalf("expected usage from fake llm, got %+v", res.Usage)
}
if res.StartTime.IsZero() || res.EndTime.IsZero() {
t.Fatal("expected start/end timestamps")
}
if res.EndTime.Before(res.StartTime) {
t.Fatalf("expected end >= start, got start=%v end=%v", res.StartTime, res.EndTime)
}
if res.Duration < 0 {
t.Fatalf("expected non-negative duration, got %s", res.Duration)
}
}

View File

@@ -0,0 +1,2 @@
archive: A catalogued collection of written records.
marker: A small label used to classify an entry.

View File

@@ -0,0 +1,2 @@
Nia labels the archive.
The archive receives a blue marker.

View File

@@ -0,0 +1,7 @@
id: contract-fast
endpoint: http://localhost:8000/v1
model: contract-fast-model
temperature: 0.2
max_tokens: 500
top_p: 1
timeout_seconds: 90

View File

@@ -0,0 +1,7 @@
id: contract-quality
endpoint: http://localhost:8000/v1
model: contract-quality-model
temperature: 0.1
max_tokens: 1000
top_p: 0.9
timeout_seconds: 120

View File

@@ -0,0 +1 @@
You summarize synthetic archive notes in clear Markdown.

View File

@@ -0,0 +1,7 @@
Summarize this transcript:
{{input "transcript"}}
Optional glossary:
{{input "glossary"}}

View File

@@ -0,0 +1,20 @@
id: contract.markdown_summary
version: "1.0.0"
default_profile: contract-fast
description: Summarize a synthetic transcript in Markdown.
inputs:
- name: transcript
required: true
content_type: text/markdown
- name: glossary
required: false
content_type: text/yaml
messages:
- role: system
content_file: ./contract.markdown_summary.system.md
- role: user
content_file: ./contract.markdown_summary.user.md
output:
format: markdown
validation_mode: basic
repair_attempts: 0

View File

@@ -0,0 +1 @@
Return only JSON that satisfies the requested event schema.

View File

@@ -0,0 +1,7 @@
Extract events from this transcript:
{{input "transcript"}}
Optional glossary:
{{input "glossary"}}

View File

@@ -0,0 +1,21 @@
id: contract.structured_events
version: "1.0.0"
default_profile: contract-quality
description: Extract synthetic events as structured JSON.
inputs:
- name: transcript
required: true
content_type: text/markdown
- name: glossary
required: false
content_type: text/yaml
messages:
- role: system
content_file: ./contract.structured_events.system.md
- role: user
content_file: ./contract.structured_events.user.md
output:
format: json
validation_mode: json_schema
schema_path: structured_events.schema.json
repair_attempts: 0

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["events"],
"properties": {
"events": {
"type": "array",
"items": {
"type": "object",
"required": ["title"],
"properties": {
"title": {"type": "string"}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}