5 Commits

Author SHA1 Message Date
Laszlo Nagy 2425630e9c fix(intercept): address second-round proof-read findings
Follow-up to bee907e. Narrow-scope corrections from an independent
second review of the masquerade-wrapper handling.

Behaviour

- filter_out_paths now normalises trailing path separators on both
  sides before comparing, so a PATH entry written as
  '/usr/lib64/ccache/' (with trailing slash) still matches an
  excluded dir derived from PathBuf::parent() (which never has one).
  Without this, the defensive "already excluded but returned again"
  branch fired and Bear gave up with a bogus "no real compiler past
  masquerade dir" warning even when a real compiler was reachable.
  New unit test filter_out_paths_matches_across_trailing_separator
  protects the case.

- integration-tests/build.rs now also scans
  /opt/homebrew/opt/ccache/libexec and /usr/local/opt/ccache/libexec,
  the default Homebrew ccache masquerade locations on Apple Silicon
  and Intel macOS. Without these, a Homebrew-equipped developer saw
  host_has_ccache_masquerade silently stay unset and the recursion
  integration test skipped.

Tests

- wrapper_mode_survives_masquerade_wrapper_in_path now also asserts
  that the recorded compiler path is absolute, matching the
  acceptance criterion's "absolute path to the real compiler"
  language.

Docs

- Requirement: struck the "nested compiler invocations ... .bear/
  stays at the front of the child's PATH" bullet from the
  acceptance criteria. That guarantee belongs to
  interception-wrapper-mechanism and is preserved here by not
  modifying the child's PATH; the previous wording implied this
  requirement owns a guarantee it only protects.
- Requirement: clarified that the PATH-scan path
  (compiler_candidates) filters per-file rather than per-directory.
  Distro-shipped masquerade dirs contain only symlinks, so the
  behaviours coincide in practice; the wording now matches what
  the code does.
- Requirement: added a "detection is symlink-based" entry under
  non-functional constraints. Masquerade wrappers installed as
  shell scripts or hard copies are out of scope and will not be
  detected; if a non-symlink masquerade appears in the wild,
  extend detection rather than widen the classification helper to
  read file contents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:22:35 +00:00
Laszlo Nagy bee907ea2e fix(intercept): apply review findings for masquerade-wrapper handling
Follow-up to d7305ba. Addresses issues raised in an independent review
of the wrapper-recursion fix.

Behaviour

- resolve_program_path now also filters masquerade wrappers when the
  supplied CC/CXX/... value is an absolute path or a relative path
  with a directory component. Before, CC=/usr/lib64/ccache/gcc
  (or CC=./ccache-dir/gcc) bypassed the filter entirely and Bear
  would store the ccache symlink in the wrapper config, recreating
  the exact loop the filter is meant to prevent. When the supplied
  path IS a masquerade wrapper, resolution falls back to the
  basename via PATH past the masquerade dir; if no real compiler is
  found, resolution returns None and the caller logs that it is
  skipping the compiler.

- resolve_past_masquerade_wrappers now emits a WARN when PATH is
  exhausted after excluding one or more masquerade dirs, naming the
  compiler and the dir(s). The acceptance criterion about logging
  "names the compiler and the detected wrapper" was otherwise only
  served by the generic "could not resolve to an executable on PATH"
  warning.

Performance

- is_masquerade_wrapper short-circuits with symlink_metadata before
  calling canonicalize. Without this, every executable in every
  PATH directory paid a real canonicalize syscall during PATH
  discovery; masquerade targets are always symlinks, so the
  non-symlink path is common and should be cheap.

Tests

- New unit test resolve_program_path_falls_back_past_masquerade_for_absolute_cc
  covers CC=/abs/path/to/masquerade/gcc with a real compiler
  elsewhere on PATH.
- The integration test wrapper_mode_survives_masquerade_wrapper_in_path
  replaces the .contains(".bear") substring check with a
  Path::starts_with on the exact wrapper directory -- the loose
  substring check could false-positive on any path that happens to
  contain ".bear".

Docs

