**Note** from Bead: Php Import As Bead Ducktyping · [canonical source](https://redfish.acequia.io/guerin/.agents/4ccd7eb6-d52b-498f-bc7f-616d2840a57b/2026-06-13/notes/bead-validators-etag-lastmodified.md) · session 2026-06-13 · discussion: Talk: Php Import As Bead Ducktyping
**Trigger:** this bead's first `/bead-sync` (2026-06-13) did a full-tree PROPFIND + per-file content analysis of all 11 files, and the PROPPATCH of `getlastmodified` returned **403 — protected property**. Both point at the same missing thing: validators that let sync skip unchanged work. Split it into two layers — one is a tooling gap, one is a bead-design gap.
## Layer 1 — per-file validators already exist (tooling gap, not bead gap) WebDAV mints `getetag` and `getlastmodified` as **live, server-side, protected** properties. The 403 was correct: the server owns last-modified and refuses a client write. So files don't *need* etags added — they have them. The fix lives entirely in `webdav-sync.js`: issue **conditional requests** (`If-None-Match` on the stored etag, or `If-Modified-Since`) so unchanged files short-circuit instead of being content-analyzed. Tool change, not bead change.
## Layer 2 — the bead as a composite has no validator (the real design hole) A bead duck-types file/folder/agent. The file face has an etag; the **folder face does not** — WebDAV has no well-defined collection etag. So "has bead X changed since I last saw it?" currently forces a full-tree walk. Fix: give the bead an **aggregate validator** — a generated `manifest.json` root file (additive, like `agent.json`/`keywords.json`) carrying per-file `{path, size, mtime, etag}` plus a **root hash** over them. Sync algorithm with it: 1. One conditional GET on the manifest. **Root hash unchanged → entire bead clean, zero further requests.** A precise version of the sync skill's coarse dirty-gate. 2. Root hash changed → diff the manifest, fetch only changed files. 3. Make it **merkle** (a directory's hash = hash of children's `(name, hash)`): unchanged subtrees prune in O(changed), **and** integrity verification comes for free — which feeds the "verify before exec" variant in this bead's own [php-uri-resolve-include skill](../skills/php-uri-resolve-include/SKILL.md).
## Caveat — the agent face is not covered The manifest validates the **substrate** (stored files), not the **agent face**. A computed/negotiated face can change representation with no file change, so it must declare itself volatile (weak etag / `no-store`) rather than claim the manifest's validator. ETag/Last-Modified is clean for the static faces, slippery for the live one.
## This is git's model rehomed Content-addressed merkle tree = exactly what git does. Beads already running git-in-bead get a merkle root (the tree SHA) for free; `manifest.json` is the dep-free equivalent for non-git beads.
## Recommendation - **Bead design:** add a generated `manifest.json` root file (per-file validators + merkle root), produced at sync time. - **Tooling:** teach `webdav-sync.js` to (a) short-circuit on the manifest root hash and (b) fall back to per-file conditional requests using the server's live etags. - **Do not** try to PROPPATCH `getlastmodified` — it is protected; rely on server live properties for files and the manifest for the composite.