Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/oci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,19 @@ jobs:
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}


qualify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build qualification image
run: docker build -t storyforge:qualification .
- name: CLI starts as packaged
run: docker run --rm storyforge:qualification --help
- name: Validate Lighthouse in container
run: docker run --rm -v "$PWD:/work:ro" storyforge:qualification validate examples/lighthouse/story.yaml
- name: Solve Lighthouse in container
run: docker run --rm -v "$PWD:/work:ro" storyforge:qualification solve examples/lighthouse/story.yaml
- name: Analyze Lighthouse in container
run: docker run --rm -v "$PWD:/work:ro" storyforge:qualification analyze examples/lighthouse/story.yaml
15 changes: 15 additions & 0 deletions docs/oci-qualification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# OCI qualification

The OCI workflow does more than prove that the image can be assembled. A separate qualification job builds the packaged image and executes StoryForge inside it.

The qualification currently proves:

- the installed CLI starts through the image entrypoint;
- the Lighthouse reference story validates;
- the deterministic solver completes against Lighthouse;
- deterministic analysis completes against Lighthouse;
- the repository is mounted read-only during reference-story checks.

This catches packaging errors that Python unit tests cannot see, including missing package data, broken console entrypoints, dependency omissions, and container-only path problems.

AI-backed network providers are intentionally excluded from OCI qualification. Their nondeterminism, credentials, and external availability should not decide whether the deterministic StoryForge runtime image is healthy.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ storyforge = "storyforge.cli:main"
[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]

[tool.setuptools.package-data]
storyforge = ["schemas/*.json"]
11 changes: 6 additions & 5 deletions src/storyforge/schema.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import json
from pathlib import Path
from importlib.resources import files

from jsonschema import Draft202012Validator


def _schema_path() -> Path:
return Path(__file__).resolve().parents[2] / "schemas" / "storyforge-v0.schema.json"
def _load_schema() -> dict:
resource = files("storyforge").joinpath("schemas/storyforge-v0.schema.json")
return json.loads(resource.read_text(encoding="utf-8"))


def validate_schema(story: dict) -> list[str]:
schema = json.loads(_schema_path().read_text(encoding="utf-8"))
validator = Draft202012Validator(schema)
validator = Draft202012Validator(_load_schema())
errors = sorted(validator.iter_errors(story), key=lambda error: list(error.path))
return [
"schema: " + (".".join(map(str, error.path)) + ": " if error.path else "") + error.message
Expand Down
Loading
Loading