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
73 changes: 60 additions & 13 deletions docs/architecture/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,63 @@ boundary; the engine remains responsible for configuration, source acquisition,
candidate generation, review, immutable acceptance, reports, and generated
outputs.

The foundation slice provides a read-only loopback HTTP server with a beginner
status shell and safe health/status JSON. When `gui/dist` contains a Vite build,
the server serves only its `index.html` and allowlisted static asset types.
Without that build output, `/` falls back to the safe placeholder shell. Static
requests are confined to the configured `gui/dist` subtree, do not list
directories, and do not expose accepted-generation contents or provider files.
The built landing surface consumes `GET /api/status` from the same origin and
projects only its validated running, lineup, next-action, and read-only facts
into beginner copy. The browser retains no raw status payload and has no
state-changing API capability; unavailable, failed, or invalid status responses
render a safe degraded message.
The local server provides a beginner status shell and safe health/status JSON.
The landing surface consumes `GET /api/status` from the same origin and
projects validated running, lineup, next-action, and read-only facts into
beginner copy. It retains no raw status payload. The server also has explicit
guided-setup proposal/acceptance and source-refresh routes; those state-changing
operations remain engine-owned and are not part of the read-only guide contract.
The loopback listener serves only its `index.html` and allowlisted static asset
types when `gui/dist` contains a Vite build. Otherwise `/` falls back to the
safe placeholder shell. Static requests are confined to the configured
`gui/dist` subtree, do not list directories, and do not expose private files.

The One Guide API (`GET`/`HEAD /api/one-guide/...`) returns the versioned
`one-guide/v1` projection defined by
[`one-guide-projection.schema.json`](../../schemas/one-guide-projection.schema.json).
It reads the verified immutable accepted XMLTV artifact through a disposable
derived in-memory catalogue; it does not run recovery, enumerate configured
sources, fetch the network, or mutate accepted/source/evidence state. Live Now
and Starting Soon are evaluated in UTC at request time; the public query
function takes an explicit evaluation instant.
If no accepted generation exists, or accepted XMLTV is unavailable/not
generated, these routes return HTTP 503 with `one-guide-unavailable`. An
accepted guide that has no programmes matching a query remains a successful
HTTP 200 response with an empty result; unavailable source state is never
represented as a valid empty guide.
The Slice 1 built-in category registry contains only Live Now, Starting Soon,
Wrestling, Football, Baseball, Soccer, Movies, and News. It maps only those
recognized XMLTV categories to stable keys; unrecognized categories are omitted.
Cross-channel grouping requires an explicit `CanonicalEventId` in projection
input; the generated item identity also includes the normalized title, subtitle,
episode number, and schedule. Without that explicit ID, rows are scoped to a
source/channel and sanitized source-row evidence, including descriptions,
categories (recognized or not), and XMLTV new/live/premiere flags. Conflicting
evidence remains separate; exact equivalent rows share an item. The fixed
category registry still omits unrecognized categories from output. Accepted
XMLTV does not preserve `CanonicalEventId` or provider attribution per
programme. The HTTP projection labels its source `Accepted guide`. It reports
`Kind` as `Programme` unless input explicitly carries a recognized kind.
Sport, league, participant, promotion, entitlement, and launch metadata are
reported only when actually supplied; title text is never used to invent them.
Source, channel, and promotion identifiers are opaque domain-separated hashes.

The server keeps one bounded in-memory projection for the current accepted
generation per repository root, keyed by its pointer and manifest identity. Catalogue construction
precomputes parsed UTC interval ticks and a direct item-ID lookup. Warm list
requests validate the current pointer, scan the cached records, and clone only
the returned page (maximum 100); Details uses the ID lookup. Warm requests do
not reread or reparse accepted XMLTV. A new generation replaces the entry. The
cache is derived and disposable; no database or second source of truth is
introduced.

List routes use `limit` and `offset` for bounded enumeration (maximum 100
items); each item reports the number of offerings and whether its 16-offering
summary is truncated.

The read API exposes a bounded safe projection, not the accepted generation's
raw M3U/XMLTV bytes, hashes, credentials, private paths, or stream URLs. The
current server remains loopback-only; authenticated LAN access is not provided.

The primary deployment modes are:

Expand All @@ -110,8 +156,9 @@ commands, accepted-generation contract, and safety boundaries. A native wrapper
is optional future packaging only; it is not a second product UI or a second
state authority.

Docker, Windows service, full HTTP/API behavior, and appearance modes remain
future work. The local server foundation does not change those deployment claims.
Docker, Windows service, authenticated LAN access, appearance modes, and the
distributed client contract remain future work. The local server does not
change those deployment claims.

#### Reuse existing GUI work

Expand Down
85 changes: 68 additions & 17 deletions docs/developer/DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,49 @@ Follow [CONTRIBUTING.md](../../CONTRIBUTING.md) for the branch, commit, and revi

ChannelForge's primary UI is a browser-based local web UI served by the ChannelForge engine. The browser consumes the engine's documented HTTP/API boundary; it does not host a second state authority or receive direct access to provider files, accepted generations, private paths, or local processes.

