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
27 changes: 16 additions & 11 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ const eslintConfig = defineConfig([
plugins: { vocabulary },
rules: { "vocabulary/no-banned-vocabulary": "error" },
},
{
// Files with copy that predates the vocabulary decision. The list lives in
// vocabulary.json under banned_global.allowlist, where each entry names the
// chunk that clears it — this just consumes it, so there is one place to
// look and no second list to keep in sync.
//
// It may only shrink; `vocabulary.test.ts` asserts that. A new file with
// retired vocabulary in it fails the rule, which is the point.
files: ALLOWLISTED_FILES.map(escapeGlob),
rules: { "vocabulary/no-banned-vocabulary": "off" },
},
// Files with copy that predates the vocabulary decision. The list lives in
// vocabulary.json under banned_global.allowlist, where each entry names the
// chunk that clears it — this just consumes it, so there is one place to
// look and no second list to keep in sync.
//
// It may only shrink; `vocabulary.test.ts` asserts that. A new file with
// retired vocabulary in it fails the rule, which is the point. When the list
// is empty the block is omitted: ESLint rejects `files: []`.
...(ALLOWLISTED_FILES.length > 0
? [
{
files: ALLOWLISTED_FILES.map(escapeGlob),
rules: { "vocabulary/no-banned-vocabulary": "off" },
},
]
: []),
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
Expand Down
16 changes: 16 additions & 0 deletions src/app/(app)/tasks/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { redirect } from "next/navigation";
import { getEnv } from "@/lib/env";
import { normalizeBasePath, withBasePath } from "@/lib/paths";
import { DESTINATION_PATH } from "@/lib/vocabulary";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

/**
* Retired destination. The app is organised around one object — the issue case
* — so this route only exists to keep old links, bookmarks, and alert
* notifications from 404ing.
*/
export default function RedirectToIssues() {
redirect(withBasePath(normalizeBasePath(getEnv("BASE_URL")), DESTINATION_PATH.issues));
}
57 changes: 0 additions & 57 deletions src/app/api/pages/[id]/agent-issues/route.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/components/bits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function FieldRecommendationStatusBadge({ rec }: { rec: Pick<Rec, "source
: status === "active"
? { state: "new" as const, title: "Real visitors to this exact page are outside the good range, and the nightly test cannot reproduce it." }
: status === "verifying"
? { state: "fixed" as const, title: "Verifying recovery: one 28-day window of visitor figures is good; a second is needed to confirm it." }
? { state: "fixed" as const, title: "Gone once; one more clean window confirms it." }
: { state: "resolved" as const, title: "Settled: two separate 28-day windows of visitor figures are inside the good range." };

return (
Expand Down Expand Up @@ -110,7 +110,7 @@ export function FieldEvidenceChip({ signal }: { signal: RecommendationEvidenceSi
* is the one chip and nothing else.
*/
const PERFORMANCE_ISSUE_CHIP: Record<PerformanceIssueStatus, { state: WorkState; title: string }> = {
regressed: { state: "reopened", title: "Returned after a confirmed resolution" },
regressed: { state: "reopened", title: "Came back after a confirmed resolution" },
resolved: { state: "resolved", title: "Gone from the last two nightly tests" },
verifying: { state: "fixed", title: "Gone once; one more clean night confirms it" },
active: { state: "new", title: "Found in the latest nightly test" },
Expand Down
34 changes: 2 additions & 32 deletions src/components/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ import {
import { issueCasesFrom, lastRunAtOf } from "@/lib/issue-cases";
import type { CaseDecision, CaseDecisionRequest } from "@/lib/case-decisions";
import { partitionByImpact } from "@/lib/impact-format";
import { APPLICABILITY_LABEL, COUNTED_QUEUES, ISSUE_ACTION_LABEL, QUEUE_LABEL, type ExclusionReason, type Queue } from "@/lib/vocabulary";
import { APPLICABILITY_LABEL, COUNTED_QUEUES, ISSUE_ACTION_LABEL, type ExclusionReason, type Queue } from "@/lib/vocabulary";
import { normalizeNativeElementControls } from "@/lib/nativeElements";
import { localISODate } from "@/lib/ui";
import { withBasePath } from "@/lib/paths";
import { defaultNewPageFlag, flagCapacityError } from "@/lib/watchCapacity";
import { applyWatchlistPageOrder, changePageFlagOrder } from "@/lib/watchlistOrder";
import { isTaskMarker } from "@/lib/taskMarkers";
import { pageTrend } from "@/lib/scoring";
import { normalizeState } from "@/lib/store/normalize";
import type { Project } from "@/lib/projects";
import { LAST_PROJECT_KEY } from "@/lib/projectSelection";
import { APPEARANCE_STORAGE_KEY, isAppearance, resolveSurface, type Appearance } from "./appearance";
Expand Down Expand Up @@ -155,7 +154,6 @@ interface StoreValue extends AppState {
setVisitorExperienceVisible: (visible: boolean) => void;
setExternalAgentAuditEnabled: (enabled: boolean) => void;
refreshExternalAgentAudit: (pageId: string) => void;
addAgentIssueTask: (pageId: string, caseKey: string) => void;
externalAgentAuditRefreshing: boolean;
removePage: (id: string) => void;
ignoreRec: (key: string) => void;
Expand Down Expand Up @@ -876,33 +874,6 @@ export function StoreProvider({
[flash, pathFor],
);

const addAgentIssueTask = useCallback(
(pageId: string, caseKey: string) => {
// No optimistic apply: the server re-assembles the case from stored
// evidence, so the authoritative task is whatever it returns.
void (async () => {
try {
const response = await fetch(pathFor(`/api/pages/${encodeURIComponent(pageId)}/agent-issues`), {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ caseKey }),
cache: "no-store",
});
const body = (await response.json().catch(() => null)) as { state?: AppState } | null;
if (!response.ok || !body?.state) {
flash(`Couldn't add this to ${QUEUE_LABEL.fix} — try again`);
return;
}
apply(normalizeState(body.state));
flash(`Added to ${QUEUE_LABEL.fix} with its verification target`);
} catch {
flash(`Couldn't add this to ${QUEUE_LABEL.fix} — try again`);
}
})();
},
[apply, flash, pathFor],
);

const setExternalAgentAuditEnabled = useCallback(
(enabled: boolean) => {
const cur = dataRef.current;
Expand Down Expand Up @@ -960,7 +931,7 @@ export function StoreProvider({
mutate(
{ ...cur, recs: cur.recs.map((r) => (r.key === key ? { ...r, status: "ignored" } : r)) },
{ url: `/api/recs`, body: { key, action: "ignore" } },
{ success: "Ignored — cleared from Inbox, still listed on the page", failure: "Couldn't ignore — try again" },
{ success: "Cleared from Decide — still listed on the page", failure: "Couldn't clear this — try again" },
);
},
[mutate],
Expand Down Expand Up @@ -1213,7 +1184,6 @@ export function StoreProvider({
updateCollectionSchedule,
setExternalAgentAuditEnabled,
refreshExternalAgentAudit,
addAgentIssueTask,
externalAgentAuditRefreshing,
updateAlertWebhookUrl,
setVisitorExperienceVisible,
Expand Down
Loading
Loading