Phase 1e — container image, entrypoint, and podman quadlets #17

Manually merged
marvin8 merged 17 commits from feat/issue-6-phase-1e-ops-containers into main 2026-10-16 23:17:28 +00:00
Collaborator

Closes #6

Summary

  • XIQUE_HOST/XIQUE_PORT settings with a settings-driven uvicorn bind; database_path default moves to /data/db/xique.db
  • Containerfile implementing the CI-encoded contract (uv-pinned python:3.14-slim, editable install at /app/src, non-root uid 1000, tag-baked release metadata) plus container-entrypoint.sh and .containerignore
  • container and publish-container CI jobs enabled; smoke amended to real first-boot order (worker migrates, then web serves) with a CI-only throwaway secret
  • fastapi/ty compatible bumps (zaojun) with aligned ty-pre-commit rev
  • Rootless-podman quadlets in deploy/: pod + web + worker + tailscale serve sidecar, host bind mount for the database, named volume for transient Trakt uploads; README "Running it" section

Docs site and operator guide: #16.

Verification

  • uv run nox green on the branch
  • container job on this PR: image builds on zhuiri, metadata version check, /app/src resolution, real entrypoint boots worker→web and /login renders
  • Quadlet units are not exercisable in CI (no systemd user session on the runner); accepted per the spec, install steps documented in README
Closes #6 ## Summary - `XIQUE_HOST`/`XIQUE_PORT` settings with a settings-driven uvicorn bind; `database_path` default moves to `/data/db/xique.db` - `Containerfile` implementing the CI-encoded contract (uv-pinned python:3.14-slim, editable install at `/app/src`, non-root uid 1000, tag-baked release metadata) plus `container-entrypoint.sh` and `.containerignore` - `container` and `publish-container` CI jobs enabled; smoke amended to real first-boot order (worker migrates, then web serves) with a CI-only throwaway secret - fastapi/ty compatible bumps (zaojun) with aligned `ty-pre-commit` rev - Rootless-podman quadlets in `deploy/`: pod + web + worker + tailscale `serve` sidecar, host bind mount for the database, named volume for transient Trakt uploads; README "Running it" section Docs site and operator guide: #16. ## Verification - `uv run nox` green on the branch - `container` job on this PR: image builds on zhuiri, metadata version check, `/app/src` resolution, real entrypoint boots worker→web and `/login` renders - Quadlet units are not exercisable in CI (no systemd user session on the runner); accepted per the spec, install steps documented in README
🔧 chore: add podman quadlets, tailscale serve config, and running docs
Some checks failed
/ gitleaks (pull_request) Successful in 13s
/ checks (pull_request) Successful in 3m33s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Failing after 1m44s
/ publish-container (pull_request) Failing after 1m48s
/ pr-review (pull_request) Successful in 5m56s
944d8b3d6a
forgejo-actions left a comment

WuMing

Found 14 issue(s). See inline comments below.

## WuMing Found **14** issue(s). See inline comments below.
@ -0,0 +1,42 @@
FROM python:3.14-slim

shell [HIGH]

Python 3.14 is not yet released and does not exist on official Docker registries. Using 'python:3.14-slim' will cause the image build to fail immediately. Pin to a stable, available release (e.g., 3.12 or 3.13) until 3.14 is officially published.

**shell** [HIGH] Python 3.14 is not yet released and does not exist on official Docker registries. Using 'python:3.14-slim' will cause the image build to fail immediately. Pin to a stable, available release (e.g., 3.12 or 3.13) until 3.14 is officially published. <!-- wuming:sha256:7206c6839ecc2425778b7a91e95be61bf583d143197584c7f7e7f8359cbe619e -->
marvin8 marked this conversation as resolved
@ -0,0 +6,4 @@
ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir "uv==${UV_VERSION}"

shell [MEDIUM]

The build argument UV_VERSION lacks a default value. If passed as empty or unset, it expands to 'uv==', which triggers a pip syntax error or installs an unintended version. Add a default value in the ARG declaration (e.g., 'ARG UV_VERSION=0.5.26') or validate the variable before building.

**shell** [MEDIUM] The build argument UV_VERSION lacks a default value. If passed as empty or unset, it expands to 'uv==', which triggers a pip syntax error or installs an unintended version. Add a default value in the ARG declaration (e.g., 'ARG UV_VERSION=0.5.26') or validate the variable before building. <!-- wuming:sha256:4e5d762a7070a68c5c5cef933677a13de9e85254fca99578fd5bd16bfc081ada -->
marvin8 marked this conversation as resolved
Containerfile Outdated
@ -0,0 +36,4 @@
&& chown -R appuser:appuser /data
USER appuser
COPY --chown=root:root container-entrypoint.sh /usr/local/bin/container-entrypoint.sh

shell [LOW]

The COPY command specifies '--chown=root:root' but executes after 'USER appuser' (line 37). Docker/Podman evaluation order means post-USER COPY operations inherit the active user context, overriding the --chown flag. The file will be created as appuser:appuser. Move this COPY before the USER instruction, or add an explicit 'RUN chown root:root /usr/local/bin/container-entrypoint.sh' afterward for predictable ownership.

**shell** [LOW] The COPY command specifies '--chown=root:root' but executes after 'USER appuser' (line 37). Docker/Podman evaluation order means post-USER COPY operations inherit the active user context, overriding the --chown flag. The file will be created as appuser:appuser. Move this COPY before the USER instruction, or add an explicit 'RUN chown root:root /usr/local/bin/container-entrypoint.sh' afterward for predictable ownership. <!-- wuming:sha256:18df3fae6a70630c0187b59e2cf8a9c66966f1bcb8b52c95d71f3550089f0bda -->
@ -115,0 +117,4 @@
## Running it
The container image is published to `registry.marvin8.zone/marvin8/xique` on
release tags, and the `deploy/` directory holds the rootless-podman quadlets:

docs [MEDIUM]

Missing code formatting for the directory name. Add backticks around 'deploy/' for consistency with other technical paths in the document.

**docs** [MEDIUM] Missing code formatting for the directory name. Add backticks around 'deploy/' for consistency with other technical paths in the document. <!-- wuming:sha256:e5a776e503e520d129c26c625f0122d82d561795ee0fb9492fe67bef64be806f -->
marvin8 marked this conversation as resolved
@ -115,0 +128,4 @@
- Host directories: `~/docker/xique/data/db` (the sqlite database — the only
backed-up content), `~/docker/xique/ts-state` (Tailscale node state), and
`~/docker/xique/ts-serve` containing `xique-serve.json` (copied from
`deploy/`; adjust the `${TS_CERT_DOMAIN}` proxy target if you change ports).

docs [MEDIUM]

Variable '${TS_CERT_DOMAIN}' should be wrapped in backticks. For consistency, also wrap 'xique-serve.json' (referenced on the previous line) in backticks.

**docs** [MEDIUM] Variable '${TS_CERT_DOMAIN}' should be wrapped in backticks. For consistency, also wrap 'xique-serve.json' (referenced on the previous line) in backticks. <!-- wuming:sha256:671f70ff5852c735f89b535d3096c05db7760c9c179a062cebb08e1cb4375607 -->
marvin8 marked this conversation as resolved
@ -115,0 +132,4 @@
- `~/docker/xique/xique.env` with at least `XIQUE_SECRET_KEY` (required),
optionally `XIQUE_TMDB_TOKEN`, and `XIQUE_IMPORT_DIR=/data/imports-temp`
so uploads stream into the shared volume web and worker both mount.
- The shared `~/docker/tailscale-systemd.env` pinning `TAILSCALE_VERSION`

docs [MEDIUM]

File path '~/docker/tailscale-systemd.env' and environment variable 'TAILSCALE_VERSION' lack code formatting. Wrap both in backticks for clarity and scanability.

**docs** [MEDIUM] File path '~/docker/tailscale-systemd.env' and environment variable 'TAILSCALE_VERSION' lack code formatting. Wrap both in backticks for clarity and scanability. <!-- wuming:sha256:8e430d69b61881f61b9b3f5f782cc1bcf3b402df1ab55c2f02c5d493e6d9e2d9 -->
marvin8 marked this conversation as resolved
README.md Outdated
@ -115,0 +135,4 @@
- The shared `~/docker/tailscale-systemd.env` pinning `TAILSCALE_VERSION`
for the sidecar image.
Bring-up:

docs [LOW]

'Bring-up:' sits loosely between the prerequisite list and the numbered steps. Converting it to a subheading (e.g., '### Bring-up:') will improve document hierarchy and prevent rendering quirks in some markdown parsers.

