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
287 changes: 244 additions & 43 deletions .dev-loop/INGEST_REPORT.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions log.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ Append-only. Format: `## [YYYY-MM-DD] <ingest|revise|lint|gap|contradiction|drif
## [2026-08-06] revise | Union-merged amendments from the same 15 PRs into 24 existing pages (largest: tests-that-cannot-fail — 7 PRs: per-assertion mutation granularity, restore-mechanism-by-commit-state, suite-total-drop detection; portable-shell-scripts — 8 PRs: POSIX inline set-- reordering, ${VAR:-} vs ${VAR-} colon semantics, quote-by-what-the-text-is; test-data-and-isolation — 6 PRs: env-derived write paths, absent-variable cases, exec-bit fixtures, leak attribution; change-impact/call-site-enumeration — 8 unique additions from 7 path-variants: rename-in-place, same-type reorder, *args forwarding, two-searches-agree fallacy, codemod, coverage completion check). All non-canonical cross-page ids remapped to canonical pages; every added line traced to its source PR hunk (fabricated worker output was detected by orphan-line verification and replaced with true PR content).
## [2026-08-06] dedup | Cross-PR duplicate clusters collapsed: client-side rate limiting (8 PR versions at 3 paths → 1), call-site enumeration (7 versions at 7 paths → additions folded into the already-merged canonical page), stderr/exit-0 diagnostics (4 versions at 4 paths → 1), macOS sysroot (2→1), env-var off switch (2→1), completion predicates (2→1), robots.txt (2→1), harness-mediated results (2→1), leaked test artifacts (2→1), orchestration category naming (orchestration vs agent-orchestration → agent-orchestration). Kept distinct after trigger comparison: differential-testing vs differential-run-agreement (setup vs interpretation), unset-versus-empty-parameters vs env-var-off-switches (semantics vs design, cross-linked), import-time-side-effects vs test-level-choice edge (tactics vs level choice, cross-linked). Merged-main near-dup scan (Jaccard over title+trigger, 141 pages): 0 pairs — no duplication among previously merged content.
## [2026-08-06] ingest | Reconciled post-consolidation flush PRs #42–#43 (created against pre-#44 main). Folded unique content: platforms/filesystems/permissions-and-exec-bits (+`sh "$SCRIPT"` interpreter-invocation rows — stubs need no exec bit, avoids EDR chmod+x heuristics), platforms/shells/command-text-inspected-before-execution (+gate-AUTHOR step 8: parse all three POSIX quoting forms, expand only ~/$HOME/${HOME}; red-then-green bats evidence), testing/quality/checks-that-cannot-pass (+vacuously-green pre-implementation test row), platforms/processes/tool-diagnostics-without-a-failing-exit-code (+repeat-suppression edge, +clang reproduction source), infrastructure/ci-cd/secrets-handling (+independent credential channels row: gh token vs git SSH vs API token, git push --dry-run / author:@me probes), testing/mocking/what-to-mock (+ESM read-only-bindings DI rows, +PATH-emptied tripwire negative-proof). Dropped as duplicates of the #44 state: #42's rate-limit row (already in timeouts-and-retries + client-side-rate-limiting), #42's warning-only-diagnostics page (same trigger as tool-diagnostics-without-a-failing-exit-code), #42's leak-attribution row (already in test-data-and-isolation).

## [2026-08-10] ingest | knowledge-flush of 3 queued insights → 2 new pages, 1 folded into open PR #52. New: backend/common/integrations/consumer-required-fields (an adapter built from the consumer's docstring cannot tell required from optional — call the real consumer once with a mapped record, split the fields it reads into loud (presence check / subscript → raises on record 1) and silent (read with a default → wrong value, no error), and give each silent field a two-run assertion that drops it and requires the output to differ; sources: docs.pact.io "check that all the calls to your test doubles return the same results as a call to the real application would" + "unlike a schema or specification (eg. OAS), which is a static artefact"; json-schema.org "By default, the properties defined by the `properties` keyword are not required"; Python reproduction 2026-08-10 — missing `assignee_id` raised ValueError, missing `desc` moved the score 21.0 → 1.0 silently). New: infrastructure/observability/suppression-state-and-delivery-failure (write the cooldown/sent mark only after a send that reported success, give the send its own exit status, make it injectable, and assert the succeeding-sender and failing-sender worlds as two tests; sources: Alertmanager `RetryStage` "notifies via passed integration with exponential backoff until it succeeds" then `SetNotifiesStage` "sets the notification information about passed alerts. The passed alerts should have already been sent to the receivers." with `DedupStage` filtering "based on a notification log"; prometheus-operator Watchdog runbook "an alert meant to ensure that the entire alerting pipeline is functional" for the external heartbeat; field measurement rtb-mac-server-k8s bin/gitops-deploy.sh where the always-failing stub had fixed the pre-send ordering as the expected contract — the correct fix turned 3 tests red). Folded (not re-ingested): the comment-vs-code source-assertion insight went to open PR #52's testing/quality/source-text-wiring-assertions rather than a sibling page; verification there found the queued directive's own strip regex defective — measured 2026-08-10 in Node, `s.replace(/\/\*[\s\S]*?\*\//g,'').replace(/\/\/.*$/gm,'')` truncates `"https://api.example.com//v2/items"` to `"https:` and a string-aware variant still truncates a regex literal containing `//`, so the page takes comment removal from the language's tokenizer with a control, not from a regex.
97 changes: 97 additions & 0 deletions wiki/backend/common/integrations/consumer-required-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
id: backend-common-integrations-consumer-required-fields
domain: backend
category: integrations
applies_to: [general]
confidence: verified
sources:
- https://docs.pact.io/
- https://json-schema.org/understanding-json-schema/reference/object
last_verified: 2026-08-10
related:
[
backend-common-change-impact-call-site-enumeration,
backend-common-integrations-externally-owned-defaults,
backend-common-llm-completion-response-validation,
testing-quality-tests-that-cannot-fail,
testing-quality-minimum-case-set,
]
---

# Building a Payload for a Consumer Whose Required Fields You Inferred

## When this applies

You are writing an adapter that maps one module's records (a selection query, a
repository row, a scraped item) into the shape a second module consumes — a
scoring engine, a plugin, an external API client — and you took that shape from
the consumer's docstring, README example, or a sample payload. Also when such an
adapter runs end to end with no error and the downstream numbers look low, and
when deciding which mapped fields need an assertion of their own.

