Add Repixelizer witness advertisement export

This commit is contained in:
Meta Vens 2026-06-03 03:22:39 +01:00
commit 1e1a3441fe
9 changed files with 673 additions and 14 deletions

View file

@ -73,7 +73,9 @@ That is the machine described in `docs/lean-optimizer-algorithm-map.md`.
Comparison mode just runs that same `phase-field` result next to the baselines.
For hosted service state, queue visibility, and the future CultCache/CultMesh/Eve
operator surface, see `docs/verse-service-contract.md`.
operator surface, see `docs/verse-service-contract.md`. The read-only Odin
advertisement fixture/export is documented in
`docs/repixelizer-witness-contract.md`.
## Current Status

View file

@ -0,0 +1,191 @@
{
"commands": [
{
"authority": "repixelizer-route-policy",
"command": "repixelizer.job.submit",
"method": "POST",
"result": "accepted-denied-or-queued",
"route": "/api/jobs",
"surfaceId": "repixelizer.web.app",
"transport": "http"
},
{
"authority": "repixelizer-job-owner",
"command": "repixelizer.job.cancel_own",
"method": "DELETE",
"result": "accepted-denied-or-not-found",
"route": "/api/jobs/{job_id}",
"surfaceId": "repixelizer.web.app",
"transport": "http"
}
],
"contacts": [
{
"kind": "repo",
"path": "E:\\Projects\\repixelizer"
}
],
"freshness": {
"lastSeenAt": "2026-06-03T00:00:00Z",
"maxAgeMs": 15000,
"state": "fresh"
},
"kind": "service.product",
"nestedVerses": [
{
"authorityBoundary": "repixelizer-local-session-or-heimdall-account",
"carryRules": {
"commands": "repixelizer-route-policy-only",
"identity": "heimdall-claims-projected-when-enabled"
},
"kind": "session-space",
"parentVerseId": "gamecult.local",
"stateSchemas": [
"repixelizer.job.v0",
"repixelizer.job_event.v0",
"repixelizer.auth_projection.v0"
],
"surfaceIds": [
"repixelizer.web.app"
],
"verseId": "repixelizer.session.{sessionId}"
},
{
"authorityBoundary": "read-only-first-cut",
"kind": "hosted-operator-space",
"parentVerseId": "gamecult.local",
"stateSchemas": [
"repixelizer.job.v0",
"repixelizer.queue_snapshot.v0",
"repixelizer.job_event.v0",
"repixelizer.runtime_config.v0",
"repixelizer.auth_projection.v0"
],
"surfaceIds": [
"repixelizer.operator.surface"
],
"verseId": "repixelizer.operator"
}
],
"providerId": "repixelizer",
"runtime": {
"auth": {
"enabled": false,
"mode": "off",
"protectQueue": false,
"providers": [],
"required": false
},
"hostedDemo": false,
"queueCapacity": 32,
"showQueuePanel": false,
"spoolPath": "C:\\Users\\Meta\\AppData\\Local\\Temp\\repixelizer-gui-spool"
},
"schema": "gamecult.eve.provider_advertisement.v1",
"schemas": [
{
"authority": "accepted",
"description": "Job id, owner binding, status, timestamps, input/output artifact refs, error summary, and retention state.",
"owner": "repixelizer.gui",
"portable": true,
"schema": "repixelizer.job.v0",
"storage": "memory-dev-only-with-cc-witness-reserved"
},
{
"authority": "accepted",
"description": "Queue capacity, waiting/running counts, active job ids, oldest waiting age, and hosted mode.",
"owner": "repixelizer.gui",
"portable": true,
"schema": "repixelizer.queue_snapshot.v0",
"storage": "memory-dev-only-with-cc-witness-reserved"
},
{
"authority": "redacted-projection",
"description": "Redacted progress event, stage, index, timestamp, and job id.",
"owner": "repixelizer.gui",
"portable": true,
"schema": "repixelizer.job_event.v0",
"storage": "memory-dev-only-with-cc-witness-reserved"
},
{
"authority": "accepted",
"description": "Hosted flags, queue limits, visible UI flags, solver config hash placeholder, deployment id, and spool path.",
"owner": "repixelizer.gui",
"portable": true,
"schema": "repixelizer.runtime_config.v0",
"storage": "memory-dev-only-with-cc-witness-reserved"
},
{
"authority": "external-authority-projection",
"description": "Auth mode, provider availability, subject capability summary, and Heimdall claim freshness without raw tokens.",
"owner": "repixelizer.access",
"portable": true,
"schema": "repixelizer.auth_projection.v0",
"storage": "heimdall-projection-with-cc-witness-reserved"
}
],
"serviceId": "repixelizer.service",
"styleCapabilities": [
{
"lossiness": {
"read-only-first-cut": "operator surface names state and routes but does not own queue truth",
"tui": "no-pixel-canvas-editing; status, queue, artifact refs, and command intents only"
},
"preferredLowerings": [
"css",
"android-native",
"native-eve",
"tui"
],
"styleProfile": "repixelizer.product",
"tokenGroups": [
"repixelizer.comparisonCanvas",
"repixelizer.cleanupTool",
"repixelizer.queuePanel",
"repixelizer.operatorStatus"
]
}
],
"surfaces": [
{
"audience": "operator",
"commands": [],
"key": "cultmesh://repixelizer/surfaces/operator",
"mode": "read-only-first-cut",
"schema": "gamecult.eve.surface.v1",
"status": "planned",
"surfaceId": "repixelizer.operator.surface",
"transport": "cultmesh-document"
},
{
"canonical": false,
"canonicalSurfaceId": "repixelizer.operator.surface",
"schema": "gamecult.eve.surface.v1",
"surfaceId": "repixelizer.web.app",
"transport": "browser-lowering",
"url": "https://repixelizer.gamecult.org/app/"
}
],
"title": "Repixelizer",
"updatedAt": "2026-06-03T00:00:00Z",
"verseId": "gamecult.local",
"witnesses": [
{
"freshness": {
"state": "planned",
"updatedAt": "2026-06-03T00:00:00Z"
},
"id": "repixelizer.hosted.witness",
"kind": "cc-export-path-reserved",
"path": "state/repixelizer.witness.cc",
"redaction": "bulk-images-raw-tokens-and-private-claims-removed",
"schemas": [
"repixelizer.job.v0",
"repixelizer.queue_snapshot.v0",
"repixelizer.job_event.v0",
"repixelizer.runtime_config.v0",
"repixelizer.auth_projection.v0"
]
}
]
}

