Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ All notable changes to this project will be documented in this file.

---

## [0.30.3] - 2026-02-12

### Fixed (0.30.3)

- **Backlog refine writeback parsing for ADO/GitHub**
- `specfact backlog refine --write` now parses structured refinement output (markdown headings and label-style fields like `Description:`, `Acceptance Criteria:`, `Story Points:`, `Business Value:`, `Priority:`) into canonical fields before adapter writeback.
- ADO writeback now avoids writing labeled refinement blocks verbatim into description and instead updates mapped fields with split canonical values.
- GitHub writeback now preserves canonical field updates even when refined bodies include structured headings that do not explicitly include all core field sections.
- **Refine command maintainability**
- Decomposed `backlog refine` orchestration into focused helper methods (stdin refinement capture, update-field construction, writeback, optional OpenSpec comment) to reduce top-level command complexity while keeping behavior parity.

### Changed (0.30.3)

- **Version**: Bumped to `0.30.3` (patch).

---

## [0.30.2] - 2026-02-11

### Fixed (0.30.2)
Expand Down
4 changes: 4 additions & 0 deletions docs/guides/backlog-refinement.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ This helps copilot identify missing elements that need to be added during refine
- Records `refinement_timestamp`
- Updates template detection metadata
- **Preserves all other fields** (assignees, tags, state, priority, due_date, story_points, etc.)
- Parses structured refinement output into canonical fields before writeback:
- accepts markdown-heading sections and label-style sections (for example `Description:`, `Acceptance Criteria:`, `Story Points:`)
- maps ADO description/acceptance criteria/metrics to separate provider fields
- avoids writing prompt label blocks verbatim into ADO description

**Field Preservation Policy**:

Expand Down
4 changes: 4 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -3697,6 +3697,10 @@ specfact backlog refine <ADAPTER> [OPTIONS]

- `--preview` / `--no-preview` - Preview mode: show what will be written without updating backlog (default: `--preview`)
- `--write` - Write mode: explicitly opt-in to update remote backlog (requires `--write` flag)
- During `--write`, structured refinement output is parsed into canonical fields before adapter updates.
- Supports markdown headings and label-style sections (for example `Description:`, `Acceptance Criteria:`, `Story Points:`).
- ADO updates mapped fields separately (description, acceptance criteria, metrics) instead of writing label blocks verbatim to description.
- GitHub keeps field updates consistent even when refined body contains headings that omit some core field sections.

**Definition of Ready (DoR):**

