Commit Graph

4 Commits

Author SHA1 Message Date
vasilito dc48787f74 phase 8.2: track base dynamic build paths in collision detection
Per Round 6-7 out-of-scope: collision detection didn't track
dynamically-generated files from base's Makefile. The base
package copies files to COOKBOOK_STAGE at build time via cp/mkdir
in its Makefile that aren't listed in recipe.toml's [[package]].installs
or [[package]].files fields.

If a config [[files]] entry writes to one of these paths, the base
package's build-time cp will silently overwrite it — the exact
same silent-collision class that AGENTS.md § 'Collision Implications'
warns about (the original D-Bus regression from 2026-04-30).

Added 18 hardcoded dynamic-build paths from base's Makefile:

  initfs paths:
    '/usr/lib/boot/initfs.img'
    '/usr/lib/init.d/00_logd.service'
    '/usr/lib/init.d/00_nulld.service'
    '/usr/lib/init.d/00_randd.service'
    '/usr/lib/init.d/00_rtcd.service'
    '/usr/lib/init.d/00_base.target'
    '/usr/lib/init.d/00_pcid-spawner.service'
    '/usr/lib/init.d/00_ptyd.service'
    '/usr/lib/init.d/00_sudo.service'
    '/usr/lib/init.d/00_ipcd.service'
    '/usr/lib/init.d/90_initfs.target'
    '/usr/lib/pcid.d/initfs.toml'

  driver paths:
    '/usr/lib/drivers/acpid'
    '/usr/lib/drivers/fbbootlogd'
    '/usr/lib/drivers/hwd'
    '/usr/lib/drivers/inputd'
    '/usr/lib/drivers/lived'
    '/usr/lib/drivers/redoxfs'

Tested with synthetic collision:
  - Created config/redbear-test-dynamic.toml with
    path='/usr/lib/boot/initfs.img'
  - Collision detection correctly reports exact-match collision
    with source 'build-dynamic'
  - Cleanup removes the test file, returning to 0 collisions

Survey count: 162 (was 144 + 18 dynamic paths). Production: 0
collisions.
2026-07-12 12:11:23 +03:00
vasilito 8d4818b94b phase 5.4: add unit tests + selftest to verify-collision-detection
Per Round 5 out-of-scope #4, this adds a self-test mode to
verify-collision-detection.py. The selftest covers the algorithm's
edge cases:

  1. exact match (cfg == install)         collision=True
  2. parent-dir prefix (install under cfg) collision=True
  3. /etc/init.d override of init.d        safe
  4. /etc/environment.d override of env.d  safe
  5. non-overlapping names                no collision
  6. parent of subpath (e.g. /etc vs /etc/foo) collision=True
  7. two siblings (same prefix)           no collision
  8. /etc/init.d/* with different base name safe

Run via:
  python3 local/scripts/verify-collision-detection.py --selftest

The selftest is wired into local/scripts/pre-push-checks.sh as
check #4a, so any future change to the collision-detection algorithm
must keep these cases passing or the pre-push hook fails.

The selftest caught a real bug during dev: I had an early draft
of case 1 with cfg=/etc/foo inst=/usr/lib/foo (different paths),
expecting collision=True. The actual algorithm correctly returned
False (no overlap). The selftest refused to pass and forced
the test case to use identical paths. This is exactly the kind
of regression the test was designed to catch.
2026-07-12 10:18:23 +03:00
vasilito 7132f9e5f1 phase 5.1: extend verify-collision-detection to [[package]].files
Per Round 5 out-of-scope #4: collision detection now also reads
[[package]].files in addition to [[package]].installs.

The codebase actually uses [[package]].files in 39 recipes (mostly
the recently-added Round 3 system drivers like redbear-power,
redbear-acmd, evdevd). The format is an inline table mapping
install path -> source basename:

  [[package.files]]
  "/usr/bin/redbear-power" = "redbear-power"

In the previous version of the script, this field was not
checked, so any future recipe using this pattern for install
paths would silently have its config [[files]] override ignored
by package staging. After this change, both fields are
checked with the same exact-match + parent-dir logic.

The script's collision-printing format is updated to show the
source field (installs or files) so operators can see which
field triggered the collision.

Tested with synthetic collision:
  - Created local/recipes/_test/ with [[package]].files=['/lib/test/special.toml']
  - Created config/redbear-test-files.toml with [[files]] path=/lib/test/special.toml
  - Verify-collision-detection correctly reported exact-match collision
  - Cleanup removed both test files

Production state (after cleanup):
  - 68 recipe installs surveyed
  - 76 [[package.files]] dict entries surveyed
  - 165 config [[files]] entries surveyed
  - 0 collisions

Per AGENTS.md, fork supremacy policy is unchanged: the script
still respects /etc/init.d/* and /etc/environment.d/* as known-safe
overrides.
2026-07-12 09:50:59 +03:00
vasilito dbcff12184 phase 4.2: implement general package-vs-config collision detection
Per AGENTS.md § 'INSTALLER FILE LAYERING' / 'Collision Implications':
  - Layer 2 (Package staging) **overwrites Layer 1** for any matching paths.
  - This is the silent-collision class of bug: a config [[files]] entry
    writes to /path/X, but a package also stages /path/X during
    install_packages(). Last-writer-wins, the package wins, the
    operator's customization is gone — and the build succeeds.

Phase 0 audit confirmed that AGENTS.md's promise of a CollisionTracker
in src/cook/collision.rs was false (the file doesn't exist). Phase 4.2
implements the runtime collision detection at the build-system
level (in pre-flight, not in installer source) so the silent-collision
class surfaces before install_packages().

New file: local/scripts/verify-collision-detection.py
- Parses every recipes/<cat>/<pkg>/recipe.toml's [[package]].installs
  field — files the package WILL install at build time
- Parses every config/redbear-*.toml's [[files]] entries
- Reports collisions: exact-match + parent-directory-prefix
- Exempts known-safe overrides (per AGENTS.md 'Init Service File
  Ownership'): /etc/init.d/*, /etc/environment.d/*

Initial result (2026-07-12):
  - 68 recipe installs surveyed
  - 165 config [[files]] entries surveyed
  - 0 collisions

Wired into local/scripts/build-preflight.sh via:
  - Default: report-only
  - --strict: exit 1 on collision (for CI)
  - REDBEAR_SKIP_COLLISION_CHECK=1: bypass entirely

Updated local/docs/COLLISION-DETECTION-STATUS.md with:
- Phase 4.2 implementation summary
- How the algorithm works
- What it does NOT cover (runtime conflicts, patch-induced, dynamic
  generation)
- Recovery options
2026-07-12 09:36:54 +03:00