- Requirement wording about detection rewritten: drops the
  prescriptive "read_link iteratively, not canonicalize" language
  (the /usr/bin/gcc -> gcc-13 rationale applied to compiler
  registration, not masquerade detection), and spells out that
  canonicalize must NOT be used for the registration path.
- Striked the "nested compiler invocation" Testing scenario from
  this requirement -- that guarantee belongs to
  interception-wrapper-mechanism and is preserved here by not
  modifying the child's PATH. Added a short note pointing there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:57:18 +00:00
Laszlo Nagy d7305bac20 fix(intercept): resolve past masquerade compiler wrappers in wrapper mode
Wrapper mode previously stored whatever `which(gcc)` returned as the
"real compiler" for each wrapper. On distributions with a ccache
masquerade in PATH (Fedora/Arch/Gentoo by default), that is the
ccache symlink, so the wrapper's child process was ccache. ccache
then searched PATH for gcc, skipping only symlinks to itself; Bear's
hard-linked wrapper in `.bear/` passed the self-check and was
re-executed, producing an infinite loop.

environment.rs now detects masquerade wrappers at discovery time by
canonicalising candidate paths and checking the target's basename
against a fixed set (ccache, distcc, icecc, colorgcc, buildcache).
The containing directory is stripped from the lookup PATH and
resolution retries, so the wrapper config always names the real
compiler. Both the CC-env and PATH-scan discovery paths are covered.

Other changes in the same fix:
- Requirement reworked around "resolve past masquerade wrappers at
  discovery time"; the original CCACHE_COMPILER proposal is
  documented as rejected, verified empirically to reproduce the
  hang via CCACHE_COMPILER pointing at the ccache symlink.
- Nine new unit tests cover detection, filtering, and the
  no-real-compiler fallback.
- New integration test wrapper_mode_survives_masquerade_wrapper_in_path
  prepends the masquerade dir to its own child PATH so the
  recursion scenario is exercised regardless of host PATH, while
  keeping other tests ccache-free.
- build.rs scans well-known masquerade locations (/usr/lib/ccache,
  /usr/lib64/ccache, /usr/libexec/ccache), exposes the found dir
  via CCACHE_MASQUERADE_DIR, and sets cfg(host_has_ccache_masquerade)
  to gate the new test.
- The manual ccache_free_path_and_compiler workaround in the
  wrapper-mode tests is gone; the tests now run against the host's
  real PATH and also protect this requirement.
- CI: Ubuntu job runs apt-get install ccache so the masquerade dir
  exists on every PR. The job PATH is deliberately not modified --
  ccache first on PATH would inflate event counts for preload-mode
  tests that assert exact compiler-event counts.

Side effect: ccache is bypassed while Bear is observing. That
matches Bear's observe-don't-optimise stance and keeps
compile_commands.json recording the real compiler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:35:38 +00:00
Laszlo Nagy 51d1bbb48d requirements: link tests to requirements via source tags
Replace the `tests:` frontmatter list in each requirement file with a
`Requirements: <id>` tag placed directly on the protecting test(s). This
gives tests a single source of truth for the link, so renaming or deleting
a test cannot silently orphan a requirement.

- Drop `tests:` from the template and all 11 requirement files.
- Document the new tag convention in requirements/CLAUDE.md and
  integration-tests/CLAUDE.md; remove the unused `test_req_<id>_<desc>`
  naming rule.
- Tag the existing integration tests (compilation_output, config,
  exit_codes, intercept) with the requirements they protect.
- Add `requirements/check-coverage.sh`, which scans implemented
  requirements and fails when any has zero tagged tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:23:11 +00:00
Laszlo Nagy 16406858ac requirements: document output pipeline and simplify naming
Add requirement pages for the output pipeline features:
- output-append: --append flag and merge behavior
- output-atomic-write: temp file + rename pattern
- output-duplicate-detection: configurable hash-based dedup
- output-path-format: as-is/absolute/relative/canonical strategies
- output-source-directory-filter: include/exclude rules by path

Extend output-json-compilation-database with the full Clang spec
format definition, command field escaping details, and compiler
path handling.

Remove the sequential number from requirement filenames and drop
the redundant id frontmatter field. The filename itself serves as
the unique identifier for cross-references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 06:02:31 +00:00