Gives the system specification an owner. It defines the PR-nnn project requirements and SR-nnn cross-component contracts that every component spec traces up to, and until now it lived in no repository at all. The three component repositories are linked from the README and gitignored here rather than added as submodules. A submodule pins a commit, so with feature branches and worktrees in flight across the components, every component commit would leave this repository's pointer stale. The dependency is meant to run the other way: components pull this repository in for the shared tooling and system spec, both of which change rarely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
124 lines
4.8 KiB
Markdown
124 lines
4.8 KiB
Markdown
# JRay
|
|
|
|
**A self-hosted alternative to Amazon X-Ray.** While watching, the viewer can
|
|
see who is on screen — for their own library, on their own hardware, with nobody
|
|
else learning what they own.
|
|
|
|
This is the project home. It owns the [system specification](SPEC.md) — the
|
|
requirements that span more than one component — and the tooling shared between
|
|
them. The code lives in three separate repositories, linked below.
|
|
|
|
---
|
|
|
|
## The three components
|
|
|
|
| Repository | Role | Language |
|
|
|---|---|---|
|
|
| [`scene-actor-extraction`](https://gitea.tourolle.paris/dtourolle/scene-actor-extraction) | Derives presence data from a media file | C++ / Python |
|
|
| [`jRay`](https://gitea.tourolle.paris/dtourolle/jRay) | Jellyfin plugin: surfaces it in the player, owns the truth-file format | C# |
|
|
| [`JRay-public-server`](https://gitea.tourolle.paris/dtourolle/JRay-public-server) | Exchanges presence data between instances | Rust |
|
|
|
|
They ship independently — the plugin to Jellyfin's catalogue, the server as a
|
|
binary, the pipeline to a GPU host — which is why they are separate repositories
|
|
rather than one monorepo.
|
|
|
|
```
|
|
media file ──► extraction ──► truth file (sidecar or pushed) ──► plugin ──► player overlay
|
|
▲ │
|
|
│ ▼
|
|
gallery Jmanifest ──► public server ──► other instances
|
|
(Jellyfin + TMDB)
|
|
```
|
|
|
|
Two axes, deliberately separate: **presence data** flows outward and is
|
|
shareable, being timings against public identifiers. **Gallery data** — the
|
|
actor reference faces — is built locally and never leaves the instance (SR-005).
|
|
|
|
---
|
|
|
|
## Setting up
|
|
|
|
Clone this repository, then the components beside it:
|
|
|
|
```sh
|
|
git clone git@gitea.tourolle.paris:dtourolle/jray-project.git
|
|
cd jray-project
|
|
|
|
git clone git@gitea.tourolle.paris:dtourolle/scene-actor-extraction.git
|
|
git clone git@gitea.tourolle.paris:dtourolle/jRay.git
|
|
git clone git@gitea.tourolle.paris:dtourolle/JRay-public-server.git
|
|
```
|
|
|
|
The component directories are `.gitignore`d here, so they sit beside the system
|
|
spec without this repository trying to track them. Each has its own README with
|
|
build instructions.
|
|
|
|
### Why not submodules for the components
|
|
|
|
A submodule pins a commit. With feature branches and worktrees in flight across
|
|
the components, every component commit would leave this repository's pointer
|
|
stale and its `git status` dirty until someone committed a pointer bump — churn
|
|
that buys nothing, since the components are developed together in one directory
|
|
anyway.
|
|
|
|
The dependency runs the other way instead: **components pull *this* repository
|
|
in** for the shared tooling and the system spec, both of which change rarely.
|
|
That is the asymmetry submodules suit.
|
|
|
|
---
|
|
|
|
## Where to start reading
|
|
|
|
| Doc | Owns |
|
|
|---|---|
|
|
| [`SPEC.md`](SPEC.md) | **System requirements** — `PR-nnn` project goals, `SR-nnn` cross-component contracts |
|
|
| [`CLAUDE.md`](CLAUDE.md) | Working notes and the invariants that must not be violated silently |
|
|
| Each repo's `SPEC.md` | That component's software requirements |
|
|
| Each repo's `docs/requirements.md` | Its stable requirement IDs, status, and verification plan |
|
|
|
|
Read the system spec first. Every component requirement traces up to an
|
|
`SR-nnn`, and every `SR-nnn` to a `PR-nnn`, so the chain explains *why* a given
|
|
piece of code exists.
|
|
|
|
---
|
|
|
|
## Requirement traceability
|
|
|
|
Requirements are traceable **up** to the project goal and **down** to the code
|
|
implementing them. A requirement nothing traces to is either unnecessary or
|
|
unimplemented, and both are worth knowing.
|
|
|
|
```
|
|
PR-nnn project requirement (SPEC.md §1) — why the system exists
|
|
└─ SR-nnn system requirement (SPEC.md §3) — what spans components
|
|
└─ component requirement (each repo's docs/requirements.md)
|
|
└─ TRACES tag (source)
|
|
```
|
|
|
|
Tag the code that *satisfies* a requirement:
|
|
|
|
```rust
|
|
/// TRACES: UR-003 | SR-004
|
|
pub fn validate_manifest(m: Jmanifest) -> VResult<ValidManifest> { … }
|
|
```
|
|
|
|
A pipe separates requirement *types*; a comma separates IDs within a type. Tag
|
|
the unit that decides, not every helper it calls — a tag on every function is
|
|
noise, and rots faster than it helps.
|
|
|
|
The gate reports coverage, orphan tags (an ID no register defines), and untraced
|
|
requirements. Two rules it inherits from JellyTau, both learned the hard way:
|
|
|
|
- **Denominators are read from the register at run time, never hardcoded.** A
|
|
gate that divides by a frozen literal reported 158% coverage for months and so
|
|
could never fail — worse than no gate, because it was trusted.
|
|
- **Coverage above 100% is a hard failure**, not a pass. It means the
|
|
computation is broken, and it is the signal that catches the above
|
|
immediately.
|
|
|
|
---
|
|
|
|
## Licence
|
|
|
|
GPLv3, matching the Jellyfin plugin it serves.
|