Skip to content

fix(context): fall back to path basename for unnamed bootstrap files#48021

Closed
jnMetaCode wants to merge 1 commit into
openclaw:mainfrom
jnMetaCode:fix/bootstrap-undefined-label
Closed

fix(context): fall back to path basename for unnamed bootstrap files#48021
jnMetaCode wants to merge 1 commit into
openclaw:mainfrom
jnMetaCode:fix/bootstrap-undefined-label

Conversation

@jnMetaCode

Copy link
Copy Markdown
Contributor

Fixes #47941

Hook-injected virtual bootstrap files may omit the name field. buildBootstrapInjectionStats now falls back to path.basename(file.path) (then a "(virtual file)" placeholder) so /context breakdown never renders a literal undefined label.

Changes

  • bootstrap-budget.ts: derive name from file.name || basename(path) || "(virtual file)"
  • bootstrap-budget.test.ts: add test covering missing/empty name with a valid path

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Mar 16, 2026
@greptile-apps

greptile-apps Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a display bug where /context breakdown would render a literal undefined label for hook-injected virtual bootstrap files that omit the name field. The fix adds a graceful fallback chain in buildBootstrapInjectionStats: file.name || path.basename(pathValue) || "(virtual file)", and updates the path field in the returned stat to use the same derived name when no path is available.

  • bootstrap-budget.ts: derives name with a two-level fallback (basename then a placeholder); path now also uses the derived name as its own last-resort fallback, preventing undefined from leaking into either field.
  • bootstrap-budget.test.ts: adds a regression test for both undefined and empty-string name fields, asserting the correct basename is derived from the path.
  • Minor: the new path.basename(pathValue) call uses a different basename implementation than the injection-map builder (which uses path.posix.basename on a backslash-normalized path). Aligning them would be more consistent and robust on Windows-style paths.
  • The new test only asserts name; asserting injectedChars / truncated as well would confirm injection tracking continues to work correctly for unnamed files.

Confidence Score: 4/5

  • Safe to merge; the fix is minimal and well-targeted with no risk of regressions.
  • The change is a one-liner fallback that only activates when file.name is falsy, so it cannot break existing behaviour for files that already have a name. The only minor open items are a path.basename vs path.posix.basename style inconsistency and a slightly shallow test assertion — neither is blocking.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/bootstrap-budget.ts
Line: 153

Comment:
**`path.basename` vs `path.posix.basename` inconsistency**

The injection-map building logic (lines 138–141) normalizes backslashes before calling `path.posix.basename`:

```ts
const normalizedPath = pathValue.replace(/\\/g, "/");
const baseName = path.posix.basename(normalizedPath);
```

The new `name` derivation uses `path.basename(pathValue)` without the same normalization. On a POSIX host this is harmless because both calls treat `/` as the only separator, but if a Windows-style path (`C:\foo\bar.md`) ever reaches `buildBootstrapInjectionStats`, `path.posix.basename` (used to key the map) would return the full string while `path.basename` (on a POSIX host) would also return the full string, so no match would be found. Aligning the two usages makes the intent clearer and more robust:

```suggestion
    const name = file.name || path.posix.basename(pathValue.replace(/\\/g, "/")) || "(virtual file)";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/bootstrap-budget.test.ts
Line: 68-75

Comment:
**Test doesn't assert injection tracking for unnamed files**

The new test verifies that the derived `name` is correct, but it doesn't assert that `injectedChars` and `truncated` are also computed correctly for the unnamed files. The content matches 1:1 in both cases, so a complete assertion would confirm injection tracking still works end-to-end:

```suggestion
    const stats = buildBootstrapInjectionStats({ bootstrapFiles, injectedFiles });
    expect(stats[0]?.name).toBe("SELF_IMPROVEMENT_REMINDER.md");
    expect(stats[0]?.injectedChars).toBe("reminder text".length);
    expect(stats[0]?.truncated).toBe(false);
    expect(stats[1]?.name).toBe("NOTES.md");
    expect(stats[1]?.injectedChars).toBe("notes".length);
    expect(stats[1]?.truncated).toBe(false);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 9ca1f67

Comment thread src/agents/bootstrap-budget.ts Outdated
@@ -150,9 +150,10 @@ export function buildBootstrapInjectionStats(params: {
injectedByBaseName.get(file.name);
const injectedChars = injected ? injected.length : 0;
const truncated = !file.missing && injectedChars < rawChars;
const name = file.name || path.basename(pathValue) || "(virtual file)";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path.basename vs path.posix.basename inconsistency

The injection-map building logic (lines 138–141) normalizes backslashes before calling path.posix.basename:

const normalizedPath = pathValue.replace(/\\/g, "/");
const baseName = path.posix.basename(normalizedPath);

The new name derivation uses path.basename(pathValue) without the same normalization. On a POSIX host this is harmless because both calls treat / as the only separator, but if a Windows-style path (C:\foo\bar.md) ever reaches buildBootstrapInjectionStats, path.posix.basename (used to key the map) would return the full string while path.basename (on a POSIX host) would also return the full string, so no match would be found. Aligning the two usages makes the intent clearer and more robust:

Suggested change
const name = file.name || path.basename(pathValue) || "(virtual file)";
const name = file.name || path.posix.basename(pathValue.replace(/\\/g, "/")) || "(virtual file)";
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/bootstrap-budget.ts
Line: 153

Comment:
**`path.basename` vs `path.posix.basename` inconsistency**

The injection-map building logic (lines 138–141) normalizes backslashes before calling `path.posix.basename`:

```ts
const normalizedPath = pathValue.replace(/\\/g, "/");
const baseName = path.posix.basename(normalizedPath);
```

The new `name` derivation uses `path.basename(pathValue)` without the same normalization. On a POSIX host this is harmless because both calls treat `/` as the only separator, but if a Windows-style path (`C:\foo\bar.md`) ever reaches `buildBootstrapInjectionStats`, `path.posix.basename` (used to key the map) would return the full string while `path.basename` (on a POSIX host) would also return the full string, so no match would be found. Aligning the two usages makes the intent clearer and more robust:

```suggestion
    const name = file.name || path.posix.basename(pathValue.replace(/\\/g, "/")) || "(virtual file)";
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 08bd7b7 — switched to path.posix.basename with backslash normalization to match the injection-map building logic at line 139.

Comment on lines +68 to +75
const injectedFiles = [
{ path: "/workspace/SELF_IMPROVEMENT_REMINDER.md", content: "reminder text" },
{ path: "/workspace/NOTES.md", content: "notes" },
];

const stats = buildBootstrapInjectionStats({ bootstrapFiles, injectedFiles });
expect(stats[0]?.name).toBe("SELF_IMPROVEMENT_REMINDER.md");
expect(stats[1]?.name).toBe("NOTES.md");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test doesn't assert injection tracking for unnamed files

The new test verifies that the derived name is correct, but it doesn't assert that injectedChars and truncated are also computed correctly for the unnamed files. The content matches 1:1 in both cases, so a complete assertion would confirm injection tracking still works end-to-end:

Suggested change
const injectedFiles = [
{ path: "/workspace/SELF_IMPROVEMENT_REMINDER.md", content: "reminder text" },
{ path: "/workspace/NOTES.md", content: "notes" },
];
const stats = buildBootstrapInjectionStats({ bootstrapFiles, injectedFiles });
expect(stats[0]?.name).toBe("SELF_IMPROVEMENT_REMINDER.md");
expect(stats[1]?.name).toBe("NOTES.md");
const stats = buildBootstrapInjectionStats({ bootstrapFiles, injectedFiles });
expect(stats[0]?.name).toBe("SELF_IMPROVEMENT_REMINDER.md");
expect(stats[0]?.injectedChars).toBe("reminder text".length);
expect(stats[0]?.truncated).toBe(false);
expect(stats[1]?.name).toBe("NOTES.md");
expect(stats[1]?.injectedChars).toBe("notes".length);
expect(stats[1]?.truncated).toBe(false);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/bootstrap-budget.test.ts
Line: 68-75

Comment:
**Test doesn't assert injection tracking for unnamed files**

The new test verifies that the derived `name` is correct, but it doesn't assert that `injectedChars` and `truncated` are also computed correctly for the unnamed files. The content matches 1:1 in both cases, so a complete assertion would confirm injection tracking still works end-to-end:

```suggestion
    const stats = buildBootstrapInjectionStats({ bootstrapFiles, injectedFiles });
    expect(stats[0]?.name).toBe("SELF_IMPROVEMENT_REMINDER.md");
    expect(stats[0]?.injectedChars).toBe("reminder text".length);
    expect(stats[0]?.truncated).toBe(false);
    expect(stats[1]?.name).toBe("NOTES.md");
    expect(stats[1]?.injectedChars).toBe("notes".length);
    expect(stats[1]?.truncated).toBe(false);
```

How can I resolve this? If you propose a fix, please make it concise.

…missing

Hook-injected virtual bootstrap files may omit the name field.
buildBootstrapInjectionStats now falls back to basename(path) so
the context breakdown never renders a literal "undefined" label.

Closes openclaw#47941
@jnMetaCode jnMetaCode force-pushed the fix/bootstrap-undefined-label branch from 9ca1f67 to 08bd7b7 Compare March 16, 2026 09:14
@jnMetaCode

Copy link
Copy Markdown
Contributor Author

Friendly ping — reduced my open PRs from 9 to 4 to make review easier. This fixes unnamed bootstrap files showing as undefined in the context label by falling back to path.basename. Small change, easy to review.

@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep open: current main still exposes the generic /context undefined-label path, and this PR is a focused likely-correct fix, but it still lacks contributor-provided real behavior proof and fresh exact-head validation.

Canonical path: Close this PR as superseded by #84736.

So I’m closing this here and keeping the remaining discussion on #84736.

Review details

Best possible solution:

Close this PR as superseded by #84736.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: path-only hook entries can survive sanitization, buildBootstrapInjectionStats returns raw file.name, and /context renders that name directly. I did not run a live /context scenario because this review is read-only.

Is this the best way to solve the issue?

Yes. Normalizing the bootstrap report label in the stat builder is the narrowest maintainable generic fix; the merged Codex-specific fix does not replace this path.

Security review:

Security review cleared: The diff only changes context report label derivation and a colocated unit test, with no dependency, CI, install, release, secret, or package-execution surface touched.

What I checked:

  • linked superseding PR: fix(codex): guard path-only bootstrap files [AI-assisted] #84736 (fix(codex): guard path-only bootstrap files [AI-assisted]) is merged at 2026-05-21T01:55:30Z.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • gumadeiras: GitHub commit metadata ties this handle to the commit that introduced the bootstrap budget/reporting helper and tests containing the current raw stat shape. (role: feature-history contributor; confidence: high; commits: e4b4486a96a9; files: src/agents/bootstrap-budget.ts, src/agents/bootstrap-budget.test.ts, src/agents/system-prompt-report.ts)
  • steipete: Commit history ties this handle to the /context prompt breakdown renderer and the bootstrap hook surface that make hook-mutated bootstrap files visible in context reports. (role: feature-history and adjacent contributor; confidence: high; commits: bcde09ae91ab, ad3c12a43a70; files: src/auto-reply/reply/commands-context-report.ts, src/agents/bootstrap-hooks.ts, src/hooks/internal-hooks.ts)
  • scoootscooob: Recent commits changed bootstrap warning/reporting and prompt-cache behavior in the same helper and tests. (role: recent bootstrap-budget contributor; confidence: medium; commits: 80a2af1d656e, 4e912bffd899; files: src/agents/bootstrap-budget.ts, src/agents/bootstrap-budget.test.ts)
  • Takhoffman: Recent history shows this handle changing session-selection behavior in the same /context report renderer. (role: recent context-report contributor; confidence: medium; commits: 07232b90c9b6; files: src/auto-reply/reply/commands-context-report.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 25e489395ab0.

@clawsweeper clawsweeper Bot added the P2 Normal backlog priority with limited blast radius. label May 17, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 17, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels May 18, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Context breakdown can label virtual bootstrap files as 'undefined' instead of falling back to path/basename

1 participant