**docs** [LOW] 'Bring-up:' sits loosely between the prerequisite list and the numbered steps. Converting it to a subheading (e.g., '### Bring-up:') will improve document hierarchy and prevent rendering quirks in some markdown parsers. <!-- wuming:sha256:371aea849b20dd0b8c499fa29985806d5f11b5217e90d1488856683d30f78079 -->
README.md Outdated
@ -115,0 +148,4 @@
tailnet. On a first-ever boot the worker migrates the database while the web
process restarts until the schema is ready — this settles in seconds.
Upgrades pin `:latest`: `podman pull registry.marvin8.zone/marvin8/xique:latest

docs [LOW]

The container tag ':latest' is missing backticks. Wrap in backticks to clearly distinguish it from surrounding prose.

**docs** [LOW] The container tag ':latest' is missing backticks. Wrap in backticks to clearly distinguish it from surrounding prose. <!-- wuming:sha256:ec5318bdab6fd8864d3ce69a56b630f3e04d29a7ea2f32a54ac69a385f0f7c6f -->
@ -50,6 +52,8 @@ class Settings(BaseSettings):
trusted_proxies: str = ""
login_attempt_limit: int = 5
login_window_seconds: int = 900
host: str = "127.0.0.1"

code [MEDIUM]

The new host setting has no validation. An empty XIQUE_HOST value is accepted and passed to uvicorn, which can cause socket.getaddrinfo('') to bind all interfaces, silently overriding the secure loopback default. Add a field validator that rejects empty host strings or explicitly documents/permits only intended values.

**code** [MEDIUM] The new host setting has no validation. An empty XIQUE_HOST value is accepted and passed to uvicorn, which can cause socket.getaddrinfo('') to bind all interfaces, silently overriding the secure loopback default. Add a field validator that rejects empty host strings or explicitly documents/permits only intended values. <!-- wuming:sha256:30639a677479337422fbb85ae840d0eb6246ddc1d234784a28768b1d64c5444c -->
marvin8 marked this conversation as resolved
@ -56,3 +56,3 @@
settings = Settings()
expect(settings.database_path).to_equal(Path("/data/xique.db"))
expect(settings.database_path).to_equal(Path("/data/db/xique.db"))

tests [MEDIUM]

Assertion checks the default of database_path was updated to /data/db/xique.db. This is a literal constant expected to match the new source default. While not a tautology, the test is brittle: it duplicates the default value from the source and would need updating if the default changes again. Consider deriving the expected value from a shared constant or verifying the path is non-None and of type Path.

**tests** [MEDIUM] Assertion checks the default of `database_path` was updated to `/data/db/xique.db`. This is a literal constant expected to match the new source default. While not a tautology, the test is brittle: it duplicates the default value from the source and would need updating if the default changes again. Consider deriving the expected value from a shared constant or verifying the path is non-None and of type Path. <!-- wuming:sha256:6955d327b9f1bc20a9db82f18bc453d4d9f2796845a42c62f006f75f7b5d0697 -->
@ -57,2 +57,3 @@
expect(settings.database_path).to_equal(Path("/data/xique.db"))
expect(settings.database_path).to_equal(Path("/data/db/xique.db"))
expect(settings.host).to_equal("127.0.0.1")

tests [LOW]

The default host assertion duplicates the host = "127.0.0.1" literal from the source. It is a valid test but brittle; a change to the source default would require a test change. Not critical.

**tests** [LOW] The default host assertion duplicates the `host = "127.0.0.1"` literal from the source. It is a valid test but brittle; a change to the source default would require a test change. Not critical. <!-- wuming:sha256:1cd83cdd8fdafe110dcd59a601cca0dce0e1a994c428605d8c8425c06f100995 -->
@ -58,1 +58,3 @@
expect(settings.database_path).to_equal(Path("/data/xique.db"))
expect(settings.database_path).to_equal(Path("/data/db/xique.db"))
expect(settings.host).to_equal("127.0.0.1")
expect(settings.port).to_equal(8000)

tests [LOW]

The default port assertion duplicates the port = 8000 literal. Same brittleness concern.

**tests** [LOW] The default port assertion duplicates the `port = 8000` literal. Same brittleness concern. <!-- wuming:sha256:941089ca83e0500c95db6ccd17b12c65acb4a16316dccfabe89c8cc9a7ee4a51 -->
@ -0,0 +79,4 @@
original_uvicorn = install_run_recorder(calls)
try:
server.main()

code [LOW]

server.main() calls the real configure_logging(settings), which mutates process-wide logging configuration and can leak into later tests. Patch or mock configure_logging in these entrypoint-binding tests to keep them isolated.

**code** [LOW] server.main() calls the real configure_logging(settings), which mutates process-wide logging configuration and can leak into later tests. Patch or mock configure_logging in these entrypoint-binding tests to keep them isolated. <!-- wuming:sha256:effa3ecab56165503ced0b18f4c7b99ea1bda261066c6a665043af2a7724c934 -->
@ -0,0 +62,4 @@
calls.append({"args": args, "kwargs": kwargs})
original_uvicorn = server.uvicorn
server.uvicorn = cast(Any, SimpleNamespace(run=recording_run))

tests [HIGH]

Module-level global server.uvicorn is mutated by install_run_recorder and restored in a finally block only inside the two tests. If either test fails before the finally block or if tests run in an order where cleanup is missed, the global remains poisoned for subsequent tests in the same session. Use a fixture with teardown (yield-based cleanup) to guarantee restoration regardless of test outcome.

**tests** [HIGH] Module-level global `server.uvicorn` is mutated by `install_run_recorder` and restored in a `finally` block only inside the two tests. If either test fails before the `finally` block or if tests run in an order where cleanup is missed, the global remains poisoned for subsequent tests in the same session. Use a fixture with teardown (yield-based cleanup) to guarantee restoration regardless of test outcome. <!-- wuming:sha256:7c6d2041111902474a65a527a1df15b1af0df061b93cc94f7bddd54708c73ac5 -->
📝 docs: make Bring-up a proper heading
Some checks failed
/ gitleaks (pull_request) Successful in 13s
/ checks (pull_request) Successful in 3m37s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Failing after 1m28s
/ pr-review (pull_request) Successful in 5m39s
/ publish-container (pull_request) Has been cancelled
13e1f2dbe4
Author
Collaborator

WuMing review wave — resolutions

src/xique/config.py — code [MEDIUM]

code [MEDIUM] (original) <!-- wuming:sha256:30639a677479337422fbb85ae840d0eb6246ddc1d234784a28768b1d64c5444c -->

✅ Fixed in 185206c — a _valid_host validator now rejects empty/whitespace-only values (RED→GREEN: web host must be a non-empty address).

tests/test_web_server.py — code [LOW]

code [LOW] (original) <!-- wuming:sha256:effa3ecab56165503ced0b18f4c7b99ea1bda261066c6a665043af2a7724c934 -->

✅ Fixed in a00cb9c — the recorded_uvicorn fixture now stubs configure_logging alongside uvicorn.run, so process-wide logging is never touched by these tests.

tests/test_web_server.py — tests [HIGH]

tests [HIGH] (original) <!-- wuming:sha256:7c6d2041111902474a65a527a1df15b1af0df061b93cc94f7bddd54708c73ac5 -->

✅ Adopted in a00cb9c — both module bindings are now owned by a yield-based fixture whose teardown restores them and clears the settings cache regardless of test outcome. For the record the original code was already safe (the finally runs even when soft assertions fail afterwards), but the fixture is the better shape.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:e5a776e503e520d129c26c625f0122d82d561795ee0fb9492fe67bef64be806f -->

🔴 Already formatted — deploy/ appears backticked at first mention: "the deploy/ directory holds the rootless-podman quadlets".

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:671f70ff5852c735f89b535d3096c05db7760c9c179a062cebb08e1cb4375607 -->

🔴 Already formatted — both xique-serve.json and ${TS_CERT_DOMAIN} are wrapped in backticks in the prerequisites list.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:8e430d69b61881f61b9b3f5f782cc1bcf3b402df1ab55c2f02c5d493e6d9e2d9 -->

🔴 Already formatted — ~/docker/tailscale-systemd.env and TAILSCALE_VERSION are both backticked.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:371aea849b20dd0b8c499fa29985806d5f11b5217e90d1488856683d30f78079 -->

✅ Fixed in 13e1f2d — now a proper ### Bring-up heading.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:ec5318bdab6fd8864d3ce69a56b630f3e04d29a7ea2f32a54ac69a385f0f7c6f -->

🔴 Already formatted — :latest sits inside a code span ("Upgrades pin :latest").

Containerfile — shell [HIGH]

shell [HIGH] (original) <!-- wuming:sha256:7206c6839ecc2425778b7a91e95be61bf583d143197584c7f7e7f8359cbe619e -->

🔴 Incorrect — Python 3.14 was released in October 2025; this repository requires python >=3.14 and its CI runs 3.14 nox sessions. Empirically, the failing CI build pulled python:3.14-slim and reached STEP 11/20 — the tag exists and works. The real failure was a missing LICENSE file in the build context (fixed in 185206c).

Containerfile — shell [MEDIUM]

shell [MEDIUM] (original) <!-- wuming:sha256:4e5d762a7070a68c5c5cef933677a13de9e85254fca99578fd5bd16bfc081ada -->

🔴 Declined by design — CI derives UV_VERSION from [build-system] requires and exits 1 when it cannot parse it, so an empty value cannot reach the build silently. A baked-in default would mask a contract break between pyproject.toml and the image instead of failing loudly.

Containerfile — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:18df3fae6a70630c0187b59e2cf8a9c66966f1bcb8b52c95d71f3550089f0bda -->

✅ Simplified in 185206c — dropped the redundant --chown=root:root (plain COPY already yields root:root; the flag was a no-op, though not for the stated reason — --chown overrides the active USER regardless of instruction order). The exec-bit chmod remains.

tests/test_config.py — tests [LOW]

tests [LOW] (original) <!-- wuming:sha256:1cd83cdd8fdafe110dcd59a601cca0dce0e1a994c428605d8c8425c06f100995 -->

🔴 Declined — default-pinning tests exist to pin the documented default verbatim. README documents 127.0.0.1 as the XIQUE_HOST default; the literal is the contract, mirroring the existing test_settings_uses_defaults pattern.

tests/test_config.py — tests [LOW]

tests [LOW] (original) <!-- wuming:sha256:941089ca83e0500c95db6ccd17b12c65acb4a16316dccfabe89c8cc9a7ee4a51 -->

🔴 Declined — same rationale: 8000 is the documented default in README and must stay pinned.

tests/test_config.py — tests [MEDIUM]

tests [MEDIUM] (original) <!-- wuming:sha256:6955d327b9f1bc20a9db82f18bc453d4d9f2796845a42c62f006f75f7b5d0697 -->

🔴 Declined — same rationale: /data/db/xique.db is the documented XIQUE_DATABASE_PATH default; the test pins the documentation.

## WuMing review wave — resolutions ### `src/xique/config.py` — code [MEDIUM] > code [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5606)) `<!-- wuming:sha256:30639a677479337422fbb85ae840d0eb6246ddc1d234784a28768b1d64c5444c -->` ✅ Fixed in 185206c — a `_valid_host` validator now rejects empty/whitespace-only values (RED→GREEN: `web host must be a non-empty address`). ### `tests/test_web_server.py` — code [LOW] > code [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5607)) `<!-- wuming:sha256:effa3ecab56165503ced0b18f4c7b99ea1bda261066c6a665043af2a7724c934 -->` ✅ Fixed in a00cb9c — the `recorded_uvicorn` fixture now stubs `configure_logging` alongside `uvicorn.run`, so process-wide logging is never touched by these tests. ### `tests/test_web_server.py` — tests [HIGH] > tests [HIGH] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5616)) `<!-- wuming:sha256:7c6d2041111902474a65a527a1df15b1af0df061b93cc94f7bddd54708c73ac5 -->` ✅ Adopted in a00cb9c — both module bindings are now owned by a yield-based fixture whose teardown restores them and clears the settings cache regardless of test outcome. For the record the original code was already safe (the `finally` runs even when soft assertions fail afterwards), but the fixture is the better shape. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5608)) `<!-- wuming:sha256:e5a776e503e520d129c26c625f0122d82d561795ee0fb9492fe67bef64be806f -->` 🔴 Already formatted — `deploy/` appears backticked at first mention: "the `deploy/` directory holds the rootless-podman quadlets". ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5609)) `<!-- wuming:sha256:671f70ff5852c735f89b535d3096c05db7760c9c179a062cebb08e1cb4375607 -->` 🔴 Already formatted — both `xique-serve.json` and `${TS_CERT_DOMAIN}` are wrapped in backticks in the prerequisites list. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5610)) `<!-- wuming:sha256:8e430d69b61881f61b9b3f5f782cc1bcf3b402df1ab55c2f02c5d493e6d9e2d9 -->` 🔴 Already formatted — `~/docker/tailscale-systemd.env` and `TAILSCALE_VERSION` are both backticked. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5611)) `<!-- wuming:sha256:371aea849b20dd0b8c499fa29985806d5f11b5217e90d1488856683d30f78079 -->` ✅ Fixed in 13e1f2d — now a proper `### Bring-up` heading. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5612)) `<!-- wuming:sha256:ec5318bdab6fd8864d3ce69a56b630f3e04d29a7ea2f32a54ac69a385f0f7c6f -->` 🔴 Already formatted — `:latest` sits inside a code span ("Upgrades pin `:latest`"). ### `Containerfile` — shell [HIGH] > shell [HIGH] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5613)) `<!-- wuming:sha256:7206c6839ecc2425778b7a91e95be61bf583d143197584c7f7e7f8359cbe619e -->` 🔴 Incorrect — Python 3.14 was released in October 2025; this repository requires `python >=3.14` and its CI runs 3.14 nox sessions. Empirically, the failing CI build pulled `python:3.14-slim` and reached STEP 11/20 — the tag exists and works. The real failure was a missing LICENSE file in the build context (fixed in 185206c). ### `Containerfile` — shell [MEDIUM] > shell [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5614)) `<!-- wuming:sha256:4e5d762a7070a68c5c5cef933677a13de9e85254fca99578fd5bd16bfc081ada -->` 🔴 Declined by design — CI derives `UV_VERSION` from `[build-system] requires` and exits 1 when it cannot parse it, so an empty value cannot reach the build silently. A baked-in default would mask a contract break between pyproject.toml and the image instead of failing loudly. ### `Containerfile` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5615)) `<!-- wuming:sha256:18df3fae6a70630c0187b59e2cf8a9c66966f1bcb8b52c95d71f3550089f0bda -->` ✅ Simplified in 185206c — dropped the redundant `--chown=root:root` (plain COPY already yields root:root; the flag was a no-op, though not for the stated reason — `--chown` overrides the active USER regardless of instruction order). The exec-bit `chmod` remains. ### `tests/test_config.py` — tests [LOW] > tests [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5618)) `<!-- wuming:sha256:1cd83cdd8fdafe110dcd59a601cca0dce0e1a994c428605d8c8425c06f100995 -->` 🔴 Declined — default-pinning tests exist to pin the documented default verbatim. README documents `127.0.0.1` as the `XIQUE_HOST` default; the literal is the contract, mirroring the existing `test_settings_uses_defaults` pattern. ### `tests/test_config.py` — tests [LOW] > tests [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5619)) `<!-- wuming:sha256:941089ca83e0500c95db6ccd17b12c65acb4a16316dccfabe89c8cc9a7ee4a51 -->` 🔴 Declined — same rationale: `8000` is the documented default in README and must stay pinned. ### `tests/test_config.py` — tests [MEDIUM] > tests [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5617)) `<!-- wuming:sha256:6955d327b9f1bc20a9db82f18bc453d4d9f2796845a42c62f006f75f7b5d0697 -->` 🔴 Declined — same rationale: `/data/db/xique.db` is the documented `XIQUE_DATABASE_PATH` default; the test pins the documentation.
forgejo-actions left a comment

