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
279 changes: 194 additions & 85 deletions .dev-loop/INGEST_REPORT.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ 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, 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, migrating call sites of a changed signature) 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) |
| [infrastructure](wiki/infrastructure/index.md) | **seeded** | CI/CD pipelines, secrets in build/deploy, container image builds, per-environment and path-valued configuration, 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) |
| [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) |
Expand Down
1 change: 1 addition & 0 deletions log.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ 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-04] ingest | 4 queued insights → 4 new pages, 5 amendments. New: testing/quality/injected-clock-duration-assertions (one-sided tolerance on duration bounds, fake clock starts at 0.0 — PEP 564 records CPython doing the same for monotonic()/perf_counter(); measured start-value table 0.0/1.0/100/1000/1e6/1e9), testing/quality/guard-shape-vs-consequence (a repo-wide artifact guard that fires on a legitimate artifact is sharpened from shape S to "S and the consequence C", C computed by the production derivation, with a required-red fixture; exemption lists deleted), infrastructure/config/path-valued-config (NEW-adjacent to environment-config: a path-valued key is rejected unless absolute after expanduser, directory statted at startup — a missing dir globs to an empty result indistinguishable from "no work"), backend/common/refactoring/call-site-enumeration (NEW category refactoring: enumerate by callee/find-references, not by parameter name — positional calls carry no name; sweep helper producers; keyword-only marker makes stale positional calls raise). Amendments: environment-config +path-valued edge row, background-services +working-directory edge row and systemd.exec source (WorkingDirectory defaults to / for system instances, home for user instances), reciprocal related-links on tests-that-cannot-fail / behavior-not-implementation / async-testing. Verification: launchd LaunchAgent probe with no WorkingDirectory key measured cwd=/ (cleaned up); float and glob/is_absolute behaviour reproduced on CPython 3.14.6; guard page left confidence field-tested — its general failure mode is sourced to the change-detector post but the compute-the-consequence technique rests on one real case (linkly #35)
83 changes: 83 additions & 0 deletions wiki/backend/common/refactoring/call-site-enumeration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
id: backend-common-refactoring-call-site-enumeration
domain: backend
category: refactoring
applies_to: [general]
confidence: verified
sources:
- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
- https://docs.python.org/3/reference/expressions.html#calls
- https://peps.python.org/pep-3102/
last_verified: 2026-08-04
related: [testing-quality-behavior-not-implementation, qa-process-regression-scope, backend-common-api-design-pagination-contract]
---

# Enumerating Call Sites When a Signature Changes

## When this applies

You are changing a function, method, or constructor's signature — adding,
removing, reordering, or restructuring a parameter — and every existing call
must be migrated. Includes scoping the change up front ("how many places does
this touch?") and explaining why a migration you believed complete broke tests.

## Do this

1. **Enumerate by the callee, not by the parameter name.** Search for the call
token (`verify(`, `Interpreter(`, `ClassName(`) and read every hit. A
positional argument carries no parameter name anywhere in the call
expression, so a parameter-name search cannot see it — it finds keyword call
sites only, and reports a scope smaller than the real one.

2. **Prefer resolved references over text search.** Use the language server's
find-references (LSP `textDocument/references`, an IDE's "Find Usages") or an
AST query. These resolve the symbol, so they return positional and keyword
calls alike, and they do not over-match a same-named function on another
type. Use callee-token grep as the fallback when no server is available.

3. **Sweep helper and factory definitions as their own step.** A test helper or
fixture factory that builds the old argument shape appears once in a callee
search while supplying many call sites. Search the parameter name too — not
to enumerate calls, but to find the *producers* of the old shape.

4. **Make a stale call fail loudly rather than bind silently.** Where the
language allows it, mark the changed parameter keyword-only (Python's `*`
marker, PEP 3102) or otherwise change arity, so a not-yet-migrated positional
call raises instead of passing its value into a neighbouring parameter.

5. **Run the whole suite, not the files you edited.** The call sites the recon
search missed are exactly the ones whose tests you had no reason to run.

| Migration shape | Enumerate with |
|-----------------|----------------|
| Renaming a parameter | Callee references — the old name exists only at keyword call sites, so a name search reports a subset |
| Changing a parameter's type or data shape | Callee references, then read the argument at that position in every hit |
| Removing or reordering parameters | Callee references, plus a keyword-only or arity change so unmigrated positional calls raise |
| Changing what a helper/factory produces | Search the produced structure's field names to find every producer, then the callee references of each producer |

## Edge cases

| Case | Then |
|------|------|
| The callee is reached dynamically (`getattr`, reflection, a DI container, a registry keyed by name) | Find-references and callee grep both miss it — additionally search the bare name as a string literal, and keep a runtime assertion on the new shape |
| The name is shared by several types (`save(`, `run(`) | Callee-token grep over-matches; use find-references, or scope the search to the defining module's importers |
| Arguments are supplied by a wrapper (`functools.partial`, a decorator, a curried factory) | The binding happens at the wrapper, not at the visible call — migrate the wrapper and treat its own call sites as a second enumeration pass |
| The recon search and the migration search return the same count | Confirm the count came from callee references; two searches agreeing on the wrong basis is not corroboration |
| The language has no keyword arguments (Go, Java) | The compiler enumerates for you once the arity or type changes — make the change type-incompatible rather than type-compatible, so a missed site fails to build instead of compiling with a wrong value |

## Instead of

| If you are about to | Do this instead | Why |
|---------------------|-----------------|-----|
| Scope the migration by grepping the parameter name (`repo_rows`) | Grep the callee token (`verify(`) or run find-references | A positional call never mentions the parameter name, so the name search returns keyword call sites only and understates the scope |
| Treat the recon search's hit count as the migration's size | Read every hit, and re-enumerate by callee before declaring the migration complete | The count answers "how many places name this parameter", not "how many places call this function" |
| Migrate the call sites and leave the parameter positionally compatible | Make it keyword-only or change arity | A stale positional call otherwise binds its value to whatever parameter now occupies that slot, which type checks and runs |
| Verify the migration by running the tests for the files you edited | Run the full suite | The tests that catch a missed call site live in the files you did not know to open |

## Sources

- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/ — `textDocument/references` resolves all references to a symbol, with `includeDeclaration` controlling whether the declaration is returned; a resolved-reference query is independent of how each call spells its arguments
- https://docs.python.org/3/reference/expressions.html#calls — argument binding rules: positional arguments are matched to parameters by position, keyword arguments by name; the two forms of the same call therefore share no common text
- https://peps.python.org/pep-3102/ — keyword-only arguments: parameters after a bare `*` can only be supplied by keyword
- Local reproduction 2026-08-04 (CPython 3.14.6): for a file holding `verify("s","a",1, repo_rows=[…])` and `verify("s","a",1,[…])`, `grep -n "repo_rows"` returns the definition and the keyword call (2 hits) while `grep -n "verify("` returns the definition and both calls (3 hits) — the positional call is invisible to the parameter-name search. Marking the parameter keyword-only (`def verify(spec, mode, budget, *, repo_rows=None)`) turns the stale positional call into `TypeError: verify() takes 3 positional arguments but 4 were given`
- Field evidence (linkly, 2026-08-04): a signature migration scoped by `grep -rn "repo_rows" impl/tests/` (13 hits, all keyword) reported 7 remaining sites; the full suite then reported `Ran 472 tests / FAILED (failures=11)`, all in one file that passed the value as `verify()`'s fourth positional argument, and a `rows_for()` helper was still producing the old shape for 5 further call sites
8 changes: 7 additions & 1 deletion wiki/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ three stack subtrees — route by concern first, stack second:

| Subtree | Route there when |
|---------|------------------|
| [common](#common-language-agnostic) (below) | The concern is language-agnostic: API contracts, idempotency, JWT issuance, outbound calls, caching, jobs, transactions in app code, shared state/pools, exception structure, LLM completion validation & context budgeting, consuming external-API responses, externally-owned defaults, object-storage references |
| [common](#common-language-agnostic) (below) | The concern is language-agnostic: API contracts, idempotency, JWT issuance, outbound calls, caching, jobs, transactions in app code, shared state/pools, exception structure, LLM completion validation & context budgeting, consuming external-API responses, externally-owned defaults, object-storage references, migrating call sites of a changed signature |
| [java](java/index.md) | You are writing/reviewing JVM backend code (Java/Kotlin, Spring, JPA/Hibernate) and the concern is stack-specific: entity mapping, persistence context, proxy pitfalls, JVM threads/memory |
| [node](node/index.md) | You are writing/reviewing Node.js/TypeScript backend code: event-loop blocking, promise error handling, runtime validation at boundaries, graceful shutdown |
| [python](python/index.md) | You are writing/reviewing Python backend code: GIL/concurrency model, pydantic validation, WSGI/ASGI workers, language traps |
Expand Down Expand Up @@ -89,3 +89,9 @@ Match your situation to a "load when" line; load only matching pages.
| Page | Load when |
|------|-----------|
| [object-key-persistence](common/storage/object-key-persistence.md) | Persisting the result of an object-storage upload (`s3.upload()`, `lib-storage` `Upload`, a transfer manager) — choosing which response field goes in the DB column; building the read/signing path from a stored reference; migrating a column that holds URLs to keys; only large uploads 404 on read |

### refactoring

| Page | Load when |
|------|-----------|
| [call-site-enumeration](common/refactoring/call-site-enumeration.md) | Changing a function/method/constructor signature (adding, removing, reordering, or reshaping a parameter) and migrating every call; estimating up front how many places a signature change touches; a migration you believed complete broke tests in files you had not opened; deciding between parameter-name grep, callee grep, and find-references; making unmigrated call sites fail loudly |
3 changes: 2 additions & 1 deletion wiki/infrastructure/config/environment-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
- https://12factor.net/build-release-run
- https://12factor.net/dev-prod-parity
last_verified: 2026-07-10
related: [infrastructure-deploy-rollout-and-rollback, infrastructure-ci-cd-secrets-handling, backend-node-boundaries-runtime-validation, backend-python-boundaries-runtime-validation, backend-common-integrations-externally-owned-defaults]
related: [infrastructure-deploy-rollout-and-rollback, infrastructure-ci-cd-secrets-handling, backend-node-boundaries-runtime-validation, backend-python-boundaries-runtime-validation, backend-common-integrations-externally-owned-defaults, infrastructure-config-path-valued-config]
---

# Configuration That Differs Per Environment
Expand Down Expand Up @@ -62,6 +62,7 @@ files, and env vars; reviewing how a service gets its settings.
| A key is only meaningful in prd (e.g. a payments endpoint) | Declare it required in every environment and give dev/stg a working sandbox value — an optional-in-dev key hides a missing-prd-value crash until the prd deploy |
| A value must change without a redeploy (kill switch, tuning knob) | Use a runtime flag ([infrastructure-deploy-rollout-and-rollback] config/flag row); flag names and allowed values still belong in the schema inventory |
| A required value names a resource the repo does not own (model alias, endpoint, bucket, queue) | Startup validation must resolve the name against the owner's catalog, not just check that the string is present — [backend-common-integrations-externally-owned-defaults] owns the review-time and startup checks |
| The value is a filesystem path | Presence is not enough — a relative path passes every presence check and then resolves against a working directory the launcher owns ([infrastructure-config-path-valued-config]) |
| Config service or mounted config unreachable at boot | Crash and let the orchestrator restart/retry; starting with fallback values means each instance runs config you cannot account for |
| Legacy code full of `if (env === 'prod')` branches | On each touch, replace the branch you are editing with a named config value; record the remaining branches as inventory gaps |

Expand Down
Loading
Loading