Schemas:
- default-mdl.form.yaml: declare the six readOnly audit fields
(created_at/by, updated_at/by, revision, previous_sha) so the form
UI renders them disabled. additionalProperties: false is preserved;
WriteWithHistory strips any client-supplied values before validation.
- default-rsk.form.yaml: overhaul to reflect the new shape. Each row
now carries the table-tracking components (originator/phase?/project/
area?/discipline/type/sequence/suffix?) plus a server-assigned `row`
field; type is enum-locked to RSK to mirror the cascade's locked: rule.
Drops the old `id` field (D-001/R-001-style identifiers are now
composed from the components and stored in the filename).
- default-ssr.form.yaml: append the six audit fields.
Handlers:
- serveFormCreateSSR routes the write through WriteWithHistory so
audit fields are stamped on first create (revision=1, created_*=
updated_*=request principal/now). ssr.yaml's identity stays the
party folder name; no filename composition runs.
- serveFormCreateRollup now resolves the cascade at the row's parent
folder and uses the matched records: entry's filename_format to
compose the row filename from body fields. For RSK rows the rule
carries row_field+row_scope_fields, so the server auto-assigns the
next sequence (001, 002, ...) within the table-tracking group and
injects it into the body before composition. Defaults from
field_defaults: are injected where the client omitted them
(type=RSK locks in via the locked: list). Falls back to the
historical date+email naming only when no records: rule is in
scope (covers deployments that override defaults.zddc.yaml without
declaring their own records: entries).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The project-level MDL/RSK rollup specs lose `addable: false` and gain
a sibling form schema (default-project-{mdl,rsk}.form.yaml) that
makes `party` a required field. + Add row on the rollup view is now
live: the user types the party name in the Package column, the
server reads `party` from the body, validates that
<project>/archive/<party>/ exists on disk, strips the field, and
writes the row into archive/<party>/<slot>/<date>-<email>.yaml. The
response Location is the synthetic <project>/<slot>/<party>__<file>.yaml
URL so the rollup table client swaps the draft URL cleanly.
Wrong party = 422 with a clear error pointing at the SSR view as the
place to create the folder first. No auto-creation here — the rollup
is for filing deliverables/risks against existing packages, not for
spinning up new ones.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the risk register as a sibling of MDL under archive/<party>/, and
three project-level virtual aggregations at <project>/{ssr,mdl,rsk}:
- SSR aggregates archive/<party>/ssr.yaml; "+ Add row" materializes a
new party folder (mkdir + auto-own .zddc + ssr.yaml). Renames go
through X-ZDDC-Op: ssr-rename, which os.Rename's the party
directory so every row inside follows. Party name doubles as the
folder name (no opaque IDs) and is path-derived on read.
- MDL/RSK rollups list every deliverable / every risk across all
parties with a derived `party` column; "+ Add row" is suppressed
because party affiliation is ambiguous in the aggregate view.
All four virtual roots are declared `virtual: true` in
defaults.zddc.yaml. Spec/form bytes come from six new embedded
defaults (default-rsk.*, default-ssr.*, default-project-{mdl,rsk}.*)
served via a generalized IsDefaultSpec/IsDefaultSpecAbs that replaces
the MDL-only recognizer. Listing synthesis lives in fs/tree.go;
ACL on each synthetic row evaluates against the canonical
archive/<party>/ chain so non-owners see rows read-only. PUT/DELETE
through virtual URLs rewrite to canonical paths in fileapi.go via
sibling-shape blocks that don't touch the ACL gate. SSR row DELETE
returns 405 (delete the party folder via the archive view).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>