# 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.

Exact reference: {"kind":"skill_version","skill_id":"skl_yvd7HMkctp4N31hL8i6niQ","version_id":"skv_gDACWCdPB29DV2nkNzqzWQ"}

Applicability: [{"constraint":"expected-byte baseline captured with presence metadata before write","technology":"application-managed configuration fragments","version_scheme":"unknown"},{"constraint":"regular-file fragment may be zero-length or absent on first create","technology":"POSIX filesystem","version_scheme":"unknown"}]

# 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:

1. **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.
2. **Absent path.** The contract expects first-time creation and the baseline records that the path did not exist at capture time.
3. **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`, or `must_exist` (contract-defined).
- **observed_length:** byte length at capture time (zero allowed only when presence is `present_regular` or 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.

1. **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.

2. **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_creation` unless 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_present` unless the contract explicitly treats empty present same as absent for create; default is fail closed. See worked example below.

   **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 observed_length was non-zero: **fail closed** as `concurrent_wipe`.
   - 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_edit` or `stale_baseline`.

3. **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.

4. **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.

## Worked example: unexpected_empty_present versus wipe misread

Reasoned example (not an executed test). This sharpens branch B interpretation; it does not change the default fail-closed policy.

**Setup.** Application contract defines first-create for fragment `managed/overlay.toml`. At operation start the agent captures baseline with presence `absent`, observed_length zero, and no byte digest because the path did not exist. Before the agent reaches the expected-bytes gate, deployment infrastructure pre-seeds an empty placeholder file at that path (present regular file, zero bytes, correct ownership).

**Gate observation.** Open and observe finds a present regular file with zero bytes and matching ownership.

**Classification.** Apply branch B third bullet: baseline presence was `absent`, gate observes present zero-length regular file, contract does not equate empty-present to absent. Outcome is **fail closed** as `unexpected_empty_present`.

**Why this is not concurrent_wipe.** concurrent_wipe requires baseline presence `must_exist` (or branch C path) with baseline observed_length non-zero and gate observing zero bytes at a still-present regular file. Here baseline bytes were never non-empty; presence at capture was `absent`, not `present_regular` or `must_exist` with content.

**Why this is not stale_baseline.** stale_baseline applies when gate observes non-zero bytes that no longer match the recorded digest. Here gate observes zero bytes; the mismatch is presence transition from absent-at-capture to empty-present-at-gate, not byte drift.

**Operator misread this example prevents.** Without the timeline above, a zero-byte observation plus fail-closed abort can look like a wipe of prior content or a digest mismatch. Logging `unexpected_empty_present` together with recorded baseline presence `absent` tells the operator to inspect whether infrastructure or another writer created an empty placeholder between capture and gate, then restart with a fresh baseline that records the new reality rather than treating the event as content loss.

**Contract override unchanged.** If the application contract explicitly treats empty-present the same as absent for first-create, branch B permits pass; this example documents the default when no such equating rule exists.

**Out of scope for this example.** A symmetric race where the path existed at capture as absent because it was deleted, then was recreated empty before the gate, can resemble pre-seed unless capture_generation or an external audit log disambiguates timing. Do not infer intent from gate observation alone in that case.

## Diagnostic contrast at zero-byte gate observations

When the gate reads zero bytes from a present regular file, use baseline metadata to choose the category:

| Recorded baseline presence | Recorded observed_length | Gate sees | Category |
|---|---|---|---|
| absent | 0 | absent path | pass (first-create) |
| absent | 0 | present, 0 bytes | unexpected_empty_present (default fail closed) |
| present_regular | 0 | present, 0 bytes | pass (empty equals empty) |
| must_exist | non-zero | present, 0 bytes | concurrent_wipe |
| must_exist or present_regular | any | present, non-zero mismatch | concurrent_edit or stale_baseline |

All rows are reasoned classification outcomes, not executed test results.

## 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.
- **Not post-failure recovery checklist.** After classified failure, restart and verification steps are separate maintenance scope.

## Failure policy

- Never treat zero bytes at the gate as automatically invalid when the baseline recorded `present_regular` with observed_length zero.
- Never treat zero bytes at the gate as a pass when the baseline recorded non-empty `must_exist` content.
- 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.
- Never map an absent baseline plus empty-present gate observation to concurrent_wipe; use unexpected_empty_present unless the contract overrides.

## 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.

The worked example and diagnostic contrast table follow from maintenance conversation thr_ZbdD3NWrGMZZxxE2KWI-XQ sequences 1 and 2, which proposed and assessed sharpening the unexpected_empty_present boundary without changing default fail-closed policy.

All timelines, branch outcomes, and table rows are reasoned examples only; no filesystem or concurrency tests were executed for this skill or this revision.


## Supporting basis and limitations

Maintenance conversation thr_ZbdD3NWrGMZZxxE2KWI-XQ sequences 1 and 2 propose and assess improvement A: a first-create contract with baseline presence absent, infrastructure pre-seeding an empty placeholder before the gate, correct fail-closed classification, and explicit contrast with concurrent_wipe and stale_baseline because baseline bytes were never non-empty. Sequence 2 notes limitations: explanatory prose only, default policy unchanged, symmetric delete-then-recreate-empty race not covered without generation or external audit. No executed filesystem or concurrency tests support this addition.

## Change and rationale

Add a worked reasoned example for the unexpected_empty_present boundary when an absent baseline meets a present zero-length file at gate time, plus a diagnostic contrast table so operators do not misread that outcome as concurrent_wipe or stale_baseline.

Revision 1 names unexpected_empty_present but gives no timeline where absent-at-capture meets empty-present-at-gate. Maintenance conversation thr_ZbdD3NWrGMZZxxE2KWI-XQ sequence 1 identified operator misreading as the gap; sequence 2 confirmed the example sharpens interpretation without changing default fail-closed policy.
