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
392 changes: 349 additions & 43 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 @@ -14,9 +14,9 @@ follow the cross-pointers in their index or take the next matching seeded domain
| [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), per-environment/path-valued config, multi-agent orchestration (worker liveness signals, shared run state, tmux pane delivery, completion gates, worktree-isolated workers) |
| [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 (guarded-path coverage, override matrices), scope-purity gates, sourcing deliverable documents from generated artifacts, automated verification of document deliverables (spec/RFC gates) (writing automated test code → testing) |
| [qa](wiki/qa/index.md) | **seeded** | Release-quality process: release gates, regression scoping, bug reports, severity/priority triage, exploratory testing (guarded-path coverage, override matrices), scope-purity gates, sourcing deliverable documents from generated artifacts, verifying the quantitative claims in a document before publishing it, 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) |
| [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, the author identity a commit publishes to a public repository (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 |
| [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 |

Expand Down
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-06] ingest | knowledge-flush of 8 queued insights → 2 new pages, 3 merges, 3 drops. New: qa/deliverables/quantitative-claims-in-a-published-document (enumerate every count in a document about to be published, recompute each from source with a recorded command, and treat a reported-wrong number as a sample rather than the defect — counts drift silently because no test asserts them; field incident: 5 of 9 README claims stale, one reported), security/data/commit-identity-in-public-repos (compare `git config user.email` against `git log -1 --format=%ae` before committing to public history and override per commit with `git -c`, never globally — git takes the author from GIT_AUTHOR_*, then user.email, then EMAIL, then a hostname guess, and warns on none of it). Merged: debugging/methodology/hypothesis-testing (+2 edge cases: an explicit error refutes a suspect whose defining property is concealment; a zero-result query cannot separate "removed" from "never accepted" — run a positive control through the same path), infrastructure/agent-orchestration/worktree-isolated-workers (+4 edge cases + a 1.2.0 reproduction: the worktree_escape rule ANDs a surviving main-root mention with a write verb matched independently anywhere in the command, so a bare cross-worktree read passes but the same read sharing a line with `mkdir` fires `ask`; only the worker's OWN worktree path is stripped, so sibling-worktree reads stay one write verb from firing), infrastructure/agent-orchestration/pane-delivery-confirmation (+4 edge cases + 1 Instead-of: check the idle prompt BEFORE binding new work — a worker's "done" is a report from inside its turn, not its end — and branch recovery on the failure stage, since an occupied runtime wants a wait-and-rebind while a dead agent wants a new agent). Dropped: 2 candidates already merged verbatim on main with the same field incidents (call-site enumeration by callee name incl. the `rows_for()` helper fan-out → backend/common/change-impact/call-site-enumeration; `${VAR:-default}` discarding an empty off-switch incl. the `WATCH_TMUX=/nonexistent` sentinel → platforms/shells/env-var-off-switches), and 1 as out of scope (giving a user an outline instead of a draft when a community forbids generated text — a working-practice rule, not a software-engineering domain this wiki covers).
## [2026-08-06] ingest | knowledge-flush of 3 queued insights → 1 new page, 2 folded into this branch's existing merges. New: backend/common/change-impact/widening-a-closed-value-table (when adding an entry to a closed name→magnitude table held as a named constant, enumerate by the table's *values* and member strings, not only its name, and reconcile every inlined copy to read the single table before widening — consumers inline the pairs instead of importing the constant, so the name grep counts imports and misses exactly the sites that break; local reproduction: 1 name hit vs 7 value hits across 4 files, three inlined tuples already narrower than the canonical map, so the predicted divergence was live before the change). Folded (already carried by this branch as C6/C7, re-verified today against guardrails 1.2.0 and the shipped orchestrate scripts): the worktree_escape read-vs-write correction, and the idle-prompt-before-binding dispatch taxonomy.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
- 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]
related: [security-input-validation-at-trust-boundaries, infrastructure-config-environment-config, backend-common-api-design-error-responses, qa-process-acceptance-criteria, backend-common-change-impact-widening-a-closed-value-table]
---

# Accepting a Declaration the System Does Not Enforce
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
- https://docs.python.org/3/library/ast.html
- https://peps.python.org/pep-0570/
last_verified: 2026-08-05
related: [qa-process-regression-scope, backend-python-language-mutable-state-traps, testing-data-test-data-and-isolation]
related: [qa-process-regression-scope, backend-python-language-mutable-state-traps, testing-data-test-data-and-isolation, backend-common-change-impact-widening-a-closed-value-table]
---

# Enumerating Call Sites Before Changing a Callee's Contract
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
id: backend-common-change-impact-widening-a-closed-value-table
domain: backend
category: change-impact
applies_to: [general]
confidence: verified
sources:
- https://refactoring.com/catalog/replaceMagicLiteral.html
- https://pragprog.com/tips/
last_verified: 2026-08-06
related: [backend-common-change-impact-call-site-enumeration, backend-common-api-design-unenforced-declarations]
---

# Widening a Closed Value Table Whose Consumers Inlined It

## When this applies

You are adding an entry to a closed table that maps a name to a magnitude or a
code — duration units, status codes, currency exponents, retry tiers, severity
levels — and the table exists as a named constant in one module. Also when a
newly added entry is accepted at one layer and rejected, ignored, or
mis-converted at another, so the symptom is a divergence between two paths
rather than a parse error.

## Do this

1. **Enumerate by the table's values, not only by its name.** Grep a
distinctive magnitude from the table (`60000`, `86400`, `4290`) and a
distinctive member string (`"ms"`), across the whole repo including tests,
fixtures, generators, and any second-language backend. The name grep lists
the sites that import the table; the value grep lists the sites that copied
it, and only the second set is where widening breaks.
2. **Treat the gap between the two counts as the work item**, and state both in
the plan: "1 site imports `DURATION_UNITS`, 7 mention `60000`" is checkable
and shows the scope; "the units table has 1 consumer" hides it.
3. **Classify every value hit before editing:**

| Value hit is | Do |
|--------------|----|
| The canonical table's own definition | Nothing — this is the site the others should read |
| An inlined copy of the pairs (`(("ms",1),("s",1000),("m",60000))`) | Replace the literal with a read of the canonical table |
| Bare arithmetic on one member (`value % 60000`, `ms // 86400000`) | Replace the literal with a lookup into the table; it is a copy of one row |
| A second named table over the same vocabulary in the same module | Derive one from the other so a single edit widens both |
| A copy in another language, a generated artifact, or a backend that re-implements the conversion | Generate it from the table, or add a conformance test asserting both accept every member |

4. **Fix every copy to read the single table before adding the new entry**, so
the entry lands in one place. Widening first and reconciling after means the
new member exists in the table while each copy silently defines the old,
narrower vocabulary.
5. **Add a test that drives every consumer with every member of the table**,
parameterized over the table itself. It fails on the next widening if a new
copy has appeared, which is the only check that survives the next author.

## Edge cases

| Case | Then |
|------|------|
| The inlined copies are already narrower than the canonical table | The divergence predates your change — the table's later members are already unreachable through those paths. Fix them as part of this change and note the pre-existing gap, or your widening gets blamed for it |
| The magnitude is not distinctive (`1`, `60`, `1000`) | Grep the member name string (`"ms"`, `"USD"`) and the suffix form instead; a bare `1000` returns every unrelated site and the enumeration stops being readable |
| The same vocabulary is expressed in two units across a boundary (seconds inside, milliseconds at the edge) | Grep both magnitudes (`60` and `60000`) — a copy converted at the boundary matches neither the table's values nor its name |
| Members of the table are persisted (stored in rows, serialized into messages, written into config already deployed) | Widening is a migration, not an edit: old readers must keep accepting stored values, and the new member cannot be written until every reader ships |
| The table is consumed by a caller you do not own (published package, other repo, plugin API) | Widening is a versioned release for them; enumerate only what you own, and treat the new member as unsupported until their version pins forward |
| A copy exists only in a test fixture | It still diverges — a fixture pinned to the narrow vocabulary keeps passing while the widened path is never exercised, so the suite reports green on an unmigrated consumer |
| The lookup is a `switch`/`if` chain over member names rather than a value copy | The value grep misses it — grep the member names as well, since the chain encodes the same table as control flow |

## Instead of

| If you are about to | Do this instead | Why |
|---------------------|-----------------|-----|
| Grep the constant's name, report N consumers, and scope the change from that | Grep the table's values and member strings too, and scope from the union | Consumers routinely inline the pairs instead of importing the constant, so the name grep counts imports and misses copies — the copies are the sites that break |
| Add the entry to the canonical table and run the suite | Reconcile the copies first, then widen | A green suite means the consumers the tests reach accepted the entry; the copied ones were never asked |
| Leave one inlined copy because it is a hot path or avoids an import cycle | Have that path read the table once at import and keep the local binding | The copy is not cheaper than a module-level lookup, and it is the site that silently defines a different vocabulary |
| Extend a second same-vocabulary table alongside the first to keep both callers happy | Derive the second from the first in the same module | Two canonical-looking tables make the next author's name grep authoritative and wrong |

## Sources

- https://refactoring.com/catalog/replaceMagicLiteral.html — *Replace Magic Literal*, alias "Replace Magic Number with Symbolic Constant": a literal with a particular meaning becomes a named constant. The refactoring exists because the inlined literal is the default state of such values, which is what makes the value the reliable search handle
- https://pragprog.com/tips/ — Tip 15, DRY: "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." A copied value table is a second representation, and widening one representation is what produces the divergence
- Local reproduction 2026-08-06 (`linkly`, Python, macOS): `grep -rn "DURATION_UNITS" impl/lnpl/*.py` returns **1** hit (the definition); `grep -rn "60000" impl/lnpl/*.py` returns **7** across four files — a second named table (`DURATION_UNIT_MS`, `lexer.py:23`), three independently inlined `(("ms",1),("s",1000),("m",60000))` tuples (`condition.py:353`, `interp.py:1020`, `backend.py:446`), and two bare-literal arithmetic sites (`condition.py:269-270`). The predicted divergence was already present: the canonical map carries `h` and `d`, while all three inlined copies stop at `m`, so those paths cannot convert a unit the lexer accepts
1 change: 1 addition & 0 deletions wiki/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Match your situation to a "load when" line; load only matching pages.

| Page | Load when |
|------|-----------|
| [widening-a-closed-value-table](common/change-impact/widening-a-closed-value-table.md) | Adding an entry to a closed table mapping names to magnitudes or codes (duration units, status codes, currency exponents, severity levels) that lives as a named constant; scoping that change from a search for the constant's name; a new entry parses at one layer and is rejected or mis-converted at another; deciding what to do about an inlined copy of the table in a hot path, a second language backend, or a fixture |
| [call-site-enumeration](common/change-impact/call-site-enumeration.md) | Changing the contract of a function/method/constructor other code calls — adding, removing, reordering or redefining a parameter — and you need the complete call-site list; scoping such a migration from a search; a migration scoped from recon came back green and then failed on call sites the search never listed; deciding whether to append a parameter or make it keyword-only (release-level re-test scope → qa/process/regression-scope) |

### reliability
Expand Down
Loading
Loading