View file

@ -0,0 +1,80 @@
# Repixelizer Witness Contract
Repixelizer's hosted queue is still owned by `src/repixelizer/gui.py`.
This document defines the read-only first cut for making that state visible to
Odin, Huginn, CultMesh, and Eve without moving queue authority.
## Objective
Publish a `gamecult.eve.provider_advertisement.v1` fixture/export that names
Repixelizer's typed witness shapes and reserved CultCache `.cc` witness path.
The export is an authority map. It is not the queue store, not an Eve renderer,
and not a replacement for the current hosted routes.
Generate it with:
```powershell
repixelize witness-advertisement --out docs/fixtures/repixelizer.provider-advertisement.json
```
The checked-in fixture is:
`docs/fixtures/repixelizer.provider-advertisement.json`
## Authority Map
- Owner: `src/repixelizer/gui.py` owns queue admission, job lifecycle, progress
events, cancellation, heartbeat expiry, and spool cleanup.
- Inputs: uploaded image bytes, normalized hosted options, queue capacity,
worker heartbeat, cancellation requests, app-local subject binding, and
Heimdall-derived access projection.
- Outputs: in-process job records, queue summaries, SSE progress events, output
image artifacts, diagnostics refs, and route-level JSON projections.
- Derived state: provider advertisement JSON, future `.cc` witness export,
CultMesh publication, Odin discovery state, and Eve operator surfaces.
- Forbidden writers: Odin, Eve renderers, provider-advertisement fixtures,
witness exporters, and `.cc` inspection tools do not decide queue truth.
- Shared paths: browser GUI, hosted API routes, future `.cc` witness export,
future CultMesh publication, and Eve surfaces must describe the same
Repixelizer-owned job state.
- Cut line: this pass reserves witness names and paths only. A later pass may
add a real `.cc` writer or move queue ownership, but that must be an explicit
rebuild of ownership, not an exporter pretending to be persistence.
## Witness Schemas
The first advertisement names these schemas:
- `repixelizer.job.v0`: job id, account/session binding, status, timestamps,
input artifact ref, output artifact ref, error summary, and retention state.
- `repixelizer.queue_snapshot.v0`: queue capacity, waiting/running counts,
active job ids, oldest waiting age, and hosted-demo mode.
- `repixelizer.job_event.v0`: redacted progress event, stage, index, timestamp,
and job id.
- `repixelizer.runtime_config.v0`: hosted flags, queue protection, visible UI
flags, solver config hash placeholder, deployment id, and spool path.
- `repixelizer.auth_projection.v0`: auth mode, provider availability, subject
capability summary, and Heimdall claim freshness without raw tokens.
The reserved witness path is `state/repixelizer.witness.cc`.
## Provider Advertisement
`repixelize witness-advertisement` emits:
- `schema: gamecult.eve.provider_advertisement.v1`
- `providerId: repixelizer`
- schema catalog entries for the witness shapes above
- a reserved `.cc` witness entry with bulk image bytes, raw tokens, and private
claims marked as redacted
- a planned canonical Eve operator surface key:
`cultmesh://repixelizer/surfaces/operator`
- the current browser lowering at `/app/`
- route-owned command boundaries for job submit and own-job cancellation
- nested Verse declarations for session and hosted-operator spaces
- runtime projection from `HostedDemoConfig.from_env()` and
`AccessController.from_env()`
This is deliberately read-only. The current queue is still in-process memory.
The advertisement only tells discovery organs where truth lives and what shape
future durable witnesses must take.

