Skip to content
Merged
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
1 change: 1 addition & 0 deletions log.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ Append-only. Format: `## [YYYY-MM-DD] <ingest|revise|lint|gap|contradiction|drif
## [2026-08-06] ingest | Consolidated review of knowledge PRs #17–#40 (15 open PRs, 123 file-versions across ~75 unique pages) into one reconciled state. New pages (38): backend/common/api-design/unenforced-declarations, backend/common/integrations/robots-txt-and-source-selection, backend/common/reliability/client-side-rate-limiting (best-of 8 variants across 3 paths), backend/python/language/bytecode-cache-staleness, databases/data-survey/surveying-live-data-for-a-rule (NEW category), databases/schema-design/verifying-additive-migrations, infrastructure/agent-orchestration/{control-signals-vs-primary-artifacts, shared-run-state, pane-delivery-confirmation, session-completion-gates, worktree-isolated-workers} (NEW category, unified from two competing category names), infrastructure/config/{path-valued-config, keys-ahead-of-their-consumer}, platforms/processes/{parsing-cli-structured-output, driving-a-tui-in-a-tmux-pane, tool-diagnostics-without-a-failing-exit-code (best-of 4 variants)}, platforms/shells/{escapes-in-shell-string-literals, env-var-off-switches (best-of 2), option-like-argument-values, unset-versus-empty-parameters}, platforms/toolchains/compiler-sysroot-on-macos (best-of 2, link-step nuance preserved), platforms/tools/{harness-mediated-tool-results (best-of 2), version-keyed-artifact-cache}, qa/deliverables/generated-artifacts-as-deliverable-source (NEW category), qa/exploratory/{guard-true-path-coverage, lowered-declaration-survival, override-control-pairs}, qa/process/scope-purity-checks, security/data/masking-verification, testing/data/artifact-leakage-from-a-suite (best-of 2), testing/mocking/destructive-operations-on-shared-daemons, testing/quality/{completion-predicates (best-of 2), differential-run-agreement, guard-shape-vs-consequence, injected-clock-duration-assertions, write-path-assertions}, testing/strategy/{differential-testing, import-time-side-effects}.
## [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).
8 changes: 7 additions & 1 deletion wiki/infrastructure/ci-cd/secrets-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ sources:
- https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect
- https://12factor.net/config
- https://docs.docker.com/build/building/secrets/
last_verified: 2026-07-10
- https://cli.github.com/manual/gh_auth_login
- https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests
last_verified: 2026-08-06
related: [infrastructure-containers-image-builds, infrastructure-ci-cd-pipeline-structure]
---

Expand Down Expand Up @@ -59,6 +61,7 @@ package fetch, signing key), or you are reviewing how secrets flow through CI.
| Secret already pushed to a shared branch | Rotate first — history rewriting does not unleak; then scrub history (git filter-repo or provider support) so scanners stop flagging it |
| Local development needs credentials too | Local `.env` in `.gitignore`, values distributed through the team's secret manager, using dev-scoped credentials distinct from CI's and prd's |
| Third-party CI plugin/action receives a secret | Pin the action/plugin to a full version or commit hash before giving it a secret; an unpinned dependency can start exfiltrating on its next release |
| A non-interactive job or agent must push and open a PR, but its primary CLI token (e.g. `gh`) reports invalid and interactive re-auth is impossible | Test the remaining credential channels independently before declaring the run blocked — they are separate stores: `gh` keeps its own OAuth token in the system credential store, git remote auth can ride SSH keys or a credential helper (`gh` even configures the git protocol separately), and any configured API/MCP integration holds a third token. `git push --dry-run` proves push auth without mutating anything; an issue/PR search with `author:@me` reveals an API token's account without any write call. When the working channels belong to the same account, combine them: push via git, open the PR via the API |

## Instead of

Expand All @@ -75,3 +78,6 @@ package fetch, signing key), or you are reviewing how secrets flow through CI.
- https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect — OIDC short-lived tokens replacing stored cloud credentials
- https://12factor.net/config — credentials live in the environment, never in code
- https://docs.docker.com/build/building/secrets/ — build args/env unsuitable for secrets; secret mounts
- https://cli.github.com/manual/gh_auth_login — `gh` stores its token in the system credential store; git protocol (ssh/https) is configured independently of the API token
- https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests — `@me` value for `author:`/`commenter:` qualifiers resolves to the authenticated account
- Field incident 2026-08-06 (non-interactive agent session; the credential-channel row is field-tested on top of the doc-verified store separation): `gh auth status` 401 → `git push --dry-run` over SSH OK → GitHub MCP `search_issues author:@me` resolved the token's account → PR opened via API while pushing via git
4 changes: 3 additions & 1 deletion wiki/platforms/filesystems/permissions-and-exec-bits.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ sources:
- https://man7.org/linux/man-pages/man7/inode.7.html
- https://docs.docker.com/engine/containers/run/
- https://docs.docker.com/engine/storage/bind-mounts/
last_verified: 2026-08-04
last_verified: 2026-08-06
related: [platforms-filesystems-paths-case-and-line-endings, testing-data-test-data-and-isolation]
---

Expand Down Expand Up @@ -51,13 +51,15 @@ channels that preserve modes.
| You need a mode other than 755/644 tracked in git | git tracks only executable-or-not (`100755`/`100644`) — enforce fuller modes (setgid, 600 secrets) in a deploy/entrypoint step, not via git |
| Exec bit committed but Windows-checkout users still can't run it | Windows doesn't consume the POSIX exec bit; invoke via the interpreter there. Line-ending/casing breakage on the same journey: [platforms-filesystems-paths-case-and-line-endings] |
| Rootless Docker / userns-remap in play | uids are remapped, so host-uid matching arithmetic changes — verify with `ls -ln` on the host and `id` inside the container before choosing `--user` |
| An endpoint-security agent (EDR, e.g. SentinelOne) flags `chmod +x` on temp or test-injected files, or tests must swap in stub scripts via an env var | Design the calling code to invoke the helper as `sh "$SCRIPT"` (POSIX: the command file "need not be executable") — stubs are then plain read-only files needing no `chmod`, and the same call keeps working when a distribution path (plugin cache, artifact store) strips modes |

## Instead of

| If you are about to | Do this instead | Why |
|---------------------|-----------------|-----|
| `chmod 777` to make a permission error go away | Identify WHICH user/process needs WHICH access (`ls -ln` + the failing process's uid) and grant exactly that — owner change, group+setgid, or `--user` | 777 is an incident deferred: any local user/process can now modify or replace the file |
| `chmod +x` locally and moving on | `git update-index --chmod=+x <file>` and commit | The local bit doesn't reach the repo; every fresh clone and CI run re-breaks |
| `chmod +x` a test stub so the code under test can exec it | Have the code call helpers via `sh "$SCRIPT"` and inject the stub as a plain file | The exec-bit requirement is a property of the caller's invocation style; interpreter invocation removes it for stubs and deployed copies alike, and avoids EDR rules that treat `chmod +x` on fresh files as malicious |
| Running the container as root because the mount "just works" that way | `--user` matching the host owner, or entrypoint `chown` | Root-in-container writes root-owned files onto the host and widens container-escape blast radius |

## Sources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
- https://clang.llvm.org/docs/UsersManual.html
- https://www.gnu.org/software/bash/manual/bash.html#Redirections
- https://code.claude.com/docs/en/hooks
last_verified: 2026-08-05
last_verified: 2026-08-06
related: [platforms-processes-non-interactive-cli-invocation, platforms-shells-command-text-inspected-before-execution, testing-quality-checks-that-cannot-pass]
---

Expand Down Expand Up @@ -78,6 +78,7 @@ OUT=$(tool "$FILE" 2>&1 >/dev/null)
| The tool is run through a pipeline (`tool f \| tee log`) | Capture into a variable or file first, then inspect; a pipeline reports the last command's status and the diagnostics land in the pipe |
| The tool offers `-Werror` / `--max-warnings 0` | Use it **in addition** — it converts the status, and the captured text is still what names which warning fired |
| The wrapper runs under `set -e` | Command substitution failure inside `OUT=$(…)` is not suppressed by a condition context — assign first, test after, as above |
| Warnings must not repeat on every run of an unchanged file | Hash `$OUT` per file and forward only on change; an unconditional exit 2 re-feeds the same text each time |

## Instead of

Expand All @@ -96,3 +97,4 @@ OUT=$(tool "$FILE" 2>&1 >/dev/null)
- https://www.gnu.org/software/bash/manual/bash.html#Redirections — "Redirections are processed in the order they appear, from left to right", with the `ls > dirlist 2>&1` vs `ls 2>&1 > dirlist` example
- https://code.claude.com/docs/en/hooks — exit 2: "stderr text is fed back to Claude as an error message"; `PostToolUse` cannot block ("the tool already ran") but shows stderr to Claude
- Field reproduction 2026-08-05: compiler with warning input → exit 0 with 3 warnings on stderr; clean input → exit 0 with empty stderr; error → non-zero exit with errors; the three outcomes observed directly when the gate was tested
- Local reproduction 2026-08-06 (macOS, Apple clang): `cc -Wall` on a snippet with an unused variable → exit 0, 157 bytes on stderr; `OUT=$(cc … 2>&1 >/dev/null)` captured the diagnostic while the reversed redirection order captured 0 chars
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ confidence: verified
sources:
- https://code.claude.com/docs/en/hooks
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
last_verified: 2026-08-05
last_verified: 2026-08-06
related: [platforms-shells-portable-shell-scripts, platforms-environment-path-resolution, platforms-shells-escapes-in-shell-string-literals, infrastructure-agent-orchestration-control-signals-vs-primary-artifacts, platforms-tools-harness-mediated-tool-results, platforms-processes-tool-diagnostics-without-a-failing-exit-code]
---

Expand Down Expand Up @@ -71,6 +71,16 @@ on the first attempt.
consumer polling for a file that will never appear waits forever, and an
improvised workaround defeats a control the human put there deliberately.

8. **When you author the gate, accept all three shell quoting forms and expand
only prefixes the gate can resolve from its own environment.** Correct shell
style quotes paths, so an extraction pattern that excludes quote characters
(`[^ '"]+`) denies exactly the well-formed commands, with a misleading
missing-argument error. Parse the argument bare, single-quoted, and
double-quoted (POSIX 2.2 defines only these three forms), expand `~`, `$HOME`,
and `${HOME}` against the gate's own environment, and state in the gate's
error message that any other variable must be written as a literal path.
Prove the parser with one regression test per form before relying on it.

## Edge cases

| Case | Then |
Expand All @@ -94,6 +104,7 @@ on the first attempt.
| Reword prose to get a dangerous-looking string past a scanner | Put the prose in a file and pass `--notes-file`/`--body-file` | Editing meaning to satisfy a text scanner degrades the artifact; a file is not scanned as a command |
| Treat a side-effect command's empty output as success | `ls`/`stat` the artifact it should have produced | A blocked command and a silent successful one produce identical stdout; only the artifact distinguishes them |
| Retry a gated status-emitting command with a reshaped path | Report the signal as un-emitted to its consumer | The consumer polls forever on a wrong assumption, and evading the gate removes a control the human installed |
| Extract a gate's file argument with a bare-token pattern like `[^ '"]+` | Parse bare, single-quoted, and double-quoted forms and expand `~`/`$HOME`/`${HOME}` yourself (step 8) | The quote characters callers are taught to use land inside the match window, so the extractor returns empty and the gate reports a present argument as missing |

## Sources

Expand All @@ -111,10 +122,10 @@ missing), `--body-file $REPO/…` → literal `$REPO/…` (blocked as nonexisten
heredoc body-file was separately blocked as not-yet-existing until moved to a
preceding call.

On 2026-08-05 a worker session inside a git worktree ran the orchestrator's own
`status-update.sh` with the status directory pointing at the main checkout. A
`worktree_escape` guardrail matched the absolute main-checkout path and returned
only an escalation notice; `ls -la` on the status directory then showed it empty,
so the orchestrator would have polled indefinitely for a file that was never
written. A Write-tool call to the same tree succeeded, confirming the block was
scoped to the Bash command text rather than to filesystem permissions.
2026-08-05/06, same repo: a `worktree_escape` guardrail blocked the
orchestrator's own `status-update.sh` (path text match) — `ls` showed the status
directory empty while a Write-tool call succeeded, proving a command-text-scoped
block, and the consumer would have polled forever. Gate-author side: the
bare-token extractor denied a double-quoted `--body-file` path as missing; after
quoted-form parsing plus `~`/`$HOME`/`${HOME}` expansion, the bats regressions
(`tests/pre-flush-pr-gate.bats` 12–13) went red-then-green and still pass.
11 changes: 9 additions & 2 deletions wiki/testing/mocking/what-to-mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ sources:
- https://martinfowler.com/articles/mocksArentStubs.html
- https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html
- https://abseil.io/resources/swe-book/html/ch12.html
last_verified: 2026-07-10
related: [testing-strategy-test-level-choice, testing-quality-behavior-not-implementation]
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
- https://nodejs.org/api/test.html
last_verified: 2026-08-06
related: [testing-strategy-test-level-choice, testing-quality-behavior-not-implementation, testing-quality-tests-that-cannot-fail]
---

# Deciding Whether to Mock, Fake, or Use the Real Dependency
Expand Down Expand Up @@ -51,6 +53,8 @@ where mocks are breaking on refactors.
| You need to test your code's handling of a provider's failure modes (500, timeout, malformed body) | Stub those responses at your wrapper boundary per case — this is the main payoff of mocking unowned I/O |
| The mock setup has grown to mirror the collaborator's logic (conditional returns, sequencing) | Replace it with a fake or the real object — a mock that reimplements the dependency is a second implementation that can drift |
| The fake and the real implementation can diverge | Run one shared contract test suite against both; the fake stays trustworthy only while it passes the real thing's tests |
| The dependency is consumed as ESM named imports (e.g. `import { spawnSync } from "node:child_process"`) and you planned to monkey-patch it | Inject it behind a deps interface the subject receives instead — imported bindings are read-only live bindings the importer cannot reassign, and `node:test`'s `mock.module()` only works under the `--experimental-test-module-mocks` flag (still Stability 1 as of Node 26), so DI is the unflagged interception point |
| The test must prove a negative — "no real child process / external command ran" | Route every spawn through the injected deps interface, then in the test empty `process.env.PATH` as a tripwire and deep-equal the full stub-recorded call sequence: a clean exit fully explained by the stubs proves no PATH-resolved spawn could have succeeded, turning "trust the structure" into an executable assertion ([testing-quality-tests-that-cannot-fail]) |

## Instead of

Expand All @@ -66,3 +70,6 @@ where mocks are breaking on refactors.
- https://martinfowler.com/articles/mocksArentStubs.html — stub/fake/mock distinctions; state vs behavior verification
- https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html — prefer real objects, fakes, or local test databases over mocks
- https://abseil.io/resources/swe-book/html/ch12.html — prefer state testing; interaction testing only where the interaction is the contract
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import — imported bindings are read-only live bindings; reassignment by the importer throws
- https://nodejs.org/api/test.html — `mock.module()` requires the `--experimental-test-module-mocks` CLI flag
- Field evidence 2026-08-06 (ESM CLI, `node:test`; the two ESM/tripwire rows are field-tested on top of the doc-verified mechanism): boundary test passed with `PATH=""` while a real `spawnSync("npm")` under the same emptied PATH returned ENOENT; 365/365 suite green
Loading
Loading