README: pin --version to this repo's commit in the ingest recipe
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EvxzSKi1zR395wvYXUenVE
This commit is contained in:
@@ -34,12 +34,17 @@ Markdown needs no conversion and no extraction verifier — admit it straight fr
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
index.py admit ~/dev/handbooks/git-operations.md --corpus engineering \
|
index.py admit ~/dev/handbooks/git-operations.md --corpus engineering \
|
||||||
--vouch mikkeli --title 'Git operations' --collection dev-practice
|
--vouch mikkeli --title 'Git operations' --collection dev-practice \
|
||||||
|
--version "$(git -C ~/dev/handbooks rev-parse --short HEAD)"
|
||||||
```
|
```
|
||||||
|
|
||||||
Without `--title` the document is titled by its filename, which is what a retrieval hit then shows.
|
Without `--title` the document is titled by its filename, which is what a retrieval hit then shows.
|
||||||
`--collection` is a flat cluster tag; getting it wrong costs recall and nothing else.
|
`--collection` is a flat cluster tag; getting it wrong costs recall and nothing else.
|
||||||
|
|
||||||
|
⚠ **`--version` is this repo's commit, and it is the one that matters.** Re-admitting replaces a
|
||||||
|
document outright with no version history, so the pin is the only record of which revision a chunk
|
||||||
|
reflects.
|
||||||
|
|
||||||
⚠ **Editing a file here does not update the corpus.** Re-admission is deliberate, because the vouch
|
⚠ **Editing a file here does not update the corpus.** Re-admission is deliberate, because the vouch
|
||||||
is a claim about a specific version of a document.
|
is a claim about a specific version of a document.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Failure handling
|
||||||
|
|
||||||
|
> Composed 2026-08-22. General practice, not tied to any one project or language. Input contract
|
||||||
|
> discipline is composed; failure-propagation discipline is still outline.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
General discipline for how code should behave when it encounters conditions it can't cleanly
|
||||||
|
complete against — malformed input, an unreachable dependency, an assumption that turns out false.
|
||||||
|
Two sections: what a function's contract should say about the input space before it's written, and
|
||||||
|
what happens once something has actually gone wrong.
|
||||||
|
|
||||||
|
Excluded on purpose: language-specific exception syntax, logging infrastructure choices,
|
||||||
|
testing-framework mechanics, and environment/verification parity ("tested" vs. "tested where it
|
||||||
|
actually runs") — that last one is close enough to earn a future handbook of its own rather than a
|
||||||
|
section here.
|
||||||
|
|
||||||
|
## Input contract discipline
|
||||||
|
|
||||||
|
**Degenerate input is the default case to design for, not an edge case bolted on after.** Empty
|
||||||
|
collections, duplicate entries, out-of-range values, malformed or wrong-typed data, oversized
|
||||||
|
payloads, and concurrently-modified reads are not rare — they're what actually arrives once code
|
||||||
|
leaves a demo. Design for that space first, and treat "obviously fine" input as the special case, not
|
||||||
|
the other way around.
|
||||||
|
|
||||||
|
**Validate, sanitize, and reject are three different responses — not one blurred behavior.**
|
||||||
|
|
||||||
|
- **Validate** — check conformance against the contract, making no change to the input. Proceed only
|
||||||
|
if it holds.
|
||||||
|
- **Sanitize** — transform nonconforming input into conforming input. The transformed value is what
|
||||||
|
proceeds, treated exactly as if the caller had sent it that way to begin with.
|
||||||
|
- **Reject** — refuse to proceed, surfacing why (see Failure-propagation discipline for what
|
||||||
|
"surfacing why" should actually look like).
|
||||||
|
|
||||||
|
**Sanitize only what's meaning-preserving; reject anything that requires guessing intent.**
|
||||||
|
Sanitizing has a real cost: the caller's actual input is never seen again, only its cleaned-up
|
||||||
|
substitute. That's fine when the correction is unambiguous — trimming incidental whitespace,
|
||||||
|
normalizing Unicode form, case-folding under an already-documented case-insensitive contract. It's
|
||||||
|
not fine when the correction requires guessing what the caller meant — clamping an out-of-range
|
||||||
|
number to its nearest valid bound, silently truncating an oversized value, defaulting a missing
|
||||||
|
required field. The test: *would the caller be surprised to learn what actually happened to their
|
||||||
|
input?* If yes, reject instead of sanitizing — finding out via an error immediately is strictly
|
||||||
|
better than finding out weeks later that a value was quietly substituted for the one actually sent.
|
||||||
|
Silent sanitization of an out-of-range value and a guard that returns success without checking are
|
||||||
|
the same failure shape: something couldn't proceed correctly, and produced a plausible-looking result
|
||||||
|
instead of saying so.
|
||||||
|
|
||||||
|
**Validate at trust boundaries; trust your own internals.** Check input where it crosses from outside
|
||||||
|
your control into it — user input, an external API response, a file read from disk, a message from
|
||||||
|
another service. Code on the inside of that boundary, already known to satisfy its own contract (by
|
||||||
|
type, by test, by the caller being code under the same control), doesn't need to re-validate what its
|
||||||
|
caller already guaranteed — that's redundant defense that doubles maintenance cost without doubling
|
||||||
|
safety. The boundary is where trust actually changes; that's the only place validation earns what it
|
||||||
|
costs.
|
||||||
|
|
||||||
|
**Before writing the implementation, write the contract's answer for each category.** A function's
|
||||||
|
contract should state, explicitly, what happens for: empty input, a duplicate where uniqueness was
|
||||||
|
assumed, a value outside the expected range, a type mismatch, an oversized input, and a stale or
|
||||||
|
concurrently-modified read. Leave any of those unanswered, and the implementation will invent an
|
||||||
|
answer under latitude — usually the same way, by quietly doing something plausible instead of
|
||||||
|
refusing. Writing the answer down first is what closes that gap; discovering it during implementation,
|
||||||
|
or worse in production, is what leaves it open.
|
||||||
|
|
||||||
|
## Failure-propagation discipline
|
||||||
|
|
||||||
|
*(Not yet composed.)*
|
||||||
|
|
||||||
|
*(Handbook in progress — sections above stand as citable units; the rest is outline.)*
|
||||||
Reference in New Issue
Block a user