WuMing

Found 8 issue(s). See inline comments below.

## WuMing Found **8** issue(s). See inline comments below.
@ -0,0 +1,42 @@
FROM python:3.14-slim

shell [LOW]

Python 3.14-slim may not be fully stable or widely published yet. Ensure this tag is validated in your registry before merging to avoid CI pipeline breakage during image pulls. Consider pinning to a known digest or waiting for a stabilized tag.

**shell** [LOW] Python 3.14-slim may not be fully stable or widely published yet. Ensure this tag is validated in your registry before merging to avoid CI pipeline breakage during image pulls. Consider pinning to a known digest or waiting for a stabilized tag. <!-- wuming:sha256:5e139f80e7a01c85226e8e1abe9a607960c03a9055080d6c9ccf83e7e1197d96 -->
marvin8 marked this conversation as resolved
@ -0,0 +6,4 @@
ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir "uv==${UV_VERSION}"

shell [LOW]

Installing uv via pip introduces unnecessary overhead and dependency resolution steps compared to native installation methods. Use uv's official installer script or download the static binary directly for faster, more reliable setups.

**shell** [LOW] Installing `uv` via `pip` introduces unnecessary overhead and dependency resolution steps compared to native installation methods. Use `uv`'s official installer script or download the static binary directly for faster, more reliable setups. <!-- wuming:sha256:c0e7797648abc68554debb6109b82c7fd1b18216957a62b06a5e9cb139268e12 -->
marvin8 marked this conversation as resolved
README.md Outdated
@ -115,0 +131,4 @@
`deploy/`; adjust the `${TS_CERT_DOMAIN}` proxy target if you change ports).
- `~/docker/xique/xique.env` with at least `XIQUE_SECRET_KEY` (required),
optionally `XIQUE_TMDB_TOKEN`, and `XIQUE_IMPORT_DIR=/data/imports-temp`
so uploads stream into the shared volume web and worker both mount.

docs [LOW]

Sentence structure is slightly awkward. Consider adding 'that' or restructuring for better readability.

**docs** [LOW] Sentence structure is slightly awkward. Consider adding 'that' or restructuring for better readability. <!-- wuming:sha256:5cfdd04a35073ffd4da1f2b742d55cde12a25f5abe16713b2aff1e35e285ee4c -->
@ -115,0 +132,4 @@
- `~/docker/xique/xique.env` with at least `XIQUE_SECRET_KEY` (required),
optionally `XIQUE_TMDB_TOKEN`, and `XIQUE_IMPORT_DIR=/data/imports-temp`
so uploads stream into the shared volume web and worker both mount.
- The shared `~/docker/tailscale-systemd.env` pinning `TAILSCALE_VERSION`

docs [LOW]

Configuration file path is missing backticks for consistent code formatting.