Enumerating call sites when *you* own the callee →
[backend-common-change-impact-call-site-enumeration].

## Do this

1. **Call the real consumer once with a mapped record before writing the rest of
the adapter.** An example payload is an instance, not a specification: in JSON
Schema terms "the properties defined by the `properties` keyword are not
required" unless a `required` list says so, so a sample cannot tell you which
keys the consumer depends on. Pact makes the same distinction — "unlike a
schema or specification (eg. OAS), which is a static artefact", a contract is
"enforced by executing a collection of test cases", and a test double is
trusted only when its calls "return the same results as a call to the real
application would".

2. **Split the fields the consumer reads into loud and silent by how it reads
them**, and drive the split from the consumer's source, not its prose:

| How the consumer reads the field | Class | Missing-field symptom |
|---|---|---|
| Presence check or direct subscript (`if k not in d`, `d[k]`, required-field validation) | Loud | Raises on the first record; the run stops |
| Read with a default (`d.get(k, …)`, `??`, `COALESCE`, optional destructuring) | Silent | No error; the computed value is wrong for every record |
| Used only for logging or display | Cosmetic | No error; output text is thinner |

3. **Let the loud fields be found by one end-to-end run, and write an explicit
assertion for every silent field.** The loud ones announce themselves; the
silent ones cannot fail any test that only checks "the pipeline completed"
([testing-quality-tests-that-cannot-fail]).

