All checks were successful
Build + deploy releases / build-and-deploy (push) Successful in 8s
Schema-driven form renderer plus zddc-server endpoints that turn any
<name>.form.yaml into a working data-collection form at <path>/<name>.form.html.
Submissions land in <path>/<name>/<YYYY-MM-DD>-<email-sanitized>.yaml,
ACL-gated by the existing .zddc cascade. The form posts back to its own URL;
the server strips ".html" and routes by what's underneath, so create and
update use the same client-side code path.
Form spec dialect: JSON Schema 2020-12 + RJSF-style ui:* hints, written in
YAML. Chosen for LLM authorability — it's the canonical structured-output
target for OpenAI/Anthropic, and the ui:* convention is the most-trained UI
hint vocabulary. Supported subset for v0: type (string/number/integer/boolean/
array/object), enum, min/max, minLength/maxLength, required, additionalProperties:
false, properties, items, format (date, email). Round-trip mode is form-as-truth:
submission YAML is regenerated each save, comments are not preserved (the v1
file-as-truth mode for hand-edited files like .zddc itself is deferred).
New components:
* form/ — sixth single-file HTML tool, vanilla JS renderer (~760 LoC)
* zddc/internal/jsonschema/ — focused JSON Schema validator covering only
the v0 keyword subset. Match-implementation-cost-to-surface-used: a full
library brings 70%+ surface we don't use; revisit when v1 adds $ref +
oneOf + if/then/else.
* zddc/internal/handler/formhandler.go — RecognizeFormRequest / ServeForm,
capability-URL re-edit, atomic submission writes via the new
zddc.WriteAtomic helper extracted from writer.go.
* dispatch() in zddc-server/main.go now intercepts *.form.html and
*.yaml.html before the static-file path; spec existence is the trigger.
Build pipeline: form joins ZDDC_RELEASE_TOOLS in lockstep, gets its own
embedded copy in handler/form.html (separate from the apps cascade —
the form renderer is fixed, not subject to per-folder version overrides).
Tests: 5 new Playwright specs (form-safety) + 14 new Go tests across the
validator and handler. All 172 Playwright tests + 10 Go packages green.
End-to-end manual verification: GET empty → POST 201 + capability URL →
GET re-edit (pre-filled) → POST update → 200, raw YAML browsable, ACL
deny → 403.
Docs: form/ section added to AGENTS.md and ARCHITECTURE.md. AGENTS.md
also documents the implementation-vs-dependency policy. CLAUDE.md repo-shape
list extended.
Deferred (v1+): .zddc editor migration onto this system, file-as-truth
lossless YAML round-trip, ui:show-when conditional visibility, oneOf/anyOf,
apps-cascade preview hook, cascade-fetched form definitions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79 lines
11 KiB
Markdown
79 lines
11 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Authoritative docs — read these first
|
|
|
|
This repo already has two thorough agent-facing references. **Always consult them before working** — they cover details intentionally omitted here:
|
|
|
|
- **`AGENTS.md`** — commands, build-system rules, per-tool parser quirks, testing gotchas, git/worktree workflow, release process, zddc-server notes
|
|
- **`ARCHITECTURE.md`** — single-file HTML pattern rationale, JS module/state patterns, per-tool architecture, security model
|
|
|
|
If something in this CLAUDE.md conflicts with those, those win — and please update them rather than letting drift accumulate.
|
|
|
|
## Repo shape
|
|
|
|
This is a **monorepo of independent tools**, not one application:
|
|
|
|
- `archive/`, `transmittal/`, `classifier/`, `mdedit/`, `landing/`, `form/` — six self-contained HTML tools, each compiled to a single inlined HTML file in its own `dist/`. Most output `dist/tool.html`; **`landing/` outputs `dist/index.html`** (it's the project picker served at the root of `zddc-server`). The sixth tool, `form/`, is the schema-driven renderer for the form-data system (any `<name>.form.yaml` file in the tree becomes an editable form at `<path>/<name>.form.html`); see AGENTS.md "Form-data system" and ARCHITECTURE.md "Form Renderer".
|
|
- `zddc/` — Go HTTP server (separate sub-project; Go 1.24+). Serves `ZDDC_ROOT/index.html` at `GET /` as the landing page; `Accept: application/json` on `/` returns the ACL-filtered project list. Cross-compiled binaries are produced by `./build` and live in `dist/release-output/` (gitignored); `./deploy` rsyncs them to `/srv/zddc/releases/` on the deploy host (Caddy serves them at `https://zddc.varasys.io/releases/`). The `helm/` charts in this repo build from source at deploy time.
|
|
- `shared/` — `base.css` plus shared JS modules (`zddc.js`, `hash.js`, `zddc-filter.js`, `theme.js`, `help.js`) included by every tool's build, and `build-lib.sh` (POSIX sh helpers sourced by every tool's `build.sh` AND by the top-level `build` for lockstep release helpers).
|
|
- **Two-repo + deploy-host model.** Source code lives here (`codeberg.org/VARASYS/ZDDC`). Hand-edited website content lives in a separate repo (`codeberg.org/VARASYS/ZDDC-website`, typically cloned at `~/src/zddc-website/` — just `index.html`, `reference.html`, `css/`, `js/`, `img/`; no releases, no LFS). The live site at `zddc.varasys.io` is served from `/srv/zddc/` on the deploy host: Caddy bind-mounts that path, and it's populated by `./deploy` from this repo's `dist/release-output/` plus `~/src/zddc-website/`. **Releases are NOT in any git history** — they're reproducible from this repo's `<tool>-vX.Y.Z` tags by checking out the tag and running `./build release X.Y.Z`. Per-version files (`<tool>_v<X.Y.Z>.html`) are immutable; partial-version pins (`<tool>_v<X.Y>.html`, `<tool>_v<X>.html`) and channel mirrors (`<tool>_{stable,beta,alpha}.html`) are symlinks; zddc-server has analogous `zddc-server_v<X.Y.Z>_<platform>` per-version binaries plus channel/partial-version symlinks plus `zddc-server_<X>.html` stub pages that fan out the four-platform download in one cell. **Install model:** local use is a download from `/releases/`. Server use is `zddc-server`, which has the current-stable build of all six tools baked in via `//go:embed` (compile-time default). Tools auto-served at folder-name-driven paths: `archive` everywhere, `classifier` in `Incoming`/`Working`/`Staging` subtrees, `mdedit` in `Working` subtrees, `transmittal` in `Staging` subtrees, `landing` only at root. Override via `.zddc apps:` cascade entry (channel/version/URL/path) — fetched once, cached at `<ZDDC_ROOT>/_app/`. Drop a real `.html` file at any path to override.
|
|
- `helm/` — example Helm charts for zddc-server (`zddc-server-prod/`, `zddc-server-dev/`). Both compile from source via init container. Operators copy `values.yaml.example` and customize. No secrets in repo.
|
|
- `tests/` — Playwright specs (Chromium only, requires File System Access API). `tests/schema.spec.js` validates `transmittal.schema.json` against canonical fixtures via `ajv` (only dev dep besides Playwright)
|
|
|
|
## Most-used commands
|
|
|
|
```bash
|
|
# Source-side dev build only — assembles tool/dist/ + cross-compiles
|
|
# zddc-server. Does NOT touch dist/release-output/ or the live site.
|
|
./build
|
|
|
|
# Channel/release cuts — produce a complete release bundle in
|
|
# dist/release-output/ (gitignored). Cuts seed from the live site
|
|
# (/srv/zddc/releases/) so the bundle is a complete intended-live
|
|
# snapshot, not a sparse diff. Run ./deploy to publish.
|
|
./build alpha # cut alpha (cascades nothing)
|
|
./build beta # cut beta (cascades alpha → beta)
|
|
./build release # cut stable, coordinated next version
|
|
# (cascades alpha + beta → new stable; tags all seven tools)
|
|
./build release X.Y.Z # cut stable at explicit version
|
|
./build help # usage
|
|
|
|
# Deploy — atomic-ish rsync of the build output + content repo to
|
|
# /srv/zddc/, where Caddy serves it. The build does NOT auto-deploy.
|
|
./deploy # full sync: content + releases
|
|
./deploy --content # only ~/src/zddc-website/ → /srv/zddc/
|
|
./deploy --releases # only dist/release-output/ → /srv/zddc/releases/
|
|
|
|
sh tool/build.sh # iterate on one HTML tool's dist/
|
|
sh tool/build.sh --release [...] # single-tool release (rare; prefer the lockstep ./build)
|
|
./freshen-channel <tool> <channel> # rebuild one tool's alpha/beta from its current stable tag
|
|
|
|
npm test # all Playwright specs (build first!)
|
|
npx playwright test <tool> # one spec
|
|
./dev-server start # stop # cache-busting HTTP on :8000
|
|
|
|
# zddc/ Go server (sub-project)
|
|
(cd zddc && go test ./...) # unit tests (Go 1.24+)
|
|
```
|
|
|
|
No lint/typecheck/format commands exist for the HTML tools — vanilla JS + POSIX sh by design.
|
|
|
|
## Things that bite if you forget
|
|
|
|
- **`dist/` is gitignored.** `tool/dist/<tool>.html` is the canonical built artifact for testing and as the source for `--release` writes. `dist/release-output/` is the local-only release bundle written by `./build alpha|beta|release`. Never hand-edit a `dist/` file.
|
|
- **Build vs deploy are separate verbs.** `./build` and `./build alpha|beta|release` produce artifacts under `dist/release-output/`. Nothing escapes the source tree until the operator runs `./deploy`, which rsyncs into `/srv/zddc/` (Caddy's bind-mount). This decouples local iteration from live state.
|
|
- **Channel/release cuts seed from live state.** Before running per-tool promote, `./build alpha|beta|release` clears `dist/release-output/` and copies `/srv/zddc/releases/` into it (preserving symlinks). The cut then mutates the channels being cut on top. Result: `dist/release-output/` is always a complete intended-live snapshot, the verifier sees a complete world, and `./deploy --releases` (rsync `--delete-after`) replaces live state cleanly.
|
|
- **Lockstep releases.** Every release cut bumps all seven artifacts (6 HTML tools + zddc-server) to the same version, even if a tool didn't change. The coordinated next-stable target is `max(latest tag across all tools) + 1`. Per-tool independent versions are no longer the norm — `./build release` is the canonical path. Workflow: alpha = active dev, beta = ready for general testing, stable = ready to ship.
|
|
- **Release artifact layout** (in `dist/release-output/`, mirrored to `/srv/zddc/releases/`). HTML tools: per-version `<tool>_v<X.Y.Z>.html` (real immutable files) + partial-version pins (`<tool>_v<X.Y>.html`, `_v<X>.html`) + channel mirrors (`<tool>_{stable,beta,alpha}.html`) — all symlinks except per-version. zddc-server: `zddc-server_v<X.Y.Z>_<platform>` per-version binaries (raw bytes, no LFS), `_v<X.Y>_<platform>` / `_v<X>_<platform>` / `_<channel>_<platform>` symlinks, plus `zddc-server_<X>.html` stub pages that surface the four platform downloads in one matrix-cell link. Same cascade rule for both: stable cut → beta + alpha both reset to stable; beta cut → alpha cascades to beta.
|
|
- **No tags for alpha/beta.** Channel URLs are stable URLs by design — appending counter tags would defeat the purpose. The on-page label encodes `<date> · <sha>` for traceability. Stable cuts get clean `<tool>-vX.Y.Z` tags for every tool (six tags per cut, all sharing the same X.Y.Z).
|
|
- **Pre-release semver in the on-page label.** Plain dev builds and `--release alpha|beta` cuts embed `vX.Y.Z-{alpha,beta}` in `{{BUILD_LABEL}}` where X.Y.Z is the next-stable target. Plain dev adds a full timestamp + `-dirty` marker; `--release alpha|beta` is date-only.
|
|
- **Channel-link verifier.** Every `./build alpha|beta|release` ends with a check that every `<tool>_{stable,beta,alpha}.html` (and zddc-server's per-platform binary mirrors + stub pages) resolves. Because cuts seed from live state, the verifier always sees a complete world; missing-link errors mean a real problem, not a sparse-bundle artifact.
|
|
- **`./build` (no arg) is a source-side dev build.** Assembles `tool/dist/` + cross-compiled binaries; does NOT touch `dist/release-output/` or the live site. Use it to iterate without affecting anything. To produce a deployable bundle, run `./build alpha|beta|release`. To publish, run `./deploy`. Nothing is pushed to Codeberg automatically.
|
|
- **Always build before running tests** — Playwright opens `dist/tool.html` via `file://`.
|
|
- **`</` in JS string/template literals breaks inline `<script>`** embedding. `shared/build-lib.sh` provides `escape_js_close_tags`; every tool's `build.sh` runs JS through it before inlining.
|
|
- **All ZDDC parsing/formatting/hashing goes through `window.zddc`** (from `shared/zddc.js` + `shared/hash.js` + `shared/zddc-filter.js`). API: `parseFilename`, `parseFolder`, `parseRevision`, `formatFilename`, `formatFolder`, `compareRevisions`, `isValidStatus`, `splitExtension`, `joinExtension`, `crypto.{sha256Hex, sha256String, sha256File, bytesToHex}`, `filter.{parse, matches}`. File objects across tools use `trackingNumber` (string) and `extension` (string, **no leading dot** — use `zddc.joinExtension(name, ext)` to build a filename). Add edge cases to `tests/zddc.spec.js`, not per-tool tests.
|
|
- **Two globals only**: `window.app` (per-tool app state + modules) and `window.zddc` (shared library). No others — anything that crosses tool boundaries goes through one of these.
|
|
- **Worktrees live at `~/src/zddc-<branch>`.** Check `git worktree list` before starting a feature branch; never `git checkout`/`switch` inside a worktree another agent might be using.
|
|
- **Build scripts are POSIX `sh` with `set -eu`**, not bash. `concat_files` takes positional args only.
|