Skip to content

Latest commit

 

History

History
338 lines (256 loc) · 13.4 KB

File metadata and controls

338 lines (256 loc) · 13.4 KB

tpcli Improvement Backlog

Derived from PI-1/26 planning session (2026-04-07). Baseline: 109 BDD scenarios passing, 20 failing (behave runner).


Wave 0 — Test infrastructure migration (unblocks strict TDD)

US-0: Migrate BDD runner from behave to pytest-bdd

Files: pyproject.toml, Makefilejustfile, tests/

Context: iacre (the reference project) uses pytest-bdd with scenarios() wiring, uv run pytest, and a justfile test ladder. tpcli has both behave and pytest-bdd installed but uses behave as runner. Migrating aligns the two repos and enables pytest fixtures, markers, and parametrize across unit + BDD in one run.

Acceptance Criteria:

  • justfile created with same ladder structure as iacre: test-fasttest-unittest-bddtest-slowtest-e2e
  • All existing behave .feature files in tests/features/ converted to pytest-bdd: scenarios() wiring in tests/bdd/test_*.py, steps in tests/bdd/steps/
  • tests/bdd/features/{domain}/ directory layout mirrors iacre
  • @tags added: @unit, @bdd, @slow, @e2e, @P0/@P1/@P2
  • uv run pytest tests/unit/ tests/bdd/ -m "not slow" is the fast loop
  • Makefile kept as thin wrapper calling justfile (backwards compat)
  • Baseline: same 109 passing scenarios, 20 failing — no regressions

BDD: Not applicable (this IS the BDD infrastructure)


Wave 1 — Red tests (write failing tests before any implementation)

US-1-RED: Failing test — plural endpoint on update

Files: tests/bdd/features/plan/update.feature, tests/bdd/steps/plan/update_steps.py

Context: client.go Update() builds /api/v1/Feature/123 (singular → 404). The existing go_cli_create_update.feature scenario even asserts the wrong URL (PUT to /api/v1/TeamPIObjective/12345). Write the correct failing scenario first.

Red scenario:

@P0 @plan @update
Scenario: Update uses plural entity type in URL path
  Given a Feature with Id 2284623 exists in TargetProcess
  When I run: tpcli plan update Feature 2284623 --data '{"Name":"x"}'
  Then the API request is PUT to /api/v1/Features/2284623
  And the command succeeds with exit code 0

Acceptance Criteria (red phase):

  • Scenario exists and FAILS against current implementation
  • Existing wrong assertion (/api/v1/TeamPIObjective/12345) updated to fail correctly

US-2-RED: Failing test — nested objects persisted on create

Files: tests/bdd/features/plan/create_feature.feature

Red scenario:

@P0 @plan @create
Scenario: Create Feature with Team and Release persists nested objects
  When I run: tpcli plan create Feature --data '{"Name":"Test","Team":{"Id":2022903},"Release":{"Id":2033368},"Project":{"Id":223264}}'
  Then the command succeeds with exit code 0
  And the created Feature has Release.Name "DAD: PI-1/26"
  And the created Feature has Team.Name "DAD - Fusion - Cloud Enablement & Delivery"

Acceptance Criteria (red phase):

  • Scenario exists and FAILS (Release and Team are nil after create)

US-3-RED: Failing test — link Feature to TeamPIObjective

Files: tests/bdd/features/plan/link.feature

Red scenario:

@P0 @plan @link
Scenario: Link a Feature to a TeamPIObjective via join entity
  Given Feature 2284623 and TeamPIObjective 2284608 exist in TargetProcess
  When I run: tpcli plan link Feature 2284623 TeamPIObjective 2284608
  Then a TeamPIObjectiveFeature record is created
  And GET /api/v1/TeamPIObjective/2284608/Features includes Feature 2284623

Scenario: Unlink a Feature from a TeamPIObjective
  Given Feature 2284623 is linked to TeamPIObjective 2284608
  When I run: tpcli plan unlink Feature 2284623 TeamPIObjective 2284608
  Then the TeamPIObjectiveFeature record is deleted
  And GET /api/v1/TeamPIObjective/2284608/Features does not include Feature 2284623

Acceptance Criteria (red phase):

  • Both scenarios exist and FAIL (command not found)

US-13-RED: Failing test — config context schema

Files: tests/unit/test_config.py, tests/bdd/features/config/context.feature

Red scenarios:

@P0 @config @context
Scenario: Load default context from config
  Given config has current-context "dad-ced-pi-26-1"
  And context "dad-ced-pi-26-1" has team "DAD - Fusion - Cloud Enablement & Delivery"
  When I call get_default_team()
  Then the result is "DAD - Fusion - Cloud Enablement & Delivery"

Scenario: Switch context
  Given config has contexts "dad-ced-pi-25-4" and "dad-ced-pi-26-1"
  When I run: tpcli context use dad-ced-pi-25-4
  Then current-context in config is "dad-ced-pi-25-4"
  And get_default_team() returns the PI-25-4 team

Scenario: Config migrate preserves credentials
  Given a flat config with default-team and default-art
  When I run: tpcli config migrate
  Then a context is created with those values
  And credentials (token, url) are unchanged at root level

Acceptance Criteria (red phase):

  • All 3 scenarios FAIL against current flat-config implementation

Wave 2 — Config foundation (green US-13, enables everything else)

US-13: Config schema — contexts + natural key ordering

Files: tpcli_pi/core/config.py, pkg/config/ (Go), ~/.config/tpcli/config.yaml

Context: See US-13-RED above. kubectl-style contexts. Key naming: {org}-{team}-pi-{yy}-{n} (e.g. dad-ced-pi-26-1) sorts alphabetically = chronologically. All keys within blocks sort alphabetically.

Acceptance Criteria:

  • load_config() reads current-context and resolves context fields
  • get_default_team(), get_default_art(), etc. read from active context
  • Flat default-team / default-art still work with deprecation warning
  • Go pkg/config/ reads same schema via viper
  • tpcli config migrate converts flat → context non-destructively
  • All US-13-RED scenarios GREEN

US-12: tpcli context subcommand

Files: cmd/context.go, tpcli_pi/cli/context.py

Acceptance Criteria:

  • tpcli context list — lists contexts, marks current with *
  • tpcli context use <name> — writes current-context to config
  • tpcli context show — prints resolved fields for active context
  • tpcli context add <name> --from <existing> — clones, shifts release
  • --context <name> global flag overrides for one command
  • ID resolution lazy-cached in ~/.cache/tpcli/ not in config
  • BDD scenarios GREEN

Wave 3 — Core CLI fixes (green Wave 1 red tests)

US-1: Fix plural endpoint in Update()

Files: pkg/tpclient/client.go

Context: Update() path: /api/v1/{entityType}/{id}entityType is singular (e.g. Feature). TP API expects plural (Features). Fix: pluralise before building path. Create() uses singular POST which works — don't change it.

Acceptance Criteria:

  • Update() pluralises entity type: FeatureFeatures, TeamPIObjectiveTeamPIObjectives
  • Existing wrong assertion in go_cli_create_update.feature corrected
  • Go unit test: TestClientUpdatePluralPath
  • US-1-RED scenario GREEN
  • make test-go passes

US-2: Fix nested object persistence on create

Files: pkg/tpclient/client.go, cmd/plan.go

Context: TP API silently ignores Team and Release on POST for Feature. Workaround: after create, POST to /api/v1/Feature/{id} with {Team, Release}.

Acceptance Criteria:

  • Create() for Feature auto-issues follow-up POST if Team or Release in payload
  • Follow-up POST uses correct singular endpoint (/api/v1/Feature/{id})
  • US-2-RED scenario GREEN
  • Verified by GET after create

US-3: tpcli plan link / tpcli plan unlink

Files: cmd/plan.go, pkg/tpclient/client.go

Context: Join entity is TeamPIObjectiveFeature. POST /api/v1/TeamPIObjectiveFeatures with {TeamPIObjective:{Id}, Feature:{Id}}. Unlink: GET join entity ID, then DELETE /api/v1/TeamPIObjectiveFeatures/{id}. Parallel: ProgramPIObjectiveFeature for ART-level objectives.

Acceptance Criteria:

  • tpcli plan link Feature <fid> TeamPIObjective <oid> → creates join, prints Id
  • tpcli plan unlink Feature <fid> TeamPIObjective <oid> → finds + deletes join
  • tpcli plan link --verify → reads /TeamPIObjective/{oid}/Features, confirms present
  • ProgramPIObjectiveFeature supported with same syntax
  • US-3-RED scenarios GREEN

US-6: --quiet flag for plan create/update

Files: cmd/plan.go

Acceptance Criteria:

  • --quiet prints Id: <n> Name: <name> only
  • Default (full JSON) unchanged
  • Works for create and update

Wave 4 — Python ext fixes

US-4: Fuzzy team name resolution

Files: tpcli_pi/core/api_client.pyget_team()

Context: Config Cloud Enablement & Delivery ≠ TP name DAD - Fusion - Cloud Enablement & Delivery. Ext scripts fail with "Team not found".

Acceptance Criteria:

  • Exact match first; falls back to suffix/contains (case-insensitive)
  • >1 match → error listing candidates
  • 0 matches → error with tpcli list Teams suggestion
  • Works for config default-team and --team flag
  • BDD scenario with short name → correct team; ambiguous → error

US-10: Atomic Feature creation with objective link

Files: tpcli_pi/core/api_client.pycreate_feature()

Context: Currently 3 API calls needed: create, fix Release+Team, create join entity. Should be one logical operation with rollback.

Acceptance Criteria:

  • create_feature(..., objective_id=X) handles all 3 steps
  • Rollback (delete feature) if step 2 or 3 fails
  • tpcli plan create Feature --data '...' --link-objective <id> CLI flag
  • BDD: create with objective → verify Release, Team, and link all set

Wave 5 — Query fixes + UX

US-7: TeamPIObjectives where filter

Files: pkg/tpclient/client.go or cmd/list.go

Acceptance Criteria:

  • Root cause of API 400 on --where for TeamPIObjectives identified
  • Fixed or documented with workaround in skill quick-ref
  • If fixed: tpcli list TeamPIObjectives --where "Release.Id eq X and Team.Id eq Y" works

US-8: tpcli find team <partial-name>

Files: cmd/find.go or cmd/list.go

Acceptance Criteria:

  • tpcli find team "Cloud" fetches all teams, filters locally, returns matches
  • --exact flag for strict match
  • Documented in skill quick-ref

US-9: tpcli plan compare

Files: tpcli_pi/cli/compare.py (new ext script)

Context: PI-to-PI objective comparison — carry-over, dropped, new, status delta.

Acceptance Criteria:

  • tpcli plan compare --release "DAD: PI-1/26" --previous "DAD: PI-4/25" --team "..."
  • Columns: Objective | prev-status | curr-status | delta (carry-over/new/dropped)
  • --format markdown for pasting into planning docs
  • Uses previous-release from active context if --previous not given

Wave 6 — Skill update + sync

US-5 / US-11: Skill quick-ref — DAD/GMSGQ specifics + Jira integration

Files: ~/.claude/skills/targetprocess/references/targetprocess-quick-ref.md, ~/.claude/skills/targetprocess/references/team-scenarios.md, .github/skills/targetprocess/ (this repo), ~/shalomb/tpcli/.github/skills/targetprocess/

Acceptance Criteria:

  • TeamPIObjectiveFeature join entity: POST pattern, GET verification, DELETE unlink
  • Required Project: GMSGQ for all DAD entity creation
  • Correct full team name DAD - Fusion - Cloud Enablement & Delivery
  • tpcli list Teams --where broken — use --take 200 + grep workaround
  • tpcli list TeamPIObjectives sorts oldest-first — use --skip N to find new items
  • Jira customfield_12902 = TP entity URL (auto-synced via Jira Key custom field on Feature)
  • DAD creating objectives recipe: full minimal payload with Project, Team, Release
  • Context config schema documented (US-13)
  • rsync -av ~/.claude/skills/targetprocess/ .github/skills/targetprocess/
  • rsync -av ~/.claude/skills/targetprocess/ ~/shalomb/tpcli/.github/skills/targetprocess/

Dependency graph

US-0  (pytest-bdd migration)
  └── US-1-RED, US-2-RED, US-3-RED, US-13-RED  (write failing tests)
        └── US-13  (config schema — Python + Go)
              └── US-12  (context command)
                    ├── US-1   (fix plural endpoint)
                    ├── US-2   (fix nested objects)
                    └── US-3   (plan link/unlink)
                          ├── US-6   (--quiet flag)
                          ├── US-4   (fuzzy team resolution)
                          └── US-10  (atomic feature create)
                                ├── US-7  (where filter)
                                ├── US-8  (find team)
                                └── US-9  (plan compare)
                                      └── US-5/11  (skill update + sync)

BDD quality gates (Adzic + Farley)

Every feature file must score ≥ 7.0 on both indexes before merge:

  • Adzic: Business-Readable, Declarative, Intention-Revealing, Focused, Living, Consistent
  • Farley: Fast, Maintainable, Repeatable, Atomic, Necessary, Understandable

HTTP interactions mocked via responses (Python) / httptest (Go) — no real API calls in unit or BDD tiers. Real API calls only in @e2e marked scenarios.