View file

@ -87,20 +87,35 @@ tool palettes, and component variants. A Kotlin Android Eve runtime should be
able to run the cleanup flow with native controls while preserving the same
command boundary and product identity.
## Read-Only Witness Export
The first read-only provider advertisement is generated by:
```powershell
repixelize witness-advertisement --out docs/fixtures/repixelizer.provider-advertisement.json
```
It names the witness schemas above, reserves `state/repixelizer.witness.cc` for
the future CultCache export, and publishes the planned Eve operator surface key
without changing queue ownership. The detailed authority map lives in
`docs/repixelizer-witness-contract.md`.
## Migration Order
1. Define Repixelizer CultCache document shapes for job, queue, runtime, event,
and auth projection witness state.
2. Add a read-only export from the current `gui.py` queue/job manager into a
2. Publish a read-only `gamecult.eve.provider_advertisement.v1` fixture/export
that names those shapes and the reserved `.cc` witness path.
3. Add a read-only export from the current `gui.py` queue/job manager into a
`.cc` witness.
3. Publish the witness through CultMesh.
4. Add an Eve DSL provider over the witness and existing health/config routes.
5. Translate the existing browser GUI style and tool behavior into Eve style
4. Publish the witness through CultMesh.
5. Add an Eve DSL provider over the witness and existing health/config routes.
6. Translate the existing browser GUI style and tool behavior into Eve style
tokens, canvas primitives, and command descriptors.
6. Lower the current website from the Eve surface instead of treating it as the
7. Lower the current website from the Eve surface instead of treating it as the
product UI owner.
7. Register the surface with Odin.
8. Only after the witness is stable, decide whether queue ownership should move
8. Register the surface with Odin.
9. Only after the witness is stable, decide whether queue ownership should move
from in-process memory into the typed store.
The invariant: Heimdall owns shared auth; Repixelizer owns jobs and artifacts.

