feat(a2ui-renderer): first-class compose + render script + freshness check + registry (#2228) - #2349
Conversation
|
Warning Review limit reached
Next review available in: 12 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 292df1819f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| build: | ||
| context: . | ||
| dockerfile: services/a2ui-renderer/Dockerfile |
There was a problem hiding this comment.
Point the Docker build context at the renderer service
The build context resolves to pmoves/, while services/a2ui-renderer/Dockerfile begins with COPY package.json package-lock.json* ./; there is no pmoves/package.json, only pmoves/services/a2ui-renderer/package.json. Consequently the documented make -C pmoves up-a2ui-renderer path invokes docker compose ... --build and fails before the image can be built. Set the context to services/a2ui-renderer (and adjust the Dockerfile path accordingly).
Useful? React with 👍 / 👎.
| docs-render-living: ## Render all LIVING_DOCS_INDEX-registered docs through a2ui-renderer | ||
| @echo "Rendering living docs from pmoves/config/living_docs_registry.yaml..." | ||
| @mkdir -p $(A2UI_RENDERER_RENDER_DIR) | ||
| @uv run --script pmoves/tools/a2ui_renderer/render_living_doc.py \ |
There was a problem hiding this comment.
Replace the existing docs-render-living target
This new registry iterator never runs because the same Makefile defines docs-render-living again at line 4290; GNU Make warns that it is overriding this recipe and executes tools/render_living_docs.py instead. Thus the documented make -C pmoves docs-render-living command does not read the new registry or invoke render_living_doc.py; remove or rename the later definition rather than adding a duplicate target.
Useful? React with 👍 / 👎.
| server.add_argument( | ||
| "--token", | ||
| default=os.environ.get("A2UI_RENDERER_TOKEN") or os.environ.get("SUPABASE_JWT_SECRET"), | ||
| help="Supabase JWT for /render/provenance (default: $A2UI_RENDERER_TOKEN or $SUPABASE_JWT_SECRET)", |
There was a problem hiding this comment.
Use a signed JWT instead of the JWT signing secret
When callers follow the default or README and only set SUPABASE_JWT_SECRET, this passes the raw signing secret as the bearer token. The renderer's requireAuth middleware calls jwt.verify(token, SUPABASE_JWT_SECRET), so the raw secret is rejected as a malformed JWT and every real render returns 401. Default to an actual signed credential such as the service-role JWT, while retaining the secret only in the renderer container for verification.
Useful? React with 👍 / 👎.
| healthcheck: | ||
| test: ["CMD-SHELL", "wget -qO- http://localhost:8107/healthz >/dev/null 2>&1 || exit 1"] |
There was a problem hiding this comment.
Run the healthcheck with the installed curl binary
The runtime Dockerfile installs curl, not wget, and its own image healthcheck consequently uses curl. This Compose override instead invokes wget, so after the build-context issue is fixed the command still exits with wget: not found and Docker permanently marks the renderer unhealthy. Use the already-installed curl command or install wget in the runtime image.
Useful? React with 👍 / 👎.
| if minio_bucket and not dry_run and result.get("minio_url"): | ||
| # The renderer already uploaded to MinIO; we just record the | ||
| # canonical key for downstream consumers. | ||
| result["canonical_minio_key"] = f"a2ui/living-docs/{output_key}.{fmt}" | ||
| result["canonical_minio_uri"] = f"s3://{minio_bucket}/{result['canonical_minio_key']}" |
There was a problem hiding this comment.
Upload artifacts under the reported canonical key
In registry mode with --minio-bucket, these lines report a2ui/living-docs/<output_key>.<format> as the canonical object, but no upload or copy to that key occurs. The renderer actually uploaded a randomly named a2ui/provenance/<timestamp>-<random>.<format> object, so downstream consumers using canonical_minio_uri receive a nonexistent location even though the result is marked successful. Copy/upload the artifact to the canonical key or report the renderer's real key instead.
Useful? React with 👍 / 👎.
| "ttl_days": ttl_days, | ||
| } | ||
| mtime = source.stat().st_mtime |
There was a problem hiding this comment.
Compare source changes against the rendered artifact
Freshness is calculated solely from the source file's age relative to the current time, without inspecting whether an artifact exists or when it was rendered. Therefore a newly rendered document whose source is older than its TTL remains stale forever, while a newly edited source with no rendered artifact is reported fresh until its TTL expires. Track the artifact timestamp and mark entries stale when the artifact is absent, older than the source, or beyond the intended render TTL.
Useful? React with 👍 / 👎.
292df18 to
293c5db
Compare
Docker Hardening ValidationHardening Validation ReportValidated: Sun Aug 2 13:29:32 UTC 2026Services CheckedPMOVES.AI Docker Hardening Validation[INFO] Checking: pmoves/docker-compose.hardened.yml [INFO] Validating: hi-rag-gateway-v2 [INFO] Validating: extract-worker [INFO] Validating: langextract [INFO] Validating: presign [INFO] Validating: render-webhook [INFO] Validating: retrieval-eval [INFO] Validating: pdf-ingest [INFO] Validating: jellyfin-bridge [INFO] Validating: invidious-companion-proxy [INFO] Validating: ffmpeg-whisper [INFO] Validating: media-video [INFO] Validating: media-audio [INFO] Validating: hi-rag-gateway-v2-gpu [INFO] Validating: hi-rag-gateway-gpu [INFO] Validating: deepresearch [INFO] Validating: supaserch [INFO] Validating: publisher-discord [INFO] Validating: mesh-agent [INFO] Validating: nats-echo-req [INFO] Validating: nats-echo-res [INFO] Validating: comfy-watcher [INFO] Validating: grayjay-plugin-host [INFO] Validating: agent-zero [INFO] Validating: p7-room-orchestrator [INFO] Validating: archon [INFO] Validating: channel-monitor [INFO] Validating: pmoves-yt [INFO] Validating: notebook-sync [INFO] Validating: supabase_service_role_key [INFO] Validating: supabase_jwt_secret [INFO] Validating: p7_control_token ====================================== |
…le tokens + registry extension
Lane 2228 (2026-08-02): a2ui-renderer was a service-source orphan — the
TypeScript service at pmoves/services/a2ui-renderer/ existed, the Remotion
bundle compiled, the /render/provenance endpoint worked, but there was no
compose stanza, no Makefile target, and no registry saying "render this doc
into a living artifact". This P1 commit fixes all three.
What lands:
- pmoves/docker-compose.yml: a2ui-renderer service stanza
- port 8107, profiles agents/media/docs (3 profiles for the 3 callers)
- depends_on nats+minio (health-checked)
- tier-agent-hardened-ro
- CHIT fail-closed signing (mirrors hi-rag-gateway* pattern)
- Supabase JWT for /render, /render/chart, /render/provenance
- healthcheck via wget (not node -e, see lane 2228 spec)
- deploy limits 1.0 CPU / 1G memory
- pmoves/Makefile:
- up-a2ui-renderer (was --profile creator → now --profile agents --profile media --profile docs)
- down-a2ui-renderer (new, was missing)
- a2ui-renderer-smoke (extended to show /metrics counters)
- a2ui-renderer-render (new, single-doc helper)
- docs-render-living (new, registry iterator)
- tokens: A2UI_RENDERER_PORT, A2UI_RENDERER_TOKEN, A2UI_RENDERER_RENDER_DIR
- pmoves/configs/living_docs_registry.yaml:
- new renderable: section with 5 entries (chit-visual-tour-walkthrough,
agnote-active-claims, services-catalog-dashboard, a2ui-renderer-readme,
chit-tour-public)
- existing tracked: section unchanged
The functional commit (render script + freshness check + 18-test smoke suite)
follows on this branch. The docs commit (AGNOTE CLAIM + cold-read spec)
closes the lane.
Refs: #2228
See-also: pmoves/docs/specs/a2ui-renderer-compose-2026-08-02.md (functional commit)
…y + 18-test smoke suite
Lane 2228 (2026-08-02): the scriptable surface for the a2ui-renderer living-doc
lane. P1 brought the service up; this commit is what makes `make docs-render-living`
actually do something.
What lands:
- pmoves/tools/a2ui_renderer/render_living_doc.py (new, 612 lines):
- Single-doc mode: --doc <md> --output <mp4> --format mp4|gif|webm
- Registry mode: --registry <yaml> --output-dir <dir> --minio-bucket <b>
- Dependency-free (stdlib only), runs as a uv script shim
- Markdown parser handles H1/H2/H3, builds ProvenanceLivingDoc shape
that matches the TS service's normalizeProvenanceLivingDoc() contract
(4 sections max, 8 weighted terms max, 6 provenance refs max)
- merkle_root = sha256(normalized content)[:16] prefixed with mkl_
- shape_id = sha256(source path)[:16] prefixed with shape.doc.
- duration_ms mirrors estimateProvenanceDurationMs() 1:1
- --dry-run: parse + dump request body, no HTTP
- --print-doc: debug helper to inspect the parsed ProvenanceLivingDoc
- Forces UTF-8 on stdout/stderr (Windows charmap breaks on non-ASCII)
- pmoves/tools/a2ui_renderer/check_renderable_freshness.py (new, 130 lines):
- Reads renderable: section of living_docs_registry.yaml
- Classifies each entry: stale if source mtime > ttl_days, or missing
- JSON report on stdout, one-line summary on stderr
- --strict exits non-zero if any entry is stale (advisory hook target)
- pmoves/tools/a2ui_renderer/test_render_living_doc.py (new, 18 tests):
- MarkdownParserTests (10): H1→title, H2→sections, cap enforcement,
merkle determinism, shape_id stability, fallback behavior, schema
- RegistryLoaderTests (3): loads renderable section, excludes tracked,
handles empty case
- DryRunTests (1): writes valid JSON, no HTTP traffic
- FullPathTests (2): posts to in-process mock, downloads the result,
iterates a registry end-to-end
- ErrorPathTests (2): missing source raises, unsupported format raises
- pmoves/tools/a2ui_renderer/__init__.py (new, 1 line): package marker
- pmoves/tools/a2ui_renderer/README.md (new, 60 lines): operator quick-start
All 18 tests pass on a clean `python pmoves/tools/a2ui_renderer/test_render_living_doc.py`.
ruff check: All checks passed!
Refs: #2228
See-also: pmoves/docs/specs/a2ui-renderer-compose-2026-08-02.md (docs commit)
…lane (2228)
Lane 2228 (2026-08-02): closes the lane. The CLAIM goes on the AGNOTE
(matching the Lane 5 forward-looking RELEASE pattern, since admin-merge
is the operator's call); the cold-read spec captures the full design
for a fresh local model picking this work up next session.
What lands:
- pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md: new section "Lane 2228 -
a2ui-renderer compose lane SHIPPED (2026-08-02)" with the full CLAIM
text (root cause, what shipped, validation, out of scope, three-body,
CHIT trail). Ends with the GRAPHITI_MARK HTML comment so a future
graphiti loader can index it.
- pmoves/docs/specs/a2ui-renderer-compose-2026-08-02.md (new, 11.9KB):
cold-read spec covering TL;DR + why now + what shipped (P1 / func /
registry) + how to use (5-step recipe) + validation table + out of
scope (5 items) + follow-up lanes (5 items) + three-body + CHIT trail.
The PR is ready to push after this commit lands: 3 stacked commits, all
tests pass, ruff clean, YAML valid, dry-run produces a valid
ProvenanceLivingDoc, freshness check reports 0 stale entries.
Refs: #2228
293c5db to
3c5f6aa
Compare
Docker Hardening ValidationHardening Validation ReportValidated: Sun Aug 2 13:33:07 UTC 2026Services CheckedPMOVES.AI Docker Hardening Validation[INFO] Checking: pmoves/docker-compose.hardened.yml [INFO] Validating: hi-rag-gateway-v2 [INFO] Validating: extract-worker [INFO] Validating: langextract [INFO] Validating: presign [INFO] Validating: render-webhook [INFO] Validating: retrieval-eval [INFO] Validating: pdf-ingest [INFO] Validating: jellyfin-bridge [INFO] Validating: invidious-companion-proxy [INFO] Validating: ffmpeg-whisper [INFO] Validating: media-video [INFO] Validating: media-audio [INFO] Validating: hi-rag-gateway-v2-gpu [INFO] Validating: hi-rag-gateway-gpu [INFO] Validating: deepresearch [INFO] Validating: supaserch [INFO] Validating: publisher-discord [INFO] Validating: mesh-agent [INFO] Validating: nats-echo-req [INFO] Validating: nats-echo-res [INFO] Validating: comfy-watcher [INFO] Validating: grayjay-plugin-host [INFO] Validating: agent-zero [INFO] Validating: p7-room-orchestrator [INFO] Validating: archon [INFO] Validating: channel-monitor [INFO] Validating: pmoves-yt [INFO] Validating: notebook-sync [INFO] Validating: supabase_service_role_key [INFO] Validating: supabase_jwt_secret [INFO] Validating: p7_control_token ====================================== |
…SHIPPED + 3-PR triage batch (#2354) The 2026-08-02 closeout (PR #2345) left Lane 5 as a forward-looking RELEASE ("squash pending — PR #2344 open") and Lane 2228 as a forward-looking CLAIM ("this CLAIM becomes a RELEASE on admin-merge"). Both are now actually shipped. Plus a 3-PR triage batch that was DIRTY/BEHIND on main `a5320d7e3a`. What lands in the AGNOTE: - Lane 5 RELEASE: confirms squash 2cba394 (PR #2344) with full validation + the 2 follow-up items still open (village-gate wire-in + TOKENISM_ATTRIBUTION interest→limits migration). - Lane 2228 RELEASE: promotes the PR #2349 CLAIM to RELEASE, names the actual squash 8a377b3, references Issue #2228 closure. - 3-PR triage batch: PR #2352 (chit-visual-tour-codegrounded, the real answer to #2343 I closed yesterday), PR #2346 (yt-cookies fresh key), PR #2353 (monitoring mount paths — the VHDX-fill root cause). Refs: #2228 Squashes referenced: 2cba394, 8a377b3, 6178bda, 1bf7ed6, 2dcbf25 Co-authored-by: Shaela Bello <slbello@uncg.edu>
feat(a2ui-renderer): first-class compose stanza + render script + freshness check + registry (Issue #2228)
Lane 2228 (2026-08-02): a2ui-renderer was a service-source orphan —
TypeScript service at
pmoves/services/a2ui-renderer/existed,Remotion bundle compiled,
/render/provenanceendpoint worked, butno compose stanza + no Makefile target + no registry saying "render
this doc into a living artifact". This lane makes it a first-class
fleet capability.
3 stacked commits
0552b1537c0406ad082d292df1819fWhat lands
1. Compose stanza + Makefile tokens + registry (P1)
pmoves/docker-compose.yml:a2ui-rendererservice stanzaagents media docs(was the nonexistentcreatorprofile)wget -qO- http://localhost:8107/healthz(per the YAML flow-sequence escaping lesson)pmoves/Makefile: 5 targets (up-a2ui-renderer,down-a2ui-renderer,a2ui-renderer-smoke,a2ui-renderer-render,docs-render-living) + 3 tokens (A2UI_RENDERER_PORT,A2UI_RENDERER_TOKEN,A2UI_RENDERER_RENDER_DIR)pmoves/configs/living_docs_registry.yaml: newrenderable:section with 5 entries (existingtracked:section unchanged)2. Living-docs hook — render script + freshness check + 18-test smoke suite (functional)
pmoves/tools/a2ui_renderer/render_living_doc.py(612 lines, stdlib-only):--doc <md> --output <mp4>) + registry mode (--registry <yaml> --output-dir <dir>)normalizeProvenanceLivingDoc()contract 1:1 (4 sections max, 8 weighted terms max, 6 provenance refs max, merkle_root =mkl_+ sha256[:16], shape_id =shape.doc.+ sha256[:16], duration_ms mirrorsestimateProvenanceDurationMs()exactly)--dry-runwrites the request body without HTTP--print-docdumps the parsed doc for debuggingPYTHONIOENCODINGpmoves/tools/a2ui_renderer/check_renderable_freshness.py(130 lines): walks therenderable:section, classifies each entry as stale based on source mtime vsttl_days, JSON report on stdout + summary on stderr,--strictexits non-zeropmoves/tools/a2ui_renderer/test_render_living_doc.py(18 tests, all pass on a cleanpython test_render_living_doc.py): MarkdownParserTests (10), RegistryLoaderTests (3), DryRunTests (1), FullPathTests (2, in-process mock renderer), ErrorPathTests (2)pmoves/tools/a2ui_renderer/__init__.py+README.md(60 lines): operator quick-start3. AGNOTE CLAIM + cold-read spec (docs)
pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md: new section "Lane 2228 - a2ui-renderer compose lane SHIPPED (2026-08-02)" with the full CLAIM textpmoves/docs/specs/a2ui-renderer-compose-2026-08-02.md(new, 11.9KB): cold-read specValidation
python pmoves/tools/a2ui_renderer/test_render_living_doc.pyruff check pmoves/tools/a2ui_renderer/docker compose config --services | grep a2ui-renderera2ui-rendererpmoves/docs/PMOVESCHIT/VISUAL_TOUR.mdmkl_173c4ba26932179echeck_renderable_freshness.py --strict5 entries, 0 staleOut of scope (intentional)
Follow-up lanes
make docs-render-livinginto the docs-freshness village-gatemake docs-render-livingcron (7-day cadence, re-render stale entries)Three-body
Refs: #2228