Chen Operator Watch-Loop Implementation Plan
Historical adopted implementation contract. The completed Media milestone and subsequent scope extensions are reconciled in the canonical roadmap. Original exclusions are not a description of current deployed capability.
Date: 2026-08-25
Status: adopted by the operator; governed movie and exact-episode watch loop implemented
Repository revision inspected: 6ff007a27336de47ca3db79343eca284ff05072e
Planning authority: LegionCommander advisory planning plus repository-aware read-only analysis
Adoption decision: adopted by the operator on 2026-08-25 after an earlier deferral
Objective
Add one transport-neutral operator watch-loop inside Chen. An operator asks to watch a movie or one TV episode. Chen checks Plex first and stops when the canonical title is already present. When it is absent, Chen searches one configured Bitmagnet GraphQL endpoint, rejects candidates outside the policy, selects one deterministic eligible magnet, creates one durable exact-action Lich approval, and stops. A separate approved execution adds the torrent to qBittorrent. After verified completion, a second governed mutation imports the media, refreshes and verifies Plex, stops seeding, and reports availability.
The separately recorded operator decision authorizes implementation only within this contract. Every live acquisition and finalization mutation remains independently bound to its exact Lich approval and separate execution request.
The 2026-08-27 authorized recovery adds deterministic direct acquisition
intake, including Download the movie Interstellar for me., ahead of generic
conversation. The shared RuntimeKernel preserves the original title, checks
Plex first, and creates only the exact acquisition proposal and Lich review.
Quoted, negated, hypothetical, malformed, ambiguous, and unsupported requests
do not select this capability. Durable completed acquisitions are observed by
a bounded transport-neutral monitor, which may create the separate finalization
review but has no import, Plex, stop-seed, approval, or execution authority.
Planning provenance and limitations
LegionCommander received the objective, constraints, invariants, and the
repository paths listed below through PlanningInput. Its deterministic
planner incorrectly selected its special Zeus HTTP-client template because the
objective named Zeus evidence authority. That unrelated task graph was rejected
as a keyword-collision and no payload was materialized or executed. This is an
observed planner limitation, not authority to modify Zeus. The implementation
sequence below retains LegionCommander's advisory, staged, one-writer and
validation boundaries, and is grounded in direct inspection of the referenced
files and deployed services.
Observed current state
app/media_manager/service.pyalready checks Plex before its acquisition broker and returns immediately for an existing match.app/media_manager/acquisition_discovery.pyhas the stronger normalized candidate model, hostile-input normalization, canonical TV coordinates, deduplication, and deterministic scoring. Its runtime Bitmagnet provider is deliberately unavailable: it only checks whetherBITMAGNET_SEARCH_URLis set and returns no torrent observations.- Current acquisition scoring is not the requested policy. It awards 1080p and 720p differently, gives 2160p a low score, and penalizes large files; it does not hard-reject 2160p, movies at/over 2 GiB, or episodes at/over 500 MiB.
app/media_manager/adapters.pyhas Bitmagnet status and metadata-only search, Plex read/search, qBittorrent read status, and a paused-add executor. The Bitmagnet default islocalhost:3333, which is incorrect in the Luna container.BITMAGNET_SEARCH_URLis empty by default.app/media_manager/workflow.pycontains a legacy synchronous full-acquisition scaffold for qBittorrent completion, filesystem import, Plex refresh and verification. This is not the canonical durable Chen lifecycle.app/runtime/pipeline.pyresolves media requests transport-neutrally, but stores privatepending_chen_media_approvalsand consumes them inchen_media_approval_stage. That session state is not durable Lich approval authority and must not remain the watch-loop authority.app/governed_actions/*and the newer preparation/orchestration/execution brokerage provide reusable proposal, exact approval, preflight, evidence, execution and receipt concepts. Existing capability-specific implementations are narrower than a media workflow, so capability-local adapters and durable models are needed; a second governance system is not.docs/chen/domains/media_manager.jsonexposes the legacy paused-add mutation as enabled and import/refresh as disabled. Rubick defaults and Runtime Composition similarly describe incomplete readiness.- Compose supplies Plex and qBittorrent configuration to
discord-luna, but does not supplyBITMAGNET_SEARCH_URLorBITMAGNET_STATUS_URL. Both Luna services havehost.docker.internalmapping; media configuration is not currently identical between Discord and Web TUI. - Live inspection on 2026-08-25 found Plex and qBittorrent adapters available
from
discord-luna; Bitmagnet was unavailable there because its URLs were unset. Host GraphQLhttp://127.0.0.1:3333/graphqlreturned HTTP 200 and exposedtorrentContent.search,TorrentContent, andTorrentfields includinginfoHash,title,videoResolution,seeders,size, andmagnetUri. Host/statusreturned HTTP 503 because TMDB was down while DHT and Postgres were up.qbittorrent-gluetunwas running but Docker marked it unhealthy. These are current blockers even though earlier infrastructure evidence reported health.
Proposed architecture
any transport
-> shared intent boundary: watch(title, optional year/type/SxxExx)
-> Chen Media watch service (read-only phase)
-> canonical identity resolution
-> Plex exact/canonical lookup; present => report and stop
-> Rubick readiness + live Plex/Bitmagnet/qBit health
-> Bitmagnet GraphQL observation adapter
-> normalize -> hard filter -> deterministic ranking
-> durable Chen media acquisition proposal
-> Lich yes/no bound to proposal hash and exact magnet/action
-> separate execute request
-> qBittorrent add executor
-> Zeus before/after evidence + durable receipt
-> completion observation (read-only, explicit invocation/scheduled Chen task)
-> second durable finalize proposal
-> separate Lich approval and execution
-> validate completed payload and destination
-> atomic no-overwrite import/move
-> Plex section refresh and bounded verification
-> stop qBittorrent torrent only after verified library availability
-> Zeus evidence + receipt -> Luna reports available
Chen owns orchestration and media-specific policy. Lich remains the only approval authority. Zeus owns durable observations and receipts. Rubick reports capability/readiness evidence but grants no authority. Oracle renders facts, inferences and recommendations without executing. Transports only provide the envelope, render results, and submit explicit approval/execution commands.
Capability design
1. Read-only watch resolution
Create a capability-local MediaWatchService (name may be adjusted to existing
naming conventions) in app/media_manager/. It accepts a structured target and
returns a deterministic read model. Reuse Plex title/episode inspection and the
canonical episode identity contracts. Require clarification for ambiguous
movie remakes, missing TV coordinates, multiple Plex matches, or multiple
metadata identities. Do not query Bitmagnet until Plex absence is positively
observed.
The shared intent router should recognize affirmative operator directives such as “I want to watch …” without matching negated, quoted, hypothetical, report, log, or transcript content. The route must be transport-neutral and context free phrases must fail closed.
2. Bitmagnet observation adapter
Add a bounded BitmagnetGraphQLDiscoveryProvider beside acquisition discovery,
not a new indexer or kernel client. Configure explicit URLs:
- container GraphQL:
http://host.docker.internal:3333/graphql - container status:
http://host.docker.internal:3333/status
Use a fixed GraphQL document, variables, response byte limit, result limit,
timeout, and schema parser. Never accept caller-supplied URLs or query text.
Normalize TorrentContent/Torrent observations to RawCandidateObservation;
construct no locator beyond a validated Bitmagnet magnetUri/BTIH. Treat
GraphQL errors, partial/malformed responses, unknown sizes/resolutions,
timeouts, pagination overflow, and schema drift as unavailable. Bitmagnet
search may proceed when DHT and Postgres are up; TMDB-down or /status 503
from TMDB timeout is degraded metadata, not a closed indexer. Fail closed if
DHT or Postgres is down. Do not log raw magnets in default user output.
3. Eligibility policy before ranking
Introduce an explicit versioned policy, for example
chen_media_watch_v1. Filters run before scoring and are never ranking bonuses:
- movie size must be known and strictly less than 2 GiB;
- TV episode selected-media size must be known and strictly less than 500 MiB;
- resolution must be positively recognized as 720p or 1080p;
- reject 2160p, 4K, UHD, mixed/ambiguous resolution, packs for an episode request, wrong episode coordinates, wrong canonical title/year, non-video payloads, missing/invalid BTIH, and unsafe/malformed metadata;
- require a uniquely ranked winner. Define deterministic ordering only among eligible candidates, such as exact identity/episode match, availability, preferred 1080p then 720p, smaller size, normalized title and info-hash tie breakers. Equal decision keys fail closed rather than select arbitrarily.
Persist policy version, all rejection reasons, eligible set digest, ranking components and selected-candidate digest as evidence. Add a distinct movie identity path; do not stretch the current episode-only discovery service.
4. Durable acquisition mutation
Replace the private pending approval as authority with a media-specific consumer of the canonical durable governed-action lifecycle. Extend shared models only where required by at least this acquisition and finalization capability: an immutable structured action payload, proposal hash, capability ID, exact target/provider, expiry, idempotency key, approval ID, evidence refs, state revision, and receipt. Keep media parsing, qBit allowlists and evidence normalization outside shared platform modules.
The first proposal binds exactly one info-hash/magnet digest, destination,
category/tags, paused/start policy, expected qBit endpoint, operator and
conversation scope. Lich presents one yes/no. Approval does not execute. A
separate request revalidates proposal hash, expiry, current readiness, duplicate
torrent state, destination allowlist and exact approval before calling qBit.
Do not reuse approve_media_download_packet() as approval authority.
Plan the add as paused initially, record qBit acceptance and observed torrent hash, then explicitly start it only if the approved action includes that state transition. If a single qBit call can safely add-and-start with exact evidence, the implementation may use it, but it must remain one bounded approved action and must not weaken before/after verification.
5. Completion and finalization mutation
Use a second Chen mutation. Download completion is asynchronous, while import, refresh and stop-seed have different preconditions and rollback semantics. An explicit Chen observation or existing governed scheduler may detect completion; do not add a hidden/background loop. It creates a finalization proposal only after qBit reports the exact approved hash complete and stable, the payload is inside the configured completed-download root, and media validation succeeds.
The finalization proposal binds source path/hash/size, canonical target, destination path, Plex section key, qBit hash and ordered steps. Lich approves that exact bundle. Execute with no overwrite, traversal or symlink following. Capture source/destination evidence, move atomically when on one filesystem or use copy-fsync-hash-rename-delete semantics across filesystems. Refresh only the bound Plex section, then poll with a bounded timeout for the canonical title/episode. Stop seeding only after Plex verification. If Plex verification fails, retain the torrent state and report partial failure. A completed move is rollback-capable until Plex verification; Plex refresh is repeatable; stopping seeding is reversible by resume while the torrent/data remain present. Record each substep and compensation result in one durable receipt.
Expected file changes
Exact names for new capability-local modules should be finalized during the first implementation slice, but expected scope is:
app/media_manager/acquisition_discovery.pyor newapp/media_manager/bitmagnet_discovery.py: GraphQL adapter and normalization.app/media_manager/watch.py(new): target contract, Plex-first orchestration, policy filtering/ranking and proposal preparation.app/media_manager/adapters.py: bounded qBit state/start/stop operations and Plex section refresh/verification; keep read and mutation methods explicit.app/media_manager/models.py: watch result and capability-local action/ evidence models, if not isolated inwatch.py.app/media_manager/service.py: compose the new watch service; retire legacy resolver authority after compatibility migration.app/media_manager/workflow.pyandapproval_runtime.py: remove or demote synchronous/private approval execution after durable parity exists.app/governed_actions/models.py,preparation.py,orchestration.py,preflight.py, execution brokerage/registry, and store migrations only for the minimal reusable structured external-action lifecycle extension.app/runtime/pipeline.py: shared watch route and durable proposal/approval/ execution rendering; removechen_media_approval_stageauthority.app/runtime/kernel_factory.py: inject services, durable store, Lich, live evidence and executors consistently.app/rubick/capability_defaults.py, Runtime Composition and Oracle read models: truthful readiness/evidence and lifecycle visibility.docs/chen/domains/media_manager.json: new read, proposal, acquisition and finalization capabilities; enable only after evidence exists.docker-compose.yml: add identical Bitmagnet URL configuration to Discord and Web TUI, plus explicit media path mounts only if inspection proves they are absent. Do not change Docker socket/topology or embed secrets.tests/test_media_acquisition_discovery.py,tests/test_media_manager.py, routing/pipeline tests, governed-action lifecycle tests, Rubick/Oracle/ Runtime Composition tests, and new focused watch-loop tests.- canonical media, governed-action and deployment documentation.
Failure modes and fail-closed behavior
- Missing/unhealthy Plex: stop; absence cannot be inferred.
- Plex ambiguity or malformed response: stop and request clarification.
- Missing/unhealthy Bitmagnet or GraphQL error/schema drift: stop; no fallback indexer and no metadata-only acquisition claim.
- Missing/unhealthy qBit or VPN readiness: do not create an executable proposal.
- No eligible candidate or unresolved tie: report filters/reasons; no proposal.
- Stale/tampered/expired proposal, approval mismatch or scope mismatch: reject.
- qBit duplicate/ambiguous add response: reconcile by exact info-hash; otherwise persist failure, never claim dispatch.
- Completion timeout/error or payload mismatch: retain state and do not import.
- Destination exists, storage insufficient, symlink/traversal, unsupported extension or validation failure: do not move.
- Move succeeds but Plex refresh/index fails: preserve data, do not stop seed, record partial receipt and offer governed retry/rollback.
- Receipt/evidence persistence failure: do not claim success; compensate when safe and record the failure path.
- Process restart: recover from durable proposal/evidence/receipt and qBit/Plex truth, not session continuity.
Implementation sequence
- Define watch target/result, policy, Bitmagnet observation contract and readiness semantics; add pure tests.
- Implement fixed-query GraphQL provider and hard filters; test malformed, hostile, boundary-size, resolution, pack, ambiguity and deterministic order.
- Add Plex-first transport-neutral watch orchestration and intent routing; retain legacy paths only as non-authoritative compatibility seams.
- Extend the canonical durable governed-action platform minimally for structured external actions, with migrations, exact Lich binding, Zeus evidence and receipts. Prove it with a non-network fake executor first.
- Implement approved qBit add/start with idempotent exact-hash reconciliation.
- Implement explicit completion observation and the second finalization proposal; then import, Plex refresh/verification and stop-seed executor.
- Remove private
chen_media_approval_stageauthority after all transports reach the durable path; migrate or safely discard non-authoritative pending session entries without treating them as approvals. - Update Rubick, Oracle, Runtime Composition, manifest and canonical docs only to the readiness actually proven.
- Perform full source and deployed validation before enabling the capabilities.
Each slice should be separately reviewable. Infrastructure configuration must follow code/test readiness and current dirty work must be preserved.
Tests and acceptance criteria
Focused tests must cover:
- Plex-present short circuit with zero Bitmagnet/qBit calls;
- exact movie and episode identity, ambiguity and missing-coordinate failures;
- Bitmagnet URL/config/status/GraphQL failures and bounded response parsing;
- strict byte boundaries: movie
2 GiB - 1accepted and2 GiBrejected; episode500 MiB - 1accepted and500 MiBrejected; - 720p/1080p accepted; 2160p/4K/UHD, unknown and mixed resolution rejected;
- pack/wrong episode, unknown size, malformed magnet and hostile text rejected;
- deterministic eligible ordering, deduplication and tie refusal;
- exact proposal hash, expiry, operator/scope, approval and idempotency checks;
- approval never executes; execution never approves;
- restart recovery and duplicate qBit submission reconciliation;
- finalization preconditions, no-overwrite import, cross-filesystem copy failure, Plex timeout, stop-seed ordering, compensation and durable receipt replay;
- Discord, Web TUI and terminal parity through the shared runtime route;
- Rubick readiness does not imply authority; Oracle remains read-only;
- every new/modified function, method, class, helper and test has a meaningful docstring.
Before completion run py_compile on every changed Python file, focused media
and governed-action tests, the full practical pytest suite, architecture/static
checks, pre-commit, documentation validation and diff inspection. Investigate
and classify every failure.
Deployed validation
Runtime implementation requires docker compose up -d --build, recreation or
restart of affected Luna services, docker compose ps health verification and
real acceptance through each affected transport. Validate from inside both
discord-luna and luna-web-tui that configured Bitmagnet status and GraphQL,
Plex and qBit are reachable without printing secret values. Exercise:
- a title already in Plex;
- missing title with unhealthy dependency refusal;
- missing title with only rejected candidates;
- an eligible candidate through proposal, Lich yes/no and separate qBit execution;
- restart recovery;
- completion, second proposal, approved import/refresh/verification/stop-seed;
- final availability in a real Plex client library view (not remote playback).
Unit and integration tests use recorded GraphQL, qBit, and Plex fixtures plus fake executors. Live mutation acceptance uses the configured Bitmagnet, qBit, and Plex stack with an operator-chosen title.
Risks and dependencies
- Bitmagnet schema/version drift: pin the parsed contract and fail closed.
- The current status endpoint depends on TMDB. Recorded decision: DHT and Postgres up is enough to search; TMDB-down is degraded, not closed.
- qBit Gluetun health and qBit API readiness currently disagree. Capability readiness must require the VPN dependency evidence chosen by Rubick, not only authenticated Web API access.
- Moving completed files may cross filesystems and is higher-risk than qBit add.
- One approval covering future unknown completion paths would be too coarse; two exact proposals are the safer architecture.
- Existing media approval/workflow tests encode legacy behavior. Preserve compatibility only where it does not retain shadow authority.
Explicitly deferred
- Plex client selection, “in the living room,” remote playback, casting and playback control.
- Home Assistant lights or other home automation.
- Additional torrent indexers, Usenet or provider expansion.
- Autonomous/background polling loops; only explicit observation or the existing governed scheduling lifecycle may be used later.
bot.pysplit, a new architecture era, Compose socket/topology changes, Kiwix/model deletion, unrelated infrastructure work and cleanup.- Generalizing the platform beyond the minimal structured governed-action extension justified by acquisition and finalization.
Adopted review decisions and remaining gates
The adopted implementation uses these recorded decisions:
- Bitmagnet search may proceed when DHT and Postgres are up. TMDB down or
/status503 from TMDB timeout is degraded metadata, not a closed indexer. - Tests use recorded fixtures and fake executors. Live mutation acceptance uses the configured Bitmagnet, qBit, and Plex stack.
- Allowlisted completed-download and Plex library roots after live mount
inspection (
/media/plex/downloads/completeinto existing Plex roots). - qBit add is paused, then an explicit start, as two evidence points inside one approved acquisition action.
- Discard session
pending_chen_media_approvalsas non-authority. Do not promote them into Lich.
Plan review, portfolio adoption, implementation, and runtime mutation approval remain separate governed lifecycle stages. Source implementation exists, but completion remains gated on the full validation contract above, including live acquisition and finalization acceptance, restart recovery, independent VPN readiness evidence, and truthful compensation behavior.