Classify empty, absent, and wiped baselines at config gate
When the expected-bytes gate runs before atomic config replace on an application-owned fragment, distinguish an intentional zero-byte baseline from a missing path or a concurrent wipe from non-empty content, using presence metadata recorded with the baseline.
Classify empty, absent, and wiped baselines at config gate
When the expected-bytes gate runs before atomic config replace on an application-owned fragment, distinguish an intentional zero-byte baseline from a missing path or a concurrent wipe from non-empty content, using presence metadata recorded with the baseline.
When to use
Use this when you already follow ownership and expected-byte gating before temp-then-rename publish on an application-owned configuration fragment, and the baseline may be zero bytes long or the target may not exist yet.
Adjacent guidance covers the base replace flow, descriptor binding, publish-gate timing, and post-publish verification. This skill covers only the empty-baseline boundary at the expected-bytes gate: how to interpret zero observed bytes without aborting a valid empty fragment or accepting a concurrent wipe.
The failure it prevents
The base expected-bytes gate treats unexpected emptiness as a hard stop, but zero bytes at the gate can mean three different things:
- Intentional empty baseline. The caller previously observed a present zero-length regular file and recorded baseline bytes as the empty octet sequence with presence metadata.
- Absent path. The contract expects first-time creation and the baseline records that the path did not exist at capture time.
- Concurrent wipe. The baseline recorded non-empty bytes from a present file, but the gate now observes zero bytes at a path that still exists as a regular file.
Without classification, agents either abort valid updates to intentionally empty fragments or proceed after a silent wipe, publishing over content that disappeared between baseline capture and gate time.
Reasoned example (not an executed test): an application seeds managed/overlay.toml as a zero-length placeholder. An agent captures baseline bytes as empty with presence marked present. A separate code path treats any zero-byte read as unexpected emptiness and aborts before writing the first real overlay. The fragment never receives its intended content even though ownership and path are correct.
Reasoned example (not an executed test): an agent captured baseline {version: 1} bytes. Before publish gate, an operator truncates the file to zero length through another handle. The gate reads zero bytes. Without wipe classification, the agent compares zero bytes to the non-empty baseline digest, surfaces a generic mismatch, and the operator cannot tell wipe from stale baseline or symlink substitution.
Baseline record requirements
Every expected-bytes baseline used at the gate must carry, alongside the byte sequence or digest:
- presence: one of
present_regular,absent, ormust_exist(contract-defined). - observed_length: byte length at capture time (zero allowed only when presence is
present_regularor when contract defines absent as length zero). - capture_generation: optional monotonic token if the application exposes one; do not invent generations the contract does not define.
Do not infer presence from path existence checks alone at gate time without comparing to the recorded presence field.
Classification procedure
Run after ownership gate passes and before temp publish or rename.
- Open and observe. Prefer descriptor-bound open with no-follow semantics when the managed directory may be shared. Record observed entry type, ownership, and raw byte length.
- Branch on recorded presence.
A. Baseline presence is present_regular and observed_length is zero.
- If gate observes a present regular file with zero bytes and matching ownership: pass the expected-bytes gate (empty equals empty).
- If gate observes absent path: fail closed as
baseline_present_now_absent(deletion or repointing). - If gate observes non-zero bytes: fail closed as
concurrent_edit(treat as mismatch against empty baseline).
B. Baseline presence is absent (first-create contract).
- If gate observes absent path: pass the expected-bytes gate with zero expected bytes (nothing to match).
- If gate observes present regular file with any bytes: fail closed as
unexpected_creationunless the contract defines idempotent create when already present; do not overwrite without a fresh operation that captured the new baseline. - If gate observes present zero-length regular file: fail closed as
unexpected_empty_presentunless the contract explicitly treats empty present same as absent for create; default is fail closed.
C. Baseline presence is must_exist (non-empty or existence-required contract).
- If gate observes absent path: fail closed as
baseline_present_now_absent. - If gate observes present regular file with zero bytes but baseline observedlength was non-zero: fail closed as `concurrentwipe`.
- If gate observes present regular file with zero bytes and baseline observed_length was zero: follow branch A rules.
- If gate observes non-zero bytes: compare bytes or digest to baseline; mismatch is
concurrent_editorstale_baseline.
- Surface classified outcomes. Map failures to the categories above rather than a single generic emptiness error so callers and operators can choose abort, full operation restart, or escalation without guessing.
- On any classified failure, fail closed. Do not rename. Remove the attempt temp. Do not refresh the baseline mid-operation unless the contract defines an explicit restart flow.
Wipe versus intentional empty at publish gate
When publish-gate timing re-runs expected-bytes checks, apply the same classification against the fixed baseline from operation start, not freshly observed emptiness.
Reasoned example (not an executed test): baseline captured non-empty content with must_exist. Target still exists as a zero-length regular file at publish gate. Classify as concurrent_wipe and abort even though the path string matches and ownership gate passes. Do not treat publish-time emptiness as permission to overwrite with unrelated replacement bytes without restarting the operation.
What this does not provide
- Not a substitute for descriptor binding. Classification assumes stat and read observe the same entry when the directory is untrusted; pair with no-follow descriptor gates.
- Not writer compare-and-swap. Two writers can still race after passing empty-baseline classification.
- Not create-semantics definition. Whether first-write may create, what absent means, and whether empty-present equals absent belong in the application contract; this skill classifies observations against recorded metadata.
- Not post-publish verification. Confirming published bytes after rename is separate guidance.
Failure policy
- Never treat zero bytes at the gate as automatically invalid when the baseline recorded
present_regularwith observed_length zero. - Never treat zero bytes at the gate as a pass when the baseline recorded non-empty
must_existcontent. - Never conflate absent path with present zero-length file unless the contract explicitly equates them.
- Never refresh presence or baseline bytes mid-operation to make a failure pass.
Claims and evidence
The classification branches follow from maintenance conversation thr_TmrUGIH1ntTbdQJsd9vygw sequence 1, which identified intentional empty baseline versus absent path versus concurrent wipe as an uncovered boundary in atomic config replace guidance. All timelines and branch outcomes are reasoned examples only; no filesystem or concurrency tests were executed for this skill.