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
23 changes: 23 additions & 0 deletions .github/workflows/behavior-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Behavior Tests

on:
push:
branches: [master, main]
pull_request:

concurrency:
group: behavior-${{ github.ref }}
cancel-in-progress: true

jobs:
behavior-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"
- run: uv sync --frozen || uv sync
- name: Behavior / choreography tests (agent loop integration)
run: uv run pytest tests/behavior -q --tb=short -p no:cacheprovider
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ jobs:
python-version: "3.11"
- run: uv sync --frozen || uv sync
- name: Hermetic unit suite (no network, fake keys — enforced by conftest.py)
run: uv run pytest packages tests -q --tb=short -p no:cacheprovider
run: uv run pytest packages tests -q --tb=short -p no:cacheprovider --cov=packages --cov-branch --cov-report=xml --cov-report=term-missing
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
fail_ci_if_error: false

frontend:
runs-on: ubuntu-latest
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/contract-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Contract Tests

on:
push:
branches: [master, main]
pull_request:

concurrency:
group: contract-${{ github.ref }}
cancel-in-progress: true

jobs:
contract-tests:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"
- run: uv sync --frozen || uv sync
- name: Contract tests (deal library — input/output invariants)
run: uv run pytest tests/contracts -q --tb=short -p no:cacheprovider
148 changes: 148 additions & 0 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
name: Mutation Tests

on:
schedule:
- cron: "0 3 * * 0" # weekly, Sunday 3 AM UTC
workflow_dispatch:
inputs:
target:
description: >
Module to mutate — leave blank to run all.
Valid values: canonkeeper, resolver, npc-voice, scene-loop, story-loop,
resource-engine, world-architect, plot-hooks, delta-detection, contradiction
required: false
default: ""

jobs:
mutate:
name: "mutate / ${{ matrix.target.name }}"
runs-on: ubuntu-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
target:
- { name: canonkeeper, toml: cosmic-ray.toml }
- { name: resolver, toml: cosmic-ray-resolver.toml }
- { name: npc-voice, toml: cosmic-ray-npc-voice.toml }
- { name: scene-loop, toml: cosmic-ray-scene-loop.toml }
- { name: story-loop, toml: cosmic-ray-story-loop.toml }
- { name: resource-engine, toml: cosmic-ray-resource-engine.toml }
- { name: world-architect, toml: cosmic-ray-world-architect.toml }
- { name: plot-hooks, toml: cosmic-ray-plot-hooks.toml }
- { name: delta-detection, toml: cosmic-ray-delta-detection.toml }
- { name: contradiction, toml: cosmic-ray-contradiction.toml }
if: >-
github.event.inputs.target == '' ||
github.event.inputs.target == matrix.target.name

steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"
- run: uv sync --frozen || uv sync

- name: Install cosmic-ray
run: uv pip install "cosmic-ray>=8.0"

- name: Initialize session
run: uv run cosmic-ray init ${{ matrix.target.toml }} session-${{ matrix.target.name }}.sqlite

- name: Run mutations
run: uv run cosmic-ray exec ${{ matrix.target.toml }} session-${{ matrix.target.name }}.sqlite