4. **Make each silent-field assertion compare two runs of the real consumer —
one with the field populated, one with it dropped — and require the outputs
to differ.** Asserting only "the key is present in the mapped dict" passes on
a key the consumer never reads and on a value that never reaches the formula.

5. **Record the discovered required-field list next to the mapping code**, with
the consumer version or commit you probed. The list is the adapter's real
contract, and step 1 is re-run against it when the consumer is upgraded
([backend-common-integrations-externally-owned-defaults]).

## Edge cases

| Case | Then |
|------|------|
| The consumer is expensive or has side effects | Probe it once in a test with a single record and cache the field list — the probe is required even then, and the cache is what keeps it cheap |
| The consumer accepts the record but ignores unknown keys | The unknown key is not evidence of anything — keep the step-4 two-run comparison as the only proof a field is consumed |
| The consumer validates with a machine-readable schema (pydantic model, JSON Schema, protobuf) | Read `required`/non-default fields from the schema instead of probing, and still run step 4 for the defaulted ones |
| A silent field's absence changes the output by less than the assertion's tolerance | Choose the probe record that maximizes the field's contribution (longest text, largest count) so the two runs separate |
| The consumer defaults a missing field to a neutral value that looks plausible | Treat it as silent, not absent — a plausible default is what lets the defect reach production |
| Several silent fields feed one output number | Drop them one at a time; dropping all of them at once cannot attribute the difference |
| The consumer is non-deterministic (LLM scorer, sampling, clock-dependent) | Pin the seed/temperature or stub the non-deterministic part before comparing — otherwise the two runs differ for every field, including ones the consumer never reads, and step 4 passes vacuously |

## Instead of

| If you are about to | Do this instead | Why |
|---------------------|-----------------|-----|
| Map fields from the consumer's docstring schema and move on | Call the consumer with one mapped record first, then map the rest | The docstring lists the shape, not which keys are load-bearing; the run tells you |
| Treat "the pipeline finished with no exception" as evidence the mapping is complete | Assert each silent field changes the output when dropped | The loud fields are the only ones an exception-free run proves |
| Assert `"desc" in payload` for each field the consumer reads | Run the consumer twice, with and without the field, and assert the outputs differ | Key presence is satisfied by a key the consumer ignores or reads from a different name |
| Add a default in the adapter for a field you are unsure the consumer needs | Probe first, then map the field or leave it out deliberately | A speculative default converts a loud failure into a silent one — the class that survives to production |

## Sources

- https://docs.pact.io/ — "The contract is generated during the execution of the automated consumer tests"; contract tests "check that all the calls to your test doubles return the same results as a call to the real application would"; and "unlike a schema or specification (eg. OAS), which is a static artefact that describes all possible states of a resource, a Pact contract is enforced by executing a collection of test cases, each of which describes a single concrete request/response pair" — the basis for step 1 preferring an execution over the documented shape
- https://json-schema.org/understanding-json-schema/reference/object — "By default, the properties defined by the `properties` keyword are not required"; an example payload therefore carries no required/optional distinction
- Reproduction 2026-08-10 (Python 3): a consumer that reads `assignee_id` with `if "assignee_id" not in item: raise ValueError` and computes `1.0 + 0.5 * len(item.get("desc", ""))`. Dropping `assignee_id` raised on the first record; dropping `desc` raised nothing and moved the returned score from 21.0 (`desc` of length 40) to 1.0 — same adapter output, one failure visible in the first run and one visible only to an assertion
- Field measurement 2026-08-10 (manday estimation engine, `manday-sp/engine.py`): the two read sites are `check_assignee_ids(items)` at line 511, whose contract is "키 존재 + 값 형식" (key presence, not just value shape), and `d = it.get("desc") or ""` at line 399 — loud and silent respectively. The split is a property of the read site, not of the field, so record the file:line (a sibling copy of the same scorer reads `it["desc"]` by subscript, which makes the same field loud). A mapping built from the engine's docstring schema produced `ValueError` on 100% of records for the missing `assignee_id` key, while the missing `desc` key produced no error and scored 0.31 against 1.63 for the same item — a 5.3x under-estimate that the end-to-end run reported as success
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
- https://developers.openai.com/api/docs/api-reference/models/list
- https://docs.litellm.ai/docs/proxy/model_discovery
last_verified: 2026-07-31
related: [backend-common-llm-completion-response-validation, infrastructure-config-environment-config, qa-process-release-gates]
related: [backend-common-llm-completion-response-validation, infrastructure-config-environment-config, qa-process-release-gates, backend-common-integrations-consumer-required-fields]
---

