Skip to content

Project Layout

This doc describes the canonical on-disk structure of a videoclaw project — the unit vclaw video init <slug> creates and every downstream stage operates on.

Slug rules

Enforced at vclaw video init time:

RuleConstraint
Allowed chars[a-z0-9-]
Must start with[a-z0-9] (not -)
Must end with[a-z0-9] (not -)
Length3–64 chars
Disallowed substrings--, leading ., reserved names (history, artifacts, checkpoints, events, state, outputs, assets, obsidian, characters, notes, tmp)
Argv guardReject anything that looks like a flag (^-). Prevents the historical bug where vclaw video init --project foo parsed --project as the slug.

Recommended slug template when the user doesn't provide one: <yyyy-mm-dd>-<noun>-<noun> (e.g., 2026-05-25-disco-monster).

Canonical layout

  • projects/<slug>/rootthe unit every stage operates on
    • project.jsoncommitmanifest — slug, mode, state
    • artifacts/canonical JSON (schema-enforced)
      • history/append-only snapshots on overwrite
    • checkpoints/one file per stage — approval state
    • characters/canonical identity store
    • events/events.jsonlappend-only timeline
    • notes/free-form human / model markdown
    • outputs/gitignoredfinal + per-scene media
    • assets/gitignoredintermediate visual assets
    • obsidian/gitignoredObsidian mirror of artifacts
    • *.htmlderived preview / review / client portals
    • state/derived state cache
Green = canonical source of truth · rust = derived / gitignored
projects/<slug>/

├── project.json                # MANIFEST (see "project.json shape" below)
├── storyboard.md               # Director-mode human-readable approval doc
│                                 (absent in storyboard-mode until storyboard
│                                 stage)