- name: Report + extract score
id: score
run: |
REPORT=$(uv run cr-report session-${{ matrix.target.name }}.sqlite)
echo "$REPORT"
SCORE=$(echo "$REPORT" | python3 -c "
import sys, re
text = sys.stdin.read()
pct = re.search(r'kill rate[:\s]+(\d+\.?\d*)\s*%', text, re.IGNORECASE)
dec = re.search(r'score[:\s]+(\d+\.\d+)', text, re.IGNORECASE)
if pct:
print(round(float(pct.group(1))))
elif dec:
print(round(float(dec.group(1)) * 100))
else:
print(0)
")
echo "score=$SCORE" >> $GITHUB_OUTPUT
echo "$SCORE" > score-${{ matrix.target.name }}.txt
echo "Kill rate for ${{ matrix.target.name }}: $SCORE%"

- name: Upload score artifact
uses: actions/upload-artifact@v4
with:
name: mutation-score-${{ matrix.target.name }}
path: score-${{ matrix.target.name }}.txt

aggregate:
name: "mutate / aggregate + badge"
needs: mutate
runs-on: ubuntu-latest
if: always()

steps:
- name: Download all score artifacts
uses: actions/download-artifact@v4
with:
pattern: mutation-score-*
merge-multiple: true
path: scores/

- name: Compute aggregate kill rate and update badge
env:
GIST_TOKEN: ${{ secrets.GIST_TOKEN }}
MUTATION_GIST_ID: ${{ secrets.MUTATION_GIST_ID }}
run: |
python3 << 'PYEOF'
import glob, json, os, urllib.request

scores = {}
for path in glob.glob("scores/score-*.txt"):
name = os.path.basename(path).removeprefix("score-").removesuffix(".txt")
try:
scores[name] = int(open(path).read().strip())
except (ValueError, OSError):
pass

if not scores:
print("No score artifacts found — all mutation jobs may have failed.")
raise SystemExit(1)

avg = round(sum(scores.values()) / len(scores))
count = len(scores)
print(f"\nPer-module kill rates:")
for name, score in sorted(scores.items()):
bar = "█" * (score // 5) + "░" * (20 - score // 5)
print(f" {name:<20} {bar} {score}%")
print(f"\nAggregate: {avg}% across {count} modules\n")

token = os.environ.get("GIST_TOKEN", "")
gist_id = os.environ.get("MUTATION_GIST_ID", "")
if not token or not gist_id:
print("GIST_TOKEN or MUTATION_GIST_ID not configured — badge update skipped.")
print("See docs/contributing/BADGES.md for setup instructions.")
raise SystemExit(0)

color = "brightgreen" if avg >= 80 else "yellow" if avg >= 60 else "red"
badge = {
"schemaVersion": 1,
"label": "mutation score",
"message": f"{avg}% ({count} modules)",
"color": color,
}
payload = json.dumps({
"files": {"mutation-score.json": {"content": json.dumps(badge, indent=2)}}
}).encode()
req = urllib.request.Request(
f"https://api.github.com/gists/{gist_id}",
data=payload, method="PATCH",
headers={"Authorization": f"token {token}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as r:
print(f"Gist badge updated: {avg}% across {count} modules ({color})")
PYEOF
23 changes: 23 additions & 0 deletions .github/workflows/property-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Property Tests

on:
push:
branches: [master, main]
pull_request:

concurrency:
group: property-${{ github.ref }}
cancel-in-progress: true

jobs:
property-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.11"
- run: uv sync --frozen || uv sync
- name: Property-based tests (Hypothesis — generative input strategies)
run: uv run pytest tests/property -q --tb=short -p no:cacheprovider
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

*Multi-Ontology Narrative Intelligence Through Omniversal Representation*

<!-- CI / build -->
[![CI](https://github.com/spuentesp/monitor_dm_system/actions/workflows/ci.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/ci.yml)
[![Nightly Integration](https://github.com/spuentesp/monitor_dm_system/actions/workflows/nightly-integration.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/nightly-integration.yml)
[![Contract Tests](https://github.com/spuentesp/monitor_dm_system/actions/workflows/contract-tests.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/contract-tests.yml)
[![Property Tests](https://github.com/spuentesp/monitor_dm_system/actions/workflows/property-tests.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/property-tests.yml)
[![Behavior Tests](https://github.com/spuentesp/monitor_dm_system/actions/workflows/behavior-tests.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/behavior-tests.yml)
[![Mutation Tests](https://github.com/spuentesp/monitor_dm_system/actions/workflows/mutation.yml/badge.svg)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/mutation.yml)
<!-- Coverage — activate after signing up at codecov.io and adding CODECOV_TOKEN secret -->
[![codecov](https://codecov.io/gh/spuentesp/monitor_dm_system/branch/main/graph/badge.svg?token=CODECOV_TOKEN)](https://codecov.io/gh/spuentesp/monitor_dm_system)
<!-- Mutation score — activate after creating a Gist and adding GIST_TOKEN + MUTATION_GIST_ID secrets (see docs/contributing/BADGES.md) -->
<!-- [![Mutation Score](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/spuentesp/YOUR_GIST_ID/raw/mutation-score.json)](https://github.com/spuentesp/monitor_dm_system/actions/workflows/mutation.yml) -->

<!-- Stack & meta -->
![Python 3.11](https://img.shields.io/badge/python-3.11-blue?logo=python&logoColor=white)
![Tests](https://img.shields.io/badge/tests-5%2C900%2B-brightgreen)
![Contract Tests](https://img.shields.io/badge/contracts-deal-blueviolet)
![Property Tests](https://img.shields.io/badge/property--based-hypothesis-orange)
![Mutation Tested](https://img.shields.io/badge/mutation--tested-cosmic--ray-red)
![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey)

**An Auto-GM system for tabletop RPGs and narrative games, built on a data-first, canonization-driven architecture.**

---
Expand Down
18 changes: 18 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
codecov:
require_ci_to_pass: false

coverage:
status:
project:
default:
target: auto
threshold: 2%
patch:
default:
target: auto
threshold: 5%

ignore:
- "tests/"
- "scripts/"
- "infra/"
6 changes: 6 additions & 0 deletions cosmic-ray-contradiction.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/data-layer/src/monitor_data/tools/ingest_tools/contradiction_detection.py"
python-version = ""
timeout = 15
excluded-modules = []
test-command = "uv run pytest packages/data-layer/tests/test_tools/test_contradiction_detection.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-delta-detection.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/data-layer/src/monitor_data/tools/ingest_tools/delta_detection.py"
python-version = ""
timeout = 20
excluded-modules = []
test-command = "uv run pytest packages/data-layer/tests/test_db/test_ingest_tools.py packages/data-layer/tests/test_db/test_ingest_tools_coverage.py -q"
7 changes: 7 additions & 0 deletions cosmic-ray-npc-voice.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/npc_voice.py"
python-version = ""
timeout = 20
excluded-modules = []
# Mutation-focused test file first — kills branch-level mutations the happy-path misses
test-command = "uv run pytest packages/agents/tests/test_npc_voice_universe_scoping_mutations.py packages/agents/tests/test_npc_voice.py packages/agents/tests/test_npc_voice_universe_scoping.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-plot-hooks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/plot_hooks.py"
python-version = ""
timeout = 25
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_plot_hooks.py tests/behavior/test_plot_hooks_choreography_behavior.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-resolver.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/resolver.py"
python-version = ""
timeout = 20
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_resolver.py packages/agents/tests/test_resolver_oracle.py packages/agents/tests/test_resolver_pushback.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-resource-engine.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/resource_engine.py"
python-version = ""
timeout = 20
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_resource_engine.py packages/agents/tests/test_resource_derivation.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-scene-loop.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/loops/scene_loop.py"
python-version = ""
timeout = 30
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_scene_loop.py tests/behavior/test_scene_loop_routing_behavior.py tests/behavior/test_scene_end_choreography_behavior.py tests/behavior/test_scene_support_behavior.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-story-loop.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/loops/story_loop.py"
python-version = ""
timeout = 30
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_story_loop.py packages/agents/tests/test_story_loop_procedural.py tests/behavior/test_story_loop_choreography_behavior.py tests/behavior/test_story_completion_choreography_behavior.py -q"
6 changes: 6 additions & 0 deletions cosmic-ray-world-architect.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[cosmic-ray]
module-path = "packages/agents/src/monitor_agents/world_architect.py"
python-version = ""
timeout = 25
excluded-modules = []
test-command = "uv run pytest packages/agents/tests/test_world_architect.py packages/agents/tests/test_world_architect_procedural.py -q"
1 change: 1 addition & 0 deletions coverage_full.json

Large diffs are not rendered by default.

Loading