**docs** [LOW] Configuration file path is missing backticks for consistent code formatting. <!-- wuming:sha256:226c5ac3e442a58b613df3701ead97f05c7fa93bf6c7bf58c60c74eb5ab46bf8 -->
marvin8 marked this conversation as resolved
README.md Outdated
@ -115,0 +148,4 @@
tailnet. On a first-ever boot the worker migrates the database while the web
process restarts until the schema is ready — this settles in seconds.
Upgrades pin `:latest`: `podman pull registry.marvin8.zone/marvin8/xique:latest

docs [LOW]

Inline shell command and the :latest: tag blend with prose. Wrap the command and tag in backticks for clarity.

**docs** [LOW] Inline shell command and the `:latest:` tag blend with prose. Wrap the command and tag in backticks for clarity. <!-- wuming:sha256:3a5708fc8878183f9b27f9cd0195ee366615dfdddc6e1ea78c3cf4128dda96b6 -->
@ -115,0 +150,4 @@
Upgrades pin `:latest`: `podman pull registry.marvin8.zone/marvin8/xique:latest
&& systemctl --user restart xique-pod`. Release tags are available for
rollbacks. Every container carries an explicit memory cap in its unit

docs [LOW]

Pronoun agreement mismatch: 'Every container' is singular but implies multiple instances. Consider 'their' or rephrasing.

**docs** [LOW] Pronoun agreement mismatch: 'Every container' is singular but implies multiple instances. Consider 'their' or rephrasing. <!-- wuming:sha256:afbe1cc81f2a7d331258529dcd274f72b6de2a6ed3e3bde29be161373549d743 -->
marvin8 marked this conversation as resolved
@ -0,0 +3,4 @@
# Container entrypoint: maps PORT onto the app's XIQUE_PORT and execs the
# requested console script. The web default host is 0.0.0.0 because the
# tailscale sidecar proxies to the pod's shared loopback.
set -eu

shell [LOW]

set -eu enables strict mode but omits set -o pipefail. While no pipes are currently used, adding set -euo pipefail is a standard hardening practice that ensures sub-command failures in pipelines are correctly propagated as script failures.

**shell** [LOW] `set -eu` enables strict mode but omits `set -o pipefail`. While no pipes are currently used, adding `set -euo pipefail` is a standard hardening practice that ensures sub-command failures in pipelines are correctly propagated as script failures. <!-- wuming:sha256:55f5a93712681b3f34aea1d93dede2c974c8d8dc6632e0e709c1d9be9a2d1555 -->
marvin8 marked this conversation as resolved
@ -0,0 +5,4 @@
}
},
"Web": {
"${TS_CERT_DOMAIN}:443": {

config [MEDIUM]

Contains shell variable placeholder ${TS_CERT_DOMAIN}. Tailscale's serve configuration does not natively expand shell variables, so this literal string will be treated as the hostname unless processed by an external templating or script step before application. Consider using a proper template engine or documenting the mandatory substitution step.

**config** [MEDIUM] Contains shell variable placeholder ${TS_CERT_DOMAIN}. Tailscale's serve configuration does not natively expand shell variables, so this literal string will be treated as the hostname unless processed by an external templating or script step before application. Consider using a proper template engine or documenting the mandatory substitution step. <!-- wuming:sha256:aa9774001cc4387f662b4255063de894f0d04b254ee9f3e054d06d73dd5fa7b1 -->
marvin8 marked this conversation as resolved
🐛 fix: web restart throttling, shared import dir env, and rootless user mapping
All checks were successful
/ gitleaks (pull_request) Successful in 28s
/ pr-review (pull_request) Successful in 1m43s
/ checks (pull_request) Successful in 3m25s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m53s
/ publish-container (pull_request) Successful in 2m45s
0baba1748f
forgejo-actions left a comment

WuMing

Found 6 issue(s). See inline comments below.

## WuMing Found **6** issue(s). See inline comments below.
@ -119,0 +121,4 @@
# assertion below is web rendering the login page. The throwaway
# key lives only in this step; production supplies the real one.
buildah run --isolation chroot "$ctr" \
env XIQUE_SECRET_KEY=ci-smoke-only \

security [LOW]

A02: Hard-coded test secret XIQUE_SECRET_KEY in CI. Use CI secret management instead of embedding credentials.

**security** [LOW] A02: Hard-coded test secret XIQUE_SECRET_KEY in CI. Use CI secret management instead of embedding credentials. <!-- wuming:sha256:aaf01209be484768a8ea47531dbf4e546517dcb9373284acb8329cf03afc0641 -->
marvin8 marked this conversation as resolved
@ -141,3 +149,3 @@
# server.
setsid buildah run --isolation chroot "$ctr" \
env PORT=18099 /usr/local/bin/container-entrypoint.sh web >/tmp/xique-smoke.log 2>&1 &
env PORT=18099 XIQUE_SECRET_KEY=ci-smoke-only /usr/local/bin/container-entrypoint.sh web >/tmp/xique-smoke.log 2>&1 &

security [LOW]

A02: Hard-coded test secret XIQUE_SECRET_KEY in CI. Use CI secret management instead of embedding credentials.

**security** [LOW] A02: Hard-coded test secret XIQUE_SECRET_KEY in CI. Use CI secret management instead of embedding credentials. <!-- wuming:sha256:a6aacf2b3a7a31837c6a4735f20e1b33c2da5600662e98faadf26961af32bb1e -->
marvin8 marked this conversation as resolved
@ -0,0 +31,4 @@
# Pre-create the mount-point directories: /data/db exists for bare chroot
# smoke runs, and a freshly mounted named volume inherits this ownership.
# No USER directive: under rootless podman, container root maps to the host

security [MEDIUM]

A05: Container runs as root (no USER directive). Add a non-root USER to reduce privilege if runtime is not rootless.

**security** [MEDIUM] A05: Container runs as root (no USER directive). Add a non-root USER to reduce privilege if runtime is not rootless. <!-- wuming:sha256:cc9e35fc872c6390d6179facf610e19583449c4fdefee22f2bb827a6b136e561 -->
marvin8 marked this conversation as resolved
@ -115,0 +137,4 @@
### Bring-up
1. `cp deploy/* ~/.config/containers/systemd/`

docs [LOW]

Copying deploy/* to the systemd directory may include non-quadlet files (e.g., scripts or ignore files), which can clutter the directory or trigger warnings on daemon-reload. Consider explicitly naming only the required unit files.

**docs** [LOW] Copying deploy/* to the systemd directory may include non-quadlet files (e.g., scripts or ignore files), which can clutter the directory or trigger warnings on daemon-reload. Consider explicitly naming only the required unit files. <!-- wuming:sha256:e8dfb74fbc195e6e16f77a922b0ed3b38ae9154ca63980740bf307f23d27720d -->
@ -115,0 +140,4 @@
1. `cp deploy/* ~/.config/containers/systemd/`
2. `systemctl --user daemon-reload`
3. `systemctl --user start xique-pod`
4. First boot only: approve the Tailscale login URL printed by the sidecar —

docs [MEDIUM]

Step 4 conflates reading logs with approving a URL. Clarify that users should extract the login URL from the logs and open it in a browser to complete authentication.

**docs** [MEDIUM] Step 4 conflates reading logs with approving a URL. Clarify that users should extract the login URL from the logs and open it in a browser to complete authentication. <!-- wuming:sha256:674bc91fcfb9efbec65053b3580a9efdf750e9f01d7f4595a0f78dd17e32b180 -->
@ -115,0 +145,4 @@
The app is then reachable at `https://xique.<your-tailnet>.ts.net`; TLS comes
from Tailscale and funnel stays disabled, so nothing is exposed beyond the
tailnet. On a first-ever boot the worker migrates the database while the web

docs [MEDIUM]

This conflicts with the PR summary which states a serial boot order ('worker migrates, then web serves'). Either update the summary to reflect the concurrent/retry behavior, or clarify that the web process handles connection errors gracefully during migration rather than actively restarting.

**docs** [MEDIUM] This conflicts with the PR summary which states a serial boot order ('worker migrates, then web serves'). Either update the summary to reflect the concurrent/retry behavior, or clarify that the web process handles connection errors gracefully during migration rather than actively restarting. <!-- wuming:sha256:69c4f7302312e6b07998587158acfb39926ddd854864b106b61903e2e9dea838 -->
📝 docs: make the Running it walkthrough explicit
All checks were successful
/ gitleaks (pull_request) Successful in 13s
/ pr-review (pull_request) Successful in 2m52s
/ checks (pull_request) Successful in 3m28s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m56s
/ publish-container (pull_request) Successful in 2m23s
4c7729683e
Author
Collaborator

WuMing review wave 2 — resolutions

deploy/xique-serve.json — config [MEDIUM]

config [MEDIUM] (original) <!-- wuming:sha256:aa9774001cc4387f662b4255063de894f0d04b254ee9f3e054d06d73dd5fa7b1 -->

🔴 Declined — tailscale's container boot performs ${TS_CERT_DOMAIN} substitution when reading TS_SERVE_CONFIG; this exact config shape runs in production today on the operator's other stack, and the design deliberately preserved the proven shape.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:226c5ac3e442a58b613df3701ead97f05c7fa93bf6c7bf58c60c74eb5ab46bf8 -->

🔴 Already formatted — every path in that list (~/docker/xique/xique.env, ~/docker/tailscale-systemd.env, etc.) is backticked.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:5cfdd04a35073ffd4da1f2b742d55cde12a25f5abe16713b2aff1e35e285ee4c -->

🔴 Declined — pure style preference, no actionable ambiguity.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:3a5708fc8878183f9b27f9cd0195ee366615dfdddc6e1ea78c3cf4128dda96b6 -->

🔴 Already formatted — :latest and the whole podman pull … && systemctl … command sit inside a (multi-line) code span.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:afbe1cc81f2a7d331258529dcd274f72b6de2a6ed3e3bde29be161373549d743 -->

🔴 Correct as written — "Every container … in its unit" is valid singular agreement.

Containerfile — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:5e139f80e7a01c85226e8e1abe9a607960c03a9055080d6c9ccf83e7e1197d96 -->

🔴 Empirically false — the container job on this very head pulled python:3.14-slim, built the image, booted the real entrypoint, and passed the login-page smoke. Python 3.14 has been stable since October 2025.

Containerfile — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:c0e7797648abc68554debb6109b82c7fd1b18216957a62b06a5e9cb139268e12 -->

🔴 Deliberate — pip install uv==${UV_VERSION} pins exactly the version CI derives from [build-system] requires, keeping the image and the lock check honest; the official installer script conflicts with this environment's no-curl | sh policy.

container-entrypoint.sh — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:55f5a93712681b3f34aea1d93dede2c974c8d8dc6632e0e709c1d9be9a2d1555 -->

🔴 Would break at boot — the shebang is #!/bin/sh (dash), which rejects set -o pipefail (set: Illegal option -o); the script contains no pipelines, so strict mode is already sufficient.

.forgejo/workflows/ci.yml — security [LOW]

security [LOW] (original) <!-- wuming:sha256:aaf01209be484768a8ea47531dbf4e546517dcb9373284acb8329cf03afc0641 -->

🔴 Not a credential — the value only ever signs sessions for an ephemeral smoke server inside a throwaway chroot and grants nothing; the design documents it as CI-only, and the gitleaks job passes. Secret management would imply it means something.

.forgejo/workflows/ci.yml — security [LOW]

security [LOW] (original) <!-- wuming:sha256:a6aacf2b3a7a31837c6a4735f20e1b33c2da5600662e98faadf26961af32bb1e -->

🔴 Same as the sibling comment — throwaway smoke value, not a secret.

Containerfile — security [MEDIUM]

security [MEDIUM] (original) <!-- wuming:sha256:cc9e35fc872c6390d6179facf610e19583449c4fdefee22f2bb827a6b136e561 -->

🔴 Intentional — the deployment target is rootless podman: container root maps to the unprivileged host user, which is exactly why the USER directive was removed (see the comment block in the Containerfile). The reviewer's own hedge — "if runtime is not rootless" — does not apply.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:674bc91fcfb9efbec65053b3580a9efdf750e9f01d7f4595a0f78dd17e32b180 -->

✅ Fixed in 4c77296 — step 5 now says to extract the URL with journalctl and open it in a browser to approve the node.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:e8dfb74fbc195e6e16f77a922b0ed3b38ae9154ca63980740bf307f23d27720d -->

✅ Fixed in 4c77296 — the walkthrough now names the five unit files explicitly and copies xique-serve.json to ~/docker/xique/ts-serve/, where the sidecar actually mounts it (a plain cp deploy/* would indeed have put it in the wrong place).

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:69c4f7302312e6b07998587158acfb39926ddd854864b106b61903e2e9dea838 -->

✅ Clarified in 4c77296 — the README now states the pod starts all containers simultaneously and that web restarts until the worker migrates; the serial worker-then-web order is called out as the CI smoke's proof of the same first-boot, resolving the apparent conflict with the PR summary.

## WuMing review wave 2 — resolutions ### `deploy/xique-serve.json` — config [MEDIUM] > config [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5631)) `<!-- wuming:sha256:aa9774001cc4387f662b4255063de894f0d04b254ee9f3e054d06d73dd5fa7b1 -->` 🔴 Declined — tailscale's container boot performs `${TS_CERT_DOMAIN}` substitution when reading `TS_SERVE_CONFIG`; this exact config shape runs in production today on the operator's other stack, and the design deliberately preserved the proven shape. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5632)) `<!-- wuming:sha256:226c5ac3e442a58b613df3701ead97f05c7fa93bf6c7bf58c60c74eb5ab46bf8 -->` 🔴 Already formatted — every path in that list (`~/docker/xique/xique.env`, `~/docker/tailscale-systemd.env`, etc.) is backticked. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5633)) `<!-- wuming:sha256:5cfdd04a35073ffd4da1f2b742d55cde12a25f5abe16713b2aff1e35e285ee4c -->` 🔴 Declined — pure style preference, no actionable ambiguity. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5634)) `<!-- wuming:sha256:3a5708fc8878183f9b27f9cd0195ee366615dfdddc6e1ea78c3cf4128dda96b6 -->` 🔴 Already formatted — `:latest` and the whole `podman pull … && systemctl …` command sit inside a (multi-line) code span. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5635)) `<!-- wuming:sha256:afbe1cc81f2a7d331258529dcd274f72b6de2a6ed3e3bde29be161373549d743 -->` 🔴 Correct as written — "Every container … in its unit" is valid singular agreement. ### `Containerfile` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5636)) `<!-- wuming:sha256:5e139f80e7a01c85226e8e1abe9a607960c03a9055080d6c9ccf83e7e1197d96 -->` 🔴 Empirically false — the `container` job on this very head pulled `python:3.14-slim`, built the image, booted the real entrypoint, and passed the login-page smoke. Python 3.14 has been stable since October 2025. ### `Containerfile` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5637)) `<!-- wuming:sha256:c0e7797648abc68554debb6109b82c7fd1b18216957a62b06a5e9cb139268e12 -->` 🔴 Deliberate — `pip install uv==${UV_VERSION}` pins exactly the version CI derives from `[build-system] requires`, keeping the image and the lock check honest; the official installer script conflicts with this environment's no-`curl | sh` policy. ### `container-entrypoint.sh` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5638)) `<!-- wuming:sha256:55f5a93712681b3f34aea1d93dede2c974c8d8dc6632e0e709c1d9be9a2d1555 -->` 🔴 Would break at boot — the shebang is `#!/bin/sh` (dash), which rejects `set -o pipefail` (`set: Illegal option -o`); the script contains no pipelines, so strict mode is already sufficient. ### `.forgejo/workflows/ci.yml` — security [LOW] > security [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5644)) `<!-- wuming:sha256:aaf01209be484768a8ea47531dbf4e546517dcb9373284acb8329cf03afc0641 -->` 🔴 Not a credential — the value only ever signs sessions for an ephemeral smoke server inside a throwaway chroot and grants nothing; the design documents it as CI-only, and the gitleaks job passes. Secret management would imply it means something. ### `.forgejo/workflows/ci.yml` — security [LOW] > security [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5645)) `<!-- wuming:sha256:a6aacf2b3a7a31837c6a4735f20e1b33c2da5600662e98faadf26961af32bb1e -->` 🔴 Same as the sibling comment — throwaway smoke value, not a secret. ### `Containerfile` — security [MEDIUM] > security [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5646)) `<!-- wuming:sha256:cc9e35fc872c6390d6179facf610e19583449c4fdefee22f2bb827a6b136e561 -->` 🔴 Intentional — the deployment target is rootless podman: container root maps to the unprivileged host user, which is exactly why the `USER` directive was removed (see the comment block in the Containerfile). The reviewer's own hedge — "if runtime is not rootless" — does not apply. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5641)) `<!-- wuming:sha256:674bc91fcfb9efbec65053b3580a9efdf750e9f01d7f4595a0f78dd17e32b180 -->` ✅ Fixed in 4c77296 — step 5 now says to extract the URL with `journalctl` and open it in a browser to approve the node. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5642)) `<!-- wuming:sha256:e8dfb74fbc195e6e16f77a922b0ed3b38ae9154ca63980740bf307f23d27720d -->` ✅ Fixed in 4c77296 — the walkthrough now names the five unit files explicitly and copies `xique-serve.json` to `~/docker/xique/ts-serve/`, where the sidecar actually mounts it (a plain `cp deploy/*` would indeed have put it in the wrong place). ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5643)) `<!-- wuming:sha256:69c4f7302312e6b07998587158acfb39926ddd854864b106b61903e2e9dea838 -->` ✅ Clarified in 4c77296 — the README now states the pod starts all containers simultaneously and that web restarts until the worker migrates; the serial worker-then-web order is called out as the CI smoke's proof of the same first-boot, resolving the apparent conflict with the PR summary.
forgejo-actions left a comment

WuMing

Found 7 issue(s). See inline comments below.

## WuMing Found **7** issue(s). See inline comments below.
@ -0,0 +1,42 @@
FROM python:3.14-slim

shell [CRITICAL]

Python 3.14 does not exist in official distribution repositories. The Docker build will fail immediately. Use a released stable version (e.g., python:3.12-slim or python:3.13-slim).

**shell** [CRITICAL] Python 3.14 does not exist in official distribution repositories. The Docker build will fail immediately. Use a released stable version (e.g., python:3.12-slim or python:3.13-slim). <!-- wuming:sha256:c792432b979db52b66a6bd72a7daf23fb0299dbb390070f99f94a6ea10f230f3 -->
marvin8 marked this conversation as resolved
@ -0,0 +20,4 @@
# the pyproject version in place (CI smoke compares against pyproject).
# --no-sync keeps uv from relocking; if `uv sync --frozen` then rejects the
# version mismatch, drop --no-sync here so `uv version` relocks instead.
RUN if [ -n "${VERSION}" ]; then uv version --no-sync "${VERSION}"; fi \

shell [HIGH]

The uv CLI does not have a version subcommand that accepts --no-sync or dynamically modifies installed package metadata. This will cause a non-zero exit during the build. To bake release metadata, update pyproject.toml via a templating tool or use uv sync directly after adjusting the version field.

**shell** [HIGH] The `uv` CLI does not have a `version` subcommand that accepts `--no-sync` or dynamically modifies installed package metadata. This will cause a non-zero exit during the build. To bake release metadata, update `pyproject.toml` via a templating tool or use `uv sync` directly after adjusting the version field. <!-- wuming:sha256:de5c409fa2ddf5d376c4d1f584bdb5fcd5867251701a07e1220068a58a4fdbb5 -->
marvin8 marked this conversation as resolved
@ -99,3 +99,3 @@
| Variable | Default | Purpose |
|---|---|---|
| `XIQUE_DATABASE_PATH` | `/data/xique.db` | sqlite database file |
| `XIQUE_DATABASE_PATH` | `/data/db/xique.db` | sqlite database file |

docs [MEDIUM]

Inconsistent indentation introduces a leading space before the first pipe, which may break markdown table parsing or alignment. Remove the leading space to match the surrounding table rows.

**docs** [MEDIUM] Inconsistent indentation introduces a leading space before the first pipe, which may break markdown table parsing or alignment. Remove the leading space to match the surrounding table rows. <!-- wuming:sha256:a4528bbc7b50d2caf73b36edadf5478fa5dbfdf6091a56975fd1eb79237903e9 -->
marvin8 marked this conversation as resolved
@ -102,2 +101,4 @@
| `XIQUE_DATABASE_PATH` | `/data/db/xique.db` | sqlite database file |
| `XIQUE_SECRET_KEY` | *required* | signs sessions and CSRF tokens |
| `XIQUE_TMDB_TOKEN` | unset | TMDB read access token |
| `XIQUE_HOST` | `127.0.0.1` | web listen address |

docs [MEDIUM]

Inconsistent indentation introduces a leading space before the first pipe. Remove the leading space to maintain table alignment consistency.

**docs** [MEDIUM] Inconsistent indentation introduces a leading space before the first pipe. Remove the leading space to maintain table alignment consistency. <!-- wuming:sha256:456b7506c5cf4cbfdaab642b57a2fcbcd7dc50e226207559a188e04a9a3a041c -->
marvin8 marked this conversation as resolved
@ -102,2 +102,4 @@
| `XIQUE_SECRET_KEY` | *required* | signs sessions and CSRF tokens |
| `XIQUE_TMDB_TOKEN` | unset | TMDB read access token |
| `XIQUE_HOST` | `127.0.0.1` | web listen address |
| `XIQUE_PORT` | `8000` | web listen port |

docs [MEDIUM]

Inconsistent indentation introduces a leading space before the first pipe. Remove the leading space to maintain table alignment consistency.

**docs** [MEDIUM] Inconsistent indentation introduces a leading space before the first pipe. Remove the leading space to maintain table alignment consistency. <!-- wuming:sha256:27e3e82c9aef3c0e436d03571b5b27efb9881ef48b338c64ab4f9f8bf119709d -->
marvin8 marked this conversation as resolved
@ -115,0 +131,4 @@
`deploy/`; adjust the `${TS_CERT_DOMAIN}` proxy target if you change ports).
- `~/docker/xique/xique.env` with at least `XIQUE_SECRET_KEY` (required),
optionally `XIQUE_TMDB_TOKEN`, and `XIQUE_IMPORT_DIR=/data/imports-temp`
so uploads stream into the shared volume web and worker both mount.

docs [LOW]

Grammatically awkward phrasing in the prerequisites list. Insert a preposition/conjunction for clarity: change 'shared volume web and worker both mount' to 'shared volume that both the web and worker mount' or 'shared volume mounted by both the web and worker'.

**docs** [LOW] Grammatically awkward phrasing in the prerequisites list. Insert a preposition/conjunction for clarity: change 'shared volume web and worker both mount' to 'shared volume that both the web and worker mount' or 'shared volume mounted by both the web and worker'. <!-- wuming:sha256:3fd0efa61f7ca8b2110884cbd9e407e498f54c5ae15f29f4d22920b5f7487852 -->
@ -0,0 +93,4 @@
environment["XIQUE_PORT"] = "18099"
get_settings.cache_clear()
server.main()

code [MEDIUM]

The test calls server.main() which invokes create_app(settings) with default database path /data/db/xique.db. This can cause filesystem side effects or fail if the path is not writable in the test environment (e.g., CI). To improve test isolation and reliability, mock create_app or set XIQUE_DATABASE_PATH to a temporary directory.

**code** [MEDIUM] The test calls server.main() which invokes create_app(settings) with default database path /data/db/xique.db. This can cause filesystem side effects or fail if the path is not writable in the test environment (e.g., CI). To improve test isolation and reliability, mock create_app or set XIQUE_DATABASE_PATH to a temporary directory. <!-- wuming:sha256:4c6da70f1fa7cacf80d782613a4d7e4f95f8612ebbc43e8a7626d8718ffaaaf8 -->
Author
Collaborator

WuMing review wave 3 — resolutions

tests/test_web_server.py — code [MEDIUM]

code [MEDIUM] (original) <!-- wuming:sha256:4c6da70f1fa7cacf80d782613a4d7e4f95f8612ebbc43e8a7626d8718ffaaaf8 -->

🔴 Declined — no filesystem side effects are possible: uvicorn.run is stubbed, so the ASGI lifespan (the only code that opens the database) never runs; create_app just builds routes, and Settings merely holds a Path object. The suite is green locally and in the CI checks job.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:a4528bbc7b50d2caf73b36edadf5478fa5dbfdf6091a56975fd1eb79237903e9 -->

🔴 Not present — no table row in README begins with whitespace; verified by grep -n '^ \+|' README.md, which returns nothing.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:456b7506c5cf4cbfdaab642b57a2fcbcd7dc50e226207559a188e04a9a3a041c -->

🔴 Not present — same verification as the sibling comment.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:27e3e82c9aef3c0e436d03571b5b27efb9881ef48b338c64ab4f9f8bf119709d -->

🔴 Not present — same verification as the sibling comment.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:3fd0efa61f7ca8b2110884cbd9e407e498f54c5ae15f29f4d22920b5f7487852 -->

✅ Fixed — reworded to "the shared volume that both web and worker mount".

Containerfile — shell [CRITICAL]

shell [CRITICAL] (original) <!-- wuming:sha256:c792432b979db52b66a6bd72a7daf23fb0299dbb390070f99f94a6ea10f230f3 -->

🔴 Empirically false (third raising of this claim) — the container job on this PR pulled python:3.14-slim, built the image, booted the real entrypoint, and passed the login-page smoke. Python 3.14 has been stable since October 2025 and this repository requires >=3.14.

Containerfile — shell [HIGH]

shell [HIGH] (original) <!-- wuming:sha256:de5c409fa2ddf5d376c4d1f584bdb5fcd5867251701a07e1220068a58a4fdbb5 -->

🔴 Empirically false — verified against the exact pinned version: uv==0.12.0 --help for the version subcommand lists --no-sync Avoid syncing the virtual environment after re-locking. The release-metadata path will not exit non-zero on that account.

## WuMing review wave 3 — resolutions ### `tests/test_web_server.py` — code [MEDIUM] > code [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5650)) `<!-- wuming:sha256:4c6da70f1fa7cacf80d782613a4d7e4f95f8612ebbc43e8a7626d8718ffaaaf8 -->` 🔴 Declined — no filesystem side effects are possible: `uvicorn.run` is stubbed, so the ASGI lifespan (the only code that opens the database) never runs; `create_app` just builds routes, and `Settings` merely holds a `Path` object. The suite is green locally and in the CI `checks` job. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5651)) `<!-- wuming:sha256:a4528bbc7b50d2caf73b36edadf5478fa5dbfdf6091a56975fd1eb79237903e9 -->` 🔴 Not present — no table row in README begins with whitespace; verified by `grep -n '^ \+|' README.md`, which returns nothing. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5652)) `<!-- wuming:sha256:456b7506c5cf4cbfdaab642b57a2fcbcd7dc50e226207559a188e04a9a3a041c -->` 🔴 Not present — same verification as the sibling comment. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5653)) `<!-- wuming:sha256:27e3e82c9aef3c0e436d03571b5b27efb9881ef48b338c64ab4f9f8bf119709d -->` 🔴 Not present — same verification as the sibling comment. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5654)) `<!-- wuming:sha256:3fd0efa61f7ca8b2110884cbd9e407e498f54c5ae15f29f4d22920b5f7487852 -->` ✅ Fixed — reworded to "the shared volume that both web and worker mount". ### `Containerfile` — shell [CRITICAL] > shell [CRITICAL] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5655)) `<!-- wuming:sha256:c792432b979db52b66a6bd72a7daf23fb0299dbb390070f99f94a6ea10f230f3 -->` 🔴 Empirically false (third raising of this claim) — the `container` job on this PR pulled `python:3.14-slim`, built the image, booted the real entrypoint, and passed the login-page smoke. Python 3.14 has been stable since October 2025 and this repository requires `>=3.14`. ### `Containerfile` — shell [HIGH] > shell [HIGH] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5656)) `<!-- wuming:sha256:de5c409fa2ddf5d376c4d1f584bdb5fcd5867251701a07e1220068a58a4fdbb5 -->` 🔴 Empirically false — verified against the exact pinned version: `uv==0.12.0 --help` for the version subcommand lists `--no-sync Avoid syncing the virtual environment after re-locking`. The release-metadata path will not exit non-zero on that account.
📝 docs: smooth one prerequisites sentence
All checks were successful
/ gitleaks (pull_request) Successful in 23s
/ pr-review (pull_request) Successful in 1m54s
/ checks (pull_request) Successful in 3m32s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m51s
/ publish-container (pull_request) Successful in 2m27s
a0c1630b00
forgejo-actions left a comment

WuMing

Found 2 issue(s). See inline comments below.

## WuMing Found **2** issue(s). See inline comments below.
@ -115,0 +127,4 @@
`~/.config/containers/systemd/`.
- Host directories: `~/docker/xique/data/db` (the sqlite database — the only
backed-up content), `~/docker/xique/ts-state` (Tailscale node state), and
`~/docker/xique/ts-serve` containing `xique-serve.json` (copied from

docs [HIGH]

The xique-serve.json file is a JSON configuration, which does not natively support shell variable expansion (e.g., ${TS_CERT_DOMAIN}). Copying it directly as instructed will result in an invalid or unresolved config. Please clarify whether the file requires manual key replacement before mounting, or if it passes through a preprocessing step.

**docs** [HIGH] The `xique-serve.json` file is a JSON configuration, which does not natively support shell variable expansion (e.g., `${TS_CERT_DOMAIN}`). Copying it directly as instructed will result in an invalid or unresolved config. Please clarify whether the file requires manual key replacement before mounting, or if it passes through a preprocessing step. <!-- wuming:sha256:17ac674bec2dc9ec3f568bac726064e4208d690692d693f80a10250935375bee -->
marvin8 marked this conversation as resolved
@ -115,0 +155,4 @@
in seconds. (The CI smoke proves the same first-boot in serial order: worker
migrates first, then web serves.)
Upgrades pin `:latest`: `podman pull registry.marvin8.zone/marvin8/xique:latest

docs [MEDIUM]

"pin :latest" is functionally contradictory, as :latest is a mutable tag and cannot be pinned. Consider rewriting to "Upgrades pull :latest:" or "Images use the :latest tag; pull it to update:".

**docs** [MEDIUM] "pin `:latest`" is functionally contradictory, as `:latest` is a mutable tag and cannot be pinned. Consider rewriting to "Upgrades pull `:latest`:" or "Images use the `:latest` tag; pull it to update:". <!-- wuming:sha256:130422e111fcb953de5d8dca716307d81b350a25bd5460ec7875fc965105a2bc -->
Author
Collaborator

WuMing review wave 4 — resolutions

README.md — docs [HIGH]

docs [HIGH] (original) <!-- wuming:sha256:17ac674bec2dc9ec3f568bac726064e4208d690692d693f80a10250935375bee -->

🔴 Declined for the third time — tailscale's container boot performs ${TS_CERT_DOMAIN} substitution when reading TS_SERVE_CONFIG; this exact config shape runs in production today on the operator's other stack, and the design preserved it deliberately. No preprocessing step exists or is needed.

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:130422e111fcb953de5d8dca716307d81b350a25bd5460ec7875fc965105a2bc -->

✅ Fair — reworded in HEAD: "Upgrades use the :latest tag" (a mutable tag is tracked, not pinned).

## WuMing review wave 4 — resolutions ### `README.md` — docs [HIGH] > docs [HIGH] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5663)) `<!-- wuming:sha256:17ac674bec2dc9ec3f568bac726064e4208d690692d693f80a10250935375bee -->` 🔴 Declined for the third time — tailscale's container boot performs `${TS_CERT_DOMAIN}` substitution when reading `TS_SERVE_CONFIG`; this exact config shape runs in production today on the operator's other stack, and the design preserved it deliberately. No preprocessing step exists or is needed. ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5664)) `<!-- wuming:sha256:130422e111fcb953de5d8dca716307d81b350a25bd5460ec7875fc965105a2bc -->` ✅ Fair — reworded in HEAD: "Upgrades use the `:latest` tag" (a mutable tag is tracked, not pinned).
📝 docs: don't call the mutable tag a pin
All checks were successful
/ gitleaks (pull_request) Successful in 20s
/ pr-review (pull_request) Successful in 1m58s
/ checks (pull_request) Successful in 3m32s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m54s
/ publish-container (pull_request) Successful in 2m26s
7dca33985b
forgejo-actions left a comment

WuMing

Found 4 issue(s). See inline comments below.

## WuMing Found **4** issue(s). See inline comments below.
@ -0,0 +6,4 @@
ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir "uv==${UV_VERSION}"

shell [LOW]

Installing uv via pip bypasses the official cross-platform static binary installer. This can lead to inconsistencies or conflicts when uv's native resolver is used later in the build step. Prefer downloading the official installer script or static binary directly from Astral's distribution channel for guaranteed reproducibility.

**shell** [LOW] Installing uv via pip bypasses the official cross-platform static binary installer. This can lead to inconsistencies or conflicts when uv's native resolver is used later in the build step. Prefer downloading the official installer script or static binary directly from Astral's distribution channel for guaranteed reproducibility. <!-- wuming:sha256:0f721f30fce74a2d1ea9a7b409a16eaceb85de23182976a02a3771f4411a5172 -->
marvin8 marked this conversation as resolved
@ -115,0 +155,4 @@
in seconds. (The CI smoke proves the same first-boot in serial order: worker
migrates first, then web serves.)
Upgrades use the `:latest` tag: `podman pull registry.marvin8.zone/marvin8/xique:latest

docs [MEDIUM]

Inline code spans two lines, which can break Markdown rendering or disrupt the paragraph flow. Consolidate onto a single line or move to a fenced code block.

**docs** [MEDIUM] Inline code spans two lines, which can break Markdown rendering or disrupt the paragraph flow. Consolidate onto a single line or move to a fenced code block. <!-- wuming:sha256:989e3220ec8ea1f8e6696516371274810b4faa5f1aafc3281aad9fb883d176df -->
@ -0,0 +8,4 @@
web)
export XIQUE_HOST="${XIQUE_HOST:-0.0.0.0}"
export XIQUE_PORT="${PORT:-8000}"
exec /app/.venv/bin/xique-web

shell [MEDIUM]

Hardcodes the virtual environment path (/app/.venv/bin/), tightly coupling the entrypoint to uv's internal layout. If UV_PROJECT_ENVIRONMENT or the installation strategy changes, this command will fail. Since PATH is already configured to include the venv bin directory (Containerfile line 27), using "exec xique-web" improves portability and simplifies maintenance.

**shell** [MEDIUM] Hardcodes the virtual environment path (/app/.venv/bin/), tightly coupling the entrypoint to uv's internal layout. If UV_PROJECT_ENVIRONMENT or the installation strategy changes, this command will fail. Since PATH is already configured to include the venv bin directory (Containerfile line 27), using "exec xique-web" improves portability and simplifies maintenance. <!-- wuming:sha256:c2ba8415acb6e45f0300b32842f104ec4872f65546ae029a80baba2f73a2db39 -->
marvin8 marked this conversation as resolved
@ -0,0 +11,4 @@
exec /app/.venv/bin/xique-web
;;
worker)
exec /app/.venv/bin/xique-worker

shell [MEDIUM]

Same hardcoded path issue as line 11. Relies on a fixed virtual environment layout that may break if uv configuration changes. Prefer relying on the prepended PATH with "exec xique-worker" for better resilience.

**shell** [MEDIUM] Same hardcoded path issue as line 11. Relies on a fixed virtual environment layout that may break if uv configuration changes. Prefer relying on the prepended PATH with "exec xique-worker" for better resilience. <!-- wuming:sha256:cf9e27b721c679d15305dc2107147f7fb7420401ee45a877ec557490513cf48b -->
marvin8 marked this conversation as resolved
📝 docs: fence the upgrade commands
All checks were successful
/ gitleaks (pull_request) Successful in 14s
/ pr-review (pull_request) Successful in 2m0s
/ checks (pull_request) Successful in 5m9s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 4m46s
/ publish-container (pull_request) Successful in 5m24s
d569df5fe1
Author
Collaborator

WuMing review wave 5 — resolutions

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:989e3220ec8ea1f8e6696516371274810b4faa5f1aafc3281aad9fb883d176df -->

✅ Fixed in HEAD — the upgrade commands now live in a fenced sh block on separate lines.

Containerfile — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:0f721f30fce74a2d1ea9a7b409a16eaceb85de23182976a02a3771f4411a5172 -->

🔴 Declined (repeat of the earlier installer comment) — pip install uv==${UV_VERSION} pins exactly the version CI derives from [build-system] requires; the official installer script conflicts with this environment's no-curl | sh policy, and uv 0.12.0 resolved the lock fine in the green CI build.

container-entrypoint.sh — shell [MEDIUM]

shell [MEDIUM] (original) <!-- wuming:sha256:c2ba8415acb6e45f0300b32842f104ec4872f65546ae029a80baba2f73a2db39 -->

🔴 Declined — the venv location is not uv's incidental layout: the image pins UV_PROJECT_ENVIRONMENT=/app/.venv itself, so the absolute path is an internal contract, explicit and smoke-proven. Switching working, CI-verified config to PATH resolution for a hypothetical relocation is churn without benefit.

container-entrypoint.sh — shell [MEDIUM]

shell [MEDIUM] (original) <!-- wuming:sha256:cf9e27b721c679d15305dc2107147f7fb7420401ee45a877ec557490513cf48b -->

🔴 Declined — same rationale as the sibling comment.

## WuMing review wave 5 — resolutions ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5669)) `<!-- wuming:sha256:989e3220ec8ea1f8e6696516371274810b4faa5f1aafc3281aad9fb883d176df -->` ✅ Fixed in HEAD — the upgrade commands now live in a fenced `sh` block on separate lines. ### `Containerfile` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5670)) `<!-- wuming:sha256:0f721f30fce74a2d1ea9a7b409a16eaceb85de23182976a02a3771f4411a5172 -->` 🔴 Declined (repeat of the earlier installer comment) — `pip install uv==${UV_VERSION}` pins exactly the version CI derives from `[build-system] requires`; the official installer script conflicts with this environment's no-`curl | sh` policy, and uv 0.12.0 resolved the lock fine in the green CI build. ### `container-entrypoint.sh` — shell [MEDIUM] > shell [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5671)) `<!-- wuming:sha256:c2ba8415acb6e45f0300b32842f104ec4872f65546ae029a80baba2f73a2db39 -->` 🔴 Declined — the venv location is not uv's incidental layout: the image pins `UV_PROJECT_ENVIRONMENT=/app/.venv` itself, so the absolute path is an internal contract, explicit and smoke-proven. Switching working, CI-verified config to PATH resolution for a hypothetical relocation is churn without benefit. ### `container-entrypoint.sh` — shell [MEDIUM] > shell [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5672)) `<!-- wuming:sha256:cf9e27b721c679d15305dc2107147f7fb7420401ee45a877ec557490513cf48b -->` 🔴 Declined — same rationale as the sibling comment.
forgejo-actions left a comment

WuMing

Found 4 issue(s). See inline comments below.

## WuMing Found **4** issue(s). See inline comments below.
@ -115,0 +128,4 @@
- Host directories: `~/docker/xique/data/db` (the sqlite database — the only
backed-up content), `~/docker/xique/ts-state` (Tailscale node state), and
`~/docker/xique/ts-serve` containing `xique-serve.json` (copied from
`deploy/`; adjust the `${TS_CERT_DOMAIN}` proxy target if you change ports).

docs [MEDIUM]

Escaping the dollar sign with a backslash in prose is unnecessary and may render visibly as '$'. Use backticks to denote the variable name clearly: `${TS_CERT_DOMAIN}`.

**docs** [MEDIUM] Escaping the dollar sign with a backslash in prose is unnecessary and may render visibly as '\$'. Use backticks to denote the variable name clearly: `` `${TS_CERT_DOMAIN}` ``. <!-- wuming:sha256:f2531f828a25faa6048e55deca0325fdfc79e3d15ba7786f066510f72e002dbb -->
marvin8 marked this conversation as resolved
@ -115,0 +138,4 @@
### Bring-up
1. Copy the five quadlet units into the systemd directory:
`cp deploy/xique.pod deploy/xique-web.container deploy/xique-worker.container deploy/xique-tailscale.container deploy/xique-imports.volume ~/.config/containers/systemd/`

docs [LOW]

The shell command exceeds ~180 characters and is difficult to scan in a README. Consider breaking it across multiple lines with trailing backslashes for improved readability.

**docs** [LOW] The shell command exceeds ~180 characters and is difficult to scan in a README. Consider breaking it across multiple lines with trailing backslashes for improved readability. <!-- wuming:sha256:d614079a93f28ad119eba6359dbd684ab275c0729f445c130284d821c140edbf -->
@ -115,0 +150,4 @@
The app is then reachable at `https://xique.<your-tailnet>.ts.net`; TLS comes
from Tailscale and funnel stays disabled, so nothing is exposed beyond the
tailnet. All containers in the pod start simultaneously; on a first-ever boot
the web process finds no schema, refuses to serve, and keeps restarting every

docs [LOW]

Repeats the word 'seconds' twice within the same sentence ('every couple of seconds ... settles in seconds'). Consider rephrasing to improve flow, e.g., 'keeps restarting repeatedly... this resolves quickly.'

**docs** [LOW] Repeats the word 'seconds' twice within the same sentence ('every couple of seconds ... settles in seconds'). Consider rephrasing to improve flow, e.g., 'keeps restarting repeatedly... this resolves quickly.' <!-- wuming:sha256:4be9e7b528d8fce19c7e0ef375bc04fca869d854be404594579a16f479b478b2 -->
@ -115,0 +169,4 @@
Backups: the database is a plain file at
`~/docker/xique/data/db/xique.db`; uploaded Trakt archives live on a separate
named volume and are transient. Full backup/restore guidance (including WAL
checkpoint behaviour) is coming in the operator documentation (#16).

docs [MEDIUM]

Avoid future tense and forward references for documentation. Instead of stating guidance 'is coming', either link to the completed document when ready or simply state 'Backup/restore guidance (including WAL checkpoint behaviour) is documented in the operator guide (#16).'

**docs** [MEDIUM] Avoid future tense and forward references for documentation. Instead of stating guidance 'is coming', either link to the completed document when ready or simply state 'Backup/restore guidance (including WAL checkpoint behaviour) is documented in the operator guide (#16).' <!-- wuming:sha256:266b8101117810eb4205de815fbafdae5ecf54c1b7dcea75d71f30e89fafd742 -->
marvin8 marked this conversation as resolved
Author
Collaborator

WuMing review wave 6 — resolutions

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:f2531f828a25faa6048e55deca0325fdfc79e3d15ba7786f066510f72e002dbb -->

🔴 Not present — no backslash-escaped dollar sign exists anywhere in README (grep '\\\$' returns nothing); ${TS_CERT_DOMAIN} appears only inside backticks.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:d614079a93f28ad119eba6359dbd684ab275c0729f445c130284d821c140edbf -->

✅ Fixed in HEAD — the copy command is now a fenced, line-continued block.

README.md — docs [LOW]

docs [LOW] (original) <!-- wuming:sha256:4be9e7b528d8fce19c7e0ef375bc04fca869d854be404594579a16f479b478b2 -->

✅ Fixed in HEAD — the sentence no longer repeats "seconds".

README.md — docs [MEDIUM]

docs [MEDIUM] (original) <!-- wuming:sha256:266b8101117810eb4205de815fbafdae5ecf54c1b7dcea75d71f30e89fafd742 -->

🔴 Declined — the suggested present tense would be untrue today: the operator guide does not exist yet (#16 is open). "Is coming" is the honest state; it will be updated to a link when #16 lands.

## WuMing review wave 6 — resolutions ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5678)) `<!-- wuming:sha256:f2531f828a25faa6048e55deca0325fdfc79e3d15ba7786f066510f72e002dbb -->` 🔴 Not present — no backslash-escaped dollar sign exists anywhere in README (`grep '\\\$'` returns nothing); `${TS_CERT_DOMAIN}` appears only inside backticks. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5679)) `<!-- wuming:sha256:d614079a93f28ad119eba6359dbd684ab275c0729f445c130284d821c140edbf -->` ✅ Fixed in HEAD — the copy command is now a fenced, line-continued block. ### `README.md` — docs [LOW] > docs [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5680)) `<!-- wuming:sha256:4be9e7b528d8fce19c7e0ef375bc04fca869d854be404594579a16f479b478b2 -->` ✅ Fixed in HEAD — the sentence no longer repeats "seconds". ### `README.md` — docs [MEDIUM] > docs [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5681)) `<!-- wuming:sha256:266b8101117810eb4205de815fbafdae5ecf54c1b7dcea75d71f30e89fafd742 -->` 🔴 Declined — the suggested present tense would be untrue today: the operator guide does not exist yet (#16 is open). "Is coming" is the honest state; it will be updated to a link when #16 lands.
📝 docs: break the long copy line and tighten the first-boot sentence
All checks were successful
/ gitleaks (pull_request) Successful in 25s
/ pr-review (pull_request) Successful in 1m59s
/ checks (pull_request) Successful in 3m24s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m58s
/ publish-container (pull_request) Successful in 2m41s
d50e7df857
forgejo-actions left a comment

WuMing

Found 1 issue(s). See inline comments below.

## WuMing Found **1** issue(s). See inline comments below.
@ -0,0 +14,4 @@
from tryke import fixture
from tryke import test
from xique.config import Settings

code [LOW]

Unused import of Settings. It is not referenced in this test module. Remove to keep imports lean.

**code** [LOW] Unused import of `Settings`. It is not referenced in this test module. Remove to keep imports lean. <!-- wuming:sha256:79b6361a7939c6a777343161f3d377b1dbb99bd5657bf58c0deeec4a6816caa1 -->
Author
Collaborator

WuMing review wave 7 — resolutions

tests/test_web_server.py — code [LOW]

code [LOW] (original) <!-- wuming:sha256:79b6361a7939c6a777343161f3d377b1dbb99bd5657bf58c0deeec4a6816caa1 -->

🔴 Not unused — Settings annotates the stub_configure_logging(settings: Settings) parameter at tests/test_web_server.py:65.

## WuMing review wave 7 — resolutions ### `tests/test_web_server.py` — code [LOW] > code [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5687)) `<!-- wuming:sha256:79b6361a7939c6a777343161f3d377b1dbb99bd5657bf58c0deeec4a6816caa1 -->` 🔴 Not unused — `Settings` annotates the `stub_configure_logging(settings: Settings)` parameter at tests/test_web_server.py:65.
🐛 fix: honor an operator-set XIQUE_PORT inside the container
All checks were successful
/ gitleaks (pull_request) Successful in 20s
/ pr-review (pull_request) Successful in 1m49s
/ checks (pull_request) Successful in 3m24s
/ deploy-docs (pull_request) Has been skipped
/ container (pull_request) Successful in 1m59s
/ publish-container (pull_request) Successful in 3m12s
18ab868975
forgejo-actions left a comment

WuMing

Found 2 issue(s). See inline comments below.

## WuMing Found **2** issue(s). See inline comments below.
@ -0,0 +5,4 @@
ARG VERSION
ENV PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1

shell [LOW]

PIP_DISABLE_PIP_VERSION_CHECK=1 is declared in ENV but is entirely unused since the Dockerfile switches to uv for all dependency operations immediately after. Removing it avoids polluting the final image's environment variables.

**shell** [LOW] PIP_DISABLE_PIP_VERSION_CHECK=1 is declared in ENV but is entirely unused since the Dockerfile switches to uv for all dependency operations immediately after. Removing it avoids polluting the final image's environment variables. <!-- wuming:sha256:39c7082b19b5a5c7fa288696767308dd1e20eeb0084879edcf1a902e3a9a5548 -->
marvin8 marked this conversation as resolved
@ -0,0 +105,4 @@
environment: Annotated[MutableMapping[str, str], Depends(environment)],
calls: Annotated[list[dict[str, Any]], Depends(recorded_uvicorn)],
) -> None:
environment["XIQUE_SECRET_KEY"] = "test-key"

code [MEDIUM]

The test test_server_defaults_to_loopback uses the default database path /data/db/xique.db without mocking or overriding it. If create_app (called inside server.main()) initializes a database connection or opens the file, the test may fail on systems without that directory or write unwanted files. Consider patching the setting or using a temporary directory to isolate the test from the file system.

**code** [MEDIUM] The test `test_server_defaults_to_loopback` uses the default database path `/data/db/xique.db` without mocking or overriding it. If `create_app` (called inside `server.main()`) initializes a database connection or opens the file, the test may fail on systems without that directory or write unwanted files. Consider patching the setting or using a temporary directory to isolate the test from the file system. <!-- wuming:sha256:a6cf768fc58251ef3b378b1d60b5e455a6c4d8b39744eb9f2ff697b24a458322 -->
Author
Collaborator

WuMing review wave 8 — resolutions

Containerfile — shell [LOW]

shell [LOW] (original) <!-- wuming:sha256:39c7082b19b5a5c7fa288696767308dd1e20eeb0084879edcf1a902e3a9a5548 -->

🔴 Declined — the variable is not unused: it is consumed by the very next instruction, pip install --no-cache-dir "uv==${UV_VERSION}", which is the only pip invocation in the build. It does persist into the runtime env, where it is inert (nothing runs pip at runtime).

tests/test_web_server.py — code [MEDIUM]

code [MEDIUM] (original) <!-- wuming:sha256:a6cf768fc58251ef3b378b1d60b5e455a6c4d8b39744eb9f2ff697b24a458322 -->

🔴 Declined again (same finding as two waves ago) — no filesystem access is possible: uvicorn.run is stubbed, so the ASGI lifespan (the only code that opens the database) never executes; create_app only builds the route table and Settings merely holds a Path object. The suite is green locally and in the CI checks job.

## WuMing review wave 8 — resolutions ### `Containerfile` — shell [LOW] > shell [LOW] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5701)) `<!-- wuming:sha256:39c7082b19b5a5c7fa288696767308dd1e20eeb0084879edcf1a902e3a9a5548 -->` 🔴 Declined — the variable is not unused: it is consumed by the very next instruction, `pip install --no-cache-dir "uv==${UV_VERSION}"`, which is the only pip invocation in the build. It does persist into the runtime env, where it is inert (nothing runs pip at runtime). ### `tests/test_web_server.py` — code [MEDIUM] > code [MEDIUM] ([original](https://forge.marvin8.zone/marvin8/xique/pulls/17#issuecomment-5700)) `<!-- wuming:sha256:a6cf768fc58251ef3b378b1d60b5e455a6c4d8b39744eb9f2ff697b24a458322 -->` 🔴 Declined again (same finding as two waves ago) — no filesystem access is possible: `uvicorn.run` is stubbed, so the ASGI lifespan (the only code that opens the database) never executes; `create_app` only builds the route table and `Settings` merely holds a `Path` object. The suite is green locally and in the CI `checks` job.
marvin8 approved these changes 2026-10-16 23:15:54 +00:00
marvin8 manually merged commit 55344a176a into main 2026-10-16 23:17:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/xique!17
No description provided.