View file

@ -10,6 +10,7 @@ from .gui import main as gui_main
from .pipeline import run_pipeline
from .spritesheet import run_spritesheet
from .tuning import tune_solver_hyperparams
from .witness import main as witness_advertisement_main
def build_parser() -> argparse.ArgumentParser:
@ -131,6 +132,16 @@ def build_parser() -> argparse.ArgumentParser:
tune_parser.add_argument("--workers", type=int, default=1, help="Parallel benchmark worker processes per trial")
prepare_parser = subparsers.add_parser("prepare-corpus", help="Normalize imported corpus sheets into single sprites.")
prepare_parser.add_argument("--corpus-dir", default="examples/corpus", help="Corpus root containing originals/")
witness_parser = subparsers.add_parser(
"witness-advertisement",
help="Emit the read-only Repixelizer Eve provider advertisement.",
)
witness_parser.add_argument("--out", default=None, help="Write JSON to this path instead of stdout.")
witness_parser.add_argument("--updated-at", default=None, help="Override the advertisement timestamp.")
witness_parser.add_argument("--verse-id", default=None, help="Authoritative Verse id.")
witness_parser.add_argument("--public-base-url", default=None, help="Hosted browser lowering base URL.")
witness_parser.add_argument("--cc-witness-path", default=None, help="Reserved CultCache .cc witness path.")
witness_parser.add_argument("--eve-surface-key", default=None, help="Planned CultMesh Eve surface key.")
gui_parser = subparsers.add_parser("gui", help="Launch the web GUI.")
gui_parser.add_argument("--host", default="127.0.0.1", help="Host interface to bind")
gui_parser.add_argument("--port", type=int, default=8000, help="Port to bind")
@ -140,7 +151,19 @@ def build_parser() -> argparse.ArgumentParser:
def main(argv: list[str] | None = None) -> int:
raw_argv = list(sys.argv[1:] if argv is None else argv)
if not raw_argv or raw_argv[0] not in {"run", "compare", "spritesheet", "benchmark", "tune", "prepare-corpus", "gui", "-h", "--help"}:
commands = {
"run",
"compare",
"spritesheet",
"benchmark",
"tune",
"prepare-corpus",
"witness-advertisement",
"gui",
"-h",
"--help",
}
if not raw_argv or raw_argv[0] not in commands:
raw_argv = ["run", *raw_argv]
parser = build_parser()
args = parser.parse_args(raw_argv)
@ -218,6 +241,19 @@ def main(argv: list[str] | None = None) -> int:
if command == "prepare-corpus":
prepare_corpus(args.corpus_dir)
return 0
if command == "witness-advertisement":
witness_args: list[str] = []
for option, value in (
("--out", args.out),
("--updated-at", args.updated_at),
("--verse-id", args.verse_id),
("--public-base-url", args.public_base_url),
("--cc-witness-path", args.cc_witness_path),
("--eve-surface-key", args.eve_surface_key),
):
if value is not None:
witness_args.extend([option, str(value)])
return witness_advertisement_main(witness_args)
if command == "gui":
return gui_main(host=args.host, port=args.port, reload=args.reload)

266
src/repixelizer/witness.py Normal file
View file

