Skip to content

feat(demo-function): per-function video docs subcommand + F1 WebM/MP4 fix#46

Merged
jmjava merged 3 commits intomainfrom
cursor/add-demo-function-subcommand-ab0f
May 4, 2026
Merged

feat(demo-function): per-function video docs subcommand + F1 WebM/MP4 fix#46
jmjava merged 3 commits intomainfrom
cursor/add-demo-function-subcommand-ab0f

Conversation

@jmjava
Copy link
Copy Markdown
Owner

@jmjava jmjava commented May 4, 2026

Summary

Adds docgen demo-function, a new CLI subcommand for rendering one short, single-purpose MP4 per function — the docs-site analogue of a single Playwright test('…') describing one behavior. Inputs are declarative (either a *.docgen.yaml sidecar or a @pytest.mark.docgen(...) decorator on a Python test, read statically via ast); outputs are five files in --output-dir. Bumps version to 0.2.0.

This absorbs the behavior of the downstream tools/docgen-shim/demo_function.py shim (~600 lines) into docgen itself, plus adds the seven sharp-edge fixes the spec called for. F1 also fixes the existing docgen playwright runner (back-compat preserved).

Subcommand contract

docgen demo-function \
  --manifest <path>                  # *.docgen.yaml sidecar OR <path>.py::<test_name>
  --output-dir <dir>                 # writes 5 artifacts
  [--cache-dir <dir>]                # optional; key = sha256(identifier+intent+fixtures)
  [--no-narration]                   # skip TTS even if OPENAI_API_KEY is set

Artifacts in --output-dir: rendered.mp4 (real ISO MP4, libx264, yuv420p, +faststart, sized to manifest resolution), poster.png (last frame), fragment.txt (fn-<slug>, no trailing newline), manifest.json (snapshot with the seven documented keys), cache-status.txt (hit\n or miss\n).