The first web-server foundation is a read-only loopback server. It provides a beginner-facing placeholder shell and safe health/status responses. Status reads validated accepted-generation metadata only to derive lineup status; it does not read provider data or change any lineup state.
The local server is loopback-only. It provides a beginner-facing shell and safe
health/status responses; status reads validated accepted-generation metadata
only. Guided Setup proposal/acceptance and source-refresh operations are
separate engine-owned routes. Reads never trigger source acquisition.

The One Guide read API projects the verified accepted XMLTV artifact to the
versioned `one-guide/v1` contract in
[`one-guide-projection.schema.json`](../../schemas/one-guide-projection.schema.json).
The projection does not run accepted-state recovery, enumerate configured
sources, fetch the network, or mutate state. Query functions require an
explicit `EvaluationTimeUtc`; HTTP requests evaluate at request time. Starting
Soon means starts strictly after evaluation and no more than two hours later.
Live intervals are half-open: start is included, stop is excluded. List routes
accept `limit` (1–100) and `offset` (nonnegative decimal integer); responses
include total counts and truncation flags. Each item returns at most 16
offerings and reports `OfferingCount` and `OfferingsTruncated`.
If there is no accepted generation, or accepted XMLTV is unavailable/not
generated, the HTTP route returns 503 `one-guide-unavailable`. A valid guide
with no matching programmes still returns 200 and an empty result; this
distinguishes source unavailability from a genuine empty query.

The accepted merged XMLTV does not retain provider attribution or a
`CanonicalEventId` per programme. Cross-channel grouping requires the same
explicit identity and normalized title, subtitle, episode number, and schedule.
Without explicit identity, rows on a source/channel correlate only when their
sanitized source-row evidence also matches, including descriptions, recognized
or unrecognized categories, and XMLTV new/live/premiere flags. Conflicts remain
separate; the fixed registry still omits unrecognized categories from output.
Accepted-guide HTTP responses use
the display label `Accepted guide` and opaque domain-separated source, channel,
and promotion identifiers. They report `Kind` as `Programme` unless input
explicitly supplies a recognized kind. Sport, promotion, entitlement, and
launch metadata are emitted only when input actually contains them; title text
is never used to infer those fields.

The server holds a one-entry in-memory projection cache keyed by the current
accepted pointer and manifest identity. Catalogue construction parses and
stores UTC interval ticks once and builds a direct item-ID lookup. Warm list
requests validate the pointer and scan the cached records without rereading or
reparsing XMLTV; they create response objects only for the returned page of at
most 100 items. Details uses the direct lookup. A generation change replaces
the entry; the cache is disposable and introduces no persisted state or second
authority.

The primary deployment modes are:

Expand Down Expand Up @@ -499,22 +541,31 @@ protected, served without directory listings, and unsupported asset types
return `404`. There is no SPA fallback: an unknown static path returns `404`.
`GET` and `HEAD` return the same status and content metadata.

The read-only endpoints remain:

| Method | Path | Purpose |
| ------------- | ------------- | ----------------------------------------------- |
| `GET`, `HEAD` | `/` | Built UI or safe placeholder shell |
| `GET`, `HEAD` | `/health` | Safe health/status JSON |
| `GET`, `HEAD` | `/api/status` | Safe status contract consumed by the browser UI |

The dashboard is presentation-only. It makes one same-origin `GET` request and
never sends `POST`, `PUT`, `PATCH`, or `DELETE`. Setup and review actions remain
future until their engine/API contracts are implemented. Only the engine may
own candidate generation, review, acceptance, reports, and output publication.
The server and browser expose no provider URLs, credentials, private paths,
hashes, generation IDs, accepted-generation contents, or parser details.
Stop the foreground server with `Ctrl+C`. The server has no Docker, Windows
service, packaging, release, deployment, or tester-build behavior.
The read endpoints are:

| Method | Path | Purpose |
| ------------- | -------------------------------- | ----------------------------------------------- |
| `GET`, `HEAD` | `/` | Built UI or safe placeholder shell |
| `GET`, `HEAD` | `/health` | Safe health/status JSON |
| `GET`, `HEAD` | `/api/status` | Safe status contract consumed by the browser UI |
| `GET`, `HEAD` | `/api/one-guide/live-now` | Accepted programmes live at request evaluation |
| `GET`, `HEAD` | `/api/one-guide/starting-soon` | Accepted programmes starting within two hours |
| `GET`, `HEAD` | `/api/one-guide/category/{key}` | Bounded items in a fixed One Guide category |
| `GET`, `HEAD` | `/api/one-guide/items/{item-id}` | One deterministic item detail |

Slice 1 built-in category keys: `live-now`, `starting-soon`, `wrestling`,
`football`, `baseball`, `soccer`, `movies`, and `news`. Category aliases map
deterministically; other XMLTV categories are omitted. Responses use UTC timestamps and contain no raw
URLs, credentials, private paths, accepted hashes, or parser diagnostics. The
server remains loopback-only; authentication and LAN access are not implemented.

The dashboard remains presentation-only and currently consumes `/api/status`;
it does not yet render the One Guide routes. Guided Setup and refresh requests
remain engine-owned operations. The browser has no direct filesystem or
provider-file access. The safe One Guide DTO is a deliberate projection of
accepted guide content, not the raw accepted-generation bytes. Stop the
foreground server with `Ctrl+C`. Docker, Windows service, packaging, release,
deployment, and tester-build behavior remain unavailable.

### Reusable existing GUI work

Expand Down
Loading
Loading