@ -0,0 +1,266 @@
from __future__ import annotations
import argparse
import json
import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, TextIO
from .access import AccessController
from .gui import HostedDemoConfig
PROVIDER_ADVERTISEMENT_SCHEMA = "gamecult.eve.provider_advertisement.v1"
PROVIDER_ID = "repixelizer"
SERVICE_ID = "repixelizer.service"
DEFAULT_VERSE_ID = "gamecult.local"
DEFAULT_PUBLIC_BASE_URL = "https://repixelizer.gamecult.org"
DEFAULT_CC_WITNESS_PATH = "state/repixelizer.witness.cc"
DEFAULT_EVE_SURFACE_KEY = "cultmesh://repixelizer/surfaces/operator"
WITNESS_SCHEMAS: tuple[dict[str, Any], ...] = (
{
"schema": "repixelizer.job.v0",
"owner": "repixelizer.gui",
"authority": "accepted",
"storage": "memory-dev-only-with-cc-witness-reserved",
"portable": True,
"description": "Job id, owner binding, status, timestamps, input/output artifact refs, error summary, and retention state.",
},
{
"schema": "repixelizer.queue_snapshot.v0",
"owner": "repixelizer.gui",
"authority": "accepted",
"storage": "memory-dev-only-with-cc-witness-reserved",
"portable": True,
"description": "Queue capacity, waiting/running counts, active job ids, oldest waiting age, and hosted mode.",
},
{
"schema": "repixelizer.job_event.v0",
"owner": "repixelizer.gui",
"authority": "redacted-projection",
"storage": "memory-dev-only-with-cc-witness-reserved",
"portable": True,
"description": "Redacted progress event, stage, index, timestamp, and job id.",
},
{
"schema": "repixelizer.runtime_config.v0",
"owner": "repixelizer.gui",
"authority": "accepted",
"storage": "memory-dev-only-with-cc-witness-reserved",
"portable": True,
"description": "Hosted flags, queue limits, visible UI flags, solver config hash placeholder, deployment id, and spool path.",
},
{
"schema": "repixelizer.auth_projection.v0",
"owner": "repixelizer.access",
"authority": "external-authority-projection",
"storage": "heimdall-projection-with-cc-witness-reserved",
"portable": True,
"description": "Auth mode, provider availability, subject capability summary, and Heimdall claim freshness without raw tokens.",
},
)
def utc_now_iso() -> str:
return datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def build_provider_advertisement(
*,
updated_at: str | None = None,
verse_id: str = DEFAULT_VERSE_ID,
public_base_url: str = DEFAULT_PUBLIC_BASE_URL,
cc_witness_path: str = DEFAULT_CC_WITNESS_PATH,
eve_surface_key: str = DEFAULT_EVE_SURFACE_KEY,
config: HostedDemoConfig | None = None,
access_controller: AccessController | None = None,
) -> dict[str, Any]:
config = config or HostedDemoConfig.from_env()
access_controller = access_controller or AccessController.from_env(hosted_demo=config.hosted_demo)
updated_at = updated_at or utc_now_iso()
auth_payload = access_controller.public_payload()
return {
"schema": PROVIDER_ADVERTISEMENT_SCHEMA,
"providerId": PROVIDER_ID,
"serviceId": SERVICE_ID,
"verseId": verse_id,
"title": "Repixelizer",
"kind": "service.product",
"updatedAt": updated_at,
"freshness": {
"state": "fresh",
"lastSeenAt": updated_at,
"maxAgeMs": 15000,
},
"schemas": list(WITNESS_SCHEMAS),
"witnesses": [
{
"id": "repixelizer.hosted.witness",
"kind": "cc-export-path-reserved",
"path": cc_witness_path,
"schemas": [entry["schema"] for entry in WITNESS_SCHEMAS],
"redaction": "bulk-images-raw-tokens-and-private-claims-removed",
"freshness": {
"state": "planned",
"updatedAt": updated_at,
},
}
],
"surfaces": [
{
"surfaceId": "repixelizer.operator.surface",
"schema": "gamecult.eve.surface.v1",
"transport": "cultmesh-document",
"key": eve_surface_key,
"audience": "operator",
"mode": "read-only-first-cut",
"commands": [],
"status": "planned",
},
{
"surfaceId": "repixelizer.web.app",
"schema": "gamecult.eve.surface.v1",
"transport": "browser-lowering",
"url": f"{public_base_url.rstrip('/')}/app/",
"canonical": False,
"canonicalSurfaceId": "repixelizer.operator.surface",
},
],
"commands": [
{
"command": "repixelizer.job.submit",
"surfaceId": "repixelizer.web.app",
"transport": "http",
"route": "/api/jobs",
"method": "POST",
"authority": "repixelizer-route-policy",
"result": "accepted-denied-or-queued",
},
{
"command": "repixelizer.job.cancel_own",
"surfaceId": "repixelizer.web.app",
"transport": "http",
"route": "/api/jobs/{job_id}",
"method": "DELETE",
"authority": "repixelizer-job-owner",
"result": "accepted-denied-or-not-found",
},
],
"nestedVerses": [
{
"verseId": "repixelizer.session.{sessionId}",
"parentVerseId": verse_id,
"kind": "session-space",
"authorityBoundary": "repixelizer-local-session-or-heimdall-account",
"surfaceIds": ["repixelizer.web.app"],
"stateSchemas": [
"repixelizer.job.v0",
"repixelizer.job_event.v0",
"repixelizer.auth_projection.v0",
],
"carryRules": {
"identity": "heimdall-claims-projected-when-enabled",
"commands": "repixelizer-route-policy-only",
},
},
{
"verseId": "repixelizer.operator",
"parentVerseId": verse_id,
"kind": "hosted-operator-space",
"authorityBoundary": "read-only-first-cut",
"surfaceIds": ["repixelizer.operator.surface"],
"stateSchemas": [entry["schema"] for entry in WITNESS_SCHEMAS],
},
],
"styleCapabilities": [
{
"styleProfile": "repixelizer.product",
"tokenGroups": [
"repixelizer.comparisonCanvas",
"repixelizer.cleanupTool",
"repixelizer.queuePanel",
"repixelizer.operatorStatus",
],
"preferredLowerings": ["css", "android-native", "native-eve", "tui"],
"lossiness": {
"tui": "no-pixel-canvas-editing; status, queue, artifact refs, and command intents only",
"read-only-first-cut": "operator surface names state and routes but does not own queue truth",
},
}
],
"contacts": [
{
"kind": "repo",
"path": "E:\\Projects\\repixelizer",
}
],
"runtime": {
"hostedDemo": config.hosted_demo,
"queueCapacity": config.queue_capacity,
"showQueuePanel": config.show_queue_panel,
"spoolPath": str(config.spool_dir),
"auth": {
"enabled": bool(auth_payload.get("enabled")),
"mode": auth_payload.get("mode"),
"required": bool(auth_payload.get("required")),
"protectQueue": bool(auth_payload.get("protectQueue")),
"providers": auth_payload.get("providers", []),
},
},
}
def write_provider_advertisement(
output: Path | None,
*,
updated_at: str | None = None,
verse_id: str = DEFAULT_VERSE_ID,
public_base_url: str = DEFAULT_PUBLIC_BASE_URL,
cc_witness_path: str = DEFAULT_CC_WITNESS_PATH,
eve_surface_key: str = DEFAULT_EVE_SURFACE_KEY,
stream: TextIO = sys.stdout,
) -> None:
payload = build_provider_advertisement(
updated_at=updated_at,
verse_id=verse_id,
public_base_url=public_base_url,
cc_witness_path=cc_witness_path,
eve_surface_key=eve_surface_key,
)
encoded = json.dumps(payload, indent=2, sort_keys=True)
if output is None:
stream.write(f"{encoded}\n")
return
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(f"{encoded}\n", encoding="utf-8")
def build_witness_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="repixelize witness-advertisement",
description="Emit Repixelizer's read-only Eve provider advertisement fixture.",
)
parser.add_argument("--out", type=Path, default=None, help="Write JSON to this path instead of stdout.")
parser.add_argument("--updated-at", default=None, help="Override the advertisement timestamp.")
parser.add_argument("--verse-id", default=DEFAULT_VERSE_ID, help="Authoritative Verse id.")
parser.add_argument("--public-base-url", default=DEFAULT_PUBLIC_BASE_URL, help="Hosted browser lowering base URL.")
parser.add_argument("--cc-witness-path", default=DEFAULT_CC_WITNESS_PATH, help="Reserved CultCache .cc witness path.")
parser.add_argument("--eve-surface-key", default=DEFAULT_EVE_SURFACE_KEY, help="Planned CultMesh Eve surface key.")
return parser
def main(argv: list[str] | None = None) -> int:
parser = build_witness_parser()
args = parser.parse_args(argv)
write_provider_advertisement(
args.out,
updated_at=args.updated_at,
verse_id=args.verse_id,
public_base_url=args.public_base_url,
cc_witness_path=args.cc_witness_path,
eve_surface_key=args.eve_surface_key,
)
return 0