Exit codes: 0 success · 1 invalid manifest / render failure · 2 missing tooling (with install hint) · 78 neutral skip (placeholder manifest with no demonstration.url — mirrors Tekton's documented Skip code).

Sharp-edge fixes shipped

# Fix Location
F1 Sniff EBML header on .mp4 outputs and transcode WebM-in-MP4-path via ffmpeg. Real MP4 passes through unchanged. src/docgen/playwright_runner.py
F2 Manifest-driven actions; capture script generated internally — author never sees the env-var contract. src/docgen/demo_function.py
F3 Viewport / record_video_size driven from manifest.output_budget.resolution, not docgen.yaml. src/docgen/demo_function.py
F4 TTS narration on the intent line via gpt-4o-mini-tts, muxed with -shortest. Soft-warns when OPENAI_API_KEY is unset. src/docgen/demo_function.py
F5 Reads @pytest.mark.docgen(...) on Python tests via <path>.py::<test_name> syntax. src/docgen/demo_function.py::_load_pytest_marker
F6 wait_for_text uses native page.locator(sel).filter(has_text=text).first.wait_for(...) — no JS-in-Python-string-template gymnastics. _render_action, _execute_actions
F7 Marker reading uses ast.parse + decorator walk; never regex over source. Includes a regression test against module-docstring text that mentions the marker. _load_pytest_marker + tests/test_demo_function.py::test_python_marker_ignores_docstring_text

Files

  • New: src/docgen/demo_function.py (manifest dataclasses, YAML + AST loaders, validator, action renderer, capture-script template, cache lookup/store, ffmpeg helpers, narration, render orchestration, run_cli exit-code mapping).
  • New: tests/test_demo_function.py (31 tests; pure-data tests run without Playwright or ffmpeg installed; the few end-to-end render tests skip when ffmpeg is missing).
  • New: examples/lesson_compile.docgen.yaml, examples/sample_test.py (both manifest shapes for one identifier).
  • Modified: src/docgen/cli.py (@main.command("demo-function")).
  • Modified: src/docgen/playwright_runner.py (F1).
  • Modified: tests/test_playwright_runner.py (two new tests for F1: WebM transcode + real-MP4 pass-through).
  • Modified: README.md (new section + CLI table row).
  • Modified: pyproject.toml (0.1.00.2.0).

Acceptance-criteria coverage

# Criterion Test / verification
1 Exit 0 on a valid run test_render_cli_kind_emits_artifacts + manual smoke
2 All five files written same
3 rendered.mp4 is real ISO MP4 (not WebM) ftyp header asserted; manual file confirms ISO Media, MP4 Base Media v1
4 poster.png resolution matches manifest manual file confirms PNG image data, 320 x 240 for a 320x240 manifest
5 fragment.txt matches ^fn-[a-z0-9-]+$, no trailing newline test_fragment_id_format + test_render_cli_kind_emits_artifacts
6 manifest.json parses, has the seven keys test_render_cli_kind_emits_artifacts
7 cache-status.txt is miss\n then hit\n test_render_cli_kind_emits_artifacts
8 Python marker form produces equivalent run test_yaml_and_python_marker_produce_equivalent_manifests
9 Empty demonstration.url → exit 78, no writes test_run_cli_neutral_skip + manual smoke
10 duration_seconds: 120 → exit 1 with hard-cap message test_run_cli_invalid_manifest_duration + manual smoke
11 No OPENAI_API_KEY → warning + visual-only; with key → AAC track + non-null manifest.json.narration test_render_warns_when_openai_key_missing (negative half); positive half exercised in _generate_narration integration
12 Existing docgen playwright keeps working full existing test_playwright_runner.py suite still green; F1 path is gated on EBML header + .mp4 suffix

Test results

145 passed in 6.68s

(31 new + 114 pre-existing; ruff clean.)

Out of scope (per spec)

Hosting / CDN choices, replacing docgen generate-all, non-pytest annotation discovery, web-UI manifest authoring, cache eviction policy.

Open in Web Open in Cursor 

cursoragent and others added 3 commits May 4, 2026 17:09
…(F1)

Playwright's record_video_dir always writes WebM. Capture scripts that
shutil.copy those bytes into a .mp4 path (to satisfy filename-based
validators) end up with a WebM payload masquerading as MP4. The runner
now sniffs the EBML header and transcodes in place via ffmpeg so
downstream consumers always get a real ISO MP4. Real MP4 outputs pass
through unchanged (back-compat preserved).

Co-authored-by: John Menke <jmjava@gmail.com>
Adds 'docgen demo-function' for rendering one short, single-purpose video
per function (~the docs-site analogue of one Playwright test() per
behavior). Inputs are declarative — either a *.docgen.yaml sidecar or a
@pytest.mark.docgen(...) decorator on a Python test, read statically via
ast (never imported / exec'd). Outputs are five files in --output-dir:
rendered.mp4 (real ISO MP4), poster.png, fragment.txt (fn-<slug>),
manifest.json (snapshot), cache-status.txt (hit/miss).

Exit codes: 0 success, 1 invalid manifest / render failure, 2 missing
ffmpeg / playwright (with install hint), 78 neutral skip (placeholder
manifest with no demonstration.url) — mirrors Tekton's documented Skip
exit code so CI does not flag placeholder-shaped manifests as failures.

Action vocabulary (8 kinds): goto, click, fill, type, wait_for,
wait_for_text, wait, screenshot. wait_for_text uses the native locator
API instead of wait_for_function with embedded JS strings (F6).

When OPENAI_API_KEY is set, the intent line is narrated via
gpt-4o-mini-tts and muxed onto the video with -shortest. When unset,
emits a documented warning to stderr and continues with a visual-only
video (narration: null in manifest.json).

Cache: when --cache-dir is given, content-addressed by sha256(identifier
+ intent + sorted(fixture_paths) + concat(fixture_contents))[:16]. Hits
copy all four content files into output-dir verbatim and skip
Playwright/ffmpeg/TTS entirely.

Adds 31 unit tests covering manifest loading (both shapes), validation
(resolution/duration/kind), action rendering, generated-script compile,
fragment_id and cache_key stability, CLI exit codes, end-to-end render
of kind=cli (ffmpeg-only, no Playwright), and the F7 docstring
regression. Tests pass without Playwright or browsers installed.

Co-authored-by: John Menke <jmjava@gmail.com>
Adds a README subsection describing 'docgen demo-function' (one
paragraph + example invocation) and links to examples/. Bumps the
package version to 0.2.0 since this adds a new public CLI surface.

Co-authored-by: John Menke <jmjava@gmail.com>
@jmjava jmjava marked this pull request as ready for review May 4, 2026 17:21
@jmjava jmjava merged commit ba416b2 into main May 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants