specrails-desktop 2.49.2 → 2.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/client/dist/assets/{ActivityFeedPage-CyOxof-U.js → ActivityFeedPage-tWhhCACX.js} +1 -1
  2. package/client/dist/assets/{AgentBrowserCapture-DBGaKW4u.js → AgentBrowserCapture-CZeFRg-Y.js} +1 -1
  3. package/client/dist/assets/{AgentModeAnalyticsPane-WZpPaNMa.js → AgentModeAnalyticsPane-u4ux0xLJ.js} +2 -2
  4. package/client/dist/assets/{AgentModeCodePane-nHYnJUdP.js → AgentModeCodePane-wn1RDZ4y.js} +2 -2
  5. package/client/dist/assets/{AgentModeJobsPane-DkhcBtoZ.js → AgentModeJobsPane-CXLikWKG.js} +1 -1
  6. package/client/dist/assets/{AgentsPage-CX90q-nY.js → AgentsPage-zjVv7i9S.js} +1 -1
  7. package/client/dist/assets/{AnalyticsPage-DisrtBr7.js → AnalyticsPage-Bf2kyeeR.js} +1 -1
  8. package/client/dist/assets/{CodePage-ixqxftCT.js → CodePage-Db7jdyKI.js} +1 -1
  9. package/client/dist/assets/{DesktopAnalyticsPage-BFB-Pn-D.js → DesktopAnalyticsPage-CnF_SL4h.js} +1 -1
  10. package/client/dist/assets/{DocsDialog-BwiFrwEJ.js → DocsDialog-DJ0ATgNN.js} +1 -1
  11. package/client/dist/assets/{DocsPage-ByS0ZCco.js → DocsPage-I5217wRs.js} +1 -1
  12. package/client/dist/assets/{ExportDropdown-IPutNYvD.js → ExportDropdown-D1JqZXMo.js} +1 -1
  13. package/client/dist/assets/{InteractiveJobComposer-BEok7ntM.js → InteractiveJobComposer-Ccyfbaj1.js} +1 -1
  14. package/client/dist/assets/{JobDetailModal-BnO_V8KR.js → JobDetailModal-BV0oXeR1.js} +1 -1
  15. package/client/dist/assets/{JobDetailPage-BnS8aHKD.js → JobDetailPage-B6muzK7f.js} +1 -1
  16. package/client/dist/assets/{JobsPage-BC0mq-MF.js → JobsPage-z7YjsSNs.js} +1 -1
  17. package/client/dist/assets/{LoopBuilderPage-WmqNpDw2.js → LoopBuilderPage-cu6qiv41.js} +1 -1
  18. package/client/dist/assets/{LoopPreviewModal-BY5dDezY.js → LoopPreviewModal-Cz9CT647.js} +1 -1
  19. package/client/dist/assets/{LoopsPage-BcegqJyY.js → LoopsPage-C1PYYWno.js} +1 -1
  20. package/client/dist/assets/{MinimizedChatsContext-CX1i2kQ8.js → MinimizedChatsContext-D-wIfWD7.js} +1 -1
  21. package/client/dist/assets/{PluginsPage-BhG3qPda.js → PluginsPage-Bib-5fVd.js} +1 -1
  22. package/client/dist/assets/{ProjectSettingsDialog-JQdLwCy2.js → ProjectSettingsDialog-K5_ufJmm.js} +1 -1
  23. package/client/dist/assets/{RepositoryDeliveries-DqmlG0Zs.js → RepositoryDeliveries-BVROW9bi.js} +1 -1
  24. package/client/dist/assets/{RepositoryScopeSelector-DG8KKPrm.js → RepositoryScopeSelector-Qq2dUoCl.js} +1 -1
  25. package/client/dist/assets/ReviewPacketPage-tvI4dFpw.js +4 -0
  26. package/client/dist/assets/{TemplatePreviewModal-1AVv-hu4.js → TemplatePreviewModal-CV3fxeb1.js} +1 -1
  27. package/client/dist/assets/{TicketDetailModalContext-D2MEhRwJ.js → TicketDetailModalContext-BSpIHNsJ.js} +1 -1
  28. package/client/dist/assets/{Trans-BLljsfHR.js → Trans-BZWwRo1V.js} +1 -1
  29. package/client/dist/assets/{agent-D3jkMipn.js → agent-1Sh10wFp.js} +1 -1
  30. package/client/dist/assets/{agent-BII69Nc0.js → agent-BScqU3oM.js} +1 -1
  31. package/client/dist/assets/{agent-B76BhAyE.js → agent-Crfgp_yl.js} +1 -1
  32. package/client/dist/assets/{agent-CYFBDwFD.js → agent-CvaTFLGI.js} +1 -1
  33. package/client/dist/assets/{agent-CVeMLIu4.js → agent-DH5vUrkg.js} +1 -1
  34. package/client/dist/assets/{agent-BIO4q6b0.js → agent-DUuWo07e.js} +1 -1
  35. package/client/dist/assets/{agent-DSrflYFL.js → agent-MlnQFv-P.js} +1 -1
  36. package/client/dist/assets/{agent-ButS6kwl.js → agent-uG7w5crQ.js} +1 -1
  37. package/client/dist/assets/{formatDistanceToNow-CbQZkD8M.js → formatDistanceToNow-BHeWIeTB.js} +1 -1
  38. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-pBVK3rJC.js → getTimezoneOffsetInMilliseconds-DTT9YgCB.js} +1 -1
  39. package/client/dist/assets/index-D7f-4Igx.css +2 -0
  40. package/client/dist/assets/{index-DEqG4WdV.js → index-DnVIArMT.js} +34 -34
  41. package/client/dist/assets/{jira-api-f-sYCIuX.js → jira-api-CbYLdWCi.js} +1 -1
  42. package/client/dist/assets/packet-B8gXENJV.js +1 -0
  43. package/client/dist/assets/{packet-DF-iwqqY.js → packet-BTfIuMol.js} +1 -1
  44. package/client/dist/assets/{packet-BsRimSfZ.js → packet-CFjk65pR.js} +1 -1
  45. package/client/dist/assets/{packet-C8qzvgrZ.js → packet-CYoLyZf9.js} +1 -1
  46. package/client/dist/assets/{packet-r7J14m89.js → packet-D-v2OxI7.js} +1 -1
  47. package/client/dist/assets/{packet-DQ_GnT2b.js → packet-Dg7Ij21z.js} +1 -1
  48. package/client/dist/assets/{packet-C0pcMd6x.js → packet-tLvSufRp.js} +1 -1
  49. package/client/dist/assets/{packet-Nb8BLI5h.js → packet-zdb1UItU.js} +1 -1
  50. package/client/dist/assets/{project-repositories-DAjJbFhm.js → project-repositories-DC4nyJi7.js} +1 -1
  51. package/client/dist/assets/{spending-DJBbdJyD.js → spending-BT4ahFx2.js} +1 -1
  52. package/client/dist/assets/{useDesktop-BJ8IRBRt.js → useDesktop-DvDdw7oq.js} +1 -1
  53. package/client/dist/assets/{useRuntimeRuns-Ch1KUc7k.js → useRuntimeRuns-C6qGuPG6.js} +1 -1
  54. package/client/dist/assets/{useSharedWebSocket-BN3Lmehi.js → useSharedWebSocket-DkwOZsMN.js} +2 -2
  55. package/client/dist/index.html +17 -17
  56. package/docs/internals/safe-pr-review-flow.md +72 -0
  57. package/package.json +1 -1
  58. package/server/dist/agent-operator-prompt.js +16 -0
  59. package/server/dist/db.js +11 -0
  60. package/server/dist/loop-run-manager.js +4 -1
  61. package/server/dist/mcp/tools/rails.js +24 -0
  62. package/server/dist/multi-repo-delivery.js +4 -0
  63. package/server/dist/pr-follow-up-scope.js +224 -0
  64. package/server/dist/pr-follow-up.js +75 -0
  65. package/server/dist/rail-isolated-launch.js +3 -0
  66. package/server/dist/rail-launch-parser.js +12 -0
  67. package/server/dist/rail-pr-decision.js +20 -3
  68. package/server/dist/rail-pr-store.js +24 -3
  69. package/server/dist/rails-router.js +30 -2
  70. package/server/dist/review-packet.js +41 -0
  71. package/client/dist/assets/ReviewPacketPage-DSSi3x16.js +0 -4
  72. package/client/dist/assets/index-UDzndadF.css +0 -2
  73. package/client/dist/assets/packet-BTRySHoQ.js +0 -1
@@ -162,7 +162,13 @@ async function executePrDecision(deps, input) {
162
162
  return { status: 404, body: { error: 'Unknown prDeliveryId' } };
163
163
  if (row.parent_delivery_id && deps.repositoryChildOf !== row.parent_delivery_id)
164
164
  return { status: 409, body: { error: 'repository_delivery_requires_parent', parentDeliveryId: row.parent_delivery_id } };
165
- if (!row.parent_delivery_id && row.execution_manifest) {
165
+ // A manifest alone does not make a repository GROUP: a launch that failed
166
+ // before allocating a single repository delivery persists the parent with
167
+ // its manifest and zero children (observed: `git worktree add` refused
168
+ // because the PR branch was checked out in the main clone). Routing that
169
+ // parent into the group path 404'd on "no repositories" — which the UI read
170
+ // as "already resolved" — and left the rail pinned at pending_decision.
171
+ if (!row.parent_delivery_id && row.execution_manifest && (0, rail_pr_store_1.hasRepositoryDeliveries)(deps.db, row.id)) {
166
172
  const { executeRepositoryGroupDecision } = await Promise.resolve().then(() => __importStar(require('./multi-repo-delivery')));
167
173
  return executeRepositoryGroupDecision(deps, input);
168
174
  }
@@ -1525,6 +1531,16 @@ async function runDiscard(deps, row, opts = { removeNeedsReview: true }) {
1525
1531
  // borrows the PR/ticket lifecycle. Per-unit ownership below independently
1526
1532
  // prevents deletion of borrowed/pre-existing head refs for fresh replacements.
1527
1533
  const preserveExternalReview = row.is_continuation === 1;
1534
+ // A preparation failure never ran: no worktree, no branch, no commit, no PR.
1535
+ // Discarding it only closes the attempt and frees the rail — the spec keeps
1536
+ // the status it had (typically on_review from a still-open earlier PR), the
1537
+ // backlog/Jira are not told about a delivery that never existed, and no
1538
+ // resource cleanup can be owed. Distinct from implementation_failed, which
1539
+ // did run and did move the ticket.
1540
+ const preparationFailure = row.decision === 'pr_failed' &&
1541
+ row.implementation_outcome === 'failed' &&
1542
+ !row.pr_url && !row.delivery_sha && !preserveExternalReview &&
1543
+ snap.branches.length === 0 && snap.worktreeIds.length === 0;
1528
1544
  const cleanupWarnings = [];
1529
1545
  // A continuation borrows an existing PR/head. Discarding its local iteration
1530
1546
  // may remove Specrails' worktree, but never closes or deletes borrowed review
@@ -1620,7 +1636,7 @@ async function runDiscard(deps, row, opts = { removeNeedsReview: true }) {
1620
1636
  statusCode: cleanupWarnings.length > 0 ? 'cleanup_incomplete' : 'discarded',
1621
1637
  cleanupWarnings,
1622
1638
  };
1623
- const conflict = preserveExternalReview
1639
+ const conflict = preserveExternalReview || preparationFailure
1624
1640
  ? casTransition(deps, row, 'discarded', terminalPatch)
1625
1641
  : casTransitionWithTicketEffect(deps, row, 'discarded', terminalPatch, {
1626
1642
  deliveryId: row.id,
@@ -1634,7 +1650,7 @@ async function runDiscard(deps, row, opts = { removeNeedsReview: true }) {
1634
1650
  if (conflict)
1635
1651
  return conflict;
1636
1652
  pauseChainsOnDiscard(deps, row);
1637
- if (!preserveExternalReview)
1653
+ if (!preserveExternalReview && !preparationFailure)
1638
1654
  applyTerminalTicketEffect(deps, row, 'discarded', cleanupWarnings);
1639
1655
  finalizeTransition(deps, row.id);
1640
1656
  return {
@@ -1643,6 +1659,7 @@ async function runDiscard(deps, row, opts = { removeNeedsReview: true }) {
1643
1659
  ok: true,
1644
1660
  decision: 'discarded',
1645
1661
  ...(cleanupWarnings.length > 0 ? { cleanupWarnings } : {}),
1662
+ ...(preparationFailure ? { preparationFailure: true } : {}),
1646
1663
  ...(preserveExternalReview ? { preservedBorrowedReview: true, preservedExternalReview: true } : {}),
1647
1664
  },
1648
1665
  };
@@ -10,6 +10,7 @@ exports.createPrDeliveryGeneration = createPrDeliveryGeneration;
10
10
  exports.failPrDeliveryAndRestoreSuperseded = failPrDeliveryAndRestoreSuperseded;
11
11
  exports.getPrDelivery = getPrDelivery;
12
12
  exports.getActivePrDeliveryByRail = getActivePrDeliveryByRail;
13
+ exports.hasRepositoryDeliveries = hasRepositoryDeliveries;
13
14
  exports.listActivePrDeliveries = listActivePrDeliveries;
14
15
  exports.listTerminalPrDeliveries = listTerminalPrDeliveries;
15
16
  exports.getLatestTerminalPrDeliveryTouchingTicketSet = getLatestTerminalPrDeliveryTouchingTicketSet;
@@ -30,6 +31,16 @@ exports.toPrDeliverySnapshot = toPrDeliverySnapshot;
30
31
  exports.toRailPrStateMessage = toRailPrStateMessage;
31
32
  exports.toPrDecisionCardEnvelope = toPrDecisionCardEnvelope;
32
33
  exports.missionPhaseForDecision = missionPhaseForDecision;
34
+ /**
35
+ * CRUD for the `rail_pr_deliveries` ledger (migration 36) — one row per isolated
36
+ * rail LAUNCH tracking the ask-first PR decision lifecycle. This is the single
37
+ * authoritative server-side state both decision surfaces (the rail row on the
38
+ * dashboard and the launching agent-chat conversation) read and write, so the
39
+ * two surfaces never desync and a refresh/restart never loses a pending
40
+ * decision. Pure data access; the PR plumbing lives in rail-pr-delivery /
41
+ * pr-publisher and the launch wiring in rail-isolated-launch.
42
+ */
43
+ const pr_follow_up_1 = require("./pr-follow-up");
33
44
  const ids_1 = require("./ids");
34
45
  const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
35
46
  /** Defensive per-ticket cap; contract-layer specs run long but bounded. */
@@ -88,11 +99,11 @@ function createPrDelivery(db, input) {
88
99
  loop_name, origin_surface, origin_conversation_id,
89
100
  implementation_outcome, delivery_outcome, status_code,
90
101
  is_continuation, supersedes_delivery_id, spec_snapshot,
91
- revision_note, revision_of, parent_delivery_id, repository_id, repository_path
102
+ revision_note, revision_of, parent_delivery_id, repository_id, repository_path, follow_up
92
103
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', 'pending',
93
- 'implementation_running', ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.railIndex, input.loopId ?? null, input.railKey, JSON.stringify(input.ticketIds), input.baseBranch, input.loopName, input.originSurface, input.originConversationId ?? null, input.isContinuation ? 1 : 0, input.supersedesDeliveryId ?? null, input.specSnapshot && input.specSnapshot.length > 0
104
+ 'implementation_running', ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.railIndex, input.loopId ?? null, input.railKey, JSON.stringify(input.ticketIds), input.baseBranch, input.loopName, input.originSurface, input.originConversationId ?? null, input.isContinuation ? 1 : 0, input.supersedesDeliveryId ?? null, input.specSnapshot && input.specSnapshot.length > 0
94
105
  ? JSON.stringify(boundSpecSnapshot(input.specSnapshot))
95
- : null, input.revisionNote ? boundRevisionNote(input.revisionNote) : null, input.revisionOf ?? null, input.parentDeliveryId ?? null, input.repositoryId ?? null, input.repositoryPath ?? null);
106
+ : null, input.revisionNote ? boundRevisionNote(input.revisionNote) : null, input.revisionOf ?? null, input.parentDeliveryId ?? null, input.repositoryId ?? null, input.repositoryPath ?? null, input.followUp ? JSON.stringify(input.followUp) : null);
96
107
  return getPrDelivery(db, id);
97
108
  }
98
109
  class PrDeliveryGenerationConflict extends Error {
@@ -203,6 +214,15 @@ function getActivePrDeliveryByRail(db, railIndex) {
203
214
  .get(railIndex);
204
215
  }
205
216
  /** Every non-terminal delivery — hydrates the GET /rails prDeliveries snapshot. */
217
+ /** True when a delivery group has at least one per-repository child row. A
218
+ * parent that carries an execution manifest but no children is a launch that
219
+ * failed BEFORE any repository delivery was allocated (worktree add refused,
220
+ * DB setup error…) — it must be decided as an ordinary single row, not routed
221
+ * to the per-repository group machinery, which has nothing to act on. */
222
+ function hasRepositoryDeliveries(db, parentId) {
223
+ const row = db.prepare('SELECT 1 AS present FROM rail_pr_deliveries WHERE parent_delivery_id = ? LIMIT 1').get(parentId);
224
+ return Boolean(row);
225
+ }
206
226
  function listActivePrDeliveries(db) {
207
227
  return db
208
228
  .prepare(`SELECT * FROM rail_pr_deliveries
@@ -597,6 +617,7 @@ function toPrDeliverySnapshot(row) {
597
617
  runIds: parseJsonArray(row.run_ids ?? '[]'),
598
618
  originSurface: row.origin_surface,
599
619
  originConversationId: row.origin_conversation_id,
620
+ followUp: (0, pr_follow_up_1.readFollowUp)(row.follow_up),
600
621
  createdAt: row.created_at,
601
622
  updatedAt: row.updated_at,
602
623
  };
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.prDeliveryRevisionAllowed = prDeliveryRevisionAllowed;
7
7
  exports.createRailsRouter = createRailsRouter;
8
+ const pr_follow_up_1 = require("./pr-follow-up");
8
9
  const agent_runtime_settings_1 = require("./agent-runtime-settings");
9
10
  const path_1 = __importDefault(require("path"));
10
11
  const express_1 = require("express");
@@ -477,7 +478,29 @@ function createRailsRouter() {
477
478
  return;
478
479
  }
479
480
  let { mode = 'implement' } = req.body ?? {};
480
- const { repositoryIds: rawRepositoryIds, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch } = req.body ?? {};
481
+ const { repositoryIds: rawRepositoryIds, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch, followUp: rawFollowUp } = req.body ?? {};
482
+ // PR review follow-up (pr-follow-up-fixes): a typed, bounded, FROZEN scope
483
+ // for "resolve these review comments". It rides the delivery row and every
484
+ // ai-step prompt — never the spec, whose description Jira-linked projects
485
+ // sync. It needs something to follow up ON: an explicit target PR or the
486
+ // delivery being revised.
487
+ let followUp;
488
+ if (rawFollowUp !== undefined && rawFollowUp !== null) {
489
+ try {
490
+ followUp = (0, pr_follow_up_1.freezeFollowUp)((0, pr_follow_up_1.parseFollowUpInput)(rawFollowUp));
491
+ }
492
+ catch (err) {
493
+ if (err instanceof pr_follow_up_1.FollowUpValidationError) {
494
+ res.status(400).json({ error: 'invalid_follow_up', code: err.code, detail: err.message });
495
+ return;
496
+ }
497
+ throw err;
498
+ }
499
+ if (typeof targetPrNumber !== 'number' && !revisionOfDeliveryId) {
500
+ res.status(400).json({ error: 'follow_up_requires_target', detail: 'followUp needs the pull request to follow up on: pass targetPrNumber (an open PR) or revisionOfDeliveryId (an undecided delivery)' });
501
+ return;
502
+ }
503
+ }
481
504
  // Revision launch (nontech-review-experience Wave 3): the user asked for a
482
505
  // change to a delivery that is already awaiting their decision. Shape is
483
506
  // validated here; the narrow guard exemption is enforced below.
@@ -587,6 +610,10 @@ function createRailsRouter() {
587
610
  res.status(400).json({ error: 'target_pr_requires_pr_mode', detail: 'targetPrNumber requires loops and PR delivery to be enabled' });
588
611
  return;
589
612
  }
613
+ if (followUp && (!(0, feature_flags_1.isLoopsEnabled)() || !(0, rail_isolation_1.isRailPrDeliveryEnabled)())) {
614
+ res.status(400).json({ error: 'follow_up_requires_pr_mode', detail: 'a PR review follow-up runs on the isolated PR-delivery path; loops and PR delivery must be enabled' });
615
+ return;
616
+ }
590
617
  // Interactive in-job chat is ON by default for providers that expose the
591
618
  // spawn-time gate in QueueManager (kill-switch + persistent-stdin
592
619
  // capability) decides, so the launch no longer passes an explicit flag. A
@@ -1039,8 +1066,9 @@ function createRailsRouter() {
1039
1066
  // Revision of an undecided delivery: the guard above proved the
1040
1067
  // exemption, so pass the contract through for supersession.
1041
1068
  ...(revisionRequest ? { revision: revisionRequest } : {}),
1069
+ ...(followUp ? { followUp } : {}),
1042
1070
  });
1043
- res.status(202).json({ loopRunIds: ids, railIndex, mode, isolated: true });
1071
+ res.status(202).json({ loopRunIds: ids, railIndex, mode, isolated: true, ...(followUp ? { followUp: { id: followUp.id, version: followUp.version, hash: followUp.hash } } : {}) });
1044
1072
  return;
1045
1073
  }
1046
1074
  catch (err) {
@@ -5,6 +5,36 @@ exports.selectVariant = selectVariant;
5
5
  exports.computeDriftNudges = computeDriftNudges;
6
6
  exports.composeReviewPacket = composeReviewPacket;
7
7
  exports.packetHasUnsourcedNumericClaim = packetHasUnsourcedNumericClaim;
8
+ /**
9
+ * Review-packet composer (nontech-review-experience Wave 2).
10
+ *
11
+ * Turns a settled delivery generation into a structured document a
12
+ * non-technical person can judge — with ZERO model calls and zero live-store
13
+ * reads. Everything comes from durable rows: the launch-time spec snapshot, the
14
+ * settle-time evidence harvest, per-unit outcomes, provenance line stats, and
15
+ * the invocation cost sum. Composing is therefore free, idempotent, and
16
+ * reproducible after a restart.
17
+ *
18
+ * THE HONESTY CONTRACT (the reason this module is shaped like this):
19
+ *
20
+ * Verification claims are split into three tiers by their SOURCE, and the tier
21
+ * travels with the claim so the UI can label it:
22
+ *
23
+ * app-verified — Specrails measured it (diff stats, changed test files,
24
+ * the decider's routed verdict). Trustworthy.
25
+ * ai-reported — the agent said so (the VERIFICATION sentinel, its verify
26
+ * output). Rendered with an explicit "we did not run this"
27
+ * caveat, never as fact.
28
+ * reviewer-score— sr-reviewer's own confidence-score.json. Labelled as the
29
+ * AI grading itself, surfacing the documented
30
+ * human-review band.
31
+ *
32
+ * A numeric verification claim (e.g. "68 tests passed") is NEVER emitted: no
33
+ * structured source for it exists today, so scraping it out of prose would
34
+ * launder a self-report into a measurement. `testEvidence` states which test
35
+ * FILES changed, which is all the diff can actually prove.
36
+ */
37
+ const pr_follow_up_1 = require("./pr-follow-up");
8
38
  const delivery_evidence_1 = require("./delivery-evidence");
9
39
  const rail_pr_store_1 = require("./rail-pr-store");
10
40
  const pr_body_1 = require("./pr-body");
@@ -244,6 +274,14 @@ function computeDriftNudges(input) {
244
274
  }
245
275
  return nudges;
246
276
  }
277
+ /** Where a follow-up run's `FOLLOW-UP REPORT` can be found after settle: the
278
+ * harvested verify tails (the final reply of record). Nothing else is guessed. */
279
+ function followUpReportSource(evidence) {
280
+ if (!evidence)
281
+ return null;
282
+ const parts = evidence.units.flatMap((unit) => [unit.verifyTail, unit.sentinelDetail]).filter((v) => typeof v === 'string' && v.length > 0);
283
+ return parts.length > 0 ? parts.join('\n') : null;
284
+ }
247
285
  function composeReviewPacket({ db, row, repositoryId = row.repository_id ?? undefined, includeLegacyProvenance = false }) {
248
286
  const repository = repositoryId ? { repositoryId, includeLegacy: includeLegacyProvenance } : undefined;
249
287
  const ticketIds = safeParse(row.ticket_ids, []);
@@ -251,6 +289,7 @@ function composeReviewPacket({ db, row, repositoryId = row.repository_id ?? unde
251
289
  const runIds = safeParse(row.run_ids, []);
252
290
  const snapshot = (0, rail_pr_store_1.readSpecSnapshot)(row.spec_snapshot);
253
291
  const evidence = (0, delivery_evidence_1.readSettleEvidence)(row.settle_evidence);
292
+ const followUp = (0, pr_follow_up_1.readFollowUp)(row.follow_up);
254
293
  const churn = churnForRuns(db, runIds, repository);
255
294
  const variant = selectVariant(row, units);
256
295
  const confidence = (evidence?.units ?? [])
@@ -346,6 +385,8 @@ function composeReviewPacket({ db, row, repositoryId = row.repository_id ?? unde
346
385
  runIds,
347
386
  supersedesDeliveryId: row.supersedes_delivery_id,
348
387
  revisionNote: row.revision_note,
388
+ followUp,
389
+ followUpReport: followUp ? (0, pr_follow_up_1.parseFollowUpReport)(followUpReportSource(evidence), followUp) : null,
349
390
  versions,
350
391
  chainCostUsd: chainCosts.length > 0 ? chainCosts.reduce((sum, value) => sum + value, 0) : null,
351
392
  chainCostEstimated: versions.some((version) => version.costEstimated),
@@ -1,4 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{d as r,f as i,u as a}from"./chunk-KS7C4IRE-DEKu087k.js";import{B as ee,U as te,n as ne}from"./TicketDetailModalContext-D2MEhRwJ.js";import{n as re,t as o}from"./createLucideIcon-Bjbbp_KS.js";import{a as s,r as ie}from"./useDesktop-BJ8IRBRt.js";import{C as ae,E as oe,b as se,r as ce,t as le,w as ue}from"./RepositoryDeliveries-DqmlG0Zs.js";import{n as de}from"./save-B6wGWwBR.js";import{u as fe}from"./MinimizedChatsContext-CX1i2kQ8.js";import{t as c}from"./loader-circle-DcygOQpC.js";import{t as l}from"./sparkles-DrY8hb0X.js";import{t as u}from"./triangle-alert-H9T4jAmh.js";import{t as d}from"./utils-BC4DHlz2.js";import{n as f,t as p}from"./lib-CGdyxIWg.js";import{Dt as pe,Gt as me,Rt as m,d as he,dt as h,zt as g}from"./index-DEqG4WdV.js";var ge=o(`circle-dollar-sign`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8`,key:`1h4pet`}],[`path`,{d:`M12 18V6`,key:`zqpxq5`}]]),_=o(`file-diff`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M9 10h6`,key:`9gxzsh`}],[`path`,{d:`M12 13V7`,key:`h0r20n`}],[`path`,{d:`M9 17h6`,key:`r8uit2`}]]),v=e(t(),1),y=n();function b(e){return e.split(/\n{2,}/).map(e=>{if(e.includes(`
2
- `))return e;let t=e.match(/(?:^|\s)(\d{1,2})\.\s+(?=\S)/g);return!t||t.length<3?e:e.replace(/\s+(\d{1,2})\.\s+(?=\S)/g,(e,t,n)=>n===0?e:`\n${t}. `)}).join(`
3
-
4
- `)}function _e({children:e,muted:t=!1,className:n}){let r=(0,v.useMemo)(()=>({a:({href:e,children:t})=>(0,y.jsx)(`a`,{href:e,target:`_blank`,rel:`noreferrer`,className:`text-accent-primary underline decoration-accent-primary/40 underline-offset-2 hover:decoration-accent-primary`,children:t}),img:()=>null,code:({className:e,children:t})=>e?(0,y.jsx)(`code`,{className:e,children:t}):(0,y.jsx)(`code`,{className:`rounded-md border border-border/50 bg-background-deep/60 px-1.5 py-px font-mono text-[0.85em] text-accent-info`,children:t})}),[]);return(0,y.jsx)(`div`,{"data-testid":`packet-markdown`,className:d(`prose prose-invert prose-sm max-w-none leading-relaxed`,`prose-p:my-1.5 prose-headings:mt-3 prose-headings:mb-1 prose-headings:text-sm prose-headings:font-semibold`,`prose-strong:text-foreground prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-li:marker:text-accent-primary/70`,`prose-pre:my-2 prose-pre:rounded-lg prose-pre:border prose-pre:border-border/50 prose-pre:bg-background-deep/60 prose-pre:text-xs`,`prose-blockquote:border-accent-primary/40 prose-blockquote:text-muted-foreground prose-hr:border-border/50`,t?`text-muted-foreground`:`text-foreground/85`,n),children:(0,y.jsx)(f,{remarkPlugins:[p],skipHtml:!0,components:r,children:b(e)})})}var x=(e={})=>({verbs:[],acceptMeaning:null,fineControlOnly:!1,inFlight:!1,terminal:!1,requiresIrreversibleConfirm:!1,...e});function ve(e){let{decision:t,presentation:n,prUrl:r,canCreatePr:i}=e;if(t===`building`)return x({inFlight:!0});if(t===`merged`||t===`discarded`||n.superseded)return x({terminal:!0});if(n.manualRecovery||n.recoveryUnavailable||n.recoveryRecheck||n.retryablePush||n.retryablePrCreation||n.closed||n.deliveryBlocked&&!n.implementationFailed)return x({fineControlOnly:!0});if(n.noChanges)return x({verbs:[`accept`,`request-changes`],acceptMeaning:`mark-done`});if(n.implementationFailed)return x({verbs:n.succeededCount>0?[`request-changes`,`discard`]:[`discard`]});if(t===`pr_draft`)return x(r?{verbs:[`accept`,`request-changes`,`discard`],acceptMeaning:`publish`}:{fineControlOnly:!0});if(t===`pr_ready`)return x({verbs:[`request-changes`,`discard`]});if(t===`on_review`){let e=i?`create-pr`:`merge-local`;return x({verbs:[`accept`,`request-changes`,`discard`],acceptMeaning:e,requiresIrreversibleConfirm:e===`merge-local`})}return x({fineControlOnly:!0})}function ye(e,t){if(e===`discard`)return`discard`;if(e===`request-changes`)return null;switch(t.acceptMeaning){case`create-pr`:return`create-pr`;case`publish`:return`publish`;case`merge-local`:return`merge-local`;case`mark-done`:return`acknowledge-no-changes`;default:return null}}var be=[`app-verified`,`ai-reported`,`reviewer-score`],xe={"app-verified":h,"ai-reported":l,"reviewer-score":_},Se={"app-verified":`text-accent-success border-accent-success/30 bg-accent-success/5`,"ai-reported":`text-accent-highlight border-accent-highlight/30 bg-accent-highlight/5`,"reviewer-score":`text-accent-info border-accent-info/30 bg-accent-info/5`};function S({item:e}){let{t}=re(`packet`);return(0,y.jsxs)(`li`,{className:`flex flex-col gap-1 py-1.5`,children:[(0,y.jsx)(`span`,{className:`text-sm text-foreground`,children:t(e.code,{...e.values??{},defaultValue:e.code})}),e.rawExcerpt?(0,y.jsx)(`pre`,{className:`max-h-32 overflow-auto rounded border border-border/60 bg-background-deep/60 p-2 text-[11px] leading-relaxed text-muted-foreground whitespace-pre-wrap`,children:e.rawExcerpt}):null]})}function Ce(e){return e.implementationOutcome===`failed`?`failed`:e.deliveryOutcome===`no_changes`||e.implementationOutcome===`succeeded`&&e.changed===!1?`noChanges`:e.deliveryOutcome===`not_started`?`notStarted`:e.implementationOutcome===`succeeded`?`succeeded`:`unknown`}var we={succeeded:`border-accent-success/40 bg-accent-success/10 text-accent-success`,noChanges:`border-accent-info/40 bg-accent-info/10 text-accent-info`,failed:`border-destructive/40 bg-destructive/10 text-destructive`,notStarted:`border-border bg-background-deep/50 text-muted-foreground`,unknown:`border-border bg-background-deep/50 text-muted-foreground`};function C({title:e,children:t,defaultOpen:n=!1,badge:r}){let[i,a]=(0,v.useState)(n);return(0,y.jsxs)(`div`,{className:`rounded-lg border border-border/60 bg-surface/40`,children:[(0,y.jsxs)(`button`,{type:`button`,onClick:()=>a(e=>!e),className:`flex w-full items-center gap-2 px-4 py-3 text-left`,"aria-expanded":i,children:[i?(0,y.jsx)(g,{className:`size-4 shrink-0 text-muted-foreground`,"aria-hidden":!0}):(0,y.jsx)(m,{className:`size-4 shrink-0 text-muted-foreground`,"aria-hidden":!0}),(0,y.jsx)(`span`,{className:`flex-1 text-sm font-medium text-foreground`,children:e}),r]}),i?(0,y.jsx)(`div`,{className:`border-t border-border/60 px-4 py-3`,children:t}):null]})}function w(e={}){let t=r(),n=e.prDeliveryId??t.prDeliveryId,{t:o}=re([`packet`,`common`]),l=a(),[d,f]=i(),p=d.get(`repositoryId`),m=(0,v.useRef)(0),h=e.onClose??(()=>l(`/`)),{activeProjectId:g}=ie(),_=ce(g),b=n?_.has(n):!1,{act:x,checkout:w}=he(),{openTicketDetail:Te}=ne(),T=JSON.stringify([g,n,p]),E=(0,v.useRef)({key:T,epoch:0});E.current.key!==T&&(E.current={key:T,epoch:E.current.epoch+1});let D=E.current.epoch,O=(0,v.useRef)(!0);(0,v.useEffect)(()=>(O.current=!0,()=>{O.current=!1}),[]);let k=(0,v.useCallback)(()=>O.current&&E.current.key===T&&E.current.epoch===D,[T,D]),[A,j]=(0,v.useState)(null),M=A?.key===T&&A.epoch===D?A:null,N=M?.data??null,P=M?.loading??!0,F=M?.error??null,[I,L]=(0,v.useState)(null),[Ee,R]=(0,v.useState)(!1),[z,B]=(0,v.useState)(null),[V,H]=(0,v.useState)(``),[U,W]=(0,v.useState)(!1),[De,G]=(0,v.useState)(null),[Oe,K]=(0,v.useState)(!1),q=(0,v.useCallback)(async()=>{if(!n||!g||!k())return;let e=++m.current,t=()=>k()&&m.current===e;j(e=>({key:T,epoch:D,data:e?.key===T&&e.epoch===D?e.data:null,loading:!0,error:null}));try{let e=p?`?repositoryId=${encodeURIComponent(p)}`:``,r=await fetch(`${s()}/rails/pr-deliveries/${n}/packet${e}`);if(!t())return;if(!r.ok){j({key:T,epoch:D,data:null,loading:!1,error:r.status===404?`notFound`:`loadFailed`});return}let i=await r.json();if(!t())return;j({key:T,epoch:D,data:i,loading:!1,error:null})}catch{if(!t())return;j({key:T,epoch:D,data:null,loading:!1,error:`loadFailed`})}},[n,g,p,T,D,k]);(0,v.useEffect)(()=>(q(),R(!1),K(!1),L(null),B(null),G(null),W(!1),H(``),()=>{m.current++}),[q]);let J=(0,v.useMemo)(()=>ue(N?.snapshot),[N?.snapshot]),Y=N?.packet??null,X=P||I!==null||U||!!J?.operation,Z=N?.acceptCapability??null,Q=(0,v.useMemo)(()=>{if(!Y)return null;let e=oe({decision:Y.decision,ticketIds:Y.ticketIds,prState:Y.prUrl?`pr-created`:`none`,statusCode:Y.statusCode,prUrl:Y.prUrl,runIds:Y.runIds});return ve({decision:Y.decision,presentation:e,prUrl:Y.prUrl,canCreatePr:Z?.target===`create-pr`})},[Y,Z]),$=(0,v.useCallback)(async e=>{if(!Y||!Q||X||!k())return;let t=ye(e,Q);if(t){B(null),L(e);try{let e=await(p&&J?x(J.railIndex,t,J.decision,J.prDeliveryId,p):x(Y.railIndex,t,Y.decision,Y.prDeliveryId));if(!k())return;e.ok?g&&se(g):B(e.status===409&&e.error===`stale_decision`?`alreadyResolved`:`actionFailed`),await q()}finally{k()&&(L(null),R(!1))}}},[x,g,q,Y,Q,J,p,X,k]),ke=(0,v.useCallback)(async()=>{if(!(!Y||!V.trim()||!g||X||!k())){G(null),W(!0);try{let e=await fetch(`${s()}/rails/${Y.railIndex}/launch`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({revisionOfDeliveryId:J?.prDeliveryId??Y.prDeliveryId,revisionNote:V.trim(),...J?.executionManifest?{repositoryIds:J.executionManifest.selectedRepositoryIds}:{}})});if(!k())return;if(!e.ok){let t=await e.json().catch(()=>null);if(!k())return;G(t?.error===`invalid_revision_target`?`revisionStale`:`revisionFailed`);return}H(``),K(!1),await q()}catch{k()&&G(`revisionFailed`)}finally{k()&&W(!1)}}},[g,q,Y,V,J,X,k]);if(P&&!Y)return(0,y.jsxs)(`div`,{className:`flex h-full items-center justify-center text-muted-foreground`,children:[(0,y.jsx)(c,{className:`mr-2 size-4 animate-spin`,"aria-hidden":!0}),o(`common:states.loading`)]});if(F||!Y||!Q)return(0,y.jsxs)(`div`,{className:`mx-auto flex max-w-2xl flex-col items-center gap-4 p-10 text-center`,children:[(0,y.jsx)(u,{className:`size-6 text-accent-warning`,"aria-hidden":!0}),(0,y.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:o(F===`notFound`?`error.notFound`:`error.loadFailed`)}),(0,y.jsx)(`button`,{type:`button`,onClick:h,className:`rounded-md border border-border px-3 py-1.5 text-sm text-foreground hover:bg-surface`,children:o(`backToBoard`)})]});let Ae=be.map(e=>({tier:e,items:Y.proof.filter(t=>t.tier===e)})).filter(e=>e.items.length>0),je=Y.cost.totalUsd===null?`—`:`${Y.cost.estimated?`~`:``}$${Y.cost.totalUsd.toFixed(2)}`;return(0,y.jsxs)(`div`,{className:`mx-auto flex w-full max-w-3xl flex-col gap-4 p-6`,children:[(0,y.jsxs)(`button`,{type:`button`,onClick:h,className:`flex w-fit items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground`,children:[(0,y.jsx)(de,{className:`size-3.5`,"aria-hidden":!0}),o(`backToBoard`)]}),J?.repositoryDeliveries?.length?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,children:[o(`common:repositories.select`),(0,y.jsxs)(`select`,{value:p??``,onChange:e=>{let t=new URLSearchParams(d);e.target.value?t.set(`repositoryId`,e.target.value):t.delete(`repositoryId`),f(t,{replace:!0})},className:`rounded border border-border bg-background px-2 py-1`,children:[(0,y.jsx)(`option`,{value:``,children:o(`common:repositories.all`)}),J.repositoryDeliveries.map(e=>(0,y.jsx)(`option`,{value:e.repositoryId,children:e.name},e.repositoryId))]})]}),(0,y.jsx)(le,{deliveryId:J.prDeliveryId,repositories:J.repositoryDeliveries,busy:X,onAct:async(e,t)=>{if(!(X||!k())){L(`accept`);try{let n=await x(J.railIndex,e,J.decision,J.prDeliveryId,t);if(!k())return;n.ok||B(`actionFailed`),await q()}finally{k()&&L(null)}}},onCheckout:async e=>{if(!(X||!k())){L(`accept`);try{let t=await w(J.railIndex,J.prDeliveryId,e);if(!k())return;t.ok||B(`actionFailed`),await q()}finally{k()&&L(null)}}}})]}):null,(0,y.jsxs)(`div`,{className:`rounded-xl border border-border/70 bg-surface/60 p-5 shadow-sm`,children:[(0,y.jsx)(`h1`,{className:`text-lg font-semibold text-foreground`,children:o(Y.headlineCode,{count:Y.totalCount,succeeded:Y.succeededCount,total:Y.totalCount,defaultValue:Y.headlineCode})}),(0,y.jsxs)(`div`,{className:`mt-2 flex flex-wrap items-center gap-2 text-xs`,children:[Y.confidence?.overall!==null&&Y.confidence?.overall!==void 0?(0,y.jsx)(`span`,{className:`inline-flex items-center gap-1 rounded-full border border-accent-info/30 bg-accent-info/10 px-2 py-0.5 text-accent-info`,title:o(`confidence.tooltip`),children:o(`confidence.pill`,{overall:Y.confidence.overall})}):(0,y.jsx)(`span`,{className:`inline-flex items-center gap-1 rounded-full border border-border bg-background-deep/50 px-2 py-0.5 text-muted-foreground`,children:o(`confidence.unavailable`)}),(0,y.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-muted-foreground`,children:[(0,y.jsx)(ge,{className:`size-3.5`,"aria-hidden":!0}),o(`cost.thisBuild`,{amount:je})]}),Y.prUrl?(0,y.jsxs)(`a`,{href:Y.prUrl,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 text-accent-info hover:underline`,children:[(0,y.jsx)(te,{className:`size-3.5`,"aria-hidden":!0}),o(`openPr`,{number:Y.prNumber??``})]}):null]}),Q.fineControlOnly?(0,y.jsx)(`p`,{className:`mt-4 rounded-md border border-accent-warning/30 bg-accent-warning/5 px-3 py-2 text-xs text-accent-warning`,children:o(`fineControlOnly`)}):Q.inFlight?(0,y.jsxs)(`p`,{className:`mt-4 flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,y.jsx)(c,{className:`size-4 animate-spin`,"aria-hidden":!0}),o(`stillWorking`)]}):Q.terminal?(0,y.jsx)(`p`,{className:`mt-4 text-sm text-muted-foreground`,children:o(`alreadyResolved`)}):(0,y.jsxs)(`div`,{className:`mt-4 flex flex-wrap items-center gap-2`,children:[Q.verbs.includes(`accept`)&&(!J?.repositoryDeliveries?.length||p)?Ee?(0,y.jsxs)(`div`,{className:`flex w-full flex-col gap-2 rounded-md border border-destructive/40 bg-destructive/5 p-3`,children:[(0,y.jsx)(`p`,{className:`text-xs text-foreground`,children:o(`accept.irreversibleWarning`)}),(0,y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,y.jsx)(`button`,{type:`button`,onClick:()=>void $(`accept`),disabled:X,className:`rounded-md bg-destructive px-3 py-1.5 text-sm font-medium text-white disabled:opacity-50`,"data-testid":`packet-accept-confirm`,children:o(`accept.irreversibleConfirm`)}),(0,y.jsx)(`button`,{type:`button`,onClick:()=>R(!1),className:`rounded-md border border-border px-3 py-1.5 text-sm text-foreground`,children:o(`common:actions.cancel`)})]})]}):(0,y.jsxs)(`button`,{type:`button`,onClick:()=>{Q.requiresIrreversibleConfirm?R(!0):$(`accept`)},disabled:X,className:`inline-flex items-center gap-1.5 rounded-md bg-accent-success px-3 py-1.5 text-sm font-medium text-white disabled:opacity-50`,"data-testid":`packet-accept`,children:[I===`accept`?(0,y.jsx)(c,{className:`size-4 animate-spin`,"aria-hidden":!0}):(0,y.jsx)(fe,{className:`size-4`,"aria-hidden":!0}),o(`accept.${Q.acceptMeaning??`generic`}`)]}):null,Q.verbs.includes(`request-changes`)?(0,y.jsxs)(`button`,{type:`button`,onClick:()=>K(e=>!e),disabled:X||U,"aria-expanded":Oe,className:`inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm text-foreground hover:bg-surface disabled:opacity-50`,"data-testid":`packet-request-changes`,children:[(0,y.jsx)(ee,{className:`size-4`,"aria-hidden":!0}),o(`requestChanges.label`)]}):null,Q.verbs.includes(`discard`)?(0,y.jsxs)(`button`,{type:`button`,onClick:()=>void $(`discard`),disabled:X,className:`inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm text-foreground hover:bg-surface disabled:opacity-50`,"data-testid":`packet-discard`,children:[I===`discard`?(0,y.jsx)(c,{className:`size-4 animate-spin`,"aria-hidden":!0}):(0,y.jsx)(me,{className:`size-4`,"aria-hidden":!0}),o(`discard.label`)]}):null]}),b&&Q.verbs.includes(`discard`)?(0,y.jsx)(`p`,{className:`mt-3 rounded-md border border-accent-warning/40 bg-accent-warning/10 px-3 py-2 text-xs text-accent-warning`,"data-testid":`packet-discard-stacked-note`,children:o(`discard.stackedNote`)}):null,z?(0,y.jsx)(`p`,{className:`mt-3 text-xs text-accent-warning`,children:o(`error.${z}`)}):null,Oe?(0,y.jsxs)(`div`,{className:`mt-4 flex flex-col gap-2 rounded-md border border-border/70 bg-background-deep/40 p-3`,children:[(0,y.jsx)(`label`,{htmlFor:`packet-revision-note`,className:`text-xs font-medium text-foreground`,children:o(`requestChanges.prompt`)}),(0,y.jsx)(`textarea`,{id:`packet-revision-note`,value:V,onChange:e=>H(e.target.value),rows:2,placeholder:o(`requestChanges.placeholder`),className:`resize-y rounded-md border border-border bg-surface px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground`,"data-testid":`packet-revision-input`}),(0,y.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,y.jsxs)(`button`,{type:`button`,onClick:()=>void ke(),disabled:X||V.trim().length===0,className:`inline-flex items-center gap-1.5 rounded-md bg-accent-primary px-3 py-1.5 text-sm font-medium text-white disabled:opacity-50`,"data-testid":`packet-revision-submit`,children:[U?(0,y.jsx)(c,{className:`size-4 animate-spin`,"aria-hidden":!0}):(0,y.jsx)(ae,{className:`size-4`,"aria-hidden":!0}),o(`requestChanges.submit`)]}),(0,y.jsx)(`span`,{className:`text-[11px] text-muted-foreground`,children:o(`requestChanges.hint`)})]}),De?(0,y.jsx)(`p`,{className:`text-xs text-accent-warning`,children:o(`error.${De}`)}):null]}):null]}),Y.driftNudges.length>0?(0,y.jsxs)(`div`,{className:`rounded-lg border border-accent-highlight/30 bg-accent-highlight/5 p-4`,"data-testid":`packet-drift-nudges`,children:[(0,y.jsx)(`h2`,{className:`mb-1 text-sm font-medium text-accent-highlight`,children:o(`drift.title`)}),(0,y.jsx)(`ul`,{className:`space-y-1 text-xs text-foreground`,children:Y.driftNudges.map(e=>(0,y.jsx)(`li`,{children:o(e.code,{...e.values,defaultValue:e.code})},e.code))}),(0,y.jsx)(`p`,{className:`mt-2 text-[11px] text-muted-foreground`,children:o(`drift.advisory`)})]}):null,Y.versions.length>1?(0,y.jsxs)(C,{title:o(`versions.title`,{count:Y.versions.length}),badge:(0,y.jsx)(pe,{className:`size-3.5 text-muted-foreground`,"aria-hidden":!0}),children:[(0,y.jsx)(`ol`,{className:`space-y-2`,"data-testid":`packet-versions`,children:Y.versions.map(e=>(0,y.jsxs)(`li`,{className:`flex flex-col gap-0.5 text-sm`,children:[(0,y.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,y.jsx)(`span`,{className:e.current?`font-medium text-foreground`:`text-muted-foreground`,children:o(`versions.label`,{version:e.version})}),e.current?(0,y.jsx)(`span`,{className:`rounded-full border border-accent-primary/40 bg-accent-primary/10 px-1.5 text-[10px] text-accent-primary`,children:o(`versions.current`)}):null,(0,y.jsx)(`span`,{className:`text-[11px] text-muted-foreground`,children:e.costUsd===null?`—`:`${e.costEstimated?`~`:``}$${e.costUsd.toFixed(2)}`})]}),(0,y.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.revisionNote??o(`versions.originalBuild`)})]},e.prDeliveryId))}),(0,y.jsx)(`p`,{className:`mt-3 text-xs text-muted-foreground`,children:o(`cost.allVersions`,{amount:Y.chainCostUsd===null?`—`:`${Y.chainCostEstimated?`~`:``}$${Y.chainCostUsd.toFixed(2)}`})})]}):null,Y.watchOut.length>0?(0,y.jsxs)(`div`,{className:`rounded-lg border border-accent-warning/30 bg-accent-warning/5 p-4`,children:[(0,y.jsxs)(`h2`,{className:`mb-2 flex items-center gap-1.5 text-sm font-medium text-accent-warning`,children:[(0,y.jsx)(u,{className:`size-4`,"aria-hidden":!0}),o(`watchOut.title`)]}),(0,y.jsx)(`ul`,{className:`space-y-1`,children:Y.watchOut.map((e,t)=>(0,y.jsx)(S,{item:e},`${e.code}-${t}`))})]}):null,(0,y.jsx)(C,{title:o(`sections.whatYouAsked`),defaultOpen:!0,children:(0,y.jsx)(`ul`,{className:`space-y-3`,children:Y.sections.map(e=>(0,y.jsxs)(`li`,{className:`text-sm`,children:[(0,y.jsxs)(`button`,{type:`button`,onClick:()=>Te(e.ticketId),className:`text-accent-primary hover:underline`,children:[`#`,e.ticketId,` `,e.title??``]}),e.problem?(0,y.jsx)(_e,{muted:!0,className:`mt-1`,children:e.problem}):null]},e.ticketId))})}),(0,y.jsx)(C,{title:o(`sections.whatIDid`),children:(0,y.jsx)(`ul`,{className:`space-y-3`,children:Y.sections.map(e=>(0,y.jsxs)(`li`,{className:`text-sm`,"data-testid":`packet-done-${e.ticketId}`,children:[(0,y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,y.jsxs)(`span`,{className:`font-medium text-foreground`,children:[`#`,e.ticketId]}),e.title?(0,y.jsx)(`span`,{className:`text-foreground/80`,children:e.title}):null,(()=>{let t=Ce(e);return(0,y.jsx)(`span`,{className:`rounded-full border px-2 py-0.5 text-[10px] font-medium ${we[t]}`,"data-testid":`packet-outcome-pill`,children:o(`outcome.${t}`)})})()]}),e.churn?(0,y.jsxs)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:[o(`churn.line`,{files:e.churn.filesTouched,added:e.churn.addedLines,removed:e.churn.removedLines}),` · `,e.churn.testFilesTouched.length>0?o(`churn.testFiles`,{count:e.churn.testFilesTouched.length}):o(`churn.noTestFiles`)]}):(0,y.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:o(`churn.batchNotSplittable`)}),e.solution?(0,y.jsxs)(`details`,{className:`group mt-2 rounded-md border border-border/50 bg-background-deep/30 px-3 py-2`,children:[(0,y.jsx)(`summary`,{className:`cursor-pointer select-none text-xs font-medium text-muted-foreground group-open:text-foreground/80`,children:o(`planned.title`)}),(0,y.jsx)(`p`,{className:`mt-1 text-[11px] italic text-muted-foreground/80`,children:o(`planned.caveat`)}),(0,y.jsx)(_e,{className:`mt-2`,children:e.solutionOverflow??e.solution})]}):(0,y.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:o(`noSolutionRecorded`)})]},e.ticketId))})}),(0,y.jsx)(C,{title:o(`sections.howItWasChecked`),badge:Y.evidenceUnavailable?(0,y.jsx)(`span`,{className:`rounded-full border border-border bg-background-deep/50 px-2 py-0.5 text-[10px] text-muted-foreground`,children:o(`proof.evidenceUnavailable`)}):void 0,children:(0,y.jsx)(`div`,{className:`space-y-4`,children:Ae.map(({tier:e,items:t})=>{let n=xe[e];return(0,y.jsxs)(`div`,{className:`rounded-md border px-3 py-2 ${Se[e]}`,children:[(0,y.jsxs)(`h3`,{className:`mb-1 flex items-center gap-1.5 text-xs font-medium`,children:[(0,y.jsx)(n,{className:`size-3.5`,"aria-hidden":!0}),o(`tier.${e}.title`)]}),(0,y.jsx)(`p`,{className:`mb-2 text-[11px] opacity-80`,children:o(`tier.${e}.caveat`)}),(0,y.jsx)(`ul`,{className:`space-y-1`,children:t.map((e,t)=>(0,y.jsx)(S,{item:e},`${e.code}-${t}`))})]},e)})})})]})}export{w as default};