View file

@ -34,3 +34,4 @@
{"ts": "2026-05-30T22:47:57+00:00", "type": "implementation", "status": "ok", "note": "Added spritesheet CLI mode: detect separated foreground regions or select requested largest components, call run_pipeline_rgba per crop, pack outputs in source-row order, and write spritesheet diagnostics; fixture auto-detects six sprites."}
{"ts": "2026-05-30T23:39:55+00:00", "type": "implementation", "status": "ok", "note": "Spritesheet automatic sizing now uses a sheet-level median texel density before reconstruction; on the 12 generated spritesheets in 019e7aa3-4bbb-7302-bafd-273f2aa31e53, worst per-sheet density drift improved from 42.21% to 1.786% across 153 sprites."}
{"ts": "2026-05-31T01:13:17+00:00", "type": "implementation", "status": "ok", "note": "Spritesheet mode now supports explicit grid ownership via sheet_columns/sheet_rows plus individual sprite export. The 12 generated character sheets were rerun as 3x2 grids, producing 72 exports with no component chopping and worst within-sheet density deviation 0.124%."}
{"ts": "2026-06-03T02:21:02+00:00", "type": "implementation", "status": "ok", "note": "Added read-only Repixelizer provider advertisement fixture/export naming job/queue/event/runtime/auth witness schemas and reserved state/repixelizer.witness.cc path without moving gui.py queue ownership."}

View file

@ -53,6 +53,7 @@ accepted_design:
- Hosted mode owns the public front door in `src/repixelizer/gui.py`: `/` is a landing page there, while local/self-host runs keep root as a plain redirect to `/app/`.
- `src/repixelizer/access.py` owns the hosted access seam: trusted-header ingress still works, `GC_ACCESS_MODE=heimdall` verifies Heimdall Ed25519 access tokens locally, and auth attempts are adopted into local httpOnly cookie sessions.
- In Heimdall mode, Repixelizer keeps a separate httpOnly refresh cookie and asks Heimdall to reissue a short-lived access claim before forcing the user back through provider OAuth.
- `src/repixelizer/witness.py` owns the read-only first-cut Odin provider advertisement export. It names Repixelizer job, queue, event, runtime, and auth witness schemas plus the reserved `state/repixelizer.witness.cc` path, but it does not own queue truth or write CultCache.
- The solver starts from fixed lattice centers, adds tiny seeded noise, follows a direct hierarchical flatness signal, enforces local lattice coherence through spring relaxation, then rounds to real source pixels for final output.
- Cleanup is local and secondary; it does not define the core reconstruction model.
- `source_fidelity` and `source_structure` separate lattice agreement from visible structure.
@ -68,6 +69,7 @@ landed_baseline:
- Spritesheet CLI mode detects separated foreground regions or selects the largest requested sprite count, then repixelizes each crop independently through `phase-field`.
- `source_structure` reporting alongside `source_fidelity`.
- Hosted landing page and Heimdall auth seam in the web layer.
- Read-only `gamecult.eve.provider_advertisement.v1` fixture/export for Odin discovery and future CultCache/Eve witness work.
rejected_paths:
- name: tray_optimizer_sprawl
@ -95,11 +97,16 @@ current_status:
endpoint before falling back to provider OAuth, while
`src/repixelizer/gui.py` owns provider-start routes, direct backend callback
receipt, hosted landing-page login buttons, route guards, and queue/job
ownership checks. Self-host remains permissive by default unless
`GC_ACCESS_*` turns the seam on. The solver is shipping with a usable but
not final tuned config: local search radius `0.215`, blend `0.24`, move
weight `0.09`, grid alignment `4.75`, signal energy power `1.4`, and signal
peak power `3.25`, aligned in `config/solver_params.json` and the docs.
ownership checks. `src/repixelizer/witness.py` now emits a read-only
`gamecult.eve.provider_advertisement.v1` export and checked-in fixture that
names the Repixelizer job/queue/event/runtime/auth witness schemas and
reserved `state/repixelizer.witness.cc` path for Odin/Huginn discovery,
without moving queue ownership out of `gui.py`. Self-host remains
permissive by default unless `GC_ACCESS_*` turns the seam on. The solver is
shipping with a usable but not final tuned config: local search radius
`0.215`, blend `0.24`, move weight `0.09`, grid alignment `4.75`, signal
energy power `1.4`, and signal peak power `3.25`, aligned in
`config/solver_params.json` and the docs.
Diagnostic edge analysis remains an observer/GUI gauge. The tuning pass proved that wide
local-search windows caused neighbor feature poaching and that tighter radius
plus stronger blend is the current motion-geometry basin. It did not fully

