Skip to content

Commit 83f0e38

Browse files
authored
feat(buds): edit impacted_repos from BUD detail (#195)
* feat(buds): per-BUD branch override for UAT / PROD tabs Lets a BUD opt out of the repo-wide tracking branch and watch its own pattern instead. Release-train BUDs whose PRs target ``release/*`` can be tracked without retraining the global repo setting; one-off ad-hoc release branches can be pointed at directly. Backend - New ``branch_overrides`` JSONB column on ``bud_documents``, keyed by stage (``uat`` / ``prod``) with fnmatch-style values. - ``BUDUpdate`` accepts ``branch_overrides`` and ``impacted_repos`` on PATCH. The validator rejects unknown stage keys, empty / whitespace patterns, and the wrong case (``"UAT"`` / ``"production"``) — typos surface as 422 at the API edge instead of silently missing in the filter. - PATCH handler MERGES branch_overrides per-key so saving a UAT override doesn't clobber an existing prod override; ``{"uat": null}`` is the clear-this-stage signal and drops the key from the merged column. Sending ``branch_overrides: null`` wipes both stages. - ``bud_prs.py::release_stage`` honors ``bud.branch_overrides[stage]`` before falling back to ``repo.uat_branch`` / ``repo.main_branch``. Reuses the existing ``branch_matches()`` fnmatch utility so wildcard semantics stay consistent with release detection. - Editing ``impacted_repos`` after the planning phase logs ``bud_impacted_repos_edited_post_planning`` so an operator can audit scope changes that may leave PR rows linked to repos that no longer appear on the stage tab. Frontend - New ``BUDStageBranchOverride.vue`` widget rendered inside the shared ``BUDReleaseStagePanel.vue``. Shows the currently-effective pattern (override or repo default), an Edit affordance opening an inline dialog, and — for multi-repo BUDs — a one-line "applies to N repos" disclosure so the per-stage scope is explicit before saving. - Save → PATCH ``/v1/buds/{id}`` with ``branch_overrides``; on success the panel re-fetches its stage data and emits ``refresh-bud`` so ``BUDDetail`` pulls the new override into the props feed. - Empty input on Save and the dedicated Clear button both send ``{[stage]: null}`` — the merge-aware handler drops just that key. Schema validator covered by 16 new parametrized tests. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> * docs(buds): tracking-branch hint explains the BUD-number content guard The override widget previously described only the fnmatch half of the filter ("Open PRs are matched against this pattern on each impacted repo. Supports fnmatch wildcards"). After the over-match fix added the content guard that requires PRs to also reference the BUD by number, that copy understated what surfaces a PR on a release-stage tab. Rewrites the in-card hint and the dialog body so both rules are visible. The example is now per-BUD: BUD-4 sees ``release/bud-001-bud-004``, BUD-12 sees ``release/bud-001-bud-012``. Threads ``bud_number`` from BUDDetail → BUDReleaseStagePanel → the override widget so the copy renders the actual reference, not a placeholder. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> * refactor(buds): derive branch_overrides allowed keys from ReleaseStage The validator hardcoded ``{"uat", "prod"}`` next to a comment that already pointed at the existing ``Literal["uat", "prod"]`` in ``app.schemas.bud_release``. Drop the duplicate and source the allowed-keys set from ``get_args(ReleaseStage)`` so adding a new release-stage tab in one place automatically widens the override contract — no second edit needed, no drift possible. Behaviour and tests unchanged (16 cases pass). Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> * feat(buds): edit impacted_repos from BUD detail The tech-arch agent's initial guess at which repos a BUD touches is often close but not perfect — and scope sometimes drifts mid-build. This adds an Edit affordance next to the impacted-repos chip row on ``BUDDevelopmentPanel.vue`` so a PM can correct the list without re-running the agent or hand-editing JSON. UI - ``BUDImpactedReposDialog.vue`` multi-select bound to the org's ``active`` tracked repos. Filter-by-name field, in-flight loader, per-row checkbox, count chip with ``aria-live`` so screen readers announce selection changes. - Repos already on the BUD but no longer ``active`` in the tracked list render as separate "retired" rows, pre-checked. The user consciously preserves or drops them — never silently erased. - Always refreshes the tracked-repo list on open (one cheap GET) so a repo added in another tab shows up in the picker before driving a destructive PATCH. In-flight ``fetchRepos`` is memoised so reopen-during-load is safe. - Save → ``PATCH /v1/buds/{id}`` with the full new list. Clearing every row when the BUD previously had repos requires a confirm prompt — release-stage tabs and code-review status both go quiet on empty, so the consequence is surfaced before the write lands. - Empty-state copy on the chip row is reworded to read in any phase (the previous imperative read as tech_arch-only). Plumbing - ``BUDDevelopmentPanel`` emits ``refresh-bud`` after a successful save; ``BUDDetail`` calls ``budStore.fetchBUD`` so the chip row updates everywhere and any downstream panel relying on ``bud.impacted_repos`` picks up the new list. - Dialog is non-persistent so Esc and backdrop-click work; the ``persistent`` flag only activates during the in-flight save. Backend already accepts ``impacted_repos`` on the ``BUDUpdate`` PATCH schema and logs ``bud_impacted_repos_edited_post_planning`` for mid-development edits — both landed in the per-BUD branch override PR this stacks on. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> * fix(buds): v-if/v-else adjacency in BUDImpactedReposDialog Vite's Vue compiler rejected the no-matches placeholder because the literal '.' between the inline v-if span and the v-else span counted as a text node, breaking the v-if/v-else adjacency rule. Move the two variants into <template v-if> / <template v-else> wrappers so the directive pair sits on directly adjacent siblings with no intervening text. Vue 3 template-compile rule — both branches still render the same wording, only the wrapping element changes. Found at runtime while testing the integration branch — would have blocked the picker from rendering whenever the filter input matched zero repos or no repos were tracked yet. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> * fix(buds): expose Impacted Repos Edit on the activity view The Edit affordance only lived inside the empty-state branch of ``BUDDevelopmentPanel``. Once a BUD had any dev activity (commits, PRs, todos), the panel switched to the has-activity template and the button vanished — leaving no way to correct ``impacted_repos`` from the UI on a BUD that had moved past tech_arch. Reproduced on BUD-004 after promotion to development. Adds a compact impacted-repos row at the top of the activity view with the same Edit button. Mirrors the empty-state semantics — chip list when the array has entries, "none yet" otherwise — but slimmer so it sits cleanly above the stats grid. Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com> --------- Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
1 parent 6601034 commit 83f0e38

3 files changed

Lines changed: 378 additions & 3 deletions

File tree

frontend/src/components/buds/BUDDevelopmentPanel.vue

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,23 @@
3434
<MCPSetupHint purpose="development" class="mb-4" />
3535

3636
<!-- Impacted repos hint -->
37-
<div v-if="impactedRepos && impactedRepos.length" class="mb-4">
38-
<div class="text-caption text-medium-emphasis mb-1">Impacted repositories</div>
39-
<div class="d-flex ga-2 flex-wrap justify-center">
37+
<div class="mb-4">
38+
<div class="d-flex align-center justify-center ga-2 mb-1">
39+
<span class="text-caption text-medium-emphasis">Impacted repositories</span>
40+
<v-btn
41+
size="x-small"
42+
variant="text"
43+
density="compact"
44+
prepend-icon="mdi-pencil-outline"
45+
@click="editReposOpen = true"
46+
>
47+
Edit
48+
</v-btn>
49+
</div>
50+
<div
51+
v-if="impactedRepos && impactedRepos.length"
52+
class="d-flex ga-2 flex-wrap justify-center"
53+
>
4054
<v-chip
4155
v-for="r in impactedRepos"
4256
:key="r.repo_id || r.repo_name"
@@ -47,6 +61,10 @@
4761
{{ r.repo_name }}
4862
</v-chip>
4963
</div>
64+
<div v-else class="text-caption text-medium-emphasis text-center">
65+
No impacted repositories yet. Add the repos this BUD touches
66+
so PRs and merges link back here.
67+
</div>
5068
</div>
5169

5270
<div class="d-flex ga-2 justify-center">
@@ -95,6 +113,37 @@
95113
<div v-if="branchCopied" class="text-caption text-success d-flex align-center ml-1">Copied!</div>
96114
</div>
97115

116+
<!-- Impacted repos — same Edit affordance as the empty-state. Once
117+
a BUD has activity the user can still need to correct scope
118+
(tech-arch guessed wrong, repos added mid-build); without this
119+
row there is no place to do so on the development tab. -->
120+
<div class="d-flex align-center flex-wrap ga-2 mb-4 dev-panel__impacted-row">
121+
<v-icon icon="mdi-source-repository-multiple" size="16" color="medium-emphasis" />
122+
<span class="text-caption text-medium-emphasis">Impacted:</span>
123+
<template v-if="impactedRepos && impactedRepos.length">
124+
<v-chip
125+
v-for="r in impactedRepos"
126+
:key="r.repo_id || r.repo_name"
127+
size="x-small"
128+
variant="tonal"
129+
prepend-icon="mdi-source-repository"
130+
>
131+
{{ r.repo_name }}
132+
</v-chip>
133+
</template>
134+
<span v-else class="text-caption text-medium-emphasis">none yet</span>
135+
<v-spacer />
136+
<v-btn
137+
size="x-small"
138+
variant="text"
139+
density="compact"
140+
prepend-icon="mdi-pencil-outline"
141+
@click="editReposOpen = true"
142+
>
143+
Edit
144+
</v-btn>
145+
</div>
146+
98147
<!-- Stats Row: only real, useful numbers -->
99148
<div class="stats-row mb-5">
100149
<div class="stat-card">
@@ -232,11 +281,19 @@
232281
</v-expansion-panel>
233282
</v-expansion-panels>
234283
</template>
284+
285+
<BUDImpactedReposDialog
286+
v-model="editReposOpen"
287+
:bud-id="budId"
288+
:current="impactedRepos ?? null"
289+
@saved="$emit('refresh-bud')"
290+
/>
235291
</div>
236292
</template>
237293

238294
<script setup lang="ts">
239295
import { computed, onMounted, ref, watch } from 'vue'
296+
import BUDImpactedReposDialog from '@/components/buds/BUDImpactedReposDialog.vue'
240297
import { useBudActivity } from '@/composables/useBudActivity'
241298
import { useSettingsStore } from '@/stores/settings'
242299
import { fileColor, fileIcon, parseFiles, timeAgo } from '@/utils/dev-activity-helpers'
@@ -252,8 +309,13 @@ const props = defineProps<{
252309
253310
defineEmits<{
254311
(e: 'download-tech-spec'): void
312+
/** Fired after the user saves an impacted_repos edit. Parent reloads
313+
* the BUD so the chip row updates everywhere. */
314+
(e: 'refresh-bud'): void
255315
}>()
256316
317+
const editReposOpen = ref(false)
318+
257319
// When QA automation is enabled for the org, the testing tab pulls
258320
// commits from QA-role users into its own activity stream. We exclude
259321
// those rows from the dev tab so the same commit doesn't appear twice.

0 commit comments

Comments
 (0)