Ctrl K
ComunidadGuías…

specs/011-mylar-modern-tagger/plan.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Implementation plan: Modern tagging and optional DDL transport

**Branch**: `codex/mylar-modern-tagger` | **Date**: 2026-09-28 | [Specification](spec.md)

## Summary

Expand with tested compatibility helpers, build an isolated pinned modern runtime,
then integrate it behind an explicit backend choice. Preserve the legacy backend
through a canary and rollback window. Evaluate optional DDL discovery separately.
Do not transplant either upstream PR wholesale.

## Technical context

Python 3.10 in the pinned LinuxServer base, stdlib unittest, generated CBZ fixtures,
Mylar config and workflow.sqlite, existing normalizer receipts. Candidate ComicTagger
is 1.6.0b11.dev0 with built-in `cr`; curl_cffi 0.16.3 is the discovery candidate.
Both require a resolved hash lock and target-platform build proof before installation.
No new service, port, mount, privilege or public route is required. Process limits:
version 10 seconds, tag 180 seconds, combined captured output 64 KiB. Archive metadata
input is limited to 256 KiB. No second conversion writer or parallel tagging pool.

## Constitution check

Before and after design: portable examples, private settings excluded, no live
mutation in fixture validation, pinned dependencies, normalizer ownership preserved.
The first increment adds build-tested helpers only: Compose, env example, preparation,
metadata and ingress contracts need no runtime changes. At backend activation, update
all affected contract surfaces together and regenerate catalog output if metadata changes.

## Project structure and ownership

- `stacks/mylar3/config/tagger_runtime.py`: bounded child execution and typed results.
- `tagger_metadata.py`: explicit overrides and conservative ComicInfo reconciliation.
- `tagger_archive.py`: bounded member verification and reconciliation into a new
  operation file. It never publishes over a source.
- `tagger_adapter.py`: inactive staged archive verification, atomic exchange owner
  and private versioned receipts. Integration must coordinate other media writers.
- `patch_tagger_handoff.py` and `tagger_handoff.py`: checked native ownership guards
  and explicit in-place results. Future producer/setting adapter selects the backend.
- `patch_tagger_backend.py` and `tagger_backend.py`: persisted backend choice,
  pre-mutation validation and native dispatch. Modern stays unavailable until its
  writer, startup and canary gates close. Legacy remains the default.
- `media_writer.py`: shared local flock and durable normalizer recovery fence.
  Identical copies ship in both image contexts, checked by repository tests.
- `native_writers.py`, `processing_guard.py` and normalizer `writer_cycle.py`:
  complete manual tagging, post-processing and conversion/maintenance ownership.
- `archive_monitor.py`: receipts from verified results, not CLI banner interpretation.
- `stacks/mylar3/Dockerfile`: isolated venv copied with native ICU dependencies into
  final image. Never overwrite Mylar's Python dependency environment.
- `ddl_transport.py`: discovery session factory, exception normalization,
  resource lifetime; `verified_transfer.py` remains byte-download owner.
- Spec files: research, data model, contracts, acceptance, quickstart and tasks.

## Migration sequence

1. Implement and test process/metadata helpers without enabling a new backend.
2. Resolve/hash-lock the candidate and all transitive dependencies for the pinned
   base. Build headless without Qt. Run the real CLI and writer, offline, in final
   runtime layout. Version banner/exit behavior must be observed, not guessed.
3. Implement archive staging, page/member hashes, XML policy, source-race detection,
   permissions, fsync and atomic publication. Persist private recovery receipts and
   crash recovery before a source can be replaced. Reject unsupported inputs.
   Native manual tagging currently copies the returned temporary path over its
   destination and deletes that temporary file. Integration must replace that
  publication path, not return the original path into its cleanup branch. Automatic
   post-processing also consumes a temporary path, so its handoff needs separate proof.
   The fixture publisher now retains a displaced source via Linux atomic exchange.
   It refuses hardlinks and extended attributes/ACLs. An unsupported filesystem has
   no non-atomic fallback. Prove the real media filesystem and exclude other writers
   before native activation; conflict copies must not be deleted automatically.
4. Wire native automatic/manual tagging, annual identity, disabled tagging, overwrite
   settings and monitor outcomes. Legacy remains default; unsupported CBL writes are
   explicit and retain the configured preference. Do not auto-fallback after a write.
5. Prove matrix and both backend paths. Publish image; back up affected app state and
   each canary file, verify isolated restore, wait for active processing to clear,
   deploy only Mylar, canary generated fixtures, then selected real copies. Verify
   records/media and unrelated uptime. Retain old image until verification completes.
6. Independently add opt-in curl discovery. Requests remains archive transport. Full
   curl streaming needs a bounded-buffer design and every transport gate before use.

## NFS publication strategy

The real NFSv4 media mount rejected atomic exchange with `EINVAL` and exposed
`system.nfs4_acl`. The version-1 exchange publisher remains unchanged by default;
there is no automatic fallback. `tagger_nfs.Publisher` is a separate, inactive
version-2 strategy with its own journal directory.

It durably records intent, moves the original inode into the private same-filesystem
workspace, checks it again, then creates the candidate at the source name with a
no-clobber hard link. A competing destination is retained, never overwritten. The
original stays recoverable until the candidate's bytes, inode, ownership, mode and
attributes verify. NFS errors can describe operations that actually completed;
recovery decides from both filenames and identities, never the syscall result alone.
A crash before the candidate link restores the original without replaying tagging.
A crash after linking verifies the candidate and reconciles its temporary hard link.

Unlike exchange, this strategy has a missing-name interval, potentially lasting
until restart recovery. Native integration must exclude all media writers and
finish startup recovery before scans, tagging or placement. Uncoordinated readers
may briefly observe an absent filename. Rollback to an older image requires completed
reconciliation or restoration of verified state/media; old readers intentionally
reject v2 receipts. Do not enable this path merely because fixture tests pass.

`tagger_attributes.py` bounds and verifies exact NFSv4/POSIX access ACL and user
attribute bytes. Unsupported security attributes fail closed. Mode/owner checks run
after ACL application; an unsuccessful round trip leaves the source untouched.
Private receipts include the attribute manifest, which must never enter logs.
The syscall contracts and NFS ambiguity caveat are documented in
[rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html) and
[link(2)](https://man7.org/linux/man-pages/man2/link.2.html).

## Rollback and mixed versions

New settings are additive and legacy defaults remain intact. New receipt fields are
versioned; older observers must ignore optional fields. Store uncertain publication
intent before rename, reconcile source/destination hashes on recovery, and never
reissue a second tagger write blindly. Before native rollout, prove old image reads
new state and retains unknown settings. If not, restore verified app state with the
old image. Restore only affected canary files. No bulk-library backup for routine
updates. Remove operation backups only after successful forward or rollback proof.

## Alternatives and risks

Built-in `cr` lacks StoryArcNumber assignment and can overwrite notes/pages from its
metadata object. Preserve unsupported fields explicitly. Optional CIX/archived-tag
plugins add dependencies and different schemas, so assess separately rather than
installing all extras. CLI success is not archive success. Conversion-only native
behavior cannot simply be deleted before routing callers. Curl's unbounded stream
queue and different timeout semantics block immediate full transport replacement.


## Supported-runtime maintenance (2026-10-20)

This follow-up replaces only Modern's isolated Python runtime. Pillow 13.0.0
requires Python 3.11 or newer; installing it into the prior Python 3.10 venv is
unsupported. Build normal-GIL CPython 3.13.16 from the authenticated official
source in the same pinned LinuxServer/Jammy base, install it under
`/opt/tagger-python`, and create Modern's `/opt/comictagger` venv with it.
Retain ComicTagger 1.6.0b11.dev0, the exact cp313 linux/amd64 Pillow 13 wheel,
corresponding sources and license notices. Keep compilation tools, private pip,
headers, static libpython and test caches outside the final runtime.

Mylar and vendored Legacy 1.3.5 retain Python 3.10. Do not replace `/usr/bin/python3`,
change global PATH, copy an interpreter from another distribution, bypass
Requires-Python, or change backend selection and archive ownership. The candidate
also pins matching SSL, FFI and SQLite system libraries; their package updates
can affect the daemon's shared dependencies even though its interpreter stays
unchanged. ICU 70.1 and the final image's ELF closure require actual image proof.

T025 records completed source authentication, implementation and host checks.
T026 owns the runner/source-map integration now being handled separately.
T027 requires the reviewed final amd64 image: private interpreter identity,
Jammy glibc 2.35, ICU 70.1, dependency and ELF closure, genuine CBR conversion
through the existing owner into normalized CBZ, real metadata preservation and
failure controls. T028 owns normal CI/publication and the authorized scoped live
canary, including verified backup/restore, preservation and rollback readiness.
Earlier completed tasks and acceptance counts below their original headings
remain historical evidence; they do not close these new runtime gates.