Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 204 additions & 85 deletions .dev-loop/INGEST_REPORT.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ follow the cross-pointers in their index or take the next matching seeded domain
| Domain | Status | Route here when |
|--------|--------|-----------------|
| [databases](wiki/databases/index.md) | **seeded** | Designing schemas/tables/keys, choosing or evaluating indexes, writing or optimizing queries, choosing transaction/isolation behavior |
| [backend](wiki/backend/index.md) | **seeded** | Server-side application code — language-agnostic (`common/`: API contracts, idempotency, JWT, timeouts/retries, caching, jobs, transactions in app code, shared state/pools, errors, LLM completion validation & context budgeting, consuming external-API responses, externally-owned defaults, object-storage references) plus stack subtrees: `java/` (JPA, Spring proxies, JVM threads/memory), `node/` (event loop, promises, runtime validation, shutdown), `python/` (GIL/asyncio, pydantic, WSGI/ASGI workers, language traps) |
| [backend](wiki/backend/index.md) | **seeded** | Server-side application code — language-agnostic (`common/`: API contracts, idempotency, declarative input the system does not enforce, JWT, timeouts/retries, client-side rate limiting, caching, jobs, transactions in app code, shared state/pools, errors, LLM completion validation & context budgeting, consuming external-API responses, externally-owned defaults, object-storage references) plus stack subtrees: `java/` (JPA, Spring proxies, JVM threads/memory), `node/` (event loop, promises, runtime validation, shutdown), `python/` (GIL/asyncio, pydantic, WSGI/ASGI workers, language traps) |
| [frontend](wiki/frontend/index.md) | **seeded** | Web UI code: state placement, rendering performance, in-UI data fetching (races, infinite scroll), auth token handling, forms, XSS-safe output, accessibility |
| [infrastructure](wiki/infrastructure/index.md) | **seeded** | CI/CD pipelines, secrets in build/deploy, container image builds, rollout/rollback strategy, observability (logs/metrics/alerting) |
| [testing](wiki/testing/index.md) | **seeded** | Writing or structuring automated tests: level choice, cases/assertions, test data, mock decisions, flaky tests (release-process quality → qa) |
| [testing](wiki/testing/index.md) | **seeded** | Writing or structuring automated tests: level choice, cases/assertions, test data and leftover artifacts, mock decisions, flaky tests, verifying two implementations of one spec against each other (release-process quality → qa) |
| [qa](wiki/qa/index.md) | **seeded** | Release-quality process: release gates, regression scoping, bug reports, severity/priority triage, exploratory testing, automated verification of document deliverables (spec/RFC gates) (writing automated test code → testing) |
| [debugging](wiki/debugging/index.md) | **seeded** | Diagnosing a failure — finding what is wrong and why: reproducing, bisection, hypothesis testing, traces/logs, intermittent failures (fixing the diagnosed fault → its owning domain) |
| [security](wiki/security/index.md) | **seeded** | Trust-boundary decisions: input validation, session-vs-token auth choice, per-resource authorization (IDOR), secrets hygiene, dependency trust, PII handling (XSS rendering → frontend; CI secrets → infrastructure; JWT implementation → backend/frontend auth) |
| [platforms](wiki/platforms/index.md) | **seeded** | OS-level differences breaking code across macOS/Linux/Windows: shell portability, BSD-vs-GNU CLI, filesystem case/line endings, Unicode normalization in text/file-name matching, commands inspected before execution, background services/cron, invoking prompt-capable CLIs non-interactively, toolchain version pinning |
| [platforms](wiki/platforms/index.md) | **seeded** | OS-level differences breaking code across macOS/Linux/Windows: shell portability, BSD-vs-GNU CLI, filesystem case/line endings, Unicode normalization in text/file-name matching, commands inspected before execution, background services/cron, invoking prompt-capable CLIs non-interactively, consuming a tool's diagnostics when it exits 0, toolchain version pinning and SDK/sysroot resolution |
| [mobile](wiki/mobile/index.md) | **seeded** | App-side iOS/Android/cross-platform: process death/state survival, offline-first sync, mobile-network calls, store rollout/hotfix strategy, startup time |