61
tests/test_witness.py Normal file
View file

@ -0,0 +1,61 @@
from __future__ import annotations
import json
from pathlib import Path
from repixelizer.cli import main as cli_main
from repixelizer.witness import build_provider_advertisement
def test_provider_advertisement_names_witness_schemas(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setenv("REPIXELIZER_HOSTED_DEMO", "1")
monkeypatch.setenv("REPIXELIZER_QUEUE_CAPACITY", "7")
monkeypatch.setenv("REPIXELIZER_SPOOL_DIR", str(tmp_path / "spool"))
monkeypatch.setenv("GC_ACCESS_MODE", "off")
payload = build_provider_advertisement(updated_at="2026-06-03T00:00:00Z")
assert payload["schema"] == "gamecult.eve.provider_advertisement.v1"
assert payload["providerId"] == "repixelizer"
assert payload["runtime"]["hostedDemo"] is True
assert payload["runtime"]["queueCapacity"] == 7
assert payload["runtime"]["auth"]["mode"] == "off"
schema_names = {entry["schema"] for entry in payload["schemas"]}
assert schema_names == {
"repixelizer.auth_projection.v0",
"repixelizer.job.v0",
"repixelizer.job_event.v0",
"repixelizer.queue_snapshot.v0",
"repixelizer.runtime_config.v0",
}
witness = payload["witnesses"][0]
assert witness["kind"] == "cc-export-path-reserved"
assert witness["path"] == "state/repixelizer.witness.cc"
assert witness["freshness"]["state"] == "planned"
def test_witness_advertisement_cli_writes_json(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setenv("REPIXELIZER_SPOOL_DIR", str(tmp_path / "spool"))
monkeypatch.setenv("GC_ACCESS_MODE", "off")
out = tmp_path / "repixelizer.provider-advertisement.json"
exit_code = cli_main(
[
"witness-advertisement",
"--out",
str(out),
"--updated-at",
"2026-06-03T00:00:00Z",
"--verse-id",
"gamecult.test",
"--cc-witness-path",
"state/test-repixelizer.witness.cc",
]
)
assert exit_code == 0
payload = json.loads(out.read_text(encoding="utf-8"))
assert payload["verseId"] == "gamecult.test"
assert payload["witnesses"][0]["path"] == "state/test-repixelizer.witness.cc"
assert payload["commands"][0]["route"] == "/api/jobs"