# Defaults That Name a Resource Owned Outside the Repository
Expand Down
1 change: 1 addition & 0 deletions wiki/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Match your situation to a "load when" line; load only matching pages.
| Page | Load when |
|------|-----------|
| [externally-owned-defaults](common/integrations/externally-owned-defaults.md) | A code/config default names a resource the repo does not own (model alias, endpoint, bucket, queue, index) — reviewing or merging a PR that claims that default works, adding a startup check that the name still resolves, or diagnosing a default path that broke with no code change |
| [consumer-required-fields](common/integrations/consumer-required-fields.md) | Writing an adapter that maps one module's records into the payload a second module (scoring engine, plugin, external client) consumes, with the target shape taken from a docstring, README example, or sample payload; such an adapter runs end to end with no error and the downstream numbers come out low; deciding which mapped fields need their own assertion |
| [robots-txt-and-source-selection](common/integrations/robots-txt-and-source-selection.md) | Choosing which site to fetch a published dataset from and reading its robots.txt to decide whether your client may crawl it; the file contains a `Disallow: /` and you are deciding whose group it belongs to; setting the crawler's User-Agent and checking that token against the file; robots.txt returned a non-200 status; the origin restricts your token and you are looking for a portal that republishes the same records |

### storage
Expand Down
1 change: 1 addition & 0 deletions wiki/infrastructure/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ Match your situation to a "load when" line; load only matching pages.
| [logs-metrics-signals](observability/logs-metrics-signals.md) | Instrumenting a new or existing service (logs, metrics, correlation ids); an incident revealed you couldn't see what happened; choosing between a log line and a metric; a metric label would carry unbounded values (user ids/UUIDs) |
| [missing-container-metrics](observability/missing-container-metrics.md) | Prometheus `container_*` CPU/memory series are empty or pod dashboards blank while kubelet scrape targets all report up (common on embedded/VM Kubernetes like OrbStack); deciding between cAdvisor and kubelet `/metrics/resource` scraping |
| [alerting](observability/alerting.md) | Creating or reviewing alerts; the team ignores a noisy pager; deciding whether a condition pages, tickets, or stays on a dashboard |
| [suppression-state-and-delivery-failure](observability/suppression-state-and-delivery-failure.md) | Adding notification suppression to a script or service (cooldown file, "last alerted at" timestamp, sent-marker key) and choosing where the mark is written relative to the send; a condition stayed live while the channel went quiet for the whole cooldown window; writing the tests for that marker against a stub whose send always fails |
2 changes: 1 addition & 1 deletion wiki/infrastructure/observability/alerting.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sources:
- https://sre.google/sre-book/monitoring-distributed-systems/
- https://sre.google/workbook/alerting-on-slos/
last_verified: 2026-07-10
related: [infrastructure-observability-logs-metrics-signals, infrastructure-deploy-rollout-and-rollback]
related: [infrastructure-observability-logs-metrics-signals, infrastructure-deploy-rollout-and-rollback, infrastructure-observability-suppression-state-and-delivery-failure]
---

# Deciding What Pages a Human
Expand Down
Loading
Loading