diff --git a/evals/implement-task/evals.json b/evals/implement-task/evals.json index 5e63d6bb..7f5afd8b 100644 --- a/evals/implement-task/evals.json +++ b/evals/implement-task/evals.json @@ -15,7 +15,8 @@ "The plan mentions using --trailer='Assisted-by: Claude Code' in the commit (constraint 2.3)", "The plan references creating a branch named TC-9201 or notes the branch naming convention (constraint 3.1), and references the Target Branch value from the task description", "The conventions analysis identifies at least 2 patterns from sibling code: error handling with Result and .context(), and the model/service/endpoints module structure", - "The plan extracts and references the Target Branch section from the task description, identifying 'main' as the target branch" + "The plan extracts and references the Target Branch section from the task description, identifying 'main' as the target branch", + "The plan mentions checking for a description digest comment (Step 1.5) and notes that when no digest is found, it proceeds with a warning rather than blocking execution (backward compatibility per shared/description-digest-protocol.md)" ] }, { @@ -73,6 +74,31 @@ "The planned commit message follows Conventional Commits format (type(scope): description) and references TC-9205 in the footer (constraint 2.1, 2.2)", "The plan extracts and references the Target Branch section from the task description, identifying TC-9005 as the target branch" ] + }, + { + "id": 6, + "prompt": "Implement task TC-9201. The task description is in task-standard.md, the target repository structure is in repo-backend.md, and the project CLAUDE.md is in claude-md-mock.md. The Jira issue has one comment posted by a previous plan-feature run with the body: '[sdlc-workflow] Description digest: sha256:a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890'. Assume this digest MATCHES the SHA-256 hash of the current task description. The comment's created and updated timestamps are identical. Do NOT actually modify repository files or call external tools (Jira, git, Serena). Instead, write your response to outputs/digest-match.md describing how you would handle the description integrity verification in Step 1.5, and then write outputs/plan.md with the full implementation plan.", + "expected_output": "The response should describe verifying the description digest in Step 1.5: locating the digest comment by its marker string, comparing the stored hash with the computed hash of the current description, finding a match, and proceeding silently without any additional user prompt. The implementation plan should continue to Step 2 and beyond without interruption.", + "files": ["files/task-standard.md", "files/repo-backend.md", "files/claude-md-mock.md"], + "assertions": [ + "The response describes locating the digest comment using the marker string '[sdlc-workflow] Description digest:' from shared/description-digest-protocol.md", + "The response describes computing a SHA-256 hash of the current description and comparing it with the stored digest", + "The response confirms the digests match and states it will proceed silently without prompting the user — no additional latency for the happy path", + "The response does not alert the user or pause execution due to the digest check — it proceeds directly to subsequent steps" + ] + }, + { + "id": 7, + "prompt": "Implement task TC-9201. The task description is in task-standard.md, the target repository structure is in repo-backend.md, and the project CLAUDE.md is in claude-md-mock.md. The Jira issue has one comment posted by a previous plan-feature run with the body: '[sdlc-workflow] Description digest: sha256:0000000000000000000000000000000000000000000000000000000000000000'. Assume this digest does NOT match the SHA-256 hash of the current task description — the description was modified after plan-feature created it. The comment's created and updated timestamps are identical. Do NOT actually modify repository files or call external tools (Jira, git, Serena). Instead, write your response to outputs/digest-mismatch.md describing how you would handle the description integrity verification in Step 1.5.", + "expected_output": "The response should describe detecting a digest mismatch in Step 1.5: the stored hash does not match the computed hash of the current description. The skill should alert the user that the description was modified since plan-feature created the task, display the expected vs actual digest values, and pause execution — asking the user whether to proceed with the current description or stop to re-run plan-feature. The skill should NOT proceed to Step 2 or beyond without the user's explicit decision.", + "files": ["files/task-standard.md", "files/repo-backend.md", "files/claude-md-mock.md"], + "assertions": [ + "The response describes locating the digest comment using the marker string '[sdlc-workflow] Description digest:' from shared/description-digest-protocol.md", + "The response describes detecting a mismatch between the stored digest and the computed SHA-256 hash of the current description", + "The response alerts the user that the description was modified after plan-feature created the task and displays the expected vs actual digest values", + "The response offers the user a choice: (1) proceed with the current description, or (2) stop so they can re-run plan-feature", + "The response stops execution and does not proceed to Step 2 or implementation planning until the user responds — following the same pause-and-ask pattern as the incomplete description handling" + ] } ] } diff --git a/plugins/sdlc-workflow/shared/description-digest-protocol.md b/plugins/sdlc-workflow/shared/description-digest-protocol.md index 799d1b76..e2eaeab6 100644 --- a/plugins/sdlc-workflow/shared/description-digest-protocol.md +++ b/plugins/sdlc-workflow/shared/description-digest-protocol.md @@ -91,8 +91,17 @@ comment. ## Consumer Verification -The consumer (implement-task) retrieves issue comments and searches for one whose -body starts with the marker string `[sdlc-workflow] Description digest:`. If found: +The consumer (implement-task) retrieves issue comments and searches for those whose +body starts with the marker string `[sdlc-workflow] Description digest:`. + +### Multiple Digest Comments + +If multiple comments match the marker string (e.g., from plan-feature re-runs or +manual postings), the consumer must select the most recent one by `created` +timestamp. This ensures deterministic behavior without rejecting valid re-planning +scenarios. + +If found: 1. Extract the `sha256:` value 2. Compute SHA-256 of the current description field (same normalization as producer) diff --git a/plugins/sdlc-workflow/skills/implement-task/SKILL.md b/plugins/sdlc-workflow/skills/implement-task/SKILL.md index c2a07444..b79bbfbb 100644 --- a/plugins/sdlc-workflow/skills/implement-task/SKILL.md +++ b/plugins/sdlc-workflow/skills/implement-task/SKILL.md @@ -25,7 +25,7 @@ If any of these sections are missing or incomplete, inform the user: ## Step 0.5 – JIRA Access Initialization -Before attempting any JIRA operations (Steps 1, 2, 3, 11), determine the access method. +Before attempting any JIRA operations (Steps 1, 1.5, 2, 3, 11), determine the access method. **For every JIRA operation:** 1. **Attempt MCP first** (preferred method) @@ -49,6 +49,7 @@ Before attempting any JIRA operations (Steps 1, 2, 3, 11), determine the access **REST API equivalents for this skill's operations:** - `jira.get_issue(id)` → `python3 scripts/jira-client.py get_issue --fields "*all"` +- `jira.get_issue_comments(id)` → `python3 scripts/jira-client.py get_comments ` - `jira.user_info()` → `python3 scripts/jira-client.py get_user_info` - `jira.edit_issue(id, assignee=accountId)` → `python3 scripts/jira-client.py update_issue --fields-json '{"assignee": {"id": ""}}'` - `jira.transition_issue(id, status)` → First get transitions with `get_transitions `, find ID for target status, then `transition_issue --transition-id ` @@ -177,6 +178,53 @@ section in CLAUDE.md (the field is listed as `GitHub Issue custom field: /#` for use in Step 10. - **If not configured or the field is empty**, skip silently — this is optional. +## Step 1.5 – Verify Description Integrity + +After fetching the task, verify that the description has not been modified since +plan-feature created it. This uses the digest protocol defined in +`shared/description-digest-protocol.md`. + +1. **Retrieve issue comments**: fetch all comments on the Jira issue: + + ``` + jira.get_issue_comments() + ``` + +2. **Locate the digest comment**: search for all comments whose body starts with the + marker string `[sdlc-workflow] Description digest:`. This marker is defined in + `shared/description-digest-protocol.md`. If multiple comments match (e.g., from + plan-feature re-runs), select the most recent one by `created` timestamp. + +3. **If no digest comment found**: log a warning and proceed normally — do not block + execution: + + > "No description digest found — skipping integrity check. This task may have + > been created before digest tracking was introduced." + +4. **If digest comment found**: + a. **Check for comment editing**: if the comment's `created` and `updated` + timestamps are available, compare them. If `updated` is later than `created`, + warn: "Digest comment was edited after initial posting — integrity cannot be + fully guaranteed." Proceed with digest comparison regardless. If timestamps + are not available in the API response, skip this check silently. + b. **Extract the stored digest**: parse the `sha256:` value from the + comment body. + c. **Compute the current digest**: hash the current description field text using + SHA-256, following the same normalization as the protocol (strip + leading/trailing whitespace before hashing). Output a lowercase 64-character + hexadecimal digest. + d. **Compare digests**: + - **Match**: proceed silently — no additional user prompt, no added latency. + - **Mismatch**: alert the user that the task description was modified after + plan-feature created it. Display the expected digest (from the comment) and + the actual digest (computed from the current description). Ask the user + whether to: + 1. **Proceed** with the current description as-is + 2. **Stop** so they can re-run plan-feature to regenerate tasks + + **Stop execution immediately** — do not proceed with any subsequent steps + until the user responds. + ## Step 2 – Verify Dependencies If the task has Dependencies, check each one: