Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ expandable Live Trace progress. The immutable tag, production deployment, and
live verification remain separate delivery facts until their respective
release steps complete.

## 2026-07-22 — AXI-143 issue-local attention and human-readable references

Made issue-bound free-form action requests directly answerable from the issue
detail instead of forcing an operator through Command Center. Delivery
connection conflicts retain their richer Command Center workflow and now use
an explicit fallback label on the issue.

Added a shared issue-reference treatment that pairs the stable identifier with
the issue title. Applied it across dashboard attention, Inbox asks and waiting
items, Activity drawer asks, active runs, failed-run recovery, and running
timers so operational queues provide meaning without requiring operators to
memorize issue numbers.

Updated the release gate policy to use exact-head GitHub CI as the normal
release authority. Fast local checks remain the pre-push feedback loop; the
complete Docker and Playwright gate is now supplemental evidence for CI
outages, failure diagnosis, or environment-sensitive changes instead of a
routine duplicate of the hosted matrix.

## 2026-07-20 — v0.29.4 release preparation

Prepared the serialized patch release for AXI-139 and merged PR #81. Release
Expand Down
23 changes: 16 additions & 7 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,21 @@ Forge's explicit branch contract:

## Release Process

Use the fast quality gate while iterating:
Use the fast quality gate while iterating and before pushing a release candidate:

```bash
pnpm ci:local:quality
```

Before declaring a PR release-ready, run the complete local gate once:
The required release gate is GitHub CI on the exact PR head. Its lint,
typecheck, unit/integration, build, and Playwright jobs must finish successfully
before merge. After any new commit, wait for CI to rerun against that new SHA;
results from an earlier head do not qualify the release.

Do not routinely duplicate the complete CI matrix locally. Run the complete
local gate when GitHub Actions is unavailable, when diagnosing a CI failure, or
when a database, migration, browser, concurrency, or environment-sensitive
change warrants local integration evidence:

```bash
pnpm ci:local
Expand All @@ -74,7 +82,8 @@ the Prisma client, and runs lint, typecheck, and Vitest once. It then serializes
Playwright behind a portable machine-local lock and selects an available port.
`pnpm ci:local:e2e` runs only the E2E phase; add `-- --reuse-e2e-build` only when
the existing production build is known to match the current source. These
local commands complement, but do not replace, CI on the exact PR head.
local commands provide fast feedback or supplemental evidence; they never
replace exact-head CI when GitHub Actions is available.

### Authorized single-PR patch releases

Expand All @@ -101,10 +110,10 @@ an ordinary feature PR.
notes, and merges the release commit. Feature PRs must not race independent
version bumps or tags.

> CI note: GitHub-hosted Actions require billing to be enabled. If Actions are
> unavailable, gate the release on the equivalent local run
> (`pnpm lint && pnpm typecheck && pnpm test` + `pnpm test:e2e` against the
> `docker/docker-compose.yml` stack) and record that in the release notes.
> CI outage fallback: if GitHub Actions is unavailable, gate the release on
> `pnpm ci:local` against the guarded local service stack and record the exact
> command, commit SHA, result, and reason for using the fallback in the release
> notes. A local result is not a substitute merely because CI is slow or queued.

### 2. Tag the release

Expand Down
24 changes: 13 additions & 11 deletions src/app/(app)/w/[slug]/command-center/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from "@/components/action-requests/attention-request-model";
import { MarkdownWithAttachments } from "@/components/markdown/attachment-renderer";
import { WorkspaceActivityTimeline } from "@/components/workspace-activity-timeline";
import { IssueReference } from "@/components/issue-reference";
import { trpc } from "@/lib/trpc";
import { useWorkspace } from "@/hooks/use-workspace";
import { useRealtime } from "@/hooks/use-realtime";
Expand Down Expand Up @@ -429,9 +430,10 @@ export default function CommandCenterPage() {
<Bot className="h-3 w-3 text-ember" />
<span className="text-sm font-medium">@{row.agent.profileKey}</span>
<ArrowRight className="h-3 w-3 text-muted-foreground" />
<span className="min-w-0 truncate text-sm text-muted-foreground">
{row.issue.workspace.key}-{row.issue.number}
</span>
<IssueReference
issue={row.issue}
className="min-w-0 flex-1 text-sm text-muted-foreground"
/>
</div>
{row.currentStep ? (
<span className="text-meta text-muted-foreground">{row.currentStep}</span>
Expand Down Expand Up @@ -477,9 +479,10 @@ export default function CommandCenterPage() {
href={`/w/${ws.slug}/i/${data.runningTimer.issue.workspace.key}-${data.runningTimer.issue.number}`}
className="flex flex-col gap-1 rounded-md border border-ember/40 bg-ember/5 p-2 hover:border-ember"
>
<span className="text-sm font-medium">
{data.runningTimer.issue.workspace.key}-{data.runningTimer.issue.number}
</span>
<IssueReference
issue={data.runningTimer.issue}
className="max-w-full text-sm font-medium"
/>
<span className="text-meta text-muted-foreground">
Started {new Date(data.runningTimer.startedAt).toLocaleTimeString()}
</span>
Expand Down Expand Up @@ -1679,12 +1682,11 @@ function RunFailureCard({
<div className="flex gap-2 rounded-md border border-warning/40 bg-warning/5 p-2 hover:border-warning">
<div className="min-w-0 flex-1">
<div className="flex min-w-0 flex-wrap items-center gap-2">
<Link
<IssueReference
issue={run.issue}
href={`/w/${slug}/issues/${run.issue.id}`}
className="text-sm font-medium hover:underline"
>
{run.issue.workspace.key}-{run.issue.number}
</Link>
className="max-w-full text-sm font-medium"
/>
<span className={cn("rounded px-1 py-0.5 text-[10px] uppercase", presentation.tone)}>
{presentation.badge}
</span>
Expand Down
27 changes: 13 additions & 14 deletions src/app/(app)/w/[slug]/inbox/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { Button } from "@/components/ui/button";
import { Badge, Card, EmptyState, Kbd, MOTION, Section, SkeletonList } from "@/components/ui";
import { Picker } from "@/components/ui/modal";
import { AgentPresenceDot } from "@/components/agent-presence-dot";
import { IssueReference } from "@/components/issue-reference";
import { presenceAvailability } from "@/lib/transport-display";
import { BulkBar, type BulkBarAction } from "@/components/bulk-bar";
import { BulkProjectPicker, BulkCyclePicker } from "@/components/bulk-move-pickers";
Expand Down Expand Up @@ -1263,16 +1264,18 @@ function NeedsInputSection({
</span>
<div className="min-w-0 flex-1">
<div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
<Link href={href} className="text-id shrink-0 hover:underline">
{issue ? formatIssueId(issue.workspace.key, issue.number) : "Workspace ask"}
</Link>
<span className="min-w-0 flex-1 basis-full truncate font-medium sm:basis-auto">
{request.title}
</span>
{issue ? (
<IssueReference issue={issue} href={href} className="max-w-full" />
) : (
<Link href={href} className="text-meta shrink-0 hover:underline">
Workspace ask
</Link>
)}
{request.issueOpenCount > 1 && (
<Badge>+{request.issueOpenCount - 1} more</Badge>
)}
</div>
<div className="mt-0.5 truncate font-medium">{request.title}</div>
{request.body && (
<div className="mt-0.5 line-clamp-2 text-muted-foreground">{request.body}</div>
)}
Expand All @@ -1297,15 +1300,11 @@ function NeedsInputSection({
</span>
<div className="min-w-0 flex-1">
<div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
<Link
<IssueReference
issue={row.issue}
href={`/w/${slug}/issues/${row.issue.id}`}
className="text-id shrink-0 hover:underline"
>
{formatIssueId(row.issue.workspace.key, row.issue.number)}
</Link>
<span className="min-w-0 flex-1 basis-full truncate sm:basis-auto">
{row.issue.title}
</span>
className="max-w-full flex-1"
/>
<span
className="text-id font-mono text-muted-foreground"
title={row.lastComment.author.name}
Expand Down
14 changes: 8 additions & 6 deletions src/components/activity-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1756,12 +1756,14 @@ function MinePanel({
{issue ? `${issue.workspace.key}-${issue.number}` : "Ask"}
</span>
}
title={request.title}
meta={
request.requestedByAgent
? `@${request.requestedByAgent.profileKey} · ${relativeTime(request.createdAt)}`
: relativeTime(request.createdAt)
}
title={issue?.title ?? request.title}
meta={[
issue ? request.title : null,
request.requestedByAgent ? `@${request.requestedByAgent.profileKey}` : null,
relativeTime(request.createdAt),
]
.filter(Boolean)
.join(" · ")}
/>
);
})}
Expand Down
25 changes: 19 additions & 6 deletions src/components/dashboard/needs-you-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from "next/link";
import { ArrowRight, AtSign, Inbox, Shield } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { IssueReference } from "@/components/issue-reference";
import { trpc } from "@/lib/trpc";

/**
Expand Down Expand Up @@ -48,7 +49,12 @@ export function NeedsYouTile({ slug }: { slug: string }) {
title: string;
handle: string | null;
avatar: string | null;
issueLabel: string | null;
issue: {
id: string;
number: number;
title: string;
workspace: { key: string; slug: string };
} | null;
completionIntent: "COMPLETE" | "RECOVER" | null;
}
| {
Expand All @@ -67,7 +73,7 @@ export function NeedsYouTile({ slug }: { slug: string }) {
title: r.title,
handle: r.requestedByAgent?.profileKey ?? null,
avatar: r.requestedByAgent?.avatar ?? null,
issueLabel: r.issue ? `${r.issue.workspace.key}-${r.issue.number}` : null,
issue: r.issue,
completionIntent: readCompletionIntent(r.payload),
});
}
Expand Down Expand Up @@ -130,10 +136,17 @@ export function NeedsYouTile({ slug }: { slug: string }) {
{!card.completionIntent && "asks: "}
<span className="text-muted-foreground">&ldquo;{card.title}&rdquo;</span>
</div>
<div className="text-meta mt-0.5 text-muted-foreground">
{card.issueLabel ? <span className="text-id">{card.issueLabel}</span> : null}
{card.issueLabel ? " · " : ""}action request
</div>
{card.issue ? (
<IssueReference
issue={card.issue}
href={`/w/${card.issue.workspace.slug}/i/${card.issue.workspace.key}-${card.issue.number}`}
className="text-meta mt-1 max-w-full"
/>
) : (
<div className="text-meta mt-0.5 text-muted-foreground">
Workspace action request
</div>
)}
</div>
<Link href={commandCenterHref} className="ml-auto shrink-0 sm:ml-0">
<Button variant="ember" size="sm">
Expand Down
Loading
Loading