All ten domains are seeded. New categories grow via `skills/wiki-ingest/SKILL.md`.
3 changes: 3 additions & 0 deletions log.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ Append-only. Format: `## [YYYY-MM-DD] <ingest|revise|lint|gap|contradiction|drif
## [2026-08-03] ingest | Consolidated review of knowledge PRs #6–#13 (8 fork PRs) into 12 pages. New: backend/common/llm (completion-response-validation, context-window-budget), backend/common/integrations (externally-owned-defaults), backend/common/storage (object-key-persistence), infrastructure/containers/host-cgroup-visibility, infrastructure/observability/missing-container-metrics, platforms/environment/unicode-text-matching, platforms/shells/command-text-inspected-before-execution, platforms/processes/non-interactive-cli-invocation, qa/document-verification (spec-document-gates, editing-a-gated-document), testing/quality (checks-that-cannot-pass, spec-artifact-checks, harness-reverse-controls). All cited URLs are per-PR live-verified; three pages were reconciled from two overlapping PR versions each (see revise/dedup entries below).
## [2026-08-03] revise | Reconciled 3 pages from overlapping PR pairs, taking the more complete/better-sourced body and folding in the other's unique cases: backend/common/llm/completion-response-validation (#12 body — tool_calls/function_call carve-out, streaming, Responses API status==incomplete, "reasoning is scratch, not deliverable" — kept in llm/ per #6/#13 category, folded in #6's DeepSeek-first-party edge + the 8,173-char reasoning_content field incident); backend/common/integrations/externally-owned-defaults (#12 generalized body — any repo-external resource — folded in #6's LiteLLM-alias-removed field incident + gateway-config-vs-live-upstream nuance); platforms/processes/non-interactive-cli-invocation (#12 body — GNU-nohup extension precision, ssh -n stdin-detach vs BatchMode, pre-log DNS/TLS/proxy + curl -v — folded in #11's DEBIAN_FRONTEND, pager/color TTY case, wrapper-CLI case, and the no-request-in-gateway-log field incident).
## [2026-08-03] dedup | Dropped 3 candidate pages as duplicates/superseded during the #6–#13 consolidation: testing/docs-as-spec/document-conformance-checks (#9 — same case as testing/quality/spec-artifact-checks from #8: coverage-vs-validity split, per-check negative controls, GFM pipe parsing; #8 kept as canonical, docs-as-spec category not created); backend/common/llm/gateway-model-alias-defaults (#6 — subsumed by the generalized integrations/externally-owned-defaults; the model-alias case is one instance); backend/common/integrations/llm-response-completeness (#12 — folded into llm/completion-response-validation, kept in llm/ for category coherence with context-window-budget).
## [2026-08-05] ingest | +6 pages, 1 revise, 2 dedup from a 10-candidate flush (4 queued + 6 session-derived). NEW: platforms/toolchains/compiler-sysroot-on-macos (non-Xcode compiler resolving the macOS SDK — xcrun --show-sdk-path at build time, the -isysroot / SDKROOT / CPATH / LIBRARY_PATH channel table, -Wmissing-sysroot as the diagnosis one step before the header error, Homebrew clang ignoring -isysroot for the link step per upstream issue), platforms/processes/tool-diagnostics-without-a-failing-exit-code (warnings are non-failures so the exit code is 0 — capture with `2>&1 >/dev/null` in that order, three-outcome table clean/warned/failed, -Werror as the alternative when you own the invocation), testing/strategy/differential-testing (declare which observable classes must agree and which the contract permits to differ BEFORE writing the comparison; seed a divergence to prove the check can fail; keep the pair running through the migration window), testing/data/artifact-leakage-from-a-suite (count leftovers by name prefix and match the distribution against call sites instead of sweeping code; adopt the runner's owned-temp API; encode the convention as an AST rule and watch it fail first; assert a zero delta), backend/common/api-design/unenforced-declarations (split unrecognized from recognized-but-unenforced and diagnose each; closed-table lookup has a defined miss where inference has none; Ignore/Warn/Strict as a caller-selected level per KEP-2885), backend/common/integrations/client-side-rate-limiting (throttle at the transport layer every request passes, count auth/token issuance, stamp the clock immediately before the send, give the process's first call a defined starting state, prefer the provider's headers over a modelled window).
## [2026-08-05] revise | testing/quality/spec-artifact-checks +1 edge case +1 Instead-of row: the artifact is a hand-written projection of a code constant (a doc table restating an enum/lexicon/rule constant) — name the constant canonical inside the document and assert the document against the imported constant, so the checker cannot become a third copy and a reader who finds the table wrong knows the edit belongs in the code. last_verified bumped to 2026-08-05; related now links backend-common-api-design-unenforced-declarations.
## [2026-08-05] dedup | Dropped 2 session-derived candidates as already covered: "golden/snapshot fixtures should be generated rather than hand-maintained" (testing/quality/spec-artifact-checks already carries "The artifact is generated rather than hand-written → point the must-pass input at a committed golden output of the generator", and behavior-not-implementation owns when a snapshot is appropriate at all); "a detection harness needs a no-op control that must survive, and a partial working tree makes every case die before the rule runs" (testing/quality/harness-reverse-controls owns both — directive 1 requires the equivalent-mutation control to survive and stop the run when it is caught, directive 4 requires a working tree equivalent to the real runner's rather than a partial copy). Neither candidate added a directive the existing page lacked.
80 changes: 80 additions & 0 deletions wiki/backend/common/api-design/unenforced-declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
id: backend-common-api-design-unenforced-declarations
domain: backend
category: api-design
applies_to: [general]
confidence: verified
sources:
- https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2885-server-side-unknown-field-validation/README.md
- https://kubernetes.io/blog/2023/04/24/openapi-v3-field-validation-ga/
- https://json-schema.org/draft/2020-12/json-schema-validation
last_verified: 2026-08-05
related: [security-input-validation-at-trust-boundaries, infrastructure-config-environment-config, backend-common-api-design-error-responses, qa-process-acceptance-criteria]
---

# Accepting a Declaration the System Does Not Enforce

## When this applies

Your system takes declarative input — a config file, a DSL or manifest, a policy
block, a schema annotation — and some of what a caller can write is not
implemented: an unknown key, a verb outside your vocabulary, or a knob that is
recorded but never acted on. Also when a user reports "I declared X and nothing
happened", or a feature was "configured" in an environment where it never ran.

## Do this

1. **Separate the two failure shapes and give each its own diagnostic**, because
they read identically to the author — nothing happened:

| Shape | Meaning | Emit |
|-------|---------|------|
| Unrecognized | The name is not in your vocabulary at all | Reject, naming the unknown token and the accepted set |
| Recognized but unenforced | You parse and store it, but no code path acts on it | Accept and warn, naming the declaration and what it does *not* do |

2. **Resolve declarations by lookup in a closed, enumerable table, not by
inference.** A lookup has a defined miss (the name is absent → diagnostic); an
inference silently produces a plausible no-op for anything that looks close.
Keep the table one artifact, so "what does this system accept" has a single
answer.
3. **Offer the strictness as a caller-selected level rather than one global
choice** — `Ignore` / `Warn` / `Strict` — so a caller can demand rejection in
CI while a compatibility path keeps warning. Kubernetes made exactly this
split a first-class request parameter.
4. **Default new surfaces to rejection.** Accept-and-ignore is a compatibility
affordance for a surface that already shipped with it, not a starting point;
once callers depend on silent acceptance, tightening it is a breaking change.
5. **Publish the enforcement status next to the vocabulary.** Every declaration a
caller can write gets a row saying what the system actually does with it, and
that table is derived from the code that implements it, not maintained beside
it ([testing-quality-spec-artifact-checks]).
6. **Make deliberate use possible.** Declaring something you know is recorded but
unenforced is legitimate — documenting intent, staging a rollout. The defect
is doing it unknowingly, so the warning must be suppressible per declaration
with an explicit acknowledgement, not by lowering the level globally.

## Edge cases

| Case | Then |
|------|------|
| A newer client sends a field this older server has not learned yet | Warn rather than reject on the server, and let the *client's* strict mode catch it at author time; rejecting forward-compatible traffic breaks rolling upgrades |
| The declaration is enforced on one execution path but not another | Report it as unenforced on the path that ignores it, keyed by path — a single global status makes one of the two paths lie |
| The vocabulary is generated (parsed from a schema or enum) | Assert the parsed table is non-empty before using it to validate; an empty table accepts everything and turns strict mode into a no-op |
| Enforcement is measured but not applied (a budget reported, never imposed) | Say so in the diagnostic's wording — "measured, not enforced" — so a reader does not infer a guarantee from the value appearing in output |
| Rejecting would break an existing deployment | Ship the warning first with the version that starts rejecting named in the message, then flip the default |

## Instead of

| If you are about to | Do this instead | Why |
|---------------------|-----------------|-----|
| Ignore an unrecognized key so the parse "just works" | Reject it, naming the unknown token and the accepted set | Silent acceptance turns a typo into a feature that never ran, discovered in production instead of at parse time |
| Infer an action for a verb outside your vocabulary because it looks close | Look it up in the closed table and fail the miss | Inference has no defined miss, so an unsupported name produces a plausible no-op instead of a diagnostic |
| Record a policy/security declaration and treat its presence as the guarantee | Emit what is recorded-only, and keep the enforcement table alongside the vocabulary | A declaration that only annotates reads as protection to everyone downstream who sees it in the source |
| Add a global "strict mode" flag nobody turns on | Make the level a per-request/per-invocation parameter with rejection as the default for new surfaces | A global opt-in stays off, so the diagnostics exist without reaching anyone |

## Sources

- https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2885-server-side-unknown-field-validation/README.md — KEP-2885 defines three server-side validation levels selected per request via `?fieldValidation=`: `Strict` ("erroring on unknown fields"), `Warn` (errors returned as warnings in response headers), and `Ignore` (no validation); it moves the decision from each client to the server so unrecognized fields are not simply accepted
- https://kubernetes.io/blog/2023/04/24/openapi-v3-field-validation-ga/ — Server Side Field Validation reached GA in Kubernetes 1.27, validating create, update and patch requests at the apiserver
- https://json-schema.org/draft/2020-12/json-schema-validation — `additionalProperties` is the schema-level control over whether properties outside the declared set are permitted; strictness is an explicit schema decision rather than a parser default
- Field observation 2026-08-05: a declarative platform carried two issues with the same root — step verbs outside its closed lexicon compiled to silent no-ops, and `security`/`policy`/`performance` declarations were recorded without being enforced. Both were resolved by one enforcement matrix generated from the implementing constants plus a diagnostic channel that names the unenforced declaration
Loading
Loading