Skip to content

Copilot launcher preamble (ℹ NODE_OPTIONS=… saved preference) leaks into goal slugs / branch names #4376

Description

@rysweet

Bug: Copilot CLI launcher preamble leaks into goal slugs / branch names

Evidence (reproducible)

This very worktree is checked out on branch:

feat/issue-1-nodeoptions-max-old-space-size32768-saved-preferen

The nodeoptions-max-old-space-size32768-saved-preferen segment is a slugified
copy of the Copilot CLI launcher preamble:

ℹ NODE_OPTIONS=--max-old-space-size=32768 (saved preference). To change: /home/azureuser/.amplihack/config

The real title of issue #1 is "Implement the first high-quality version of
Simard…"
— it has nothing to do with NODE_OPTIONS. So a description/title
string that still contained the launcher banner was fed into slug generation.

Root cause

crate::goals::goal_slug (src/goals/types.rs:183) is a pure slugifier — it
does not strip launcher/recipe noise. Its ingestion callers pass externally
sourced descriptions straight in without first running the established
noise chokepoint recipe_output::extract::strip_recipe_noise:

  • src/ooda_loop/curate.rs:169goal_slug(&decision.description)
  • src/ooda_loop/curate.rs:219goal_slug(&item.description)
  • src/ooda_loop/cycle.rs:534goal_slug(&directive.title)

When the upstream meeting/handoff extraction misses the banner, it survives into
decision.description, then into the goal ID, then into the
feat/issue-N-<goal-slug> branch/worktree name.

The codebase already has a documented single chokepoint for exactly this class
of noise (strip_recipe_noise / is_copilot_launcher_line, issues #2496,
#2570, #2426, #3181) — this path just isn't routed through it.

Secondary gap

is_copilot_launcher_line (src/recipe_output/extract.rs:178) only matches the
ℹ NODE_OPTIONS= line when it ALSO contains (saved preference). A real
launcher variant without that marker exists in-tree
(src/tests_base_type_copilot.rs:531: ℹ NODE_OPTIONS=--max-old-space-size=8192),
so the shared chokepoint misses it while the per-path handler in
src/base_type_copilot/transcript.rs:208 catches it. These two handlers
disagree.

Proposed fix direction (needs a design decision, hence an issue not a blind edit)

  1. Route externally sourced goal titles/descriptions through
    strip_recipe_noise (or a title-specific variant) before goal_slug, and
    decide the contract when the entire title is noise (reject / fall back to a
    placeholder id) so slugs never become empty.
  2. Reconcile is_copilot_launcher_line with the base_type_copilot handler so a
    ℹ NODE_OPTIONS= line is dropped regardless of the (saved preference)
    marker.
  3. Add a regression test asserting a banner-contaminated description slugifies to
    a clean id (no nodeoptions/max-old-space-size tokens).

Context

Surfaced while executing an ecosystem OODA step whose task input itself arrived
contaminated with this same banner. No code changed on this branch; filing so the
correct extraction chokepoint is fixed with maintainer context rather than a
speculative unilateral edit to the deliberately-conservative filter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions