workflow-fix: harden _breadcrumb_fields against prose-glued/empty key=value tokens
Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Auto-filed by the workflow-fix-on-bug protocol from a workflow-fix candidate raised on task #1689 (emitting agent: issue-orchestrator).
Goal
Harden _breadcrumb_fields token parsing so prose-adjacent tokens (glued
trailing punctuation, bare key= substrings in prose) cannot misroute
executing_followup_label to the queue-head fallback.
Workflow gap
- Bug observed: stage-dispatch breadcrumb label token with glued trailing
punctuation (
label=wider-lambda-ceilings:) or a proselabel=substring re-binding the field to empty makesexecuting_followup_labelfall back to the queue-head unrun label, misidentifying the executing round. - Why it is a workflow gap:
_breadcrumb_fieldswhitespace-splitskey=valuetokens with last-wins semantics and no value hygiene, so (a) a breadcrumb author who ends the token run with a prose colon (label=wider-lambda-ceilings: detached ..., task #1689 event v110, 2026-07-29T08:28:32Z) produces a label that matches no group, and (b) any prose containing a barekey=sequence ("its label= token was prose-glued", #1689 event v115) re-binds the field to EMPTY, silently discarding the correct earlier token. Both makeexecuting_followup_labelfall back to the DISPATCHABLE queue head — on #1689 that was the user-chat labelderived-vs-free-answer-map, NOT the executing roundwider-lambda-ceilings. Downstream consumers of the helper (autonomous_session_watch._post_followup_run_marker's on-behalf run-marker post; the Step 9b step-4 completion-marker label derivation;tick_triage.py's pid-probe crumb read) would then close / probe the WRONG label — a never-run user-chat round would be marked run. - Confidence (emitter): high (live repro on #1689 events v110/v115/v116,
2026-07-29: v110 parsed
label='wider-lambda-ceilings:'→ no group match; v115 parsedlabel=''via prose re-bind; the equals-free v116 re-post resolved correctly). - verified-at-filing:
grep -n "def _breadcrumb_fields" src/explore_persona_space/task_workflow.py→ 1 hit (line 1907) in the named target; consumers grepgrep -rln "_breadcrumb_fields" src/ scripts/→src/explore_persona_space/task_workflow.py+scripts/tick_triage.py(consumer via the shared helper, no independent parser). Live-repro parse values reproduced at filing time viatask_workflow._breadcrumb_fieldson #1689 events v110 ('wider-lambda-ceilings:') and v115 ('') (2026-07-29).
Proposed change (candidate diff sketch — refine in planning)
# src/explore_persona_space/task_workflow.py, _breadcrumb_fields (line ~1907)
+ _BREADCRUMB_VALUE_TRAILING_PUNCT = ":;,."
...
- fields[key] = value
+ value = value.rstrip(_BREADCRUMB_VALUE_TRAILING_PUNCT)
+ if value and key not in fields:
+ # first NON-EMPTY wins: a later bare `key=` prose substring can
+ # no longer re-bind an already-parsed field to empty/garbage
+ fields[key] = value
Planner note: weigh first-non-empty-wins vs last-wins semantics against
existing pinned tests (tests/test_task_workflow*.py); trailing-punct strip
must not eat legitimate values (labels are kebab-slugs, paths in log= /
harvest= never end in :;,. by convention — state this in the plan).
Add regression tests reproducing both #1689 shapes (glued colon; prose
label= re-bind).
Scope / surfaces
- Primary target:
src/explore_persona_space/task_workflow.py - Grep the workflow surface for the pattern before editing
(
grep -rn "_breadcrumb_fields" src/ scripts/ .claude/) and update every hit; list them in the plan. (scripts/tick_triage.pyconsumes the shared helper — behavior change flows through automatically; verify its pid-probe arm against the hardened parser.)
Constraints / invariants
- Workflow-surface only — never experiment code,
configs/, ortasks/. scripts/workflow_lint.py --check-askspasses; ruff on touched files passes; ifworkflow.yamlorCLAUDE.mdchange, they stay consistent with the rule file.- This session runs under
EPM_WORKFLOW_FIX_SESSION=1and carries aworkflow_fix_target:Provenance line — it MUST NOT auto-route any of its own subagents' workflow-fix candidates (recursion guard,.claude/rules/workflow-fix-on-bug.md§ Recursion guard).
Provenance
- workflow_fix_target: src/explore_persona_space/task_workflow.py
- fingerprint: 2331b34907b3
target_file: src/explore_persona_space/task_workflow.py bug_observed: stage-dispatch breadcrumb label token with glued trailing punctuation (label=wider-lambda-ceilings:) or a prose 'label=' substring re-binding the field to empty makes executing_followup_label fall back to the queue-head unrun label, misidentifying the executing round why_workflow_gap: _breadcrumb_fields whitespace-splits key=value tokens last-wins with no value hygiene, so prose-glued punctuation or bare key= prose substrings silently misroute the round-label resolution every consumer (watcher on-behalf run-marker, step-4 completion marker, tick_triage pid probe) depends on proposed_change: harden _breadcrumb_fields token parsing: strip trailing punctuation from values and ignore empty-value rebinds so prose-adjacent tokens cannot misroute executing_followup_label diff_sketch: |
- value = value.rstrip(":;,.")
- if value and key not in fields:
-
fields[key] = value
- fields[key] = value confidence: high related_task: #1689