Motivation
The diagnosis in #1204 correctly reconstructed the failure mechanism (a ~/.claude.json credential mapping tilde-expanded against the guest daemon's HOME=/, EROFS on the read-only rootfs) but misattributed where claude-code entered the failing tasks' package closures: it guessed the applied loadout, when the actual source was the project's default dev profile (follow-up).
The misattribution was structural, not a reasoning error: the bundle carries user-scope config (config/config.toml, user_policy.toml, config/loadouts/*.toml) but nothing project-scope and no record of the resolved package closure. The only claude-code sighting available in the bundle was an unapplied loadout file. Every fact needed for the correct attribution sat in the project directory min bug was run from.
Proposed enhancements
1. Project-scope collector
crates/minimal/src/diag/collect.rs gains a collector that discovers the invoking project via mfile from the bundle's cwd and adds:
project/minimal.toml.redacted — through the existing add_redacted_toml path (values redacted; keys, package names, task names, and [defaults] survive, which is what closure attribution needs).
project/profiles.json — profiles are Nickel, so redact_toml cannot process them; instead of a Nickel redactor, evaluate them through the decode crate and emit a names-only JSON of profile → resolved package list (inheritance flattened). Fail-closed by construction: no config values leave the machine. A decode failure is recorded as a skip — itself diagnostic.
When no project exists at the cwd, the collector records a skip ("no project at <cwd>") per the manifest's absence-is-explainable contract; the diag portal can then prompt the reporter to re-run from the failing project.
Collectors must not mutate what they read; decode evaluation is read-only, and the existing collect_step! timeout bounds an evaluation that hangs on a broken config.
2. Effective-loadout resolution record
"Applying loadouts: default" was ambiguous in the bundle because nothing showed what default resolves to. Emit config/loadouts-resolved.json: each loadout name that would apply, its source (built-in vs user file path, via the shadowing logic in crates/minimal/src/loadouts.rs), and its package list. For #1204 this would have stated outright that default is the built-in from #1128 with zero packages — eliminating the wrong suspect, not just adding the right one.
3. Mapping provenance in task-exec logs
The task-exec path logs no resolved fs_mappings (only the session path does, post credential-filter), so the ~ → / expansion in #1204 had to be established entirely by code reading. Log the resolved mapping list at exec setup — pre- and post-expansion path plus the contributing package. Guest logs already reach the bundle via the nested daemon bundle, so no collector change is needed. This pairs with #1204's existing recommendation that the EROFS error name the contributing package and the unexpanded path.
Scope note
Items 1–2 amend the diagnostics spec series (docs/specs/10-spec-diagnostics/ has no project-scope collection — unaddressed rather than deliberately excluded) and belong in a new demoable unit. Item 3 is a small daemon logging change independent of the spec.
Items 1 and 2 alone would have converted #1204's closure attribution from "medium confidence, wrong" to "high confidence, correct".
Motivation
The diagnosis in #1204 correctly reconstructed the failure mechanism (a
~/.claude.jsoncredential mapping tilde-expanded against the guest daemon'sHOME=/, EROFS on the read-only rootfs) but misattributed whereclaude-codeentered the failing tasks' package closures: it guessed the applied loadout, when the actual source was the project's defaultdevprofile (follow-up).The misattribution was structural, not a reasoning error: the bundle carries user-scope config (
config/config.toml,user_policy.toml,config/loadouts/*.toml) but nothing project-scope and no record of the resolved package closure. The onlyclaude-codesighting available in the bundle was an unapplied loadout file. Every fact needed for the correct attribution sat in the project directorymin bugwas run from.Proposed enhancements
1. Project-scope collector
crates/minimal/src/diag/collect.rsgains a collector that discovers the invoking project viamfilefrom the bundle's cwd and adds:project/minimal.toml.redacted— through the existingadd_redacted_tomlpath (values redacted; keys, package names, task names, and[defaults]survive, which is what closure attribution needs).project/profiles.json— profiles are Nickel, soredact_tomlcannot process them; instead of a Nickel redactor, evaluate them through thedecodecrate and emit a names-only JSON of profile → resolved package list (inheritance flattened). Fail-closed by construction: no config values leave the machine. A decode failure is recorded as a skip — itself diagnostic.When no project exists at the cwd, the collector records a skip ("no project at
<cwd>") per the manifest's absence-is-explainable contract; the diag portal can then prompt the reporter to re-run from the failing project.Collectors must not mutate what they read; decode evaluation is read-only, and the existing
collect_step!timeout bounds an evaluation that hangs on a broken config.2. Effective-loadout resolution record
"Applying loadouts: default" was ambiguous in the bundle because nothing showed what
defaultresolves to. Emitconfig/loadouts-resolved.json: each loadout name that would apply, its source (built-in vs user file path, via the shadowing logic incrates/minimal/src/loadouts.rs), and its package list. For #1204 this would have stated outright thatdefaultis the built-in from #1128 with zero packages — eliminating the wrong suspect, not just adding the right one.3. Mapping provenance in task-exec logs
The task-exec path logs no resolved
fs_mappings(only the session path does, post credential-filter), so the~ → /expansion in #1204 had to be established entirely by code reading. Log the resolved mapping list at exec setup — pre- and post-expansion path plus the contributing package. Guest logs already reach the bundle via the nested daemon bundle, so no collector change is needed. This pairs with #1204's existing recommendation that the EROFS error name the contributing package and the unexpanded path.Scope note
Items 1–2 amend the diagnostics spec series (
docs/specs/10-spec-diagnostics/has no project-scope collection — unaddressed rather than deliberately excluded) and belong in a new demoable unit. Item 3 is a small daemon logging change independent of the spec.Items 1 and 2 alone would have converted #1204's closure attribution from "medium confidence, wrong" to "high confidence, correct".