ci(e2e): default mention round-trips to self-mention via /myself - #796
Conversation
…R_E2E_MENTION_ACCOUNT_ID gate) The four live-Jira mention round-trip E2E tests (VP-674-014/015/016/017) previously clean-skipped unless JR_E2E_MENTION_ACCOUNT_ID was set, requiring a separately-configured account seam to run in CI at all. mention_account_id() now takes the harness and self-discovers a mention target via GET /rest/api/3/myself when the env var is unset/empty, mentioning the authenticated CI account's own accountId (bracket-form [~accountid:<id>], so there's no display-name ambiguity concern). This still fully validates the round trip: real Jira accepting and persisting the mention node with that accountId is what's being proven, independent of whose accountId it is. JR_E2E_MENTION_ACCOUNT_ID remains as an optional override to mention a different controlled account. Test-infra + docs only; no src/ changes. Claude-Session: https://claude.ai/code/session_01FitXFyeeyQ7zUuRVTgb7hd
…through to the live suite Claude-Session: https://claude.ai/code/session_01FitXFyeeyQ7zUuRVTgb7hd
Zious11
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (ready to merge). Posted as a
COMMENTreview rather than anAPPROVEreview only because GitHub refusesCan not approve your own pull request— theghcredential in this environment is the PR author. The verdict below is the reviewer's actual verdict; a formal approving review still needs a second GitHub account (code-owner approval is required by branch protection regardless).
Fresh-eyes PR review — PR #796
Verdict: APPROVE. No blocking findings. One SUGGESTION and three NITs, none of which need to hold up the merge.
Scope reviewed: git diff cef4a021..92da86cc — 4 files, +62/-32. Base (develop) is at cef4a021, identical to this PR's merge-base, so the tested tree is the merged tree (relevant given strict: false on develop).
What I verified (not a rubber stamp)
| Check | Result |
|---|---|
cargo clippy --tests --all-features -- -D warnings on PR head |
clean |
cargo test --test e2e_cli_surface_guard |
10/10 pass — jr api path still declared in SURFACE; parser-consistency guard did not flag the new mention_account_id invocation |
cargo test --test claude_md_citations |
61/61 pass |
cargo test --test e2e_live (offline guards) |
30 pass / 77 ignored — incl. test_every_ignored_test_has_gate_guard and test_no_test_function_exceeds_line_budget (the latter matters: the four skip messages grew) |
actionlint .github/workflows/e2e.yml |
exit 0 |
| YAML duplicate-key scan (strict PyYAML loader that raises on any duplicate mapping key) | none; the 4 new keys land at the correct 10-space indent inside the Run live E2E tests step's env: mapping, sourced from ${{ vars.* }} (not secrets.*) and never spliced into a run: body |
Every new e2e.yml comment vs. the code it describes |
accurate — JR_E2E_PARENT_KEY/JR_E2E_CHILD_TYPE really are independently _ => return gated (so "both must be set" holds), JR_E2E_EDIT_FIELD really requires a = (Ok(f) if f.contains('=')), and JR_E2E_JSM_RESOLUTION really is checked first in jsm_discover_resolution before the jr issue resolutions fallback |
mention_account_id precedence |
env override first (trimmed, non-empty) → return; only then /myself. Matches both the rustdoc and the CLAUDE.md / spec prose exactly |
| Clean-skip semantics still correct | yes — fetch_raw returns None on spawn failure, non-zero exit, or unparseable JSON, so mention_account_id → None → early return with a [SKIP] line, never a panic or a false-green |
| Teardown for the now-actually-running tests | unchanged and adequate — MentionCommentDropGuard on the comment path, --label e2e-<run_id> + best_effort_close on create/edit, jsm_self_close on the JSM path |
| Commit hygiene | two cleanly-scoped Conventional Commits; commit 1 is tests+docs only, commit 2 is e2e.yml only. Diff is 94 lines total, well under the 500-line flag |
| PR description vs. diff | accurate, including the "no src/ change" claim (git diff --stat confirms: no src/ file touched) |
The core design call is sound. A self-mention proves exactly what VP-674-014/015/016/017 exist to prove — that live Jira accepts and persists a mention ADF node carrying a given accountId — and the round trip is genuinely independent of whose accountId it is. Trading an out-of-band manual variable for a runtime /myself lookup is the right call for tests that have silently never executed.
Findings
| # | Severity | Category | Finding |
|---|---|---|---|
| 1 | suggestion | missing | JR_E2E_MENTION_ACCOUNT_ID is documented as a settable jira-e2e environment variable but is not threaded through e2e.yml — the documented override is unreachable in CI |
| 2 | nit | description | The four [SKIP] messages describe a narrower condition than the one that actually triggers them |
| 3 | nit | coherence | mention_account_id runs the same /myself lookup four times per suite run |
| 4 | nit | coherence | In the JSM test, the harness/mention-target resolution was hoisted above the JR_E2E_JSM_PROJECT gate |
1. JR_E2E_MENTION_ACCOUNT_ID override is documented but not wired into e2e.yml — SUGGESTION
docs/specs/e2e-live-jira-testing.md §8 — the table headed "Configuration inventory (GitHub Environment jira-e2e)" — now lists JR_E2E_MENTION_ACCOUNT_ID as variable (optional override) and instructs the reader in bold: "Set this var only to mention a different CONTROLLED test account instead." CLAUDE.md carries the same claim. But JR_E2E_MENTION_ACCOUNT_ID appears nowhere in .github/workflows/e2e.yml (grep MENTION → no match), so a maintainer who follows that instruction and sets the variable will see no effect: the tests will keep self-mentioning, silently.
This is a pre-existing gap (the variable was never wired), so it is not a defect this PR introduces, and I am not blocking on it. I am raising it because it is the same defect class commit 92da86cc exists to fix — its own message says these vars were "documented and read by the test suite but never actually wired into the workflow's env: block" — and because commit c0322efc strengthens the doc's assertion that the override is available. Fixing it is one line in the block this PR already edits:
# Optional override: a CONTROLLED test account's accountId to mention instead of
# the authenticated CI account. Unset → the four test_e2e_mention_* tests
# self-discover the target via GET /rest/api/3/myself (self-mention default).
JR_E2E_MENTION_ACCOUNT_ID: ${{ vars.JR_E2E_MENTION_ACCOUNT_ID }}Alternatively, if leaving it unwired is deliberate (e.g. you want the self-mention path to be the only CI behavior), say so in the doc row so the bolded "Set this var" instruction isn't misleading. Either resolution is fine; the current state — documented-as-settable, not settable — is the one to avoid.
2. [SKIP] messages name a narrower cause than the one that fires them — NIT
All four messages read ... and GET /rest/api/3/myself returned no accountId .... mention_account_id also returns None when fetch_raw fails to spawn the subprocess, gets a non-zero exit, or cannot parse the stdout as JSON — none of which is "returned no accountId". Operationally nothing is lost, because fetch_raw already prints a [WARN] fetch_raw: ... line naming the real cause immediately above. Worth noting that the rustdoc on mention_account_id gets this exactly right ("the /myself lookup fails or carries no accountId") — it is only the four user-facing strings that narrow it. Something like ... and GET /rest/api/3/myself did not yield an accountId ... would cover both.
3. Four identical /myself subprocess calls per run — NIT
mention_account_id(&h) is called once per test, so a full live run spawns jr api /rest/api/3/myself four times with an identical result. Harmless — the suite runs --test-threads=1 and this is a trivial authenticated GET, matching the PR's own "negligible" assessment. A static OnceLock<Option<String>> would collapse it to one if you ever want it; not worth churning the diff for now.
4. JSM test: mention-target resolution hoisted above the JSM-project gate — NIT
In test_e2e_mention_jsm_create_roundtrip, let h = e2e_harness(); + mention_account_id(&h) now sit ahead of the JR_E2E_JSM_PROJECT check. When that variable is unset, the test now pays for a /myself round trip before skipping, and reports the mention-target skip reason rather than the more specific "JSM project not configured" one. Cosmetic only, and moot in the canonical repo where JR_E2E_JSM_PROJECT=EJ is set. The equivalent hoist in the other three tests is required (the harness is now a parameter) and correct; only here is the gate ordering swappable, and only here does swapping buy anything.
Checklist results
- Diff coherence — PASS. Two commits, cleanly separated, both on-topic. No unrelated changes; no
src/touched. - Description accuracy — PASS. Every claim I spot-checked (clippy clean, 10/10 + 61/61 guards, actionlint exit 0, no
src/diff, additive-onlyenv:keys) holds. - Test coverage — PASS (with the caveat inherent to the change). No new offline test is possible here: the whole point is a live-Jira-gated path. The offline guards that can cover it (
e2e_cli_surface_guard,test_every_ignored_test_has_gate_guard,test_no_test_function_exceeds_line_budget) all pass, and the PR body records an orchestrator-run live verification of the self-discovery path. - Demo evidence — N/A, correctly declared. Not a story, no ACs to demo, no product behavior change. The live-Jira dry-run write-up under Test Evidence is the right substitute.
- Commit quality — PASS. Conventional Commits, scoped subjects, informative bodies.
- Diff size — PASS. 94 lines across 4 files.
- Missing changes — see finding 1 (the one thing the diff arguably should have included, non-blocking).
- Dependency status — PASS. #674/#794/#795 are all merged; branch is even with
developatcef4a021,MERGEABLE, no rebase needed.
Verdict: APPROVE. Merge when CI is green. Finding 1 is worth resolving either here or in a follow-up — your call which.
| # Optional: resolution name override for jsm_self_close teardown and | ||
| # test_e2e_jsm_resolution_enforcement (S-JSM-E2E-3). Auto-discovered from | ||
| # `jr issue resolutions` when unset. | ||
| JR_E2E_JSM_RESOLUTION: ${{ vars.JR_E2E_JSM_RESOLUTION }} |
There was a problem hiding this comment.
[SUGGESTION] This block now threads four previously-unwired optional vars through — good, and exactly the right fix. But JR_E2E_MENTION_ACCOUNT_ID has the identical defect and is not included, even though commit c0322efc in this same PR strengthens the doc claim that it works.
docs/specs/e2e-live-jira-testing.md §8 ("Configuration inventory (GitHub Environment jira-e2e)") now lists it as variable (optional override) and tells the reader in bold: "Set this var only to mention a different CONTROLLED test account instead." CLAUDE.md says the same. But grep MENTION .github/workflows/e2e.yml returns nothing — so a maintainer who follows that instruction gets no effect at all: the tests keep self-mentioning, silently. That is the same "documented and read by the test suite but never actually wired into the workflow's env: block" condition this commit's own message describes.
Pre-existing, so not blocking. One line in the block you're already editing:
# Optional override: a CONTROLLED test account's accountId to mention instead of
# the authenticated CI account. Unset → the four test_e2e_mention_* tests
# self-discover the target via GET /rest/api/3/myself (self-mention default).
JR_E2E_MENTION_ACCOUNT_ID: ${{ vars.JR_E2E_MENTION_ACCOUNT_ID }}If leaving it unwired is deliberate, that's fine too — but then soften the bolded "Set this var" instruction in the doc so it doesn't promise a knob that isn't connected.
| Ok(v) if !v.trim().is_empty() => return Some(v.trim().to_string()), | ||
| _ => {} | ||
| } | ||
| fetch_raw(h, "/rest/api/3/myself").and_then(|v| { |
There was a problem hiding this comment.
[NIT] Precedence, trimming, and the clean-skip contract all look right here, and the rustdoc above describes the None case accurately ("the /myself lookup fails or carries no accountId").
The four [SKIP] strings below are narrower than that: they all say "GET /rest/api/3/myself returned no accountId", but fetch_raw also returns None when the subprocess fails to spawn, exits non-zero, or emits unparseable JSON. Nothing is lost operationally — fetch_raw prints a [WARN] fetch_raw: ... line naming the real cause right above — so this is purely about the message matching the condition. ... did not yield an accountId ... would cover both.
[NIT, same site] mention_account_id(&h) is called once per test, so a full live run spawns jr api /rest/api/3/myself four times for an identical answer. Harmless at --test-threads=1 for a trivial GET (your "negligible" call in the PR body is right); a static OnceLock<Option<String>> would make it one if you ever care.
| return; | ||
| } | ||
| let Some(account_id) = mention_account_id() else { | ||
| let h = e2e_harness(); |
There was a problem hiding this comment.
[NIT] The harness hoist is required in the other three tests (the harness is now a parameter to mention_account_id), but here it also moves the /myself lookup ahead of the JR_E2E_JSM_PROJECT gate on line 13738. Consequence when that var is unset: the test pays for a /myself round trip before skipping, and reports "could not determine a mention target" instead of the more specific "JR_E2E_JSM_PROJECT not set".
Cosmetic, and moot in the canonical repo where JR_E2E_JSM_PROJECT=EJ. Moving the jsm_project match above the mention_account_id call would restore the cheaper/more precise ordering, since the JSM gate needs no harness.
Review Cycle 1 Triage (PR Manager)pr-reviewer verdict: APPROVE (0 blocking findings). Posted as GitHub review #5161821110 — see that review for the full findings with file/line references.
Note on review mechanics: the reviewing agent's Security review (Step 4): APPROVE, 0 critical/high/medium/low findings (2 info-level notes, one pre-existing/out-of-scope). See PR description's Security Review section. |
JR_E2E_MENTION_ACCOUNT_ID is documented in CLAUDE.md and
docs/specs/e2e-live-jira-testing.md as an optional override for the
mention round-trip tests' self-mention default, and
tests/e2e_live.rs's mention_account_id() helper already reads it via
env::var — but it was never added to e2e.yml's "Run live E2E tests"
env: block. The repo variable could be set in the jira-e2e GitHub
Environment but would never reach the test process, making the
documented override unreachable from CI.
Add JR_E2E_MENTION_ACCOUNT_ID: ${{ vars.JR_E2E_MENTION_ACCOUNT_ID }}
next to the related JR_E2E_JSM_RESOLUTION entry, following this PR's
existing pattern for optional vars.
Claude-Session: https://claude.ai/code/session_01FitXFyeeyQ7zUuRVTgb7hd
Records a completed MAINTENANCE bookkeeping event in STATE.md — no phase transition, no cycle change, pipeline stays PAUSED. Root CLAUDE.md was compacted and merged to develop @ a1f3799 via PR #797 (163,850->89,634 bytes, -45%; 458->397 lines); CI-gate round-1->16 + S-CIGATE-3 history extracted verbatim to docs/specs/ci-gate-completeness.md; ADF/attachment/JR_* gotchas condensed to operative-rule+pointer; live-state markers preserved; 2 MEDIUM operative-loss findings caught and fixed pre-merge. Appended a Phase Progress row, a Historical Content pointer row, and 3 open (not resolved) Drift/Standing-Items follow-ups: E2E-dynamic WIP branch redundancy-vs-#796 decision pending, STATE.md size-budget /compact-state candidate, and a vsdd-factory compact-claude-md engine-gap candidate. Recomputed wc -l and refreshed the SIZE BUDGET banner (428 lines). version: 4.00->4.01 (exactly one bump). No count change (754 BCs/76 VPs/118 holdouts/175 stories). Folds in concurrent benign churn already present in the worktree (regression-state.json command/timestamp update, sidecar-learning.md session-ended log lines, and the CLAUDE.md-compaction research file + its RESEARCH-INDEX.md entry, referenced by this STATE.md update), matching this repo's established precedent for reconciling such churn into the same atomic burst rather than committing separately. Claude-Session: https://claude.ai/code/session_01RT2HKoqmH3Yf9Dyn5v5oeX
…namic, no static vars) Mirrors the self-mention `/myself` default (PR #796): JR_E2E_PARENT_KEY, JR_E2E_CHILD_TYPE, and JR_E2E_EDIT_FIELD become optional overrides instead of required pairings. - test_e2e_issue_parent_roundtrip: discovers the project's sub-task issue type via GET /rest/api/3/project/<key> when JR_E2E_CHILD_TYPE is unset, and seeds a fresh throwaway parent via seed_issue when JR_E2E_PARENT_KEY is unset. New ParentChildDropGuard closes the seeded parent (never a caller-supplied override) plus the created child on teardown. Asserts fields.parent.key via a fresh fetch_raw GET rather than the create response. - test_e2e_issue_edit_custom_field: discovers a safe string field via GET .../editmeta when JR_E2E_EDIT_FIELD is unset — preferring the standard "Environment" field, else the first other editable string field excluding summary/description — writes a benign generated value, and verifies it via a fresh GET. The override path is unchanged. Docs: docs/specs/e2e-live-jira-testing.md §8 and CLAUDE.md's E2E env-var reference now describe all three vars as optional overrides with dynamic defaults, matching JR_E2E_MENTION_ACCOUNT_ID's documented pattern. Claude-Session: https://claude.ai/code/session_01FitXFyeeyQ7zUuRVTgb7hd
…namic, no static vars) (#798) Mirrors the self-mention `/myself` default (PR #796): JR_E2E_PARENT_KEY, JR_E2E_CHILD_TYPE, and JR_E2E_EDIT_FIELD become optional overrides instead of required pairings. - test_e2e_issue_parent_roundtrip: discovers the project's sub-task issue type via GET /rest/api/3/project/<key> when JR_E2E_CHILD_TYPE is unset, and seeds a fresh throwaway parent via seed_issue when JR_E2E_PARENT_KEY is unset. New ParentChildDropGuard closes the seeded parent (never a caller-supplied override) plus the created child on teardown. Asserts fields.parent.key via a fresh fetch_raw GET rather than the create response. - test_e2e_issue_edit_custom_field: discovers a safe string field via GET .../editmeta when JR_E2E_EDIT_FIELD is unset — preferring the standard "Environment" field, else the first other editable string field excluding summary/description — writes a benign generated value, and verifies it via a fresh GET. The override path is unchanged. Docs: docs/specs/e2e-live-jira-testing.md §8 and CLAUDE.md's E2E env-var reference now describe all three vars as optional overrides with dynamic defaults, matching JR_E2E_MENTION_ACCOUNT_ID's documented pattern. Claude-Session: https://claude.ai/code/session_01FitXFyeeyQ7zUuRVTgb7hd
…nding item (v4.01->4.02) WIP branch test/e2e-dynamic-parent-editfield was confirmed NOT redundant with #796 (distinct area: create --parent / edit --field self-configuring live-E2E tests, built atop #796); rebased onto develop, exit-gates verified green, local code-reviewer CLEAN, and MERGED to develop @ 3a874d9 via PR #798. Worktree .worktrees/E2E-DYNAMIC and both local+remote branches cleaned up; local develop fast-forwarded to 3a874d9. - Marked E2E-CI-DYNAMIC-TESTS-WIP-UNVERIFIED RESOLVED in Drift/Standing Items (SESSION-WRAP PAUSE table) and its cross-note under MAINTENANCE-CLAUDE-MD-COMPACTION-2026-09-10 item 1 - Appended Phase Progress row E2E-CI-DYNAMIC-TESTS-DELIVERED-2026-09-10 (COMPLETE, no quality gate) - Added one new OPEN follow-up: E2E-DISCOVER-SAFE-EDIT-FIELD-VALIDATED-SUBTYPE (LOW/MEDIUM, non-blocking, from PR #798 code review) - Left the other two MAINTENANCE follow-ups intact (STATE.md /compact-state candidate; vsdd-factory compact-claude-md engine-gap) Pipeline stays PAUSED throughout -- no phase transition, no cycle change. Counts unchanged (754 BCs / 76 VPs / 118 holdouts / 175 stories). Recomputed wc -l and refreshed the SIZE BUDGET banner: 428 -> 444 lines. Folds in benign concurrent churn (regression-state.json command/timestamp bump, sidecar-learning.md routine session-ended log lines) per the established single-atomic-commit pattern. Claude-Session: https://claude.ai/code/session_01RT2HKoqmH3Yf9Dyn5v5oeX
[test-infra] E2E mention round-trip: default to self-mention via
/myselfEpic: N/A — test-infrastructure + CI enhancement, not a story; does not close an issue
Mode: maintenance (test-infra/CI hardening on top of #674 / #794 / #795)
Convergence: N/A — no story pipeline; changes validated by local build/test/lint + one live-Jira dry run (see Test Evidence)
This PR makes the four live-Jira mention round-trip E2E tests (
tests/e2e_live.rs, VP-674-014/015/016/017) actually run in the push/nightlye2e.ymljob instead of clean-skipping. Previously they were gated on an optionalJR_E2E_MENTION_ACCOUNT_IDrepo variable that was never set, so the mention round trip against real Jira has never executed in CI since #674/#794/#795 landed.mention_account_id(h)now self-discovers a mention target viaGET /rest/api/3/myselfwhen the override is unset, so the tests exercise the realmentionADF-node round trip out of the box. A second, independent commit threads three already-existing optional E2E repo variables (JR_E2E_PARENT_KEY/JR_E2E_CHILD_TYPE/JR_E2E_EDIT_FIELD) plusJR_E2E_JSM_RESOLUTIONthroughe2e.yml, which were documented and read by the test suite but never actually wired into the workflow'senv:block — those tests clean-skip exactly as before until a human sets the corresponding repo variables.Architecture Changes
No
src/production code changed. This PR touches only test infrastructure and CI wiring:graph TD e2e_live["tests/e2e_live.rs\n(mention_account_id)"] -->|"GET /rest/api/3/myself\n(self-discovery, new)"| JiraAPI["Live Jira REST API"] e2e_live -->|"JR_E2E_MENTION_ACCOUNT_ID\n(override, unchanged)"| JiraAPI e2e_yml[".github/workflows/e2e.yml"] -->|"env: JR_E2E_PARENT_KEY / _CHILD_TYPE / _EDIT_FIELD / _JSM_RESOLUTION\n(newly threaded through)"| e2e_live style e2e_live fill:#90EE90 style e2e_yml fill:#90EE90Architecture Decision Record
ADR: Default mention E2E target to self-discovery via
/myselfrather than requiring a dedicated repo variableContext: The mention round-trip E2E tests (added by #674/#794, hardened by #795) were gated on
JR_E2E_MENTION_ACCOUNT_ID, an optional repo variable that names a controlled test account'saccountId. That variable was never set in the canonical repo'sjira-e2eGitHub Environment, so all four tests have clean-skipped in every nightly/pushe2e.ymlrun to date — the real JiramentionADF-node schema has never actually been proven end-to-end in CI.Decision:
mention_account_id(h: &E2eHarness)now takes the harness and, whenJR_E2E_MENTION_ACCOUNT_IDis unset or empty, callsGET /rest/api/3/myselfand uses the authenticated CI account's ownaccountIdas the mention target (a self-mention). The env var remains a supported override for mentioning a different controlled account.Rationale: A self-mention still fully validates what these tests exist to prove — that real Jira accepts and persists a
mentionADF node with a givenaccountId— without requiring any additional secret/variable provisioning. It closes the "tests exist but never actually run against live Jira" gap with zero new configuration surface.Alternatives Considered:
JR_E2E_MENTION_ACCOUNT_IDin thejira-e2eenvironment — rejected because it depends on an out-of-band manual step that has already gone unset for multiple release cycles; the tests would keep silently skipping.Consequences:
e2e.ymlrun without additional setup.mention_account_idnow takes&E2eHarnessand performs a network call — clean-skip is now defined as "override unset AND/myselffails/returns no accountId," which only happens if E2E connectivity/auth is already broken (i.e., other E2E tests would also be failing).Story Dependencies
Not a story — no dependency graph entry. Builds on already-merged work:
graph LR S674["#674 / #794\nmention resolution + write-path"] --> THIS["test/e2e-mention-self-target\nthis PR"] S795["#795\nmention boundary fix"] --> THIS style THIS fill:#FFD700Spec Traceability
No BC/AC change — this is test-infra scoped to already-shipped behavioral contracts (mention resolution, #674 line). Traceability is to the existing verification points, not new BCs:
flowchart LR VP1["VP-674-014..017\nmention round-trip"] --> T1["test_e2e_mention_*\n(tests/e2e_live.rs)"] T1 --> S1["mention_account_id()\nself-discovery via /myself"] S1 --> W1["e2e.yml env: block\n(JR_E2E_PARENT_KEY etc.)"]Test Evidence
Coverage Summary
cargo buildcargo test --test e2e_live --no-run#[ignore]+JR_RUN_E2E-gated)cargo test --test e2e_cli_surface_guardcargo test --test claude_md_citationscargo clippy -- -D warningscargo fmt --all -- --checkactionlint .github/workflows/e2e.ymlCoverage/mutation-kill-rate/holdout metrics are N/A — no
src/production logic changed, so the story-shaped coverage/mutation gates do not apply.ci.yml's sharded mutation gate runs against this PR's diff scope regardless (see Risk Assessment) and is expected to be near-zero mutants given the diff touches onlytests/and CI YAML.Test Flow
graph LR Build["cargo build"] NoRun["e2e_live --no-run"] Guard["e2e_cli_surface_guard 10/10"] Citations["claude_md_citations 61/61"] Clippy["clippy -D warnings"] Actionlint["actionlint e2e.yml"] Live["Live self-mention dry run"] Build --> Pass1["PASS"] NoRun --> Pass2["PASS"] Guard --> Pass3["PASS"] Citations --> Pass4["PASS"] Clippy --> Pass5["PASS"] Actionlint --> Pass6["PASS"] Live --> Pass7["PASS"] style Pass1 fill:#90EE90 style Pass2 fill:#90EE90 style Pass3 fill:#90EE90 style Pass4 fill:#90EE90 style Pass5 fill:#90EE90 style Pass6 fill:#90EE90 style Pass7 fill:#90EE90test_e2e_mention_*) modified to self-discover a target; helpermention_account_idsignature changed (() -> Option<String>to(&E2eHarness) -> Option<String>)src/diffe2e_cli_surface_guard,claude_md_citations) both green;e2e.ymlchange is additiveenv:keys only, no removed/renamed keysDetailed Test Results
Modified Tests (this PR)
test_e2e_mention_comment_add_roundtrip#[ignore]+JR_RUN_E2E-gated (not run inci.yml)test_e2e_mention_issue_create_roundtrip#[ignore]+JR_RUN_E2E-gatedtest_e2e_mention_issue_edit_roundtrip#[ignore]+JR_RUN_E2E-gatedtest_e2e_mention_jsm_create_roundtrip#[ignore]+JR_RUN_E2E-gatedJR_E2E_JSM_PROJECTLive Validation (orchestrator-verified, not part of
ci.yml)Run against a real Jira Cloud instance using a
develop-branch build authenticated via an API-token profile:mention_account_idresolved the CI account's ownaccountIdviaGET /rest/api/3/myself(noJR_E2E_MENTION_ACCOUNT_IDset), a self-mention comment was posted, the comment was re-fetched and confirmed to contain amentionADF node with the resolvedaccountId+ display name, and the comment was deleted as part of test cleanup. Per repo convention, no real Jira instance URL, org ID, or project key is included in this PR body.Coverage Analysis
N/A — no
src/lines added/changed.Mutation Testing
N/A — no
src/diff;ci.yml's sharded mutation gate (mutants-plan→mutantsshards →mutants-aggregate) will still run per its normal in-diff scoping and is expected to find ~0 in-scope mutants for atests/+YAML-only diff.Demo Evidence
N/A — test-infrastructure + CI enhancement with no product UI/behavior change (per dispatch: "Step 2 verify-demo-evidence = N/A ... record as such, do not block"). There is no
docs/demo-evidence/<STORY-ID>/to populate because this PR is not a story and has no acceptance criteria to demo. In place of per-AC demo recordings, the "Live Validation" subsection under Test Evidence documents an orchestrator-run, live-Jira verification of the actual behavior change (self-mention discovery viaGET /rest/api/3/myselfround-tripping through a real comment).Holdout Evaluation
N/A — evaluated at wave gate / story pipeline only. This is a maintenance-mode test-infra change, not a story delivery.
Adversarial Review
N/A — evaluated at Phase 5 / story pipeline only. Standard
pr-reviewer+security-reviewerdispatch (below) substitutes for this on non-story PRs per PR Manager Step 4/5.Security Review
Verdict: APPROVE — no findings requiring changes. Reviewed by
vsdd-factory:security-revieweragainstgit diff cef4a021..92da86cc.graph LR Critical["Critical: 0"] High["High: 0"] Medium["Medium: 0"] Low["Low: 0"] Info["Info: 2"] style Critical fill:#90EE90 style High fill:#90EE90 style Medium fill:#90EE90 style Low fill:#90EE90 style Info fill:#87CEEBSecurity Scan Details
Findings
mention_account_id(h: &E2eHarness)checksJR_E2E_MENTION_ACCOUNT_IDfirst (explicit override preserved, unchanged precedence) and only falls back toGET /rest/api/3/myselfwhen unset/empty. The/myselfresponse is narrowed to exactly one field (accountId) — no other field (email, displayName, timezone) is captured, logged, or forwarded. The mentioned identity is, by construction, the same CI service account whose credentials already drive the harness — never a third party. This is a safety improvement over the prior design (which trusted, but did not mechanically enforce, that a human-supplied override named a controlled test account).env:entries (JR_E2E_PARENT_KEY,JR_E2E_CHILD_TYPE,JR_E2E_EDIT_FIELD,JR_E2E_JSM_RESOLUTION) are all non-secret configuration, correctly sourced from${{ vars.* }}(not${{ secrets.* }}), and are only ever consumed asenv:mapping values passed tocargo test— never spliced into arun:script body via${{ }}templating, so no GitHub Actions script-injection (CWE-78) surface is introduced.docs/specs/e2e-live-jira-testing.md's config table carries an exampleJR_E2E_MENTION_ACCOUNT_IDvalue with the shape of a real (24-hex-char) Atlassian accountId.git log -Sconfirms this predates this PR (added in feat(mentions): effectful mention resolution + write-path wiring (S-cycle5-mention-resolution-wiring) #794/0eaf4268) — this PR only edits adjoining prose in the same row and leaves that value byte-for-byte unchanged. Flagged for optional follow-up per repo convention (never include real Jira identifiers in public content); not a defect introduced by this PR and not blocking.Dependency Audit
Cargo.toml/Cargo.lockchanges in this PR —cargo deny checkscope unaffected.Formal Verification
src/logic changed.Risk Assessment & Deployment
Blast Radius
tests/e2e_live.rs(test-only),.github/workflows/e2e.yml(CI workflow),CLAUDE.md+docs/specs/e2e-live-jira-testing.md(docs).src/production code changed;jrbinary behavior is unaffected.e2e.ymlrun and clean up via existingDrop-guard/self-close patterns (unchanged cleanup mechanism, just now actually exercised). No change to the target Jira site's production data (E2E-only site).Performance Impact
e2e.ymljob durationGET /myselfcall per mention test (×4, only when override unset)ci.yml)#[ignore]+JR_RUN_E2E-gated, inert inci.ymlRollback Instructions
Immediate rollback (< 5 min):
If feature-flagged: N/A — no feature flag; behavior is entirely test-infra/CI-scoped and reverting removes the self-discovery default, reverting to permanent clean-skip (safe, non-breaking).
Verification after rollback:
cargo test --test e2e_live --no-runstill compiles.e2e.yml'senv:block reverts to the pre-PR variable set; parent/child/edit-field/resolution variables stop being passed (tests continue clean-skipping as before this PR).Feature Flags
None.
Traceability
test_e2e_mention_*(4 tests)actionlint .github/workflows/e2e.ymlFull VSDD Contract Chain
AI Pipeline Metadata
Pipeline Details
Pre-Merge Checklist
src/change)git revert, no data migration)