prepare for review and deploy

This commit is contained in:
2026-02-27 20:29:37 +00:00
parent 7016998435
commit a77e23245e
28 changed files with 254 additions and 173 deletions

View File

@@ -3,7 +3,7 @@ LLM_MODEL=deepseek/deepseek-chat-v3-0324:free
LLM_MOCK=false
PORT=3000
API_HOST_PORT=3000
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/improv_court
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/juryrigged
TTS_PROVIDER=noop
LOG_LEVEL=info
VERDICT_VOTE_WINDOW_MS=20000

View File

@@ -1,4 +1,4 @@
# Improv Court ⚖️🎭
# JuryRigged ⚖️🎭
*Roleplay comedy + audience verdict game show format*
@@ -213,9 +213,9 @@ Designed to be naturally robust with stream delay:
### Architecture Decision Record
See [docs/ADR-001-improv-court-architecture.md](docs/ADR-001-improv-court-architecture.md) for runtime boundaries, module ownership, phase-state contracts, and API/SSE/persistence contracts.
See [docs/ADR-001-juryrigged-architecture.md](docs/ADR-001-juryrigged-architecture.md) for runtime boundaries, module ownership, phase-state contracts, and API/SSE/persistence contracts.
### Phase 1 — Improv Court = “Orchestration/Overlay Engine Test”
### Phase 1 — JuryRigged = “Orchestration/Overlay Engine Test”
Proves:
@@ -285,6 +285,6 @@ Uses everything:
### Relative runtime cost (low → high)
Improv Court (medium) < Cipher (medium) < Writers Room (medium-high) ≈ Ghost (medium-high)
JuryRigged (medium) < Cipher (medium) < Writers Room (medium-high) ≈ Ghost (medium-high)
---

View File

@@ -13,7 +13,7 @@ DOCKER_COMPOSE ?= docker compose
.PHONY: help install dev dev-dashboard lint build build-dashboard test test-spec ci smoke-staging start migrate migrate-dist docker-up docker-down docker-restart clean status
help: ## Show available commands
@awk 'BEGIN {FS = ":.*##"; printf "\nImprov Court Make targets:\n\n"} /^[a-zA-Z0-9_.-]+:.*##/ { printf " %-18s %s\n", $$1, $$2 } END { printf "\n" }' $(MAKEFILE_LIST)
@awk 'BEGIN {FS = ":.*##"; printf "\nJuryRigged Make targets:\n\n"} /^[a-zA-Z0-9_.-]+:.*##/ { printf " %-18s %s\n", $$1, $$2 } END { printf "\n" }' $(MAKEFILE_LIST)
install: ## Install Node dependencies
$(NPM) install

287
README.md
View File

@@ -1,137 +1,162 @@
# Improv Court POC (standalone)
# JuryRigged
[![CI](https://github.com/subculture-collective/court/actions/workflows/ci.yml/badge.svg)](https://github.com/subculture-collective/court/actions/workflows/ci.yml)
This is a **standalone root-level implementation** of the Improv Court proof of concept.
It does **not** depend on `subcult-corp` at runtime.
JuryRigged is a real-time, multi-agent courtroom simulation.
An Express API orchestrates agent dialogue across deterministic phases, streams live events via Server-Sent Events (SSE), and supports jury voting for verdict and sentence outcomes.
## Documentation
This repository is standalone and does not require `subcult-corp` at runtime.
| Document | Description |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| [docs/ADR-001-improv-court-architecture.md](docs/ADR-001-improv-court-architecture.md) | Architecture Decision Record: runtime boundaries, data contracts, and phase invariants |
| [docs/architecture.md](docs/architecture.md) | System architecture, agent roles, and phase flow |
| [docs/api.md](docs/api.md) | REST API endpoints, schemas, and SSE event contracts |
| [docs/operator-runbook.md](docs/operator-runbook.md) | Setup, live controls, incident response, mistrial fallback, and operational monitoring |
| [docs/moderation-playbook.md](docs/moderation-playbook.md) | Content moderation system and incident procedures |
| [docs/event-taxonomy.md](docs/event-taxonomy.md) | Canonical event taxonomy, payload schemas, and logging guidelines |
| [docs/phase5-6-implementation-plan.md](docs/phase5-6-implementation-plan.md) | Dependency-ordered implementation plan for roadmap phases 5 and 6 |
## Highlights
## What is implemented
- Multi-agent role orchestration (judge, prosecutor, defense, witnesses, bailiff)
- Strict, forward-only phase progression:
- `case_prompt``openings``witness_exam``evidence_reveal``closings``verdict_vote``sentence_vote``final_ruling`
- Optional skip: `witness_exam``closings`
- Live per-session SSE stream (`/api/court/sessions/:id/stream`)
- Jury voting APIs with phase gating + anti-spam/rate-limiting
- Main viewer UI (`public/`) and React operator dashboard (`/operator`)
- In-memory or Postgres-backed persistence (auto-selected by `DATABASE_URL`)
- Optional broadcast hook integration (`noop` or `obs`) for production workflows
- Multi-agent courtroom roles (judge, prosecutor, defense, witnesses, bailiff)
- Phase-based court flow:
- `case_prompt`
- `openings`
- `witness_exam`
- `closings`
- `verdict_vote`
- `sentence_vote`
- `final_ruling`
- Live SSE stream per session
- Jury verdict and sentence voting endpoints
- Deterministic phase-order and vote-window enforcement
- Minimal stripped web UI (`public/index.html`)
- Overlay shell with phase timer, active speaker, and live captions
- Viewer layout showing current phase context and jury voting status
- Verdict/sentence poll bars with live percentages and phase-gated voting
- SSE analytics events for poll start/close and vote completion
- **Operator Dashboard** (`/operator`)
- Real-time session monitoring with live event feed
- Vote tallies and witness cap tracking
- Moderation queue for content review
- Manual controls for session management
- Analytics dashboard with event timelines
- **Structured Logging Service**
- JSON-formatted logs with session/phase/event correlation
- Configurable log levels (debug/info/warn/error)
- Child loggers with inherited context
- Production-ready logging architecture
## Tech Stack
## Environment
- Node.js + TypeScript
- Express (API + static serving)
- React + Vite (operator dashboard)
- Postgres (optional durable store)
Copy `.env.example` to `.env` and set values as needed.
## Quick Start (Local)
Key variables:
### 1) Install dependencies
- `OPENROUTER_API_KEY` (optional for local mock mode; required for real LLM calls)
- `LLM_MODEL`
- `LLM_MOCK` (set to `true` to force deterministic mock responses)
- `PORT`
- `DATABASE_URL` (Postgres connection string for durable persistence)
- `TTS_PROVIDER` (`noop` or `mock`; defaults to `noop`)
- `VERDICT_VOTE_WINDOW_MS`
- `SENTENCE_VOTE_WINDOW_MS`
- `WITNESS_MAX_TOKENS`
- `WITNESS_MAX_SECONDS`
- `WITNESS_TOKENS_PER_SECOND`
- `WITNESS_TRUNCATION_MARKER`
- `JUDGE_RECAP_CADENCE`
- `LOG_LEVEL` (debug, info, warn, error; defaults to `info`)
```bash
npm install
```
If `OPENROUTER_API_KEY` is empty, the app falls back to deterministic mock dialogue.
### 2) Configure environment
If `DATABASE_URL` is set, the app uses Postgres-backed persistence and runs migrations at startup.
If `DATABASE_URL` is missing, the app falls back to in-memory storage (non-durable).
```bash
cp .env.example .env
```
`TTS_PROVIDER=noop` keeps TTS silent (default). `TTS_PROVIDER=mock` records adapter calls for local/testing workflows without requiring an external speech provider.
For a zero-dependency local run:
Witness response caps are controlled by `WITNESS_MAX_TOKENS` and `WITNESS_MAX_SECONDS`. The recap cadence uses `JUDGE_RECAP_CADENCE` (every N witness cycles). `WITNESS_TRUNCATION_MARKER` customizes the appended cutoff text.
- leave `OPENROUTER_API_KEY` empty (mock dialogue fallback)
- leave `DATABASE_URL` empty (in-memory session store)
## Run
### 3) Start the API server
1. Install dependencies:
- `npm install`
2. (Optional but recommended) run DB migrations explicitly:
- `npm run migrate`
3. Start dev server:
- `npm run dev`
4. Build operator dashboard:
- `npm run build:dashboard` (production build)
- `npm run dev:dashboard` (development mode on port 3001)
5. Open:
- Main app: `http://localhost:3000`
- Operator dashboard: `http://localhost:3000/operator`
```bash
npm run dev
```
## Run with Docker (API + Postgres)
Default local URL: `http://localhost:3000` (from `.env.example`).
This repo includes a `docker-compose.yml` that starts both:
### 4) Build operator dashboard assets
- `api` (the Improv Court server)
```bash
npm run build:dashboard
```
Then open:
- Main app: `http://localhost:3000`
- Operator dashboard: `http://localhost:3000/operator`
> The API serves `/operator` from `dist/dashboard`. If you havent built it yet, `/operator` will return a helpful 404 message.
## Dashboard Dev Mode (Hot Reload)
Run the dashboard separately while API dev server is running:
```bash
npm run dev:dashboard
```
- Dashboard dev URL: `http://localhost:3001/operator/`
- API proxy target in Vite is `http://localhost:3000`
If your API is not on port `3000`, update `vite.config.ts` proxy settings.
## Docker Compose (API + Postgres)
The compose stack includes:
- `api` (JuryRigged server)
- `db` (Postgres 16)
Start the full stack:
Start:
- `npm run docker:up`
```bash
npm run docker:up
```
Or directly:
Stop:
- `docker compose up --build`
```bash
npm run docker:down
```
Stop the stack:
Container behavior:
- `npm run docker:down`
- API runs on container port `3001`
- Host mapping defaults to `${API_HOST_PORT:-3001}`
- Migrations run automatically on container startup (`npm run migrate:dist`)
The API container runs migrations on startup (`npm run migrate:dist`) before starting the server.
Default compose endpoints:
Endpoints when running with compose:
- App + API: `http://localhost:${API_HOST_PORT:-3001}`
- Operator dashboard: `http://localhost:${API_HOST_PORT:-3001}/operator`
- Main app: `http://localhost:${API_HOST_PORT:-3000}`
- Operator dashboard: `http://localhost:${API_HOST_PORT:-3000}/operator`
- API: `http://localhost:${API_HOST_PORT:-3000}/api`
- Postgres: internal-only by default (`db:5432` inside compose network)
## Configuration
If port `3000` is already in use on your machine, set `API_HOST_PORT` in `.env` (for example `API_HOST_PORT=3002`) and restart compose.
Copy `.env.example` and tune as needed.
If you need host access to Postgres, add a `ports` mapping to the `db` service in `docker-compose.yml` (for example `"5433:5432"` to avoid conflicts with local Postgres).
### Core runtime
## Operations runbook (staging)
| Variable | Purpose |
| --- | --- |
| `PORT` | API port for local non-Docker runs (default in `.env.example`: `3000`) |
| `OPENROUTER_API_KEY` | Required for live LLM calls; empty enables deterministic mock fallback |
| `LLM_MODEL` | OpenRouter model identifier |
| `LLM_MOCK` | Force mock mode (`true`/`false`) |
| `DATABASE_URL` | Enables Postgres-backed durable store; omit for in-memory |
| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` |
See `docs/ops-runbook.md` for the repeatable staging deploy path, GitHub Actions
workflow (`Staging Deploy`), core SLI dashboard definitions, alert thresholds,
and incident drill/recovery steps. Ops-related configuration is validated as part of the standard test suite (see `npm test` under "Local CI parity" below).
### Voting + moderation safety
## API
| Variable | Purpose |
| --- | --- |
| `VERDICT_VOTE_WINDOW_MS` | Verdict poll window duration |
| `SENTENCE_VOTE_WINDOW_MS` | Sentence poll window duration |
| `VOTE_SPAM_MAX_VOTES_PER_WINDOW` | Vote rate cap per window |
| `VOTE_SPAM_WINDOW_MS` | Rate-limit window size |
| `VOTE_SPAM_DUPLICATE_WINDOW_MS` | Duplicate-vote suppression window |
### Witness / token controls
| Variable | Purpose |
| --- | --- |
| `WITNESS_MAX_TOKENS` | Max witness response tokens before truncation |
| `WITNESS_MAX_SECONDS` | Max witness response duration |
| `WITNESS_TOKENS_PER_SECOND` | Duration↔token heuristic |
| `WITNESS_TRUNCATION_MARKER` | Marker appended after truncation |
| `JUDGE_RECAP_CADENCE` | Emit recap every N witness cycles |
| `ROLE_MAX_TOKENS_*` | Per-role token budget overrides |
| `TOKEN_COST_PER_1K_USD` | Cost estimation coefficient |
### Broadcast integration
| Variable | Purpose |
| --- | --- |
| `BROADCAST_PROVIDER` | `noop` or `obs` |
| `OBS_WEBSOCKET_URL` | OBS WebSocket endpoint |
| `OBS_WEBSOCKET_PASSWORD` | OBS auth password (optional but recommended) |
See `docs/broadcast-integration.md` for setup details.
## API at a Glance
- `GET /api/health`
- `GET /api/court/sessions`
@@ -141,18 +166,64 @@ and incident drill/recovery steps. Ops-related configuration is validated as par
- `POST /api/court/sessions/:id/phase`
- `GET /api/court/sessions/:id/stream` (SSE)
## Local CI parity
Full schemas, error codes, and event contracts: `docs/api.md`.
Run the same checks as CI locally before pushing:
## Development Commands
```sh
npm run lint # type-check (tsc --noEmit)
npm run build # compile TypeScript to dist/
npm test # run all tests
### npm scripts
| Command | Description |
| --- | --- |
| `npm run dev` | Start API in watch mode (`src/server.ts`) |
| `npm run dev:dashboard` | Start Vite dashboard dev server |
| `npm run build` | Compile TS to `dist/` + build dashboard |
| `npm run build:dashboard` | Build dashboard only |
| `npm run start` | Run compiled server (`dist/server.js`) |
| `npm run migrate` | Run migrations from source (`tsx`) |
| `npm run migrate:dist` | Run migrations from compiled output |
| `npm test` | Run Node test suite |
| `npm run test:ops` | Run ops config tests |
| `npm run smoke:staging` | Run staging smoke script |
### Make targets
`Makefile` mirrors common workflows (`make dev`, `make test`, `make ci`, `make docker-up`, etc.).
## Local CI Parity
Run before opening a PR:
```bash
npm run lint
npm run build
npm test
```
## Repository Layout
- `src/` — server, orchestrator, store, moderation, broadcast, tests
- `public/` — viewer UI
- `dashboard/` — operator dashboard (React + Vite)
- `db/migrations/` — SQL schema migrations
- `docs/` — architecture, API, moderation, ops runbooks
- `ops/` — alert thresholds + runtime health dashboard artifacts
## Documentation Map
| Document | Description |
| --- | --- |
| `docs/ADR-001-juryrigged-architecture.md` | Core architectural decisions and invariants |
| `docs/architecture.md` | System components and phase sequencing |
| `docs/api.md` | REST + SSE contracts and schemas |
| `docs/operator-runbook.md` | Operator procedures and incident response |
| `docs/ops-runbook.md` | Staging deploy path, SLI/alert definitions |
| `docs/moderation-playbook.md` | Moderation policy and handling |
| `docs/event-taxonomy.md` | Event taxonomy and payload expectations |
| `docs/broadcast-integration.md` | OBS/broadcast automation configuration |
| `docs/phase5-6-implementation-plan.md` | Roadmap implementation plan |
## Notes
- The existing `subcult-corp` directory is used only as a **reference source** and is not imported.
- Core reusable ideas were copied into `src/` as standalone modules.
- Schema migration SQL is under `db/migrations/`.
- Migrations run automatically when using Postgres-backed storage.
- When `DATABASE_URL` is not set, sessions are non-durable and not recoverable after restart.
- On restart with Postgres, interrupted `running` sessions are recovered and resumed.

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Improv Court - Operator Dashboard</title>
<title>JuryRigged - Operator Dashboard</title>
</head>
<body>
<div id="root"></div>

View File

@@ -56,7 +56,7 @@ function App() {
<div className='flex items-center justify-between'>
<div>
<h1 className='text-2xl font-bold text-primary-400'>
Improv Court
JuryRigged
</h1>
<p className='text-sm text-gray-400'>
Operator Dashboard

View File

@@ -1,16 +1,16 @@
services:
db:
image: postgres:16-alpine
container_name: improv-court-db
container_name: juryrigged-db
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: improv_court
POSTGRES_DB: juryrigged
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres -d improv_court']
test: ['CMD-SHELL', 'pg_isready -U postgres -d juryrigged']
interval: 5s
timeout: 5s
retries: 10
@@ -19,14 +19,14 @@ services:
build:
context: .
dockerfile: Dockerfile
container_name: improv-court-api
container_name: juryrigged-api
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
PORT: 3001
DATABASE_URL: postgresql://postgres:postgres@db:5432/improv_court
DATABASE_URL: postgresql://postgres:postgres@db:5432/juryrigged
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
LLM_MODEL: ${LLM_MODEL:-deepseek/deepseek-chat-v3-0324:free}
LLM_MOCK: ${LLM_MOCK:-false}

View File

@@ -1,14 +1,14 @@
# ADR-001 — Improv Court Runtime Architecture
# ADR-001 — JuryRigged Runtime Architecture
**Status:** Accepted
**Date:** 2026-02-27
**Author:** @copilot
**Status:** Accepted
**Date:** 2026-02-27
**Author:** @copilot
---
## Context
Improv Court is a real-time multi-agent courtroom simulation.
JuryRigged is a real-time multi-agent courtroom simulation.
The runtime now includes an HTTP/SSE API layer, a phase-sequencing orchestrator, an LLM client, a content-moderation filter, a vote-spam guard, and a dual-backend session store.
Boundary ownership between these modules needs a durable reference so that future contributors can understand which module is responsible for which concern and what contracts must be honoured across module boundaries.

View File

@@ -16,7 +16,7 @@ Returns service liveness.
**Response `200`**
```json
{ "ok": true, "service": "improv-court-poc" }
{ "ok": true, "service": "juryrigged" }
```
---
@@ -218,7 +218,7 @@ data: {"type":"turn","payload":{…}}\n\n
```ts
{
mode: "improv_court";
mode: "juryrigged";
casePrompt: string;
caseType: "criminal" | "civil";
sentenceOptions: string[];

View File

@@ -1,6 +1,6 @@
# Architecture Overview
Improv Court is a real-time multi-agent courtroom simulation.
JuryRigged is a real-time multi-agent courtroom simulation.
Six AI agents are assigned courtroom roles, run through a deterministic phase sequence, and stream every turn to connected viewers via SSE.
Audience members serve as jury by voting through REST endpoints.
@@ -93,7 +93,7 @@ The store has two backends selected automatically:
| `DATABASE_URL` is set | Postgres (durable) |
| `DATABASE_URL` absent | In-memory (volatile)|
The Postgres schema is under `db/migrations/001_improv_court_core.sql`.
The Postgres schema is under `db/migrations/001_juryrigged_core.sql`.
Migrations are run automatically on startup when the Postgres backend is active.
The store exposes an event bus (`EventEmitter`) that routes session events to SSE subscribers.

View File

@@ -1,6 +1,6 @@
# Event Taxonomy
Canonical reference for all runtime events emitted by the Improv Court system.
Canonical reference for all runtime events emitted by the JuryRigged system.
Every event is represented as a [`CourtEvent`](../src/types.ts) and delivered
over the Server-Sent Events stream (`GET /api/court/sessions/:id/stream`) and
to any in-process `store.subscribe()` listener.

View File

@@ -1,12 +1,12 @@
# Moderation Playbook
This document describes the content moderation system and procedures for responding to incidents during live Improv Court sessions.
This document describes the content moderation system and procedures for responding to incidents during live JuryRigged sessions.
---
## Overview
Improv Court uses a layered moderation approach:
JuryRigged uses a layered moderation approach:
1. **Curated + screened inputs** — case prompts (operator or API-supplied) are screened with the moderation filter; unsafe topics are rejected and unsafe prompt-bank entries are skipped.
2. **LLM system prompt policy** — every agent prompt includes the Clean Courtroom Policy (see below).

View File

@@ -1,6 +1,6 @@
# Operator Runbook
This document covers everything needed to deploy, configure, and operate Improv Court.
This document covers everything needed to deploy, configure, and operate JuryRigged.
---
@@ -65,7 +65,7 @@ Open `http://localhost:3001` in a browser.
The compose file starts two services:
- `api` — the Improv Court server (builds from `Dockerfile`)
- `api` — the JuryRigged server (builds from `Dockerfile`)
- `db` — Postgres 16
```bash
@@ -218,7 +218,7 @@ There is no built-in metrics endpoint. For production observability, pipe logs t
### Schema
The schema is defined in `db/migrations/001_improv_court_core.sql`.
The schema is defined in `db/migrations/001_juryrigged_core.sql`.
The migration system tracks applied migrations in `court_schema_migrations`.
### Cleaning up old sessions

View File

@@ -1,4 +1,4 @@
# Improv Court staging deploy + ops runbook
# JuryRigged staging deploy + ops runbook
## 1) Repeatable staging deployment (Docker-first)
@@ -12,7 +12,7 @@ Run from the project root directory:
- `npm run docker:up`
3. Verify runtime health:
- `curl -fsS http://localhost:${API_HOST_PORT:-3001}/api/health`
- Expected response: `{"ok":true,"service":"improv-court-poc"}`
- Expected response: `{"ok":true,"service":"juryrigged"}`
4. Optional migration-only verification:
- `docker compose exec api npm run migrate:dist`

View File

@@ -6,7 +6,7 @@
## Overview
The Improv Court prompt bank provides a curated collection of case prompts organized by genre tags. The system automatically rotates through genres to avoid repetition and maintain audience engagement.
The JuryRigged prompt bank provides a curated collection of case prompts organized by genre tags. The system automatically rotates through genres to avoid repetition and maintain audience engagement.
## Genre Taxonomy

View File

@@ -1,7 +1,7 @@
{
"schemaVersion": "1.0",
"dashboardId": "improv-court-runtime-health",
"title": "Improv Court Runtime Health",
"dashboardId": "juryrigged-runtime-health",
"title": "JuryRigged Runtime Health",
"description": "Core SLI/SLO proxy panels for Phase 5 operations readiness.",
"datasources": {
"postgres": "court",

14
package-lock.json generated
View File

@@ -1,11 +1,11 @@
{
"name": "improv-court-poc",
"name": "juryrigged",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "improv-court-poc",
"name": "juryrigged",
"version": "0.1.0",
"dependencies": {
"dotenv": "^17.2.3",
@@ -72,6 +72,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1410,6 +1411,7 @@
"integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@@ -1646,6 +1648,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2424,6 +2427,7 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -2784,6 +2788,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -3018,6 +3023,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -3507,6 +3513,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -3549,6 +3556,7 @@
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
@@ -3668,6 +3676,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -4245,6 +4254,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},

View File

@@ -1,5 +1,5 @@
{
"name": "improv-court-poc",
"name": "juryrigged",
"version": "0.1.0",
"private": true,
"type": "module",

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Improv Court POC</title>
<title>JuryRigged</title>
<style>
:root {
color-scheme: dark;
@@ -342,7 +342,7 @@
<body>
<div class="layout">
<section class="panel">
<h1>⚖️ Improv Court POC</h1>
<h1>⚖️ JuryRigged</h1>
<p class="muted">
Text-first courtroom flow with multi-agent roles, live
transcript, and jury voting.

View File

@@ -42,7 +42,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for phase transitions',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for phase transitions',
caseType: 'criminal',
roleAssignments: {
@@ -91,7 +91,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for skipping evidence reveal',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for phase transitions',
caseType: 'criminal',
roleAssignments: {
@@ -133,7 +133,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for invalid jumps',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for invalid jumps',
caseType: 'criminal',
roleAssignments: {
@@ -181,7 +181,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for no-op transitions',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for no-op transitions',
caseType: 'civil',
roleAssignments: {
@@ -215,7 +215,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for phase events',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for phase events',
caseType: 'criminal',
roleAssignments: {
@@ -310,7 +310,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for backward jump prevention',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for backward jump prevention',
caseType: 'criminal',
roleAssignments: {
@@ -359,7 +359,7 @@ describe('Court State Machine - Phase Transitions', () => {
topic: 'Test case for poll events',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case for poll events',
caseType: 'criminal',
roleAssignments: {
@@ -410,7 +410,7 @@ describe('Court Orchestrator - TTS integration', () => {
topic: 'Did the defendant replace office coffee with soup?',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt:
'Did the defendant replace office coffee with soup?',
caseType: 'criminal',
@@ -447,7 +447,7 @@ describe('Court Orchestrator - TTS integration', () => {
topic: 'Did the defendant install a trampoline in the jury box?',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt:
'Did the defendant install a trampoline in the jury box?',
caseType: 'criminal',

View File

@@ -353,7 +353,7 @@ export async function runCourtSession(
}),
);
const allRiseCue = `All rise. The Court of Improvised Absurdity is now in session. Case: ${session.topic}`;
const allRiseCue = `All rise. The JuryRigged court is now in session. Case: ${session.topic}`;
await safelySpeak('speakCue', () =>
tts.speakCue({
sessionId: session.id,

View File

@@ -103,7 +103,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for vote phase gating',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {
@@ -162,7 +162,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for vote accumulation',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {
@@ -223,7 +223,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for vote events',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {
@@ -275,7 +275,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for invalid choices',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {
@@ -321,7 +321,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for invalid sentence',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {
@@ -368,7 +368,7 @@ describe('Vote Lifecycle Integration', () => {
topic: 'Test case for sentence voting',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case',
caseType: 'criminal',
roleAssignments: {

View File

@@ -47,7 +47,7 @@ test('e2e round completes with witness caps and recap cadence', async () => {
topic: 'Did the defendant replace all office coffee with soup?',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt:
'Did the defendant replace all office coffee with soup?',
caseType: 'criminal',

View File

@@ -1,5 +1,5 @@
/**
* Structured logging service for Improv Court
* Structured logging service for JuryRigged
* Provides JSON-formatted logs with session/phase/event correlation
*/

View File

@@ -108,7 +108,7 @@ export async function createServerApp(
app.use(express.static(publicDir));
app.get('/api/health', (_req, res) => {
res.json({ ok: true, service: 'improv-court-poc' });
res.json({ ok: true, service: 'juryrigged' });
});
app.get('/api/court/sessions', async (_req, res) => {
@@ -242,7 +242,7 @@ export async function createServerApp(
topic,
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: topic,
caseType,
sentenceOptions,
@@ -478,7 +478,7 @@ export async function bootstrap(): Promise<void> {
const port = Number.parseInt(process.env.PORT ?? '3000', 10);
app.listen(port, () => {
// eslint-disable-next-line no-console
console.log(`Improv Court POC running on http://localhost:${port}`);
console.log(`JuryRigged running on http://localhost:${port}`);
// eslint-disable-next-line no-console
console.log(`Operator Dashboard: http://localhost:${port}/operator`);
});

View File

@@ -15,7 +15,7 @@ async function createRunningSession() {
topic: 'Did the defendant replace all office coffee with soup?',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt:
'Did the defendant replace all office coffee with soup?',
caseType: 'criminal',
@@ -273,7 +273,7 @@ test(
topic: 'Did the defendant replace all office coffee with soup?',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt:
'Did the defendant replace all office coffee with soup?',
caseType: 'criminal',
@@ -326,7 +326,7 @@ test(
topic: 'Test case 1 for recovery',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case 1',
caseType: 'criminal',
sentenceOptions: ['Fine'],
@@ -343,7 +343,7 @@ test(
topic: 'Test case 2 for recovery',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case 2',
caseType: 'criminal',
sentenceOptions: ['Fine'],
@@ -361,7 +361,7 @@ test(
topic: 'Test case 3 for recovery',
participants,
metadata: {
mode: 'improv_court',
mode: 'juryrigged',
casePrompt: 'Test case 3',
caseType: 'criminal',
sentenceOptions: ['Fine'],

View File

@@ -82,7 +82,7 @@ export interface CourtTurn {
}
export interface CourtSessionMetadata {
mode: 'improv_court';
mode: 'juryrigged';
casePrompt: string;
caseType: CaseType;
sentenceOptions: string[];