├── artifacts/                  # CANONICAL machine-readable outputs.
│   │                             JSON only. Every file MUST have a schema
│   │                             in schemas/video/artifacts/.
│   ├── brief.json
│   ├── storyboard.json
│   ├── story-bible.json        # Deterministic continuity bible (cast,
│   │                             settings, props, scene timeline, continuity
│   │                             notes) auto-emitted at storyboard-write time.
│   ├── asset-manifest.json
│   ├── scene-candidates.json
│   ├── scene-selection.json
│   ├── reference-sheets.json
│   ├── execution-plan.json
│   ├── execution-report.json
│   ├── review-report.json
│   ├── publish-report.json
│   ├── analyze-output.json
│   ├── clone-plan.json
│   ├── multi-shot-prompt.json  # Written only when `vclaw video multi-shot
│   │                             --project <slug>` is used; absent for
│   │                             standalone (no --project) invocations.
│   ├── filmmaking-prompts.json # Character sheet, 9-panel storyboard grid,
│   │                             reference map, and Seedance prompt packets.
│   └── history/                # Snapshots of artifacts/ files on overwrite.
│                                 Append-only. One subdir per artifact:
│                                 history/brief/<ts>.json.

├── checkpoints/                # ONE FILE per stage. Tracks approval state,
│   ├── brief.json                who approved when, retry count, verdict.
│   ├── storyboard.json
│   ├── assets.json
│   ├── review.json
│   └── publish.json

├── characters/                 # CANONICAL identity store for this project.
│   └── characters.json

├── events/                     # Append-only timeline. JSONL.
│   └── events.jsonl              Payloads use project-relative paths only.

├── notes/                      # Human-authored or model-authored MD.
│   │                             Free-form. Anything NOT a canonical
│   │                             JSON artifact lives here.
│   └── (markdown files)

├── outputs/                    # DERIVED. Final encoded media only.
│   ├── final/<slug>-<mode>.mp4   Created by publish stage.
│   ├── scene-0.mp4               Per-scene renders.
│   ├── scene-1.mp4
│   └── ...                       (Gitignored at project level.)

├── preview.html                # DERIVED. Final portal showcase.
├── edit.html                   # DERIVED. Editor edit/check portal.
├── review.html                 # DERIVED. Editor review portal.
├── client-review.html          # DERIVED. Client review portal.
├── compare.html                # DERIVED. Version/run comparison portal.
├── project-audit.jsonl         # Append-only preview portal generation/publish audit.

├── assets/                     # DERIVED. Intermediate visual assets.
│   ├── storyboard-grid.png       Deterministic 3x3 production board from
│   │                             `vclaw video storyboard-grid`.
│   ├── storyboard/               Per-scene stills.
│   ├── upscaled/                 Upscaled variants.
│   └── ...                       (Gitignored.)

├── obsidian/                   # DERIVED. Mirror of canonical artifacts
│                                 in Obsidian-friendly MD. Created by
│                                 `vclaw video obsidian-export`.
│                                 (Gitignored.)

└── state/                      # Derived state cache. Created on init by
                                  ensureProjectWorkspace.

Directory disposition

DirectoryAlways-present on initSchema-enforcedGitignored at project level
project.jsonyesyesno (commit)
storyboard.mddirector-mode onlyn/a (free-form)no
artifacts/yes (empty)yes — every fileno
artifacts/history/yes (mkdir at init)yesno
checkpoints/yes (empty)yes (per-stage shape)no
characters/yes (empty)yes (characters.json shape)no
events/yes (empty)line shape enforcedno
state/yes (empty)internal cacheno
notes/on demandno (MD)no
outputs/on demandn/a (media)yes
preview.html / edit.html / review.html / client-review.html / compare.htmlon portal generationn/a (static HTML)no
project-audit.jsonlon portal generation/publishJSONL event shapeno
assets/on demandn/a (media)yes
obsidian/opt-in commandn/a (MD mirror)yes

Git tracking

vclaw video init does not write a per-project .gitignorehandleVideoInit only writes project.json, the pending brief checkpoint, and the project.initialized event. Any gitignore handling is at the repo root, not per-project.

The derived directories — outputs/, assets/, obsidian/ — are re-generable from canonical artifacts and are the candidates to ignore if you wire up repo-root gitignore rules. artifacts/, checkpoints/, characters/, events/, notes/, project.json, and storyboard.md are canonical by design so a project is reproducible from its on-disk state.

project.json shape

The manifest is VideoProjectManifest (src/video/workspace.ts). The pipeline manifest is embedded inline — there is no versioned reference and no schemaVersion. On init, handleVideoInit writes:

json
{
  "slug": "fresh-proof",
  "productionMode": "director",
  "createdAt": "2026-05-06T02:20:19.421Z",
  "updatedAt": "2026-05-06T02:20:19.421Z",
  "pipeline": { "name": "director", "stages": ["..."] },
  "currentStage": "brief",
  "lastCompletedStage": null,
  "lastCheckpointStatus": "pending"
}

Required fields:

  • slug — the project slug.
  • productionMode"storyboard" or "director".
  • createdAt / updatedAt — ISO timestamps.
  • pipeline — the full embedded VideoPipelineManifest object (not a pipelineRef string). In practice only pipeline.name is read back by downstream code.
  • currentStage / lastCompletedStage / lastCheckpointStatus — stage-machine cursor. On init these are "brief", null, and "pending".

Optional fields, written only by updateProjectManifestMetadata (owner, priority, dueDate, tags, blockedBy, blockedReason) and updateProjectManifestCinemaProfile (cinemaProfile). There is no schemaVersion, no pipelineRef, no createdBy, and no nested metadata object.

Event log shape

events.jsonl lines follow the VideoProjectEvent envelope (src/video/events.ts): { type, recordedAt, payload? }. There is no id/ULID and no source field.

jsonl
{"type":"artifact.review-report.written","recordedAt":"2026-05-06T21:54:52.404Z","payload":{"artifactPath":"artifacts/review-report.json","verdict":"pass"}}

Readers split on newlines and JSON.parse each non-empty line; appendProjectEvent fills recordedAt if absent. Event-payload paths are project-relative by convention.

Artifact schema coverage guardrail

The build pipeline includes check:artifact-schema-coverage (scripts/check-artifact-schema-coverage.mjs), which asserts:

  1. Every artifact name passed to writeArtifact(workspace, '<name>', ...) in src/video/**/*.ts has a matching schemas/video/artifacts/<name>.schema.json.
  2. Every schema in schemas/video/artifacts/ has either a matching writeArtifact() call OR is in the script's KNOWN_ALTERNATE_WRITERS allowlist (audit-pending; some artifacts are written via specialized helpers rather than the typed writeArtifact() shim).

Modes:

  • Default (advisory) — prints any drift but always exits 0. This is what check:release-readiness-lite invokes, so the guardrail reports status inline without blocking the release-readiness pre-flight while the allowlist is being burned down.
  • --strict — exits 1 on drift. Wire into CI gates / pre-commit hooks when you want hard enforcement. Currently zero unexpected drift with the allowlist applied.

Current allowlist (9 schemas needing per-artifact audit): analyze-output, clone-plan, execution-plan, multi-shot-prompt, publish-report, reference-sheets, review-report, scene-candidates, scene-selection. The audit work is tracked in MERGE_PLAN.md §A2.

Slug validation implementation

The slug rules above are enforced by validateInitSlug() in src/cli/vclaw.ts, called from the init command before any filesystem operation. Test coverage in src/tests/cli-init-slug-validation.test.ts (6 cases):

  1. rejects --project as the slug (the historical argv-as-slug bug)
  2. rejects uppercase / whitespace / dots / underscores / leading dots / leading dashes
  3. rejects reserved per-project directory names
  4. rejects consecutive --
  5. rejects too-short / too-long
  6. accepts a well-formed slug (2026-05-25-disco-monster)

Each failure mode has a distinct error message; the argv-as-slug case explicitly names the bug to make the fix discoverable.

Built to be driven by agent hosts like Claude Code, Claude Desktop, or Codex · Source-available, commercial use requires a paid license.