Expand Down
1 change: 1 addition & 0 deletions openspec/CHANGE_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ All entries in the table below are pending implementation.
|--------|-------|---------------|----------|------------|
| backlog-core | 01 | backlog-core-01-dependency-analysis-commands | [#116](https://github.com/nold-ai/specfact-cli/issues/116) | β€” |
| backlog-core | 02 | backlog-core-02-interactive-issue-creation | [#173](https://github.com/nold-ai/specfact-cli/issues/173) | #116 (optional: #176, #177) |
| backlog-core | 03 | backlog-core-03-refine-writeback-field-splitting | #225 | β€” |

### backlog-scrum

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Change Validation Report: backlog-core-03-refine-writeback-field-splitting

**Validation Date**: 2026-02-11T14:31:00+01:00
**Change Proposal**: [proposal.md](./proposal.md)
**Validation Method**: Local static dependency scan + OpenSpec strict validation

## Executive Summary

- Breaking Changes: 0 detected
- Dependent Files Reviewed: 4
- Impact Level: Low
- Validation Result: Pass
- User Decision: Proceed with implementation

## Dependency and Interface Analysis

Reviewed writeback path and parser touch points:

- `src/specfact_cli/modules/backlog/src/commands.py`
- `src/specfact_cli/adapters/ado.py`
- `src/specfact_cli/adapters/github.py`
- `tests/unit/commands/test_backlog_commands.py`
- `tests/unit/adapters/test_github_backlog_adapter.py`

No public CLI signature changes or adapter method signature changes were introduced by this change.

## Impact Assessment

- **Code Impact**: Refinement write path now parses structured response content before writeback.
- **Test Impact**: Added regression coverage for label-style parsing and GitHub fallback behavior.
- **Documentation Impact**: Potential wording updates for refine writeback behavior (tasked in `tasks.md`).
- **Release Impact**: Patch-level bugfix.

## Format Validation

- **proposal.md Format**: Pass
- **tasks.md Format**: Pass
- **spec delta Format**: Pass (Given/When/Then scenarios)
- **Config.yaml Compliance**: Pass

## OpenSpec Validation

- **Status**: Pass
- **Validation Command**: `openspec validate backlog-core-03-refine-writeback-field-splitting --strict`
- **Result**: `Change 'backlog-core-03-refine-writeback-field-splitting' is valid`
- **Notes**: Non-blocking telemetry network errors from PostHog occurred after validation in restricted network environment.

## Refactor Follow-up Validation (2026-02-11)

- `hatch run type-check`: Pass (`0 errors`) after decomposing `refine` command helper flow.
- `hatch test -- tests/unit/commands/test_backlog_commands.py tests/unit/adapters/test_github_backlog_adapter.py tests/unit/adapters/test_ado_backlog_adapter.py -v`: Pass (`64 passed`).
- `hatch run format`: Pass.

## Review Follow-up Validation (2026-02-12)

- `openspec validate backlog-core-03-refine-writeback-field-splitting --strict`: Pass.
- `hatch test -- tests/unit/commands/test_backlog_commands.py -k TestParseRefinementOutputFields -v`: Pass (`3 passed`).
- `hatch test -- tests/unit/adapters/test_ado_backlog_adapter.py tests/unit/adapters/test_github_backlog_adapter.py -v`: Pass (`35 passed`).
- `hatch run type-check`: Pass (`0 errors`, warnings unchanged).
- `hatch run format`: Pass.

## Next Steps

1. Complete implementation and tests per `tasks.md`.
2. Run quality gates.
3. Update version/changelog and docs if needed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# TDD Evidence: backlog-core-03-refine-writeback-field-splitting

## Pre-Implementation Failing Run

- **Timestamp**: 2026-02-11T14:30:38+01:00
- **Command**: `hatch test -- tests/unit/commands/test_backlog_commands.py -k parse_refinement_output_fields -v`
- **Result**: Failed (collection error)
- **Failure Summary**:
- `ImportError: cannot import name '_parse_refinement_output_fields'`
- New parser behavior is specified in tests but not implemented yet.

## Post-Implementation Passing Run

- **Timestamp**: 2026-02-11T14:32:52+01:00
- **Command**: `hatch test -- tests/unit/commands/test_backlog_commands.py tests/unit/adapters/test_github_backlog_adapter.py -v`
- **Result**: Passed
- **Summary**:
- `41 passed`
- Includes regression coverage for:
- label-style refinement field parsing
- markdown heading refinement parsing
- GitHub writeback fallback when structured body lacks core field headings.

## Review Follow-up: Heading-Style Narrative Regression

### Pre-Implementation Failing Run

- **Timestamp**: 2026-02-12T10:22:00+01:00
- **Command**: `hatch test -- tests/unit/commands/test_backlog_commands.py -k heading_style_notes_and_dependencies -v`
- **Result**: Failed
- **Failure Summary**:
- `test_preserves_heading_style_notes_and_dependencies_in_body_markdown` failed.
- Parsed `body_markdown` contained only description (`User-facing summary.`) and dropped heading-style `## Notes` section.

### Post-Implementation Passing Run

- **Timestamp**: 2026-02-12T10:24:00+01:00
- **Command**: `hatch test -- tests/unit/commands/test_backlog_commands.py -k TestParseRefinementOutputFields -v`
- **Result**: Passed
- **Summary**:
- `3 passed`
- Includes heading-style preservation regression test for `## Notes` and `## Dependencies`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Change: Backlog Refine Writeback Field Splitting for ADO/GitHub

## Why


`specfact backlog refine --write` currently applies the raw copilot response as `body_markdown` and does not parse structured refinement output back into canonical fields before adapter writeback. For Azure DevOps this causes `System.Description` to receive a verbatim payload containing labels like `Description`, `Acceptance Criteria`, `Story Points`, `Business Value`, `Priority`, `Area Path`, and provider markers instead of updating separate fields. GitHub can exhibit the same issue when copilot output uses label-style sections instead of markdown headings.

This breaks the provider-aware contract implied by refinement prompts and produces low-quality remote item updates.

## What Changes


- **MODIFY**: Backlog refine write path to parse structured refinement content into canonical fields (`description`, `acceptance_criteria`, `story_points`, `business_value`, `priority`, `work_item_type`) before writeback.
- **MODIFY**: Normalize label-style refinement output to canonical markdown sections so both ADO and GitHub writeback paths behave deterministically.
- **MODIFY**: ADO/GitHub writeback behavior to prefer parsed refined values over stale pre-refinement values when `--write` is used.
- **MODIFY**: Refactor `backlog refine` orchestration in `commands.py` into smaller helper methods (initialization, export/import handling, writeback/comment flow) to reduce command-function complexity and improve readability.
- **ADD**: Regression tests for ADO and GitHub writeback from label-style refinement output.

## Capabilities
- **backlog-refinement**: Provider-aware parsing and canonical field splitting for `specfact backlog refine --write`.

---

## Source Tracking

<!-- source_repo: nold-ai/specfact-cli -->
- **GitHub Issue**: #225
- **Issue URL**: <https://github.com/nold-ai/specfact-cli/issues/225>
- **Last Synced Status**: proposed
- **Sanitized**: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## MODIFIED Requirements

### Requirement: AI Refinement Writeback Preserves Provider Field Semantics

The system SHALL parse structured refinement output into canonical fields before provider writeback so provider-specific fields are updated correctly rather than storing prompt labels verbatim in description/body.

#### Scenario: ADO writeback splits label-style refined output into canonical fields

- **GIVEN** a user runs `specfact backlog refine ado --write`
- **AND** the refined output uses label-style sections such as `Description:`, `Acceptance Criteria:`, `Story Points:`, `Business Value:`, and `Priority:`
- **WHEN** the refinement is accepted
- **THEN** SpecFact parses those sections into canonical fields
- **AND** writes `description` content to ADO description field
- **AND** writes `acceptance_criteria`, `story_points`, `business_value`, and `priority` to their mapped ADO fields when present
- **AND** does not write the entire labeled structure verbatim as ADO description.

#### Scenario: GitHub writeback normalizes label-style refined output to structured markdown

- **GIVEN** a user runs `specfact backlog refine github --write`
- **AND** the refined output uses label-style sections rather than markdown headings
- **WHEN** the refinement is accepted
- **THEN** SpecFact normalizes the output into canonical markdown sections
- **AND** updates issue body and related canonical fields consistently
- **AND** avoids duplicating or flattening structured fields into a single unparsed description block.

#### Scenario: Heading-style narrative sections are preserved during writeback parsing

- **GIVEN** a user runs `specfact backlog refine <provider> --write`
- **AND** the refined output uses markdown headings like `## Notes` and `## Dependencies`
- **WHEN** the refinement output is parsed into canonical fields for writeback
- **THEN** `body_markdown` keeps those narrative sections
- **AND** canonical numeric/provider metadata sections (for example `## Story Points`, `## Business Value`, `## Priority`, `## Provider`) are not duplicated into narrative body text.

#### Scenario: Refine command orchestration remains behaviorally consistent after decomposition

- **GIVEN** `specfact backlog refine` supports initialization, filtering, export/import, interactive refinement, writeback, and summary flows
- **WHEN** the command implementation is decomposed into smaller helper methods
- **THEN** observable CLI behavior and writeback semantics remain unchanged for equivalent inputs
- **AND** command complexity in the top-level `refine` function is reduced to keep the implementation readable and maintainable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Tasks: Backlog Refine Writeback Field Splitting for ADO/GitHub

## 1. Git Workflow

- [x] 1.1 Create git branch `bugfix/backlog-refine-writeback-field-splitting` from `dev`

## 2. Spec Deltas

- [x] 2.1 Add/modify `backlog-refinement` spec scenarios for provider-aware writeback field splitting
- [x] 2.2 Run `openspec validate backlog-core-03-refine-writeback-field-splitting --strict`

## 3. Tests First (TDD)

- [x] 3.1 Add tests for label-style refined output parsing into canonical fields
- [x] 3.2 Add ADO writeback regression test: structured output does not get written verbatim to description
- [x] 3.3 Add GitHub writeback regression test for label-style output normalization
- [x] 3.4 Run targeted tests and capture failing evidence in `openspec/changes/backlog-core-03-refine-writeback-field-splitting/TDD_EVIDENCE.md`

## 4. Implementation

- [x] 4.1 Implement refinement output parser integration in `backlog refine --write` flow
- [x] 4.2 Ensure parsed canonical fields are applied before adapter update calls
- [x] 4.3 Add adapter-safe fallback handling for structured label output where needed
- [x] 4.4 Run targeted tests and capture passing evidence in `openspec/changes/backlog-core-03-refine-writeback-field-splitting/TDD_EVIDENCE.md`
- [x] 4.5 Refactor `refine` command into smaller helper methods with clear responsibilities
- [x] 4.6 Keep behavior parity for export/import/write/preview flows after refactor
- [x] 4.7 Preserve heading-style `## Notes` / `## Dependencies` sections in parsed `body_markdown` for writeback

## 5. Quality Gates

- [x] 5.1 `hatch run format`
- [x] 5.2 `hatch run type-check`
- [x] 5.3 `hatch run contract-test`
- [x] 5.4 `hatch run smart-test`

## 6. Documentation and Release Hygiene

- [x] 6.1 Review and update docs for backlog refine writeback behavior (if needed)
- [x] 6.2 Bump patch version and sync: `pyproject.toml`, `setup.py`, `src/specfact_cli/__init__.py`
- [x] 6.3 Update `CHANGELOG.md` with bugfix entry

## 7. Pull Request

- [ ] 7.1 Push branch and open PR to `dev`
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "specfact-cli"
version = "0.30.2"
version = "0.30.3"
description = "The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
if __name__ == "__main__":
_setup = setup(
name="specfact-cli",
version="0.30.2",
version="0.30.3",
description=(
"The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with "
"validation and contract enforcement for new projects and long-lived codebases."
Expand Down
2 changes: 1 addition & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
"""

# Package version: keep in sync with pyproject.toml, setup.py, src/specfact_cli/__init__.py
__version__ = "0.30.1"
__version__ = "0.30.3"
2 changes: 1 addition & 1 deletion src/specfact_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
- Supporting agile ceremonies and team workflows
"""

__version__ = "0.30.2"
__version__ = "0.30.3"

__all__ = ["__version__"]
13 changes: 8 additions & 5 deletions src/specfact_cli/adapters/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -2763,20 +2763,23 @@ def update_backlog_item(self, item: BacklogItem, update_fields: list[str] | None
# Build canonical fields from parsed refined body (use refined values)
canonical_fields = {
"description": description,
# Use extracted sections from refined body (these are the refined values)
"acceptance_criteria": existing_acceptance_criteria,
# Prefer extracted section values, but fall back to canonical item fields
# so label-style refinement parsing still writes dedicated fields.
"acceptance_criteria": existing_acceptance_criteria or item.acceptance_criteria,
"story_points": (
int(existing_story_points)
if existing_story_points and existing_story_points.strip().isdigit()
else None
else item.story_points
),
"business_value": (
int(existing_business_value)
if existing_business_value and existing_business_value.strip().isdigit()
else None
else item.business_value
),
"priority": (
int(existing_priority) if existing_priority and existing_priority.strip().isdigit() else None
int(existing_priority)
if existing_priority and existing_priority.strip().isdigit()
else item.priority
),
"value_points": item.value_points,
"work_item_type": item.work_item_type,
Expand Down
Loading
Loading