@@ -137,6 +139,18 @@ function Toolbar({
字数 {wordCount.toLocaleString()}
+
+ 大纲
+
+
+ 伏笔
+
expected_close_to AND status≠CLOSED` 的伏笔确定性置 OVERDUE。
+ * 伏笔登记/回收(PARTIAL/CLOSED)的「采纳建议」经作者经 foreshadow 端点显式确认,
+ * 不在验收事务里 AI 静默改库(不变量 #3)。**§7.4 持久性局限**:BackgroundTask 进程内
+ * 跑、重启丢失——原型可接受(见 `services/foreshadow_scan.py`,不引入 jobs 表)。
*/
post: operations["accept_chapter_projects__project_id__chapters__chapter_no__accept_post"];
delete?: never;
@@ -178,6 +185,75 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/projects/{project_id}/foreshadow": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List Foreshadow
+ * @description 伏笔看板:按 `status` 过滤(缺省=全部),按 code 升序。
+ *
+ * `status` 非法(不在 OPEN/PARTIAL/CLOSED/OVERDUE)→ VALIDATION 信封。四泳道前端
+ * 据 `status` 分组;OVERDUE 泳道 + 逾期标记用 `expected_close_to`(看板字段已齐)。
+ */
+ get: operations["list_foreshadow_projects__project_id__foreshadow_get"];
+ put?: never;
+ /**
+ * Register Foreshadow
+ * @description 登记一条伏笔(status=OPEN)。重复 `code` → DB 唯一约束冲突 → VALIDATION 信封。
+ */
+ post: operations["register_foreshadow_projects__project_id__foreshadow_post"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/projects/{project_id}/foreshadow/{code}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ /**
+ * Update Foreshadow
+ * @description 状态机转移 和/或 追加进展。非法转移 → VALIDATION 信封;不存在 → NOT_FOUND。
+ *
+ * `to_status` 与 `progress_entry` 皆可选;两者都缺 → VALIDATION(无操作)。先转移、后追加。
+ */
+ patch: operations["update_foreshadow_projects__project_id__foreshadow__code__patch"];
+ trace?: never;
+ };
+ "/projects/{project_id}/outline": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Generate Outline
+ * @description 生成大纲并逐章持久化。无凭据 → LLM_UNAVAILABLE;项目不存在 → NOT_FOUND。
+ */
+ post: operations["generate_outline_projects__project_id__outline_post"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/settings/providers": {
parameters: {
query?: never;
@@ -338,11 +414,164 @@ export interface components {
/** Text */
text: string;
};
+ /**
+ * ForeshadowBoardResponse
+ * @description GET /projects/:id/foreshadow?status=:伏笔看板(四泳道 + OVERDUE 字段齐)。
+ *
+ * `status` 缺省返回全部;按 `code` 升序(repo `list_by_status` 已排序)。前端按
+ * `status` 分四泳道(OPEN/PARTIAL/CLOSED/OVERDUE),用 `expected_close_to` 标逾期。
+ */
+ ForeshadowBoardResponse: {
+ /** Foreshadow */
+ foreshadow?: components["schemas"]["ForeshadowView"][];
+ };
+ /**
+ * ForeshadowRegisterRequest
+ * @description POST /projects/:id/foreshadow:作者显式登记一条伏笔(status 落 OPEN)。
+ */
+ ForeshadowRegisterRequest: {
+ /**
+ * Code
+ * @description 伏笔代号,`(project_id, code)` 唯一
+ */
+ code: string;
+ /**
+ * Title
+ * @description 伏笔标题/一句话描述
+ */
+ title: string;
+ /**
+ * Planted At
+ * @description 埋设章号
+ */
+ planted_at?: number | null;
+ /**
+ * Content
+ * @description 伏笔正文/线索
+ */
+ content?: string | null;
+ /**
+ * Expected Close From
+ * @description 预期回收窗口起始章
+ */
+ expected_close_from?: number | null;
+ /**
+ * Expected Close To
+ * @description 预期回收窗口结束章(到期判据用)
+ */
+ expected_close_to?: number | null;
+ /**
+ * Importance
+ * @description 重要度(自由文本,看板用)
+ */
+ importance?: string | null;
+ };
+ /**
+ * ForeshadowTransitionRequest
+ * @description PATCH /projects/:id/foreshadow/:code:状态机转移 和/或 追加一条进展。
+ *
+ * 两字段皆可选:`to_status` 走状态机(非法 → VALIDATION 信封);`progress_entry`
+ * append-only 追加到 progress JSONB。二者可同时给(先转移、后追加进展)。
+ */
+ ForeshadowTransitionRequest: {
+ /**
+ * To Status
+ * @description 目标状态(OPEN/PARTIAL/CLOSED/OVERDUE)
+ */
+ to_status?: string | null;
+ /**
+ * Progress Entry
+ * @description 追加一条进展记录(append-only,不覆盖历史)
+ */
+ progress_entry?: {
+ [key: string]: unknown;
+ } | null;
+ };
+ /**
+ * ForeshadowView
+ * @description 伏笔账本视图(登记/状态变更/看板共用;snake_case)。形对齐 `ForeshadowLedgerView`。
+ */
+ ForeshadowView: {
+ /** Code */
+ code: string;
+ /** Title */
+ title: string;
+ /** Status */
+ status: string;
+ /** Planted At */
+ planted_at?: number | null;
+ /** Content */
+ content?: string | null;
+ /** Expected Close From */
+ expected_close_from?: number | null;
+ /** Expected Close To */
+ expected_close_to?: number | null;
+ /** Importance */
+ importance?: string | null;
+ /** Links */
+ links?: {
+ [key: string]: unknown;
+ }[];
+ /** Progress */
+ progress?: {
+ [key: string]: unknown;
+ }[];
+ };
+ /**
+ * ForeshadowWindowView
+ * @description 单章关联的伏笔回收窗口(snake_case;形对齐 `ww_agents.ForeshadowWindow`)。
+ */
+ ForeshadowWindowView: {
+ /** Code */
+ code: string;
+ /** Plant Chapter */
+ plant_chapter?: number | null;
+ /** Expected Close From */
+ expected_close_from?: number | null;
+ /** Expected Close To */
+ expected_close_to?: number | null;
+ };
/** HTTPValidationError */
HTTPValidationError: {
/** Detail */
detail?: components["schemas"]["ValidationError"][];
};
+ /**
+ * OutlineChapterView
+ * @description 单章大纲视图:章号 + 卷 + 节拍 + 伏笔窗口。
+ */
+ OutlineChapterView: {
+ /** No */
+ no: number;
+ /** Volume */
+ volume: number;
+ /** Beats */
+ beats?: string[];
+ /** Foreshadow Windows */
+ foreshadow_windows?: components["schemas"]["ForeshadowWindowView"][];
+ };
+ /**
+ * OutlineGenerateRequest
+ * @description POST /projects/:id/outline:触发大纲生成 + 持久化。
+ *
+ * M3 简单分卷:`volume` 缺省=1(schema 无逐章卷号;本端点把整批章节落到同一卷)。
+ */
+ OutlineGenerateRequest: {
+ /**
+ * Volume
+ * @description 本批大纲所属卷号(M3 默认全卷 1)
+ * @default 1
+ */
+ volume: number;
+ };
+ /**
+ * OutlineResponse
+ * @description 大纲生成响应:逐章已持久化的大纲(含窗口,供前端 T3.6)。
+ */
+ OutlineResponse: {
+ /** Chapters */
+ chapters?: components["schemas"]["OutlineChapterView"][];
+ };
/**
* ProjectCreateRequest
* @description POST /projects:立项向导字段(owner_id 由后端补 stub,不入参)。
@@ -898,6 +1127,145 @@ export interface operations {
};
};
};
+ list_foreshadow_projects__project_id__foreshadow_get: {
+ parameters: {
+ query?: {
+ status?: string | null;
+ };
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ForeshadowBoardResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ register_foreshadow_projects__project_id__foreshadow_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ForeshadowRegisterRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 201: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ForeshadowView"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ update_foreshadow_projects__project_id__foreshadow__code__patch: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ code: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ForeshadowTransitionRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ForeshadowView"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ generate_outline_projects__project_id__outline_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["OutlineGenerateRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["OutlineResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
list_providers_settings_providers_get: {
parameters: {
query?: never;
diff --git a/apps/web/lib/api/server.ts b/apps/web/lib/api/server.ts
index 5b9d0f3..264b5d0 100644
--- a/apps/web/lib/api/server.ts
+++ b/apps/web/lib/api/server.ts
@@ -1,5 +1,6 @@
import { serverApiBase } from "./config";
import type {
+ ForeshadowBoardResponse,
ProjectListResponse,
ProjectResponse,
ProvidersResponse,
@@ -35,3 +36,10 @@ export async function fetchReviews(
`/projects/${projectId}/chapters/${chapterNo}/reviews`,
);
}
+
+// 伏笔看板(全量,前端按 status 分四泳道)。
+export async function fetchForeshadow(
+ projectId: string,
+): Promise {
+ return getJson(`/projects/${projectId}/foreshadow`);
+}
diff --git a/apps/web/lib/api/types.ts b/apps/web/lib/api/types.ts
index cfcab66..30ed013 100644
--- a/apps/web/lib/api/types.ts
+++ b/apps/web/lib/api/types.ts
@@ -25,3 +25,18 @@ export type ReviewHistoryResponse =
export type ConflictDecision = components["schemas"]["ConflictDecision"];
export type AcceptRequest = components["schemas"]["AcceptRequest"];
export type AcceptResponse = components["schemas"]["AcceptResponse"];
+
+// M3 伏笔 + 大纲(C3 扩 T3.2/T3.5)。
+export type ForeshadowView = components["schemas"]["ForeshadowView"];
+export type ForeshadowBoardResponse =
+ components["schemas"]["ForeshadowBoardResponse"];
+export type ForeshadowRegisterRequest =
+ components["schemas"]["ForeshadowRegisterRequest"];
+export type ForeshadowTransitionRequest =
+ components["schemas"]["ForeshadowTransitionRequest"];
+export type ForeshadowWindowView =
+ components["schemas"]["ForeshadowWindowView"];
+export type OutlineChapterView = components["schemas"]["OutlineChapterView"];
+export type OutlineGenerateRequest =
+ components["schemas"]["OutlineGenerateRequest"];
+export type OutlineResponse = components["schemas"]["OutlineResponse"];
diff --git a/apps/web/lib/foreshadow/board.test.ts b/apps/web/lib/foreshadow/board.test.ts
new file mode 100644
index 0000000..0c67403
--- /dev/null
+++ b/apps/web/lib/foreshadow/board.test.ts
@@ -0,0 +1,134 @@
+import { describe, expect, it } from "vitest";
+
+import type { ForeshadowView } from "@/lib/api/types";
+import {
+ buildRegisterRequest,
+ buildTransitionRequest,
+ extractReason,
+ groupByStatus,
+ isWindowApproaching,
+ validationReasonMessage,
+} from "./board";
+
+const view = (over: Partial): ForeshadowView => ({
+ code: "F-001",
+ title: "线索",
+ status: "OPEN",
+ ...over,
+});
+
+describe("groupByStatus", () => {
+ it("splits items into four lanes preserving order", () => {
+ const lanes = groupByStatus([
+ view({ code: "F-001", status: "OPEN" }),
+ view({ code: "F-002", status: "OVERDUE" }),
+ view({ code: "F-003", status: "OPEN" }),
+ view({ code: "F-004", status: "CLOSED" }),
+ view({ code: "F-005", status: "PARTIAL" }),
+ ]);
+ expect(lanes.OPEN.map((v) => v.code)).toEqual(["F-001", "F-003"]);
+ expect(lanes.OVERDUE.map((v) => v.code)).toEqual(["F-002"]);
+ expect(lanes.CLOSED.map((v) => v.code)).toEqual(["F-004"]);
+ expect(lanes.PARTIAL.map((v) => v.code)).toEqual(["F-005"]);
+ });
+
+ it("falls back unknown status to OPEN and handles undefined", () => {
+ const lanes = groupByStatus([view({ status: "WEIRD" })]);
+ expect(lanes.OPEN).toHaveLength(1);
+ expect(groupByStatus(undefined).OPEN).toEqual([]);
+ });
+});
+
+describe("isWindowApproaching", () => {
+ it("is true when current chapter is inside [from,to] for OPEN/PARTIAL", () => {
+ const f = view({
+ status: "PARTIAL",
+ expected_close_from: 40,
+ expected_close_to: 60,
+ });
+ expect(isWindowApproaching(f, 50)).toBe(true);
+ expect(isWindowApproaching(f, 39)).toBe(false);
+ expect(isWindowApproaching(f, 61)).toBe(false);
+ });
+
+ it("never flags CLOSED/OVERDUE or items without a window", () => {
+ expect(
+ isWindowApproaching(
+ view({ status: "CLOSED", expected_close_to: 60 }),
+ 50,
+ ),
+ ).toBe(false);
+ expect(isWindowApproaching(view({ status: "OPEN" }), 50)).toBe(false);
+ });
+});
+
+describe("buildRegisterRequest", () => {
+ it("trims and omits empty optionals", () => {
+ expect(
+ buildRegisterRequest({
+ projectId: "p1",
+ code: " F-007 ",
+ title: " 旧誓约 ",
+ content: " ",
+ expectedCloseTo: 110,
+ }),
+ ).toEqual({ code: "F-007", title: "旧誓约", expected_close_to: 110 });
+ });
+
+ it("keeps numeric and non-empty string optionals", () => {
+ expect(
+ buildRegisterRequest({
+ projectId: "p1",
+ code: "F-1",
+ title: "t",
+ plantedAt: 8,
+ content: "线索",
+ expectedCloseFrom: 40,
+ importance: "主线",
+ }),
+ ).toEqual({
+ code: "F-1",
+ title: "t",
+ planted_at: 8,
+ content: "线索",
+ expected_close_from: 40,
+ importance: "主线",
+ });
+ });
+});
+
+describe("buildTransitionRequest", () => {
+ it("includes only provided fields", () => {
+ expect(buildTransitionRequest({ projectId: "p1", toStatus: "PARTIAL" })).toEqual({
+ to_status: "PARTIAL",
+ });
+ expect(
+ buildTransitionRequest({
+ projectId: "p1",
+ progressEntry: { chapter: 23, note: "发光" },
+ }),
+ ).toEqual({ progress_entry: { chapter: 23, note: "发光" } });
+ expect(buildTransitionRequest({ projectId: "p1" })).toEqual({});
+ expect(
+ buildTransitionRequest({ projectId: "p1", progressEntry: {} }),
+ ).toEqual({});
+ });
+});
+
+describe("validationReasonMessage / extractReason", () => {
+ it("maps known reasons to friendly text", () => {
+ expect(validationReasonMessage("duplicate")).toContain("已存在");
+ expect(validationReasonMessage("invalid_transition")).toContain("终态");
+ expect(validationReasonMessage("empty_update")).toContain("进展");
+ expect(validationReasonMessage("invalid_status")).toContain("筛选");
+ expect(validationReasonMessage(undefined)).toContain("校验");
+ });
+
+ it("extracts details.reason from envelope", () => {
+ expect(
+ extractReason({ error: { details: { reason: "duplicate" } } }),
+ ).toBe("duplicate");
+ expect(extractReason({ error: { details: null } })).toBeUndefined();
+ expect(extractReason("oops")).toBeUndefined();
+ });
+});
diff --git a/apps/web/lib/foreshadow/board.ts b/apps/web/lib/foreshadow/board.ts
new file mode 100644
index 0000000..9bf3a5d
--- /dev/null
+++ b/apps/web/lib/foreshadow/board.ts
@@ -0,0 +1,141 @@
+// 伏笔看板纯逻辑:四泳道分组、登记/转移请求体组装、422 reason 映射。
+// 对齐 C3 扩(GET/POST/PATCH .../foreshadow)。纯逻辑,便于 node 环境单测。
+
+import type {
+ ForeshadowRegisterRequest,
+ ForeshadowTransitionRequest,
+ ForeshadowView,
+} from "@/lib/api/types";
+
+// 四状态泳道(UX §6.8);OVERDUE 用琥珀强调(渲染层处理)。
+export type ForeshadowStatus = "OPEN" | "PARTIAL" | "CLOSED" | "OVERDUE";
+
+export const LANES: readonly ForeshadowStatus[] = [
+ "OPEN",
+ "PARTIAL",
+ "CLOSED",
+ "OVERDUE",
+] as const;
+
+export const LANE_LABELS: Record = {
+ OPEN: "OPEN",
+ PARTIAL: "PARTIAL",
+ CLOSED: "CLOSED",
+ OVERDUE: "OVERDUE",
+};
+
+export type ForeshadowLanes = Record;
+
+function emptyLanes(): ForeshadowLanes {
+ return { OPEN: [], PARTIAL: [], CLOSED: [], OVERDUE: [] };
+}
+
+function isStatus(v: string): v is ForeshadowStatus {
+ return v === "OPEN" || v === "PARTIAL" || v === "CLOSED" || v === "OVERDUE";
+}
+
+// 按 status 分四泳道(未知 status 落 OPEN 兜底);保持 repo 已排序的 code 顺序。
+export function groupByStatus(
+ items: readonly ForeshadowView[] | undefined,
+): ForeshadowLanes {
+ const lanes = emptyLanes();
+ for (const item of items ?? []) {
+ const status = isStatus(item.status) ? item.status : "OPEN";
+ lanes[status] = [...lanes[status], item];
+ }
+ return lanes;
+}
+
+// 接近回收窗口判定:current 在 [from, to] 内,或已超 from 但尚未 CLOSED(提示安排回收)。
+// 仅对 OPEN/PARTIAL 提示;CLOSED 已回收、OVERDUE 已逾期另有强调。
+export function isWindowApproaching(
+ item: ForeshadowView,
+ currentChapter: number,
+): boolean {
+ if (item.status === "CLOSED" || item.status === "OVERDUE") return false;
+ const to = item.expected_close_to;
+ if (typeof to !== "number") return false;
+ const from = item.expected_close_from ?? to;
+ return currentChapter >= from && currentChapter <= to;
+}
+
+export interface RegisterInput {
+ projectId: string;
+ code: string;
+ title: string;
+ plantedAt?: number | null;
+ content?: string | null;
+ expectedCloseFrom?: number | null;
+ expectedCloseTo?: number | null;
+ importance?: string | null;
+}
+
+// 组装登记请求体(去空白;空可选字段省略)。
+export function buildRegisterRequest(
+ input: RegisterInput,
+): ForeshadowRegisterRequest {
+ const body: ForeshadowRegisterRequest = {
+ code: input.code.trim(),
+ title: input.title.trim(),
+ };
+ if (typeof input.plantedAt === "number") body.planted_at = input.plantedAt;
+ const content = input.content?.trim();
+ if (content) body.content = content;
+ if (typeof input.expectedCloseFrom === "number")
+ body.expected_close_from = input.expectedCloseFrom;
+ if (typeof input.expectedCloseTo === "number")
+ body.expected_close_to = input.expectedCloseTo;
+ const importance = input.importance?.trim();
+ if (importance) body.importance = importance;
+ return body;
+}
+
+export interface TransitionInput {
+ projectId: string;
+ toStatus?: ForeshadowStatus | null;
+ // 一条进展记录(自由 dict;如 {chapter, note})。
+ progressEntry?: Record | null;
+}
+
+// 组装转移/进展请求体(两字段皆可选;至少给一项由调用方校验)。
+export function buildTransitionRequest(
+ input: TransitionInput,
+): ForeshadowTransitionRequest {
+ const body: ForeshadowTransitionRequest = {};
+ if (input.toStatus) body.to_status = input.toStatus;
+ if (input.progressEntry && Object.keys(input.progressEntry).length > 0)
+ body.progress_entry = input.progressEntry;
+ return body;
+}
+
+// 422 VALIDATION 信封 details.reason(C3 扩)→ 用户文案。
+export type ValidationReason =
+ | "duplicate"
+ | "invalid_transition"
+ | "empty_update"
+ | "invalid_status";
+
+export function validationReasonMessage(reason: string | undefined): string {
+ switch (reason) {
+ case "duplicate":
+ return "该伏笔代号已存在,请换一个。";
+ case "invalid_transition":
+ return "非法状态转移(CLOSED 为终态,不可再改)。";
+ case "empty_update":
+ return "请填写目标状态或一条进展。";
+ case "invalid_status":
+ return "无效的筛选状态。";
+ default:
+ return "操作未通过校验,请检查输入。";
+ }
+}
+
+// 从错误信封安全提取 details.reason。
+export function extractReason(error: unknown): string | undefined {
+ if (typeof error !== "object" || error === null) return undefined;
+ const env = error as {
+ error?: { details?: { reason?: unknown } | null };
+ };
+ const reason = env.error?.details?.reason;
+ return typeof reason === "string" ? reason : undefined;
+}
diff --git a/apps/web/lib/foreshadow/useForeshadow.ts b/apps/web/lib/foreshadow/useForeshadow.ts
new file mode 100644
index 0000000..a3013ed
--- /dev/null
+++ b/apps/web/lib/foreshadow/useForeshadow.ts
@@ -0,0 +1,96 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { ForeshadowView } from "@/lib/api/types";
+import {
+ buildRegisterRequest,
+ buildTransitionRequest,
+ extractReason,
+ validationReasonMessage,
+ type ForeshadowStatus,
+ type RegisterInput,
+ type TransitionInput,
+} from "./board";
+
+export interface UseForeshadow {
+ items: ForeshadowView[];
+ busy: boolean;
+ register: (input: RegisterInput) => Promise;
+ transition: (code: string, input: TransitionInput) => Promise;
+}
+
+// 伏笔登记/状态变更(乐观更新 + 回滚 + Toast)。
+// register:成功后把返回行追加进 items;422 duplicate → 友好提示,不改 items。
+// transition:先乐观替换该行 status,失败回滚到旧 items 并 toast(读 details.reason)。
+export function useForeshadow(initial: ForeshadowView[]): UseForeshadow {
+ const [items, setItems] = useState(initial);
+ const [busy, setBusy] = useState(false);
+ const toast = useToast();
+
+ const register = useCallback(
+ async (input) => {
+ setBusy(true);
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/foreshadow",
+ {
+ params: { path: { project_id: input.projectId } },
+ body: buildRegisterRequest(input),
+ },
+ );
+ if (error || !data) {
+ toast(validationReasonMessage(extractReason(error)), "error");
+ return false;
+ }
+ setItems((prev) => [...prev, data]);
+ toast(`已登记伏笔 ${data.code}`, "success");
+ return true;
+ } finally {
+ setBusy(false);
+ }
+ },
+ [toast],
+ );
+
+ const transition = useCallback(
+ async (code, input) => {
+ const snapshot = items;
+ // 乐观:本地先改 status(若给了 toStatus)。
+ if (input.toStatus) {
+ const next = input.toStatus as ForeshadowStatus;
+ setItems((prev) =>
+ prev.map((it) => (it.code === code ? { ...it, status: next } : it)),
+ );
+ }
+ setBusy(true);
+ try {
+ const { data, error } = await api.PATCH(
+ "/projects/{project_id}/foreshadow/{code}",
+ {
+ params: {
+ path: { project_id: input.projectId, code },
+ },
+ body: buildTransitionRequest(input),
+ },
+ );
+ if (error || !data) {
+ setItems(snapshot); // 回滚
+ toast(validationReasonMessage(extractReason(error)), "error");
+ return false;
+ }
+ // 用服务端权威行替换(status + progress 都对齐)。
+ setItems((prev) => prev.map((it) => (it.code === code ? data : it)));
+ toast(`已更新 ${data.code}`, "success");
+ return true;
+ } finally {
+ setBusy(false);
+ }
+ },
+ [items, toast],
+ );
+
+ return { items, busy, register, transition };
+}
diff --git a/apps/web/lib/outline/outline.test.ts b/apps/web/lib/outline/outline.test.ts
new file mode 100644
index 0000000..716128b
--- /dev/null
+++ b/apps/web/lib/outline/outline.test.ts
@@ -0,0 +1,63 @@
+import { describe, expect, it } from "vitest";
+
+import type {
+ ForeshadowWindowView,
+ OutlineChapterView,
+} from "@/lib/api/types";
+import {
+ groupByVolume,
+ isCloseWindow,
+ windowBadgeLabel,
+} from "./outline";
+
+const ch = (over: Partial): OutlineChapterView => ({
+ no: 1,
+ volume: 1,
+ beats: [],
+ foreshadow_windows: [],
+ ...over,
+});
+
+const win = (
+ over: Partial,
+): ForeshadowWindowView => ({ code: "F-012", ...over });
+
+describe("groupByVolume", () => {
+ it("groups by volume and sorts volumes and chapters ascending", () => {
+ const groups = groupByVolume([
+ ch({ no: 127, volume: 3 }),
+ ch({ no: 12, volume: 1 }),
+ ch({ no: 126, volume: 3 }),
+ ]);
+ expect(groups.map((g) => g.volume)).toEqual([1, 3]);
+ expect(groups[1]?.chapters.map((c) => c.no)).toEqual([126, 127]);
+ });
+
+ it("handles undefined", () => {
+ expect(groupByVolume(undefined)).toEqual([]);
+ });
+});
+
+describe("windowBadgeLabel", () => {
+ it("renders a range, half-open, or bare badge", () => {
+ expect(
+ windowBadgeLabel(win({ expected_close_from: 40, expected_close_to: 60 })),
+ ).toBe("⚑ F-012 (40-60)");
+ expect(windowBadgeLabel(win({ expected_close_to: 60 }))).toBe(
+ "⚑ F-012 (≤60)",
+ );
+ expect(windowBadgeLabel(win({ expected_close_from: 40 }))).toBe(
+ "⚑ F-012 (≥40)",
+ );
+ expect(windowBadgeLabel(win({}))).toBe("⚑ F-012");
+ });
+});
+
+describe("isCloseWindow", () => {
+ it("is true when chapter no is inside the recovery window", () => {
+ const w = win({ expected_close_from: 40, expected_close_to: 60 });
+ expect(isCloseWindow(ch({ no: 50 }), w)).toBe(true);
+ expect(isCloseWindow(ch({ no: 61 }), w)).toBe(false);
+ expect(isCloseWindow(ch({ no: 50 }), win({}))).toBe(false);
+ });
+});
diff --git a/apps/web/lib/outline/outline.ts b/apps/web/lib/outline/outline.ts
new file mode 100644
index 0000000..776a8d6
--- /dev/null
+++ b/apps/web/lib/outline/outline.ts
@@ -0,0 +1,49 @@
+// 大纲编辑器纯逻辑:按卷分组、伏笔窗口徽标文案、接近回收窗口提示(UX §6.7)。
+// OutlineChapterView/ForeshadowWindowView 已由 OpenAPI 强类型,无需手动收窄。
+
+import type { ForeshadowWindowView, OutlineChapterView } from "@/lib/api/types";
+
+export interface VolumeGroup {
+ volume: number;
+ chapters: OutlineChapterView[];
+}
+
+// 按 volume 分组(保持章号升序);卷号升序。
+export function groupByVolume(
+ chapters: readonly OutlineChapterView[] | undefined,
+): VolumeGroup[] {
+ const byVolume = new Map();
+ for (const ch of chapters ?? []) {
+ const list = byVolume.get(ch.volume) ?? [];
+ byVolume.set(ch.volume, [...list, ch]);
+ }
+ return [...byVolume.entries()]
+ .sort(([a], [b]) => a - b)
+ .map(([volume, list]) => ({
+ volume,
+ chapters: [...list].sort((a, b) => a.no - b.no),
+ }));
+}
+
+// 伏笔窗口徽标文案:`⚑ F-012 (40-60)` / `⚑ F-012 (≤60)` / `⚑ F-012`。
+export function windowBadgeLabel(window: ForeshadowWindowView): string {
+ const from = window.expected_close_from;
+ const to = window.expected_close_to;
+ if (typeof from === "number" && typeof to === "number") {
+ return `⚑ ${window.code} (${from}-${to})`;
+ }
+ if (typeof to === "number") return `⚑ ${window.code} (≤${to})`;
+ if (typeof from === "number") return `⚑ ${window.code} (≥${from})`;
+ return `⚑ ${window.code}`;
+}
+
+// 接近回收:本章号落在窗口 [from,to] 内(提示「本章可回收」,UX §6.7 ⚑ 可回收)。
+export function isCloseWindow(
+ chapter: OutlineChapterView,
+ window: ForeshadowWindowView,
+): boolean {
+ const to = window.expected_close_to;
+ if (typeof to !== "number") return false;
+ const from = window.expected_close_from ?? to;
+ return chapter.no >= from && chapter.no <= to;
+}
diff --git a/apps/web/lib/outline/useOutline.ts b/apps/web/lib/outline/useOutline.ts
new file mode 100644
index 0000000..3948052
--- /dev/null
+++ b/apps/web/lib/outline/useOutline.ts
@@ -0,0 +1,63 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { OutlineChapterView } from "@/lib/api/types";
+
+export type OutlineStatus = "idle" | "generating" | "ready" | "error";
+
+export interface OutlineError {
+ code: string;
+ message: string;
+}
+
+export interface UseOutline {
+ chapters: OutlineChapterView[];
+ status: OutlineStatus;
+ error: OutlineError | null;
+ generate: (projectId: string, volume: number) => Promise;
+}
+
+// 生成大纲:POST .../outline(body {volume})。无凭据/网关失败 → 503 LLM_UNAVAILABLE
+// (流前 JSON 信封),经 error 检出、引导去设置(同 draft/review 范式)。
+export function useOutline(initial: OutlineChapterView[]): UseOutline {
+ const [chapters, setChapters] = useState(initial);
+ const [status, setStatus] = useState(
+ initial.length > 0 ? "ready" : "idle",
+ );
+ const [error, setError] = useState(null);
+ const toast = useToast();
+
+ const generate = useCallback(
+ async (projectId, volume) => {
+ setStatus("generating");
+ setError(null);
+ const { data, error: apiError } = await api.POST(
+ "/projects/{project_id}/outline",
+ {
+ params: { path: { project_id: projectId } },
+ body: { volume },
+ },
+ );
+ if (apiError || !data) {
+ const env = apiError as
+ | { error?: { code?: string; message?: string } }
+ | undefined;
+ const code = env?.error?.code ?? "OUTLINE_FAILED";
+ const message = env?.error?.message ?? "大纲生成失败,请重试。";
+ setError({ code, message });
+ setStatus("error");
+ toast(message, "error");
+ return;
+ }
+ setChapters(data.chapters ?? []);
+ setStatus("ready");
+ toast("大纲已生成", "success");
+ },
+ [toast],
+ );
+
+ return { chapters, status, error, generate };
+}
diff --git a/apps/web/lib/pace/beatmap.test.ts b/apps/web/lib/pace/beatmap.test.ts
new file mode 100644
index 0000000..0756f7c
--- /dev/null
+++ b/apps/web/lib/pace/beatmap.test.ts
@@ -0,0 +1,37 @@
+import { describe, expect, it } from "vitest";
+
+import { SPARK_CHARS, toBeatCells, toSparkline } from "./beatmap";
+
+describe("toBeatCells", () => {
+ it("maps min→▁ and max→▇ via linear normalization", () => {
+ const cells = toBeatCells([1, 7]);
+ expect(cells[0]?.level).toBe(0);
+ expect(cells[0]?.char).toBe("▁");
+ expect(cells[1]?.level).toBe(SPARK_CHARS.length - 1);
+ expect(cells[1]?.char).toBe("▇");
+ });
+
+ it("centers all cells when every value is equal", () => {
+ const cells = toBeatCells([5, 5, 5]);
+ expect(cells.every((c) => c.level === 3)).toBe(true);
+ expect(cells.every((c) => c.char === "▄")).toBe(true);
+ });
+
+ it("gives every cell a visible min height", () => {
+ const cells = toBeatCells([0, 10]);
+ expect(cells[0]?.heightPct).toBeGreaterThanOrEqual(12);
+ expect(cells[1]?.heightPct).toBe(100);
+ });
+
+ it("returns [] for empty or non-finite-only input", () => {
+ expect(toBeatCells([])).toEqual([]);
+ expect(toBeatCells([Number.NaN, Number.POSITIVE_INFINITY])).toEqual([]);
+ });
+});
+
+describe("toSparkline", () => {
+ it("joins chars into a sparkline string", () => {
+ expect(toSparkline([1, 4, 7])).toBe("▁▄▇");
+ expect(toSparkline([])).toBe("");
+ });
+});
diff --git a/apps/web/lib/pace/beatmap.ts b/apps/web/lib/pace/beatmap.ts
new file mode 100644
index 0000000..9d50b09
--- /dev/null
+++ b/apps/web/lib/pace/beatmap.ts
@@ -0,0 +1,53 @@
+// 爽点节拍图:把 pace.beat_map(int 序列)映射成 ▁▃▅ 风格的 sparkline 格子(UX §6.4)。
+// 纯逻辑,便于 node 环境单测;渲染时配合高度类与 prefers-reduced-motion。
+
+// 七级火花字符(低→高),与高度档位一一对应。
+export const SPARK_CHARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇"] as const;
+
+export interface BeatCell {
+ // 原始强度值。
+ value: number;
+ // 归一化档位 0..6(映射到 SPARK_CHARS / 高度百分比)。
+ level: number;
+ // 对应火花字符(▁..▇)。
+ char: string;
+ // 渲染用高度百分比(12%..100%,最低也可见)。
+ heightPct: number;
+}
+
+const MIN_HEIGHT_PCT = 12;
+const MAX_LEVEL = SPARK_CHARS.length - 1;
+
+// 把 beat_map 映射成可渲染的格子序列。
+// 归一化按「本序列 min..max」线性分桶到 0..6;全相等 → 全部居中档(level 3)。
+export function toBeatCells(beatMap: readonly number[]): BeatCell[] {
+ const values = beatMap.filter((v) => typeof v === "number" && Number.isFinite(v));
+ if (values.length === 0) return [];
+
+ const min = Math.min(...values);
+ const max = Math.max(...values);
+ const span = max - min;
+
+ return values.map((value) => {
+ const level =
+ span === 0
+ ? Math.round(MAX_LEVEL / 2)
+ : Math.round(((value - min) / span) * MAX_LEVEL);
+ const clamped = Math.min(MAX_LEVEL, Math.max(0, level));
+ const heightPct =
+ MIN_HEIGHT_PCT + (clamped / MAX_LEVEL) * (100 - MIN_HEIGHT_PCT);
+ return {
+ value,
+ level: clamped,
+ char: SPARK_CHARS[clamped] ?? SPARK_CHARS[0],
+ heightPct: Math.round(heightPct),
+ };
+ });
+}
+
+// 文本版 sparkline(无障碍/紧凑场景;UX §6.4 示例 ▁▃▅▂▁▇)。
+export function toSparkline(beatMap: readonly number[]): string {
+ return toBeatCells(beatMap)
+ .map((c) => c.char)
+ .join("");
+}
diff --git a/apps/web/lib/review/history.test.ts b/apps/web/lib/review/history.test.ts
index dd0feec..2d8da35 100644
--- a/apps/web/lib/review/history.test.ts
+++ b/apps/web/lib/review/history.test.ts
@@ -1,7 +1,12 @@
import { describe, expect, it } from "vitest";
import type { ReviewHistoryItem } from "@/lib/api/types";
-import { latestReview, normalizeConflicts } from "./history";
+import {
+ latestReview,
+ normalizeConflicts,
+ normalizeForeshadowSug,
+ normalizePace,
+} from "./history";
const item = (
conflicts: Record[],
@@ -43,6 +48,56 @@ describe("normalizeConflicts", () => {
});
});
+describe("normalizeForeshadowSug", () => {
+ it("tightens flat list with kind, defaulting kind to planted", () => {
+ const out = normalizeForeshadowSug({
+ ...item([]),
+ foreshadow_sug: [
+ { kind: "resolved", code: "F-012", title: "神秘玉佩", where: "第4段" },
+ { title: "残破地图" },
+ ],
+ });
+ expect(out).toEqual([
+ {
+ kind: "resolved",
+ code: "F-012",
+ title: "神秘玉佩",
+ where: "第4段",
+ note: null,
+ },
+ { kind: "planted", code: null, title: "残破地图", where: null, note: null },
+ ]);
+ });
+
+ it("returns [] when missing", () => {
+ expect(normalizeForeshadowSug(item([]))).toEqual([]);
+ expect(normalizeForeshadowSug(undefined)).toEqual([]);
+ });
+});
+
+describe("normalizePace", () => {
+ it("tightens dict into PaceReport, filtering bad water/beats", () => {
+ const out = normalizePace({
+ ...item([]),
+ pace: {
+ water: [{ where: "第3段", reason: "注水" }, "junk"],
+ hook: true,
+ beat_map: [1, 3, "x", 5],
+ },
+ });
+ expect(out).toEqual({
+ water: [{ where: "第3段", reason: "注水" }],
+ hook: true,
+ beat_map: [1, 3, 5],
+ });
+ });
+
+ it("returns null when pace is missing or not a dict", () => {
+ expect(normalizePace(item([]))).toBeNull();
+ expect(normalizePace(undefined)).toBeNull();
+ });
+});
+
describe("latestReview", () => {
it("returns first item (history is newest-first)", () => {
const a = item([]);
diff --git a/apps/web/lib/review/history.ts b/apps/web/lib/review/history.ts
index a50c5f9..63adf88 100644
--- a/apps/web/lib/review/history.ts
+++ b/apps/web/lib/review/history.ts
@@ -2,17 +2,32 @@
// 纯逻辑(可单测);schema 把 conflicts 标成 {[k]:unknown}[],这里安全收窄。
import type { ReviewHistoryItem } from "@/lib/api/types";
-import type { ReviewConflict } from "./sse";
+import type {
+ ForeshadowKind,
+ ForeshadowSuggestion,
+ PaceIssue,
+ PaceReport,
+ ReviewConflict,
+} from "./sse";
function asString(v: unknown, fallback = ""): string {
return typeof v === "string" ? v : fallback;
}
+function asOptionalString(v: unknown): string | null {
+ return typeof v === "string" ? v : null;
+}
+
function asStringArray(v: unknown): string[] {
if (!Array.isArray(v)) return [];
return v.filter((x): x is string => typeof x === "string");
}
+function asIntArray(v: unknown): number[] {
+ if (!Array.isArray(v)) return [];
+ return v.filter((x): x is number => typeof x === "number" && Number.isFinite(x));
+}
+
// 把一条留痕的 conflicts 收紧(缺字段给安全默认;保持顺序=下标身份,对齐冲突 gate)。
export function normalizeConflicts(
item: ReviewHistoryItem | undefined,
@@ -26,6 +41,46 @@ export function normalizeConflicts(
}));
}
+// 把一条留痕的 foreshadow_sug(扁平 list,每条带 kind)收紧成建议(缺字段给安全默认)。
+// 对齐 collect 列映射(C4 扩 T3.3):planted/resolved 扁平成单 list、每条加 kind。
+export function normalizeForeshadowSug(
+ item: ReviewHistoryItem | undefined,
+): ForeshadowSuggestion[] {
+ const raw = item?.foreshadow_sug ?? [];
+ return raw.map((s) => ({
+ kind: asKind(s["kind"]),
+ code: asOptionalString(s["code"]),
+ title: asString(s["title"], "(未命名线索)"),
+ where: asOptionalString(s["where"]),
+ note: asOptionalString(s["note"]),
+ }));
+}
+
+function asKind(v: unknown): ForeshadowKind {
+ return v === "resolved" ? "resolved" : "planted";
+}
+
+// 把一条留痕的 pace(dict)收紧成节奏报告;缺失/非 dict → null(不渲染节拍图)。
+export function normalizePace(
+ item: ReviewHistoryItem | undefined,
+): PaceReport | null {
+ const raw = item?.pace;
+ if (typeof raw !== "object" || raw === null) return null;
+ const dict = raw as Record;
+ const waterRaw = Array.isArray(dict["water"]) ? dict["water"] : [];
+ const water: PaceIssue[] = waterRaw
+ .filter((w): w is Record => typeof w === "object" && w !== null)
+ .map((w) => ({
+ where: asString(w["where"]),
+ reason: asString(w["reason"]),
+ }));
+ return {
+ water,
+ hook: dict["hook"] === true,
+ beat_map: asIntArray(dict["beat_map"]),
+ };
+}
+
// 最近一条留痕(GET .../reviews 已按新→旧排序)。
export function latestReview(
items: readonly ReviewHistoryItem[] | undefined,
diff --git a/apps/web/lib/review/sse.test.ts b/apps/web/lib/review/sse.test.ts
index 7ff0390..81dc632 100644
--- a/apps/web/lib/review/sse.test.ts
+++ b/apps/web/lib/review/sse.test.ts
@@ -49,6 +49,35 @@ describe("parseReviewBlock", () => {
});
});
+ it("parses foreshadow and pace frames (C4 扩 T3.3)", () => {
+ expect(
+ parseReviewBlock(
+ 'event:foreshadow\ndata:{"kind":"resolved","code":"F-012","title":"神秘玉佩","where":"第4段","note":"疑似回收"}',
+ ),
+ ).toEqual({
+ event: "foreshadow",
+ data: {
+ kind: "resolved",
+ code: "F-012",
+ title: "神秘玉佩",
+ where: "第4段",
+ note: "疑似回收",
+ },
+ });
+ expect(
+ parseReviewBlock(
+ 'event:pace\ndata:{"water":[{"where":"第3段","reason":"注水"}],"hook":false,"beat_map":[1,3,5,2,1,7]}',
+ ),
+ ).toEqual({
+ event: "pace",
+ data: {
+ water: [{ where: "第3段", reason: "注水" }],
+ hook: false,
+ beat_map: [1, 3, 5, 2, 1, 7],
+ },
+ });
+ });
+
it("returns null for unknown event or malformed json", () => {
expect(parseReviewBlock('event:token\ndata:{"text":"x"}')).toBeNull();
expect(parseReviewBlock("event:conflict\ndata:{not json")).toBeNull();
@@ -107,6 +136,39 @@ describe("reduceReview", () => {
]);
});
+ it("accumulates foreshadow suggestions and replaces pace report (last wins)", () => {
+ const events: ReviewSseEvent[] = [
+ {
+ event: "foreshadow",
+ data: { kind: "planted", title: "残破地图", where: "第5段" },
+ },
+ {
+ event: "foreshadow",
+ data: { kind: "resolved", code: "F-012", title: "神秘玉佩" },
+ },
+ {
+ event: "pace",
+ data: { water: [], hook: true, beat_map: [1, 2, 3] },
+ },
+ {
+ event: "pace",
+ data: {
+ water: [{ where: "第3段", reason: "注水" }],
+ hook: false,
+ beat_map: [3, 1, 5],
+ },
+ },
+ ];
+ const state = events.reduce(reduceReview, initialReviewState);
+ expect(state.foreshadow.map((f) => f.kind)).toEqual([
+ "planted",
+ "resolved",
+ ]);
+ expect(state.pace?.hook).toBe(false);
+ expect(state.pace?.beat_map).toEqual([3, 1, 5]);
+ expect(state.phase).toBe("reviewing");
+ });
+
it("marks done and captures error preserving collected data", () => {
let s = reduceReview(initialReviewState, {
event: "section",
diff --git a/apps/web/lib/review/sse.ts b/apps/web/lib/review/sse.ts
index ce903eb..c299e5c 100644
--- a/apps/web/lib/review/sse.ts
+++ b/apps/web/lib/review/sse.ts
@@ -19,6 +19,29 @@ export interface ReviewConflict {
export type SectionStatus = "started" | "done" | "incomplete";
+// 伏笔建议(C4 扩 T3.3):planted=新埋待确认 / resolved=疑似回收(只读建议)。
+export type ForeshadowKind = "planted" | "resolved";
+
+export interface ForeshadowSuggestion {
+ kind: ForeshadowKind;
+ code?: string | null;
+ title: string;
+ where?: string | null;
+ note?: string | null;
+}
+
+// 节奏报告(C4 扩 T3.3):注水段 + 章末钩子 + 爽点节拍图。
+export interface PaceIssue {
+ where: string;
+ reason: string;
+}
+
+export interface PaceReport {
+ water: PaceIssue[];
+ hook: boolean;
+ beat_map: number[];
+}
+
export interface SectionEvent {
event: "section";
data: { name: string; status: SectionStatus };
@@ -27,6 +50,24 @@ export interface ConflictEvent {
event: "conflict";
data: ReviewConflict;
}
+export interface ForeshadowEvent {
+ event: "foreshadow";
+ data: {
+ kind: ForeshadowKind;
+ code?: string | null;
+ title: string;
+ where?: string | null;
+ note?: string | null;
+ };
+}
+export interface PaceEvent {
+ event: "pace";
+ data: {
+ water: PaceIssue[];
+ hook: boolean;
+ beat_map: number[];
+ };
+}
export interface DoneEvent {
event: "done";
data: { length: number };
@@ -38,10 +79,19 @@ export interface ErrorEvent {
export type ReviewSseEvent =
| SectionEvent
| ConflictEvent
+ | ForeshadowEvent
+ | PaceEvent
| DoneEvent
| ErrorEvent;
-const KNOWN_EVENTS = new Set(["section", "conflict", "done", "error"]);
+const KNOWN_EVENTS = new Set([
+ "section",
+ "conflict",
+ "foreshadow",
+ "pace",
+ "done",
+ "error",
+]);
// 把一个完整 SSE 块(多行)解析成事件;无法解析则返回 null(跳过)。
export function parseReviewBlock(block: string): ReviewSseEvent | null {
@@ -108,6 +158,8 @@ export interface ReviewStreamState {
phase: ReviewPhase;
sections: ReviewSection[];
conflicts: ReviewConflict[];
+ foreshadow: ForeshadowSuggestion[];
+ pace: PaceReport | null;
error: { code: string; message: string; request_id?: string | null } | null;
}
@@ -115,6 +167,8 @@ export const initialReviewState: ReviewStreamState = {
phase: "idle",
sections: [],
conflicts: [],
+ foreshadow: [],
+ pace: null,
error: null,
};
@@ -136,6 +190,18 @@ export function reduceReview(
phase: "reviewing",
conflicts: [...state.conflicts, event.data],
};
+ case "foreshadow":
+ return {
+ ...state,
+ phase: "reviewing",
+ foreshadow: [...state.foreshadow, { ...event.data }],
+ };
+ case "pace":
+ return {
+ ...state,
+ phase: "reviewing",
+ pace: { ...event.data },
+ };
case "done":
return { ...state, phase: "done" };
case "error":
diff --git a/apps/web/lib/review/useReviewStream.ts b/apps/web/lib/review/useReviewStream.ts
index e9d8be9..887ffee 100644
--- a/apps/web/lib/review/useReviewStream.ts
+++ b/apps/web/lib/review/useReviewStream.ts
@@ -7,16 +7,24 @@ import {
ReviewFrameBuffer,
initialReviewState,
reduceReview,
+ type ForeshadowSuggestion,
+ type PaceReport,
type ReviewConflict,
type ReviewStreamState,
} from "./sse";
+export interface ReviewSeed {
+ conflicts: ReviewConflict[];
+ foreshadow: ForeshadowSuggestion[];
+ pace: PaceReport | null;
+}
+
type Action =
| { type: "start" }
| { type: "events"; events: ReturnType }
| { type: "abort" }
| { type: "fail"; code: string; message: string }
- | { type: "seed"; conflicts: ReviewConflict[] };
+ | { type: "seed"; seed: ReviewSeed };
function reducer(state: ReviewStreamState, action: Action): ReviewStreamState {
switch (action.type) {
@@ -33,7 +41,12 @@ function reducer(state: ReviewStreamState, action: Action): ReviewStreamState {
error: { code: action.code, message: action.message },
};
case "seed":
- return { ...initialReviewState, conflicts: action.conflicts };
+ return {
+ ...initialReviewState,
+ conflicts: action.seed.conflicts,
+ foreshadow: action.seed.foreshadow,
+ pace: action.seed.pace,
+ };
default:
return state;
}
@@ -45,8 +58,8 @@ export interface UseReviewStream {
// 用当前编辑器草稿重新审稿。
start: (projectId: string, chapterNo: number, draft: string) => Promise;
stop: () => void;
- // 进页用历史留痕的冲突种入(无需重审即可裁决)。
- seed: (conflicts: ReviewConflict[]) => void;
+ // 进页用历史留痕(冲突 + 伏笔建议 + 节奏)种入(无需重审即可裁决/查看)。
+ seed: (seed: ReviewSeed) => void;
}
// 消费 POST .../review 的 SSE 流:fetch+ReadableStream(EventSource 不支持 POST)。
@@ -62,8 +75,8 @@ export function useReviewStream(): UseReviewStream {
dispatch({ type: "abort" });
}, []);
- const seed = useCallback((conflicts: ReviewConflict[]) => {
- dispatch({ type: "seed", conflicts });
+ const seed = useCallback((seedData: ReviewSeed) => {
+ dispatch({ type: "seed", seed: seedData });
}, []);
const start = useCallback(
diff --git a/apps/web/tsconfig.tsbuildinfo b/apps/web/tsconfig.tsbuildinfo
index 640bf8a..c32d512 100644
--- a/apps/web/tsconfig.tsbuildinfo
+++ b/apps/web/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"fileNames":["./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/get-page-files.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/canary.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/experimental.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/index.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/canary.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/experimental.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/fallback.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/body-streams.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/require-hook.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/page-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-kind.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render-result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/load-components.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/with-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/route-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/page-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/pages.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/next-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/adapter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/jsx-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/search-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/http.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request-meta.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/cli/next-test.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config-shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/node.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/.pnpm/sharp@0.33.5/node_modules/sharp/lib/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/trace.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/react-dev-overlay/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/telemetry/storage.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/.pnpm/@next+env@15.1.3/node_modules/@next/env/dist/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/image-component.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/not-found.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/connection.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image-types/global.d.ts","./next-env.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/config.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./node_modules/.pnpm/@types+estree@1.0.9/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","./node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/runtime.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/@vitest+pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/index-c1cfc5e9.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/index.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/tasks-3znpj1lr.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types-bxe-2udy.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/error.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/environment.looobwuu.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment-ddx0edty.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/rawsnapshot-cpnkto81.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/config.cy0c388z.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index-z0r8hvru.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/client.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/server.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/benchmark.geerunq4.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/reporters.nr4dxcka.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/vite.czkp4x9w.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/config.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.tn5kgiih.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.b9fxpcac.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/chai.d.cts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/index.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/index.d.ts","./node_modules/.pnpm/@vitest+spy@2.1.9/node_modules/@vitest/spy/dist/index.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/types-dzoqtgin.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/mocker.crtm890j.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/suite.b2jumifp.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/index.d.ts","./lib/api/client.test.ts","./node_modules/.pnpm/openapi-typescript-helpers@0.0.15/node_modules/openapi-typescript-helpers/index.d.ts","./node_modules/.pnpm/openapi-fetch@0.13.8/node_modules/openapi-fetch/dist/index.d.ts","./lib/api/schema.d.ts","./lib/api/config.ts","./lib/api/client.ts","./lib/api/types.ts","./lib/api/server.ts","./lib/autosave/autosave.ts","./lib/autosave/autosave.test.ts","./components/toast.tsx","./lib/autosave/useautosave.ts","./lib/review/decisions.ts","./lib/review/decisions.test.ts","./lib/review/sse.ts","./lib/review/history.ts","./lib/review/history.test.ts","./lib/review/sse.test.ts","./lib/review/useaccept.ts","./lib/review/usereviewstream.ts","./lib/settings/providers.ts","./lib/stream/sse.ts","./lib/stream/sse.test.ts","./lib/stream/usedraftstream.ts","./lib/wizard/wizard.ts","./lib/wizard/wizard.test.ts","./app/layout.tsx","./components/leftnav.tsx","./components/appshell.tsx","./components/projectcard.tsx","./app/page.tsx","./components/review/acceptpanel.tsx","./components/review/annotatedtext.tsx","./components/review/conflictcard.tsx","./components/review/reviewreport.tsx","./app/projects/[id]/review/page.tsx","./components/workbench/chapterlist.tsx","./components/workbench/chapterassistant.tsx","./components/workbench/editor.tsx","./components/workbench/workbench.tsx","./app/projects/[id]/write/page.tsx","./components/projectwizard.tsx","./app/projects/new/page.tsx","./components/settings/providerssettings.tsx","./app/settings/providers/page.tsx","./.next/types/cache-life.d.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/projects/[id]/review/page.ts","./.next/types/app/projects/[id]/write/page.ts","./.next/types/app/projects/new/page.ts","./.next/types/app/settings/providers/page.ts"],"fileIdsList":[[76,124,141,142,308,562],[76,124,141,142,308,566],[76,124,141,142,308,571],[76,124,141,142,308,576],[76,124,141,142,308,578],[76,124,141,142,308,580],[76,124,141,142,399,400,401,402],[76,124,141,142,447,546],[76,124,141,142,423,542,543,564,565],[76,124,141,142,431,543,551,570],[76,124,141,142,431,543,575],[76,124,141,142,564,577],[76,124,141,142,542,543,564,579],[62,76,124,141,142,423,563],[76,124,141,142,423,431],[76,124,141,142,423,542],[62,76,124,141,142,431,541,546,560],[76,124,141,142,542],[76,124,141,142,550],[76,124,141,142,548,550],[62,76,124,141,142,542,548,550,551,554,555,564,567,568,569],[62,76,124,141,142,541,542,546,556],[62,76,124,141,142],[76,124,141,142],[62,76,124,141,142,423,542,547,559,564,572,573,574],[76,124,141,142,535],[76,124,141,142,538,539,540],[76,124,141,142,540,542],[76,124,141,142,539],[76,124,141,142,535,544],[62,76,124,141,142,541,544,546],[76,124,141,142,535,548],[76,124,141,142,535,542,551],[76,124,141,142,542,550],[76,124,141,142,535,550],[62,76,124,141,142,541,542,546,548],[62,76,124,141,142,540,550],[76,124,141,142,535,557],[62,76,124,141,142,540,557],[76,124,141,142,535,560],[76,124,141,142,447,448],[76,121,122,124,141,142],[76,123,124,141,142],[124,141,142],[76,124,129,141,142,159],[76,124,125,130,135,141,142,144,156,167],[76,124,125,126,135,141,142,144],[71,72,73,76,124,141,142],[76,124,127,141,142,168],[76,124,128,129,136,141,142,145],[76,124,129,141,142,156,164],[76,124,130,132,135,141,142,144],[76,123,124,131,141,142],[76,124,132,133,141,142],[76,124,134,135,141,142],[76,123,124,135,141,142],[76,124,135,136,137,141,142,156,167],[76,124,135,136,137,141,142,151,156,159],[76,117,124,132,135,138,141,142,144,156,167],[76,124,135,136,138,139,141,142,144,156,164,167],[76,124,138,140,141,142,156,164,167],[74,75,76,77,78,79,80,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,135,141,142],[76,124,141,142,143,167],[76,124,132,135,141,142,144,156],[76,124,141,142,145],[76,124,141,142,146],[76,123,124,141,142,147],[76,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,141,142,149],[76,124,141,142,150],[76,124,135,141,142,151,152],[76,124,141,142,151,153,168,170],[76,124,136,141,142],[76,124,135,141,142,156,157,159],[76,124,141,142,158,159],[76,124,141,142,156,157],[76,124,141,142,159],[76,124,141,142,160],[76,121,124,141,142,156,161,167],[76,124,135,141,142,162,163],[76,124,141,142,162,163],[76,124,129,141,142,144,156,164],[76,124,141,142,165],[76,124,141,142,144,166],[76,124,138,141,142,150,167],[76,124,129,141,142,168],[76,124,141,142,156,169],[76,124,141,142,143,170],[76,124,141,142,171],[76,117,124,141,142],[76,117,124,135,137,141,142,147,156,159,167,169,170,172],[76,124,141,142,156,173],[62,76,124,141,142,177,179],[62,76,124,141,142,177,178],[62,66,76,124,141,142,176,393,440],[62,66,76,124,141,142,175,393,440],[60,61,76,124,141,142],[76,124,141,142,491,492,495],[76,124,141,142,523],[76,124,141,142,526],[76,124,141,142,492,493,495,496,497],[76,124,141,142,492],[76,124,141,142,492,493,495],[76,124,141,142,492,493],[76,124,141,142,500],[76,124,141,142,487,500,501],[76,124,141,142,487,500],[76,124,141,142,487,494],[76,124,141,142,488],[76,124,141,142,487,488,489,491],[76,124,141,142,487],[76,124,141,142,530,531],[76,124,141,142,530,531,532,533],[76,124,141,142,530,532],[76,124,141,142,530],[68,76,124,141,142],[76,124,141,142,397],[76,124,141,142,404],[76,124,141,142,183,196,197,198,200,357],[76,124,141,142,183,187,189,190,191,192,346,357,359],[76,124,141,142,357],[76,124,141,142,197,213,290,337,353],[76,124,141,142,183],[76,124,141,142,377],[76,124,141,142,357,359,376],[76,124,141,142,276,290,318,445],[76,124,141,142,283,300,337,352],[76,124,141,142,238],[76,124,141,142,341],[76,124,141,142,340,341,342],[76,124,141,142,340],[70,76,124,138,141,142,180,183,190,193,194,195,197,201,269,274,320,328,338,348,357,393],[76,124,141,142,183,199,227,272,357,373,374,445],[76,124,141,142,199,445],[76,124,141,142,272,273,274,357,445],[76,124,141,142,445],[76,124,141,142,183,199,200,445],[76,124,141,142,193,339,345],[76,124,141,142,150,291,353],[76,124,141,142,291,353],[62,76,124,141,142,291],[62,76,124,141,142,270,291,292],[76,124,141,142,218,236,353,429],[76,124,141,142,334,424,425,426,427,428],[76,124,141,142,333],[76,124,141,142,333,334],[76,124,141,142,191,215,216,270],[76,124,141,142,217,218,270],[76,124,141,142,270],[62,76,124,141,142,184,418],[62,76,124,141,142,167],[62,76,124,141,142,199,225],[62,76,124,141,142,199],[76,124,141,142,223,228],[62,76,124,141,142,224,396],[62,66,76,124,138,141,142,174,175,176,393,438,439],[76,124,136,138,141,142,187,213,241,259,270,343,357,358,445],[76,124,141,142,328,344],[76,124,141,142,393],[76,124,141,142,182],[76,124,141,142,150,276,288,309,311,352,353],[76,124,141,142,150,276,288,308,309,310,352,353],[76,124,141,142,302,303,304,305,306,307],[76,124,141,142,304],[76,124,141,142,308],[62,76,124,141,142,224,291,396],[62,76,124,141,142,291,394,396],[62,76,124,141,142,291,396],[76,124,141,142,259,349],[76,124,141,142,349],[76,124,138,141,142,358,396],[76,124,141,142,296],[76,123,124,141,142,295],[76,124,141,142,209,210,212,242,270,283,284,285,287,320,352,355,358],[76,124,141,142,286],[76,124,141,142,210,218,270],[76,124,141,142,283,352],[76,124,141,142,283,292,293,294,296,297,298,299,300,301,312,313,314,315,316,317,352,353,445],[76,124,141,142,281],[76,124,138,141,142,150,187,208,210,212,213,214,218,246,259,268,269,320,348,357,358,359,393,445],[76,124,141,142,352],[76,123,124,141,142,197,212,269,285,300,348,350,351,358],[76,124,141,142,283],[76,123,124,141,142,208,242,262,277,278,279,280,281,282],[76,124,138,141,142,262,263,277,358,359],[76,124,141,142,197,259,269,270,285,348,352,358],[76,124,138,141,142,357,359],[76,124,138,141,142,156,355,358,359],[76,124,138,141,142,150,167,180,187,199,209,210,212,213,214,219,241,242,243,245,246,249,250,252,255,256,257,258,270,347,348,353,355,357,358,359],[76,124,138,141,142,156],[76,124,141,142,183,184,185,187,194,355,356,393,396,445],[76,124,138,141,142,156,167,203,375,377,378,379,445],[76,124,141,142,150,167,180,203,213,242,243,250,259,267,270,348,353,355,360,361,367,373,389,390],[76,124,141,142,193,194,269,328,339,348,357],[76,124,138,141,142,167,184,242,355,357,365],[76,124,141,142,275],[76,124,138,141,142,386,387,388],[76,124,141,142,355,357],[76,124,141,142,187,212,242,347,396],[76,124,138,141,142,150,250,259,355,361,367,369,373,389,392],[76,124,138,141,142,193,328,373,382],[76,124,141,142,183,219,347,357,384],[76,124,138,141,142,199,219,357,368,369,380,381,383,385],[70,76,124,141,142,210,211,212,393,396],[76,124,138,141,142,150,167,187,193,201,209,213,214,242,243,245,246,258,259,267,270,328,347,348,353,354,355,360,361,362,364,366,396],[76,124,138,141,142,156,193,355,367,386,391],[76,124,141,142,323,324,325,326,327],[76,124,141,142,249,251],[76,124,141,142,253],[76,124,141,142,251],[76,124,141,142,253,254],[76,124,138,141,142,187,208,358],[62,76,124,138,141,142,150,182,184,187,209,210,212,213,214,240,355,359,393,396],[76,124,138,141,142,150,167,186,191,242,354,358],[76,124,141,142,277],[76,124,141,142,278],[76,124,141,142,279],[76,124,141,142,202,206],[76,124,138,141,142,187,202,209],[76,124,141,142,205,206],[76,124,141,142,207],[76,124,141,142,202,203],[76,124,141,142,202,220],[76,124,141,142,202],[76,124,141,142,248,249,354],[76,124,141,142,247],[76,124,141,142,203,353,354],[76,124,141,142,244,354],[76,124,141,142,203,353],[76,124,141,142,320],[76,124,141,142,204,209,211,242,270,276,285,288,289,319,355,358],[76,124,141,142,218,229,232,233,234,235,236],[76,124,141,142,336],[76,124,141,142,197,211,212,263,270,283,296,300,329,330,331,332,334,335,338,347,352,357],[76,124,141,142,218],[76,124,141,142,240],[76,124,138,141,142,209,211,221,237,239,241,355,393,396],[76,124,141,142,218,229,230,231,232,233,234,235,236,394],[76,124,141,142,203],[76,124,141,142,263,264,267,348],[76,124,138,141,142,249,357],[76,124,138,141,142],[76,124,141,142,262,283],[76,124,141,142,261],[76,124,141,142,258,263],[76,124,141,142,260,262,357],[76,124,138,141,142,186,263,264,265,266,357,358],[62,76,124,141,142,215,217,270],[76,124,141,142,271],[62,76,124,141,142,184],[62,76,124,141,142,353],[62,70,76,124,141,142,212,214,393,396],[76,124,141,142,184,418,419],[62,76,124,141,142,228],[62,76,124,141,142,150,167,182,222,224,226,227,396],[76,124,141,142,199,353,358],[76,124,141,142,353,363],[62,76,124,136,138,141,142,150,182,228,272,393,394,395],[62,76,124,141,142,175,176,393,440],[62,63,64,65,66,76,124,141,142],[76,124,129,141,142],[76,124,141,142,370,371,372],[76,124,141,142,370],[62,66,76,124,138,140,141,142,150,174,175,176,177,179,180,182,246,308,359,392,396,440],[76,124,141,142,406],[76,124,141,142,408],[76,124,141,142,410],[76,124,141,142,412],[76,124,141,142,414,415,416],[76,124,141,142,420],[67,69,76,124,141,142,398,403,405,407,409,411,413,417,421,423,431,432,434,443,444,445,446],[76,124,141,142,422],[76,124,141,142,430],[76,124,141,142,224],[76,124,141,142,433],[76,123,124,141,142,263,264,265,267,299,353,435,436,437,440,441,442],[76,124,141,142,174],[76,124,141,142,537],[76,124,141,142,465],[76,124,141,142,463,465],[76,124,141,142,454,462,463,464,466,468],[76,124,141,142,452],[76,124,141,142,455,460,465,468],[76,124,141,142,451,468],[76,124,141,142,455,456,459,460,461,468],[76,124,141,142,455,456,457,459,460,468],[76,124,141,142,452,453,454,455,456,460,461,462,464,465,466,468],[76,124,141,142,468],[76,124,141,142,450,452,453,454,455,456,457,459,460,461,462,463,464,465,466,467],[76,124,141,142,450,468],[76,124,141,142,455,457,458,460,461,468],[76,124,141,142,459,468],[76,124,141,142,460,461,465,468],[76,124,141,142,453,463],[76,124,141,142,476,485],[76,124,141,142,475,476],[76,124,141,142,156,174],[76,124,141,142,470,471],[76,124,141,142,469,472],[76,124,141,142,490],[76,89,93,124,141,142,167],[76,89,124,141,142,156,167],[76,84,124,141,142],[76,86,89,124,141,142,164,167],[76,124,141,142,144,164],[76,84,124,141,142,174],[76,86,89,124,141,142,144,167],[76,81,82,85,88,124,135,141,142,156,167],[76,89,96,124,141,142],[76,81,87,124,141,142],[76,89,110,111,124,141,142],[76,85,89,124,141,142,159,167,174],[76,110,124,141,142,174],[76,83,84,124,141,142,174],[76,89,124,141,142],[76,83,84,85,86,87,88,89,90,91,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,124,141,142],[76,89,104,124,141,142],[76,89,96,97,124,141,142],[76,87,89,97,98,124,141,142],[76,88,124,141,142],[76,81,84,89,124,141,142],[76,89,93,97,98,124,141,142],[76,93,124,141,142],[76,87,89,92,124,141,142,167],[76,81,86,89,96,124,141,142],[76,124,141,142,156],[76,84,89,110,124,141,142,172,174],[76,124,141,142,505,506],[76,124,141,142,505],[76,124,141,142,486,505,506,516],[76,124,135,136,138,139,140,141,142,144,156,164,167,173,174,469,476,477,478,479,480,481,482,483,484,485],[76,124,141,142,478,479,480,481],[76,124,141,142,478,479,480],[76,124,141,142,478],[76,124,141,142,479],[76,124,141,142,476],[76,124,141,142,517],[76,124,141,142,498,511,512,535],[76,124,141,142,487,498,502,503,535],[76,124,141,142,527],[76,124,136,141,142,156,486,487,492,498,499,502,504,507,508,509,510,513,514,516,522,535],[76,124,141,142,498,511,512,513,535],[76,124,141,142,486,515],[76,124,141,142,172,520],[76,124,141,142,498,499,502,504,507,535],[76,124,136,141,142,156,486,487,492,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,522,535],[76,124,136,141,142,156,172,486,487,492,495,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,520,521,522,524,525,527,528,529,534,535],[76,124,141,142,473],[76,124,141,142,146,518]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"a305ee2f90e34e9e70aba9a9e9a154ce20c4d5cd1499cd21b8dc3617e1e5c810","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"89e7fd23f6e6ced38596054161f5fb88737018909c6529c946cb349b74b95275","impliedFormat":1},{"version":"115b2ad73fa7d175cd71a5873d984c21593b2a022f1a2036cc39d9f53629e5dc","impliedFormat":1},{"version":"1be330b3a0b00590633f04c3b35db7fa618c9ee079258e2b24c137eb4ffcd728","impliedFormat":1},{"version":"bb7a61dd55dc4b9422d13da3a6bb9cc5e89be888ef23bbcf6558aa9726b89a1c","impliedFormat":1},{"version":"3da0083607976261730c44908eab1b6262f727747ef3230a65ecd0153d9e8639","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"dd721e5707f241e4ef4ab36570d9e2a79f66aad63a339e3cbdbac7d9164d2431","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"3849a7f92d0e11b785f6ae7bedb25d9aad8d1234b3f1cf530a4e7404be26dd0a","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"57d6ac03382e30e9213641ff4f18cf9402bb246b77c13c8e848c0b1ca2b7ef92","impliedFormat":1},{"version":"f040772329d757ecd38479991101ef7bc9bf8d8f4dd8ee5d96fe00aa264f2a2b","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"57e47d02e88abef89d214cdf52b478104dc17997015746e288cbb580beaef266","impliedFormat":1},{"version":"04a2d0bd8166f057cc980608bd5898bfc91198636af3c1eb6cb4eb5e8652fbea","impliedFormat":1},{"version":"376c21ad92ca004531807ea4498f90a740fd04598b45a19335a865408180eddd","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9af0e608929aaf9ce96bd7a7b99c9360636c31d73670e4af09a09950df97841","impliedFormat":1},{"version":"48d37b90a04e753a925228f50304d02c4f95d57bf682f8bb688621c3cd9d32ec","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"b68c4ed987ef5693d3dccd85222d60769463aca404f2ffca1c4c42781dce388e","impliedFormat":1},{"version":"cfb5b5d514eb4ad0ee25f313b197f3baa493eee31f27613facd71efb68206720","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"9715fe982fccf375c88ac4d3cc8f6a126a7b7596be8d60190a0c7d22b45b4be4","impliedFormat":1},{"version":"1fe24e25a00c7dd689cb8c0fb4f1048b4a6d1c50f76aaca2ca5c6cdb44e01442","impliedFormat":1},{"version":"672f293c53a07b8c1c1940797cd5c7984482a0df3dd9c1f14aaee8d3474c2d83","impliedFormat":1},{"version":"0a66cb2511fa8e3e0e6ba9c09923f664a0a00896f486e6f09fc11ff806a12b0c","impliedFormat":1},{"version":"d703f98676a44f90d63b3ffc791faac42c2af0dd2b4a312f4afdb5db471df3de","impliedFormat":1},{"version":"0cfe1d0b90d24f5c105db5a2117192d082f7d048801d22a9ea5c62fae07b80a0","impliedFormat":1},{"version":"ef61792acbfa8c27c9bd113f02731e66229f7d3a169e3c1993b508134f1a58e0","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"414cc05e215b7fc5a4a6ece431985e05e03762c8eb5bf1e0972d477f97832956","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"5c2e5ca7d53236bbf483a81ae283e2695e291fe69490cd139b33fa9e71838a69","impliedFormat":1},{"version":"a73bee51e3820392023252c36348e62dd72e6bae30a345166e9c78360f1aba7e","impliedFormat":1},{"version":"6ea68b3b7d342d1716cc4293813410d3f09ff1d1ca4be14c42e6d51e810962e1","impliedFormat":1},{"version":"c319e82ac16a5a5da9e28dfdefdad72cebb5e1e67cbdcc63cce8ae86be1e454f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a020158a317c07774393974d26723af551e569f1ba4d6524e8e245f10e11b976","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"8bba776476c48b0e319d243f353190f24096057acede3c2f620fee17ff885dba","impliedFormat":1},{"version":"a3abe92070fbd33714bd837806030b39cfb1f8283a98c7c1f55fffeea388809e","impliedFormat":1},{"version":"ceb6696b98a72f2dae802260c5b0940ea338de65edd372ff9e13ab0a410c3a88","impliedFormat":1},{"version":"2cd914e04d403bdc7263074c63168335d44ce9367e8a74f6896c77d4d26a1038","impliedFormat":1},{"version":"ac60bbee0d4235643cc52b57768b22de8c257c12bd8c2039860540cab1fa1d82","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"3bc8605900fd1668f6d93ce8e14386478b6caa6fda41be633ee0fe4d0c716e62","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"9f31420a5040dbfb49ab94bcaaa5103a9a464e607cabe288958f53303f1da32e","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"f11d0dcaa4a1cba6d6513b04ceb31a262f223f56e18b289c0ba3133b4d3cd9a6","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"56013416784a6b754f3855f8f2bf6ce132320679b8a435389aca0361bce4df6b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"9c066f3b46cf016e5d072b464821c5b21cc9adcc44743de0f6c75e2509a357ab","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c51641ab4bfa31b7a50a0ca37edff67f56fab3149881024345b13f2b48b7d2de","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"52abbd5035a97ebfb4240ec8ade2741229a7c26450c84eb73490dc5ea048b911","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"4360ad4de54de2d5c642c4375d5eab0e7fe94ebe8adca907e6c186bbef75a54d","impliedFormat":1},{"version":"c338dff3233675f87a3869417aaea8b8bf590505106d38907dc1d0144f6402ef","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"9c9cae45dc94c2192c7d25f80649414fa13c425d0399a2c7cb2b979e4e50af42","impliedFormat":1},{"version":"068f063c2420b20f8845afadb38a14c640aed6bb01063df224edb24af92b4550","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"b8719d4483ebef35e9cb67cd5677b7e0103cf2ed8973df6aba6fdd02896ddc6e","impliedFormat":1},{"version":"643672ce383e1c58ea665a92c5481f8441edbd3e91db36e535abccbc9035adeb","impliedFormat":1},{"version":"6dd9bcf10678b889842d467706836a0ab42e6c58711e33918ed127073807ee65","impliedFormat":1},{"version":"8fa022ea514ce0ea78ac9b7092a9f97f08ead20c839c779891019e110fce8307","impliedFormat":1},{"version":"c93235337600b786fd7d0ff9c71a00f37ca65c4d63e5d695fc75153be2690f09","impliedFormat":1},{"version":"10179c817a384983f6925f778a2dac2c9427817f7d79e27d3e9b1c8d0564f1f4","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"c0a666b005521f52e2db0b685d659d7ee9b0b60bc0d347dfc5e826c7957bdb83","impliedFormat":1},{"version":"807d38d00ce6ab9395380c0f64e52f2f158cc804ac22745d8f05f0efdec87c33","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"d05fb434f4ba073aed74b6c62eff1723c835de2a963dbb091e000a2decb5a691","impliedFormat":1},{"version":"10e6166be454ddb8c81000019ce1069b476b478c316e7c25965a91904ec5c1e3","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"703989a003790524b4e34a1758941d05c121d5d352bccca55a5cfb0c76bca592","impliedFormat":1},{"version":"a58abf1f5c8feb335475097abeddd32fd71c4dc2065a3d28cf15cacabad9654a","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"671aeae7130038566a8d00affeb1b3e3b131edf93cbcfff6f55ed68f1ca4c1b3","impliedFormat":1},{"version":"f0f05149debcf31b3a717ce8dd16e0323a789905cb9e27239167b604153b8885","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"955c69dde189d5f47a886ed454ff50c69d4d8aaec3a454c9ab9c3551db727861","impliedFormat":1},{"version":"cec8b16ff98600e4f6777d1e1d4ddf815a5556a9c59bc08cc16db4fd4ae2cf00","impliedFormat":1},{"version":"9e21f8e2c0cfea713a4a372f284b60089c0841eb90bf3610539d89dbcd12d65a","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"c226288bda11cee97850f0149cc4ff5a244d42ed3f5a9f6e9b02f1162bf1e3f4","impliedFormat":1},{"version":"210a4ec6fd58f6c0358e68f69501a74aef547c82deb920c1dec7fa04f737915a","impliedFormat":1},{"version":"8eea4cc42d04d26bcbcaf209366956e9f7abaf56b0601c101016bb773730c5fe","impliedFormat":1},{"version":"f5319e38724c54dff74ee734950926a745c203dcce00bb0343cb08fbb2f6b546","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"12b8dfed70961bea1861e5d39e433580e71323abb5d33da6605182ec569db584","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"7e560f533aaf88cf9d3b427dcf6c112dd3f2ee26d610e2587583b6c354c753db","impliedFormat":1},{"version":"71e0082342008e4dfb43202df85ea0986ef8e003c921a1e49999d0234a3019da","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"e71e103fb212e015394def7f1379706fce637fec9f91aa88410a73b7c5cbd4e3","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"794998dc1c5a19ce77a75086fe829fb9c92f2fd07b5631c7d5e0d04fd9bc540c","impliedFormat":1},{"version":"2b0b12d0ee52373b1e7b09226eae8fbf6a2043916b7c19e2c39b15243f32bde2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"bdc5fd605a6d315ded648abf2c691a22d0b0c774b78c15512c40ddf138e51950","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"6cd4b0986c638d92f7204d1407b1cb3e0a79d7a2d23b0f141c1a0829540ce7ef","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"d58265e159fc3cb30aa8878ba5e986a314b1759c824ff66d777b9fe42117231a","impliedFormat":1},{"version":"ff8fccaae640b0bb364340216dcc7423e55b6bb182ca2334837fee38636ad32e","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"59ee66cf96b093b18c90a8f6dbb3f0e3b65c758fba7b8b980af9f2726c32c1a2","impliedFormat":1},{"version":"c590195790d7fa35b4abed577a605d283b8336b9e01fa9bf4ae4be49855940f9","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"026a43d8239b8f12d2fc4fa5a7acbc2ad06dd989d8c71286d791d9f57ca22b78","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"14cf3683955f914b4695e92c93aae5f3fe1e60f3321d712605164bfe53b34334","impliedFormat":1},{"version":"12f0fb50e28b9d48fe5b7580580efe7cc0bd38e4b8c02d21c175aa9a4fd839b0","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"7cd657e359eac7829db5f02c856993e8945ffccc71999cdfb4ab3bf801a1bbc6","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"29c2aa0712786a4a504fce3acd50928f086027276f7490965cb467d2ce638bae","impliedFormat":1},{"version":"f14e63395b54caecc486f00a39953ab00b7e4d428a4e2c38325154b08eb5dcc2","impliedFormat":1},{"version":"e749bbd37dadf82c9833278780527c717226e1e2c9bc7b2576c8ec1c40ec5647","impliedFormat":1},{"version":"46e4e179b295f08d0bd0176fe44cf6c89558c9091d3cb3894f9eaaa42ea1add1","impliedFormat":1},{"version":"08f52a9edaabeda3b2ea19a54730174861ceed637c5ca1c1b0c39459fdc0853e","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"29164fb428c851bc35b632761daad3ae075993a0bf9c43e9e3bc6468b32d9aa5","impliedFormat":1},{"version":"3c01539405051bffccacffd617254c8d0f665cdce00ec568c6f66ccb712b734f","impliedFormat":1},{"version":"ef9021bdfe54f4df005d0b81170bd2da9bfd86ef552cde2a049ba85c9649658f","impliedFormat":1},{"version":"17a1a0d1c492d73017c6e9a8feb79e9c8a2d41ef08b0fe51debc093a0b2e9459","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"96e1caae9b78cde35c62fee46c1ec9fa5f12c16bc1e2ab08d48e5921e29a6958","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"9e0327857503a958348d9e8e9dd57ed155a1e6ec0071eb5eb946fe06ccdf7680","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"01aa917531e116485beca44a14970834687b857757159769c16b228eb1e49c5f","impliedFormat":1},{"version":"397f568f996f8ffcf12d9156342552b0da42f6571eadba6bce61c99e1651977d","impliedFormat":1},{"version":"e2fd426f3cbc5bbff7860378784037c8fa9c1644785eed83c47c902b99b6cda9","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"a52674bc98da7979607e0f44d4c015c59c1b1d264c83fc50ec79ff2cfea06723","impliedFormat":1},{"version":"bcca16e60015db8bbf6bd117e88c5f7269337aebb05fc2b0701ae658a458c9c3","impliedFormat":1},{"version":"5e1246644fab20200cdc7c66348f3c861772669e945f2888ef58b461b81e1cd8","impliedFormat":1},{"version":"eb39550e2485298d91099e8ab2a1f7b32777d9a5ba34e9028ea8df2e64891172","impliedFormat":1},{"version":"e108f38a04a607f9386d68a4c6f3fdae1b712960f11f6482c6f1769bab056c2e","impliedFormat":1},{"version":"a3128a84a9568762a2996df79717d92154d18dd894681fc0ab3a098fa7f8ee3b","affectsGlobalScope":true,"impliedFormat":1},{"version":"347791f3792f436950396dd6171d6450234358001ae7c94ca209f1406566ccbf","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"714d8ebb298c7acc9bd1f34bd479c57d12b73371078a0c5a1883a68b8f1b9389","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"51bf55bb6eb80f11b3aa59fb0a9571565a7ea304a19381f6da5630f4b2e206c4","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"02f8ef78d46c5b27f108dbb56709daa0aff625c20247abb0e6bb67cd73439f9f","impliedFormat":1},{"version":"b7fff2d004c5879cae335db8f954eb1d61242d9f2d28515e67902032723caeab","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"6812502cc640de74782ce9121592ae3765deb1c5c8e795b179736b308dd65e90","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"2b664c3cc544d0e35276e1fb2d4989f7d4b4027ffc64da34ec83a6ccf2e5c528","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"b10bc147143031b250dc36815fd835543f67278245bf2d0a46dca765f215124e","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"1e4c6ac595b6d734c056ac285b9ee50d27a2c7afe7d15bd14ed16210e71593b0","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"3c7b3aecd652169787b3c512d8f274a3511c475f84dcd6cead164e40cad64480","impliedFormat":1},{"version":"9a01f12466488eccd8d9eafc8fecb9926c175a4bf4a8f73a07c3bcf8b3363282","impliedFormat":1},{"version":"b80f624162276f24a4ec78b8e86fbee80ca255938e12f8b58e7a8f1a6937120b","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"b95a6f019095dd1d48fd04965b50dfd63e5743a6e75478343c46d2582a5132bf","impliedFormat":99},{"version":"c2008605e78208cfa9cd70bd29856b72dda7ad89df5dc895920f8e10bcb9cd0a","impliedFormat":99},{"version":"b97cb5616d2ab82a98ec9ada7b9e9cabb1f5da880ec50ea2b8dc5baa4cbf3c16","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"00b0f43b3770f66aa1e105327980c0ff17a868d0e5d9f5689f15f8d6bf4fb1f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"272a7e7dbe05e8aaba1662ef1a16bbd57975cc352648b24e7a61b7798f3a0ad7","affectsGlobalScope":true,"impliedFormat":1},{"version":"a1219ee18b9282b4c6a31f1f0bcc9255b425e99363268ba6752a932cf76662f0","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f2b3bca04d1bfe583daae1e1f798c92ec24bb6693bd88d0a09ba6802dee362a8",{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},"8ab2020159a945e311929abd23acd6b9e2ad9b665bf31a7247f2e12ccad7662d",{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"d2e64a6f25013b099e83bfadb2c388d7bef3e8f3fdb25528225bbc841e7e7e3a","impliedFormat":99},{"version":"369ba5259e66ca8c7d35e3234f7a2a0863a770fdb8266505747c65cf346a0804","impliedFormat":99},{"version":"64d984f55025daf604f670b7dfd090ea765f2098aee871174ef2ee3e94479098","impliedFormat":99},{"version":"f147b6710441cf3ec3234adf63b0593ce5e8c9b692959d21d3babc8454bcf743","impliedFormat":99},{"version":"e96d5373a66c2cfbbc7e6642cf274055aa2c7ff6bd37be7480c66faf9804db6d","impliedFormat":99},{"version":"02bcdd7a76c5c1c485cbf05626d24c86ac8f9a1d8dc31f8924108bbaa4cf3ba9","impliedFormat":99},{"version":"c874ab6feac6e0fdf9142727c9a876065777a5392f14b0bbcf869b1e69eb46b5","impliedFormat":99},{"version":"7c553fc9e34773ddbaabe0fa1367d4b109101d0868a008f11042bee24b5a925d","impliedFormat":99},{"version":"9962ce696fbdce2421d883ca4b062a54f982496625437ae4d3633376c5ad4a80","impliedFormat":99},{"version":"e3ea467c4a7f743f3548c9ed61300591965b1d12c08c8bb9aaff8a002ba95fce","impliedFormat":99},{"version":"4c17183a07a63bea2653fbfc0a942b027160ddbee823024789a415f9589de327","impliedFormat":99},{"version":"3e2203c892297ea44b87470fde51b3d48cfe3eeb6901995de429539462894464","impliedFormat":99},{"version":"c84bf7a4abc5e7fdf45971a71b25b0e0d34ccd5e720a866dd78bb71d60d41a3f","impliedFormat":99},{"version":"e01ea380015ed698c3c0e2ccd0db72f3fc3ef1abc4519f122aa1c1a8d419a505","impliedFormat":99},{"version":"5ada1f8a9580c0f7478fe03ae3e07e958f0b79bdfb9dd50eeb98c1324f40011b","impliedFormat":99},{"version":"a8301dc90b4bd9fba333226ee0f1681aeeff1bd90233a8f647e687cb4b7d3521","impliedFormat":99},{"version":"e3225dc0bec183183509d290f641786245e6652bc3dce755f7ef404060693c35","impliedFormat":99},{"version":"09a03870ed8c55d7453bc9ad684df88965f2f770f987481ca71b8a09be5205bc","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"2cdd50ddc49e2d608ee848fc4ab0db9a2716624fabb4209c7c683d87e54d79c5","impliedFormat":99},{"version":"e431d664338b8470abb1750d699c7dfcebb1a25434559ef85bb96f1e82de5972","impliedFormat":99},{"version":"2c4254139d037c3caca66ce291c1308c1b5092cfcb151eb25980db932dd3b01a","impliedFormat":99},{"version":"970ae00ed018cb96352dc3f37355ef9c2d9f8aa94d7174ccd6d0ed855e462097","impliedFormat":99},{"version":"d2f8dee457ef7660b604226d471d55d927c3051766bdd80353553837492635c3","impliedFormat":99},{"version":"110a503289a2ef76141ffff3ffceb9a1c3662c32748eb9f6777a2bd0866d6fb1","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"310e6b62c493ce991624169a1c1904015769d947be88dc67e00adc7ebebcfa87","impliedFormat":99},{"version":"62fefda288160bf6e435b21cc03d3fbac11193d8d3bd0e82d86623cca7691c29","impliedFormat":99},{"version":"fcc46a8bcbf9bef21023bba1995160a25f0bc590ca3563ec44c315b4f4c1b18a","impliedFormat":99},{"version":"669573548930fb7d0a0761b827e203dc623581e21febf0be80fb02414f217d74","impliedFormat":99},{"version":"f974db5be6d7428044e19c2848e72495a9b56a8d33b6fcab392e7fb5328eb8df","impliedFormat":99},{"version":"aa348c4fb2f8ac77df855f07fb66281c9f6e71746fdff3b13c7932aa7642b788","impliedFormat":99},{"version":"262d4c61b446d096f0615b5acb0a09007b2a425c6a0fd38b66e404e7b6021812","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"0309a01650023994ed96edbd675ea4fdc3779a823ce716ad876cc77afb792b62","impliedFormat":99},{"version":"f13d7beeea58e219daef3a40e0dc4f2bd7d9581ac04cedec236102a12dfd2090","impliedFormat":99},{"version":"48c411efce1848d1ed55de41d7deb93cbf7c04080912fd87aa517ed25ef42639","affectsGlobalScope":true,"impliedFormat":1},{"version":"a094636c05f3e75cb072684dd42cd25a4c1324bec4a866706c85c04cecd49613","affectsGlobalScope":true,"impliedFormat":99},{"version":"fe2d63fcfdde197391b6b70daf7be8c02a60afa90754a5f4a04bdc367f62793d","impliedFormat":99},{"version":"9a3e2c85ec1ab7a0874a19814cc73c691b716282cb727914093089c5a8475955","impliedFormat":99},{"version":"cbdc781d2429935c9c42acd680f2a53a9f633e8de03290ec6ea818e4f7bff19a","impliedFormat":99},{"version":"9f6d9f5dd710922f82f69abf9a324e28122b5f31ae6f6ce78427716db30a377e","impliedFormat":99},{"version":"ac2414a284bdecfd6ab7b87578744ab056cd04dd574b17853cd76830ef5b72f2","impliedFormat":99},{"version":"c3f921bbc9d2e65bd503a56fbc66da910e68467baedb0b9db0cc939e1876c0d7","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"0cc99fbb161d78729d71fad66c6c363e3095862d6277160f29fa960744b785c6","affectsGlobalScope":true,"impliedFormat":99},"129a86faf021cbd7dd75371cef3025e3eb2137128b7cd35ea06b566f3413315c",{"version":"61de421bab744b4e1e105a40e0409221ba5729868cde52fbdbe3e21a1fb2c69c","impliedFormat":99},{"version":"6c84d9410cb39c418e76c74200dc943c8909863efe798df05c552933801dc42f","impliedFormat":99},"2f2b0c92621875746bfc2466ec57dc31ea32d7150682ddc7c8448b34e254462e",{"version":"3aa67bb15606c55fdd705fc720d1c5550078cb83f21355806a72495e8f59cdfa","signature":"07415405713faaca7975ad1bfbf9b412ba297f47a6923fe9cebafd7f4dcb7395"},"6a9a61013d77df9d48372dd9f6da3b2b7395ca4675b4fb4cfbce80060beb8d86",{"version":"f86904596a26a20f8a8e5ea2aa7784549bcb3b8cb8d5e3f25cec4aee552ede5e","signature":"97e658c85b782da4a23f4616189c629aad1f20746b0741d62e34087c3fd6d275"},{"version":"8f250abfe6d907fb058b0bc4c6d40be322ecc1ab6743db6b9f05a2b582f02341","signature":"76629349cc645a06e85c6df552397bd0ca3f0e09e7b11fdd2a56d89fbcc92309"},{"version":"0f407ed5c25d2fb2c97a216d175a3efc0fe21fd0faf4de3cca00931016375991","signature":"46f78a03a7c6287bd7eaa736b490ad45be142f90896a3d02a739501b3d5b2de0"},{"version":"ce2c97f3e5b6a222f19f005b6ef4886d89bf4c16bc1903658802d04a552fc9d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdec8f8e76fb1dfd3a6fdca604aae2bf3f51c1110f7a0012dc5604fdbb12f689","signature":"e79eaa521c0e7a44cd476d4c4ec7a2150ffd2800d3f4a92e0c4d32eee476849b"},"af7de680832f9a99aa122282a09b27ce3ca195b913239ed42775e10797136a9d",{"version":"59dd6a66b9027174bc2de83cd568fc135fa912f806adc7a2fe2eee15bdb869b8","signature":"b03385fb2f787f4429edb0274994492d1ffa934d91d88f01b5744c44686ff5ce"},{"version":"51d79ddaeeb46de8db2aa5928bf15a830b33ff0b91050c548a2ea82831010b60","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"84113bd518e3f94aa08d774a4a2088305059e2d14c1c3978b44ddb912b47f535","signature":"7c0e72bec073fd2eac188dc7d8ee98116783df0c8d7c478d14bdd2eb8d9ae108"},{"version":"429c3079ec8ef32bca7a44c2516b710e417795ce92887a8daa92072aa73a3279","signature":"a08f08cd9b2d0864fdba81ce3528f18c0ef1ca4e2a9e114d1e235c0f7a6d2d76"},{"version":"26f2cea077ea007aacd5a6702cdd76616c3eddf5bdd329e9f88258875ffa8121","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"54d753331fcddf49d121fe9d5320d2d2d14ec161504e15d7370562fd631bc957","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4caa4cac2a1e939f9a5d8b39dd20a2f318f01881a3f3864a5c5af3a1529db9c1","signature":"0746e1c710ea512e3f96f75811f718cc809bfb06a1bbbacaa2d0bfa71ffa930b"},{"version":"1dd5285b8f9e9e0b1417c1b07aff574aee793d72a87fee897b0001d48031a2df","signature":"8f0d9f945e58c09ce6189be2303fd068f5a66272e5d9778f230ceb6d15fb8c5b"},{"version":"c5a0ffa909f5277c3830ab0ea463558b135ea049b731b3852310e0b64ca3869f","signature":"40d5d8eeb71a7cfb02078d06351f4467bdf5d8dc4d47ba467480eb30b9b73e94"},{"version":"c0efe9f1a7841088fd3b0cdb9c2290a6f5c1fa7a3aebdf3aaf270d344ff56e21","signature":"7175ca141479166d4eb613d64104eeed84093343cca30afd909912737339e111"},{"version":"ff0d9317f266d554abc684692d66c0a4cc76c2ed22248400d7838032dea5d84c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"662ff9b16378365dce8ac7752370c8c8609f11c6f5d53480c5dad6bdc00a467d","signature":"b3f301ee5acbe3a478e4b2fc5c86b1eb021f624afbf2295dbc0c605cd052de16"},"fa452635b5ac56ff89f7c2bef9ae560435d24d8f50f4ba9e661dee9e50475d28","1a017381422d08696696ee73d0b477f65817ceb7db6a862c0ee1274ec8ebf19b",{"version":"ab3408c1be7894e73630304d2c8aebea85542ca23fb96a7011032812bb2f3f90","signature":"ba54342937eb116567785cc93f6ef7c8ce3b041a67bb2ecb579ab28359b06047"},{"version":"c7fd9ee637fa81986028569225441584268f0d1a00b8d42cfcf9de738a881da1","signature":"950dcb0d1b29e2ef981b7bc2d893f177a18c2d731510b2aab0aa59e2b25ca7c0"},{"version":"74a1b2fc1d585437ddadf00f90ac46fc005e41679621625cbea5f5fe868c6f30","signature":"824201568ccc90c3008484b7f5db8b7ff9563b6ab086836e9de560ee0cfe45ab"},"532ebc18ccfd0dde7741c8b3063a4d38117516b46b83b92a79587713b8e64d67","1adc127175e94440e797af519f6778fcbbd7965dbff102f9c2bb8059b3650e28",{"version":"1414615c866abf5fa5557c370fd8744a06a31e6c4403c713e17272fa2db15a91","signature":"e27f6810eed02e88d63146c2e843a57ba7846571669afc3c6a9871dc5f66d300"},{"version":"87efe6a08272fd6ad1f8c21c2c55b64cd713a48daf94a12e14ab4030944b7ba3","signature":"0f32d061b698a5b6715f2fb0bd9235bb1b1be541361ccbb9ef3075cfc661ef4e"},{"version":"304fd3f360a445e635986b3264ee8017e6a3631a2a4f6faf0d0bf7d3f98e62eb","signature":"4523fcdd7dc2a00ac6c7d5c02b0e11288069c09dc1eb5e472259cc165193351e"},{"version":"5470908d24bb9a7d1bd87b5f06733e685aadaf77b8ade7c96bc210e5050c4a5d","signature":"a533121eee3903d4cc40d812648fb68cf6f62d670913ef28f5f65a179e4ca6a8"},{"version":"187c0cd60326c19be31746d530c8082dcc0b395da934fddd17ad4a356c9a417b","signature":"272b05835aacc3bab10b150e1cbbe13b0878f49f64f027cae57c19906d07469c"},{"version":"d74845f8b6f02aff4b3dd36b99003dd4fe58ac2376ae511f6d92bcb3a153d6aa","signature":"6781825fe0bacb6828c87c307a6126fe3a0ae631d079cb04b8b852804fdaa7bb"},{"version":"a63d1dceca466019e671006f8f071c0bf5596206b98557239ca64f9e58c191e0","signature":"3b31470d2936e80d198c801cc596e0569736a32b31f5544a6453f3934e24fba7"},{"version":"4763b947677a33bd554af8624d932aedea06bcf57a25b11ef6a7e057210358d6","signature":"6cf67db3bb1d66e8d7dbe762fed6bdf98d8236cc313c69a42dffae94476336ab"},{"version":"251c70669857e6de36884f09be038138bbc8111e5d597f33d3910df9cf4f5ae6","signature":"db8a0682e6c1d96fd0116f63cde511bd7b3de4387312aabd7da4bf53d72e0ad6"},"60193090d4e861a6cc875a6f00b0616a024ac586b866dfbdf09e4ca053aa8c65","efff1f1a0df5cb30394a6323bf9fad9532cf4284de8ca3820b8c49d8bd88b463","b9aeaade08803f4f2f5e1fcf82527451afc68b6c93896a749d27d92da239344a","9da27bb4af2cd79a55068f5fa60724e96145ecd54835420f3769c1aeca2a8ebd","56900c830691def7957f3b487b19d91136aa05af0752f83673b2204303c19152","ff25d2789578679f3f1b1f5dcbb5c87a0dad6488fa583cd1fded8ae83c58b1b5",{"version":"30cdf53273913e8ee8b04cabd23d6b53df2f92265a9c88470e5d7c957d95b7ef","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"dc575c9e3b32d510c9a974135f13f83e2b836d8bb482310c40b2495b0a763dd2",{"version":"abac25862a735319832d29b4daa0eac0af8d63e7a9f6dd0d95dcb687d8cab4a6","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"6e2133f8ad52b25f2ce9c7ccc30fdd2653214949e08552ee3fbd970716d07dc2","0d7c3b7a0922f5a70e863e5dc809850c9a42d9438ab0ee919657845b3db88c18","a9f7e7f88d04c20cb51988a6c5bceaf00920c6d7034f094106613a6a1f1e3160"],"root":[449,474,519,536,[539,587]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[582,1],[583,2],[584,3],[585,4],[586,5],[587,6],[581,7],[562,8],[566,9],[571,10],[576,11],[578,12],[580,13],[564,14],[563,15],[565,16],[577,17],[567,18],[568,19],[569,20],[570,21],[579,22],[546,23],[573,24],[572,24],[574,24],[575,25],[536,26],[541,27],[540,24],[539,24],[543,28],[542,29],[545,30],[544,24],[547,31],[549,32],[548,18],[552,33],[551,34],[553,35],[550,24],[554,36],[555,37],[556,24],[558,38],[557,24],[559,39],[561,40],[560,18],[449,41],[395,24],[475,24],[121,42],[122,42],[123,43],[76,44],[124,45],[125,46],[126,47],[71,24],[74,48],[72,24],[73,24],[127,49],[128,50],[129,51],[130,52],[131,53],[132,54],[133,54],[134,55],[135,56],[136,57],[137,58],[77,24],[75,24],[138,59],[139,60],[140,61],[174,62],[141,63],[142,24],[143,64],[144,65],[145,66],[146,67],[147,68],[148,69],[149,70],[150,71],[151,72],[152,72],[153,73],[154,24],[155,74],[156,75],[158,76],[157,77],[159,78],[160,79],[161,80],[162,81],[163,82],[164,83],[165,84],[166,85],[167,86],[168,87],[169,88],[170,89],[171,90],[78,24],[79,24],[80,24],[118,91],[119,24],[120,24],[172,92],[173,93],[178,94],[179,95],[177,23],[175,96],[176,97],[60,24],[62,98],[291,23],[522,24],[523,99],[524,100],[527,101],[526,24],[487,24],[498,102],[493,103],[496,104],[511,105],[500,24],[503,106],[502,107],[514,107],[501,108],[525,24],[495,109],[497,109],[489,110],[492,111],[508,110],[494,112],[488,24],[61,24],[482,24],[532,113],[534,114],[533,115],[531,116],[530,24],[69,117],[398,118],[403,7],[405,119],[199,120],[347,121],[374,122],[274,24],[192,24],[197,24],[338,123],[266,124],[198,24],[376,125],[377,126],[319,127],[335,128],[239,129],[342,130],[343,131],[341,132],[340,24],[339,133],[375,134],[200,135],[273,24],[275,136],[195,24],[210,137],[201,138],[214,137],[243,137],[185,137],[346,139],[356,24],[191,24],[297,140],[298,141],[292,142],[426,24],[300,24],[301,142],[293,143],[430,144],[429,145],[425,24],[379,24],[334,146],[333,24],[424,147],[294,23],[217,148],[215,149],[427,24],[428,24],[216,150],[419,151],[422,152],[226,153],[225,154],[224,155],[433,23],[223,156],[261,24],[436,24],[439,24],[438,23],[440,157],[181,24],[344,158],[345,159],[368,24],[190,160],[180,24],[183,161],[313,23],[312,162],[311,163],[302,24],[303,24],[310,24],[305,24],[308,164],[304,24],[306,165],[309,166],[307,165],[196,24],[188,24],[189,137],[397,167],[406,168],[410,169],[350,170],[349,24],[258,24],[441,171],[359,172],[295,173],[296,174],[288,175],[280,24],[286,24],[287,176],[317,177],[281,178],[318,179],[315,180],[314,24],[316,24],[270,181],[351,182],[352,183],[282,184],[283,185],[278,186],[330,187],[358,188],[361,189],[259,190],[186,191],[357,192],[182,122],[380,193],[391,194],[378,24],[390,195],[70,24],[366,196],[246,24],[276,197],[362,24],[205,24],[389,198],[194,24],[249,199],[348,200],[388,24],[382,201],[187,24],[383,202],[385,203],[386,204],[369,24],[387,191],[213,205],[367,206],[392,207],[322,24],[325,24],[323,24],[327,24],[324,24],[326,24],[328,208],[321,24],[252,209],[251,24],[257,210],[253,211],[256,212],[255,212],[254,211],[209,213],[241,214],[355,215],[442,24],[414,216],[416,217],[285,24],[415,218],[353,182],[299,182],[193,24],[242,219],[206,220],[207,221],[208,222],[204,223],[329,223],[220,223],[244,224],[221,224],[203,225],[202,24],[250,226],[248,227],[247,228],[245,229],[354,230],[290,231],[320,232],[289,233],[337,234],[336,235],[332,236],[238,237],[240,238],[237,239],[211,240],[269,24],[402,24],[268,241],[331,24],[260,242],[279,243],[277,244],[262,245],[264,246],[437,24],[263,247],[265,247],[400,24],[399,24],[401,24],[435,24],[267,248],[235,23],[68,24],[218,249],[227,24],[272,250],[212,24],[408,23],[418,251],[234,23],[412,142],[233,252],[394,253],[232,251],[184,24],[420,254],[230,23],[231,23],[222,24],[271,24],[229,255],[228,256],[219,257],[284,71],[360,71],[384,24],[364,258],[363,24],[404,24],[236,23],[396,259],[63,23],[66,260],[67,261],[64,23],[65,24],[381,262],[373,263],[372,24],[371,264],[370,24],[393,265],[407,266],[409,267],[411,268],[413,269],[417,270],[448,271],[421,271],[447,272],[423,273],[431,274],[432,275],[434,276],[443,277],[446,160],[445,24],[444,278],[538,279],[537,24],[466,280],[464,281],[465,282],[453,283],[454,281],[461,284],[452,285],[457,286],[467,24],[458,287],[463,288],[469,289],[468,290],[451,291],[459,292],[460,293],[455,294],[462,280],[456,295],[477,296],[476,297],[365,298],[450,24],[472,299],[471,24],[470,24],[473,300],[512,24],[490,24],[491,301],[58,24],[59,24],[10,24],[11,24],[13,24],[12,24],[2,24],[14,24],[15,24],[16,24],[17,24],[18,24],[19,24],[20,24],[21,24],[3,24],[22,24],[23,24],[4,24],[24,24],[28,24],[25,24],[26,24],[27,24],[29,24],[30,24],[31,24],[5,24],[32,24],[33,24],[34,24],[35,24],[6,24],[39,24],[36,24],[37,24],[38,24],[40,24],[7,24],[41,24],[46,24],[47,24],[42,24],[43,24],[44,24],[45,24],[8,24],[51,24],[48,24],[49,24],[50,24],[52,24],[9,24],[53,24],[54,24],[55,24],[57,24],[56,24],[1,24],[96,302],[106,303],[95,302],[116,304],[87,305],[86,306],[115,278],[109,307],[114,308],[89,309],[103,310],[88,311],[112,312],[84,313],[83,278],[113,314],[85,315],[90,316],[91,24],[94,316],[81,24],[117,317],[107,318],[98,319],[99,320],[101,321],[97,322],[100,323],[110,278],[92,324],[93,325],[102,326],[82,327],[105,318],[104,316],[108,24],[111,328],[509,329],[506,330],[507,329],[510,331],[505,24],[486,332],[483,333],[481,334],[479,335],[478,24],[480,336],[484,24],[485,337],[518,338],[513,339],[504,340],[499,24],[528,341],[515,342],[529,343],[516,344],[521,345],[520,346],[517,347],[535,348],[474,349],[519,350]],"affectedFilesPendingEmit":[582,583,584,585,586,587,562,566,571,576,578,580,564,563,565,577,567,568,569,570,579,546,573,572,574,575,536,541,540,543,542,545,544,547,549,548,552,551,553,550,554,555,556,558,557,559,561,560,474,519],"version":"5.9.3"}
\ No newline at end of file
+{"fileNames":["./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/get-page-files.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/canary.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/experimental.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/index.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/canary.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/experimental.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/fallback.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/body-streams.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/require-hook.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/page-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-kind.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render-result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/load-components.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/with-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/route-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/page-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/pages.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/next-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/adapter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/jsx-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/search-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/http.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request-meta.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/cli/next-test.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config-shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/node.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/.pnpm/sharp@0.33.5/node_modules/sharp/lib/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/trace.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/react-dev-overlay/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/telemetry/storage.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/.pnpm/@next+env@15.1.3/node_modules/@next/env/dist/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/image-component.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/not-found.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/connection.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image-types/global.d.ts","./next-env.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/config.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./node_modules/.pnpm/@types+estree@1.0.9/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","./node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/runtime.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/@vitest+pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/index-c1cfc5e9.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/index.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/tasks-3znpj1lr.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types-bxe-2udy.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/error.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/environment.looobwuu.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment-ddx0edty.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/rawsnapshot-cpnkto81.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/config.cy0c388z.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index-z0r8hvru.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/client.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/server.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/benchmark.geerunq4.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/reporters.nr4dxcka.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/vite.czkp4x9w.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/config.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.tn5kgiih.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.b9fxpcac.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/chai.d.cts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/index.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/index.d.ts","./node_modules/.pnpm/@vitest+spy@2.1.9/node_modules/@vitest/spy/dist/index.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/types-dzoqtgin.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/mocker.crtm890j.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/suite.b2jumifp.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/index.d.ts","./lib/api/client.test.ts","./node_modules/.pnpm/openapi-typescript-helpers@0.0.15/node_modules/openapi-typescript-helpers/index.d.ts","./node_modules/.pnpm/openapi-fetch@0.13.8/node_modules/openapi-fetch/dist/index.d.ts","./lib/api/schema.d.ts","./lib/api/config.ts","./lib/api/client.ts","./lib/api/types.ts","./lib/api/server.ts","./lib/autosave/autosave.ts","./lib/autosave/autosave.test.ts","./components/toast.tsx","./lib/autosave/useautosave.ts","./lib/foreshadow/board.ts","./lib/foreshadow/board.test.ts","./lib/foreshadow/useforeshadow.ts","./lib/outline/outline.ts","./lib/outline/outline.test.ts","./lib/outline/useoutline.ts","./lib/pace/beatmap.ts","./lib/pace/beatmap.test.ts","./lib/review/decisions.ts","./lib/review/decisions.test.ts","./lib/review/sse.ts","./lib/review/history.ts","./lib/review/history.test.ts","./lib/review/sse.test.ts","./lib/review/useaccept.ts","./lib/review/usereviewstream.ts","./lib/settings/providers.ts","./lib/stream/sse.ts","./lib/stream/sse.test.ts","./lib/stream/usedraftstream.ts","./lib/wizard/wizard.ts","./lib/wizard/wizard.test.ts","./app/layout.tsx","./components/leftnav.tsx","./components/appshell.tsx","./components/projectcard.tsx","./app/page.tsx","./components/foreshadow/foreshadowcard.tsx","./components/foreshadow/kanbancolumn.tsx","./components/foreshadow/registerform.tsx","./components/foreshadow/foreshadowboard.tsx","./app/projects/[id]/foreshadow/page.tsx","./components/outline/outlinechapterrow.tsx","./components/outline/outlineeditor.tsx","./app/projects/[id]/outline/page.tsx","./components/review/acceptpanel.tsx","./components/review/annotatedtext.tsx","./components/review/conflictcard.tsx","./components/review/foreshadowsuggestions.tsx","./components/review/beatmap.tsx","./components/review/pacepanel.tsx","./components/review/reviewreport.tsx","./app/projects/[id]/review/page.tsx","./components/workbench/chapterlist.tsx","./components/workbench/chapterassistant.tsx","./components/workbench/editor.tsx","./components/workbench/workbench.tsx","./app/projects/[id]/write/page.tsx","./components/projectwizard.tsx","./app/projects/new/page.tsx","./components/settings/providerssettings.tsx","./app/settings/providers/page.tsx","./.next/types/cache-life.d.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/projects/[id]/foreshadow/page.ts","./.next/types/app/projects/[id]/outline/page.ts","./.next/types/app/projects/[id]/review/page.ts","./.next/types/app/projects/[id]/write/page.ts","./.next/types/app/projects/new/page.ts","./.next/types/app/settings/providers/page.ts"],"fileIdsList":[[76,124,141,142,308,570],[76,124,141,142,308,574],[76,124,141,142,308,579],[76,124,141,142,308,582],[76,124,141,142,308,590],[76,124,141,142,308,595],[76,124,141,142,308,597],[76,124,141,142,308,599],[76,124,141,142,399,400,401,402],[76,124,141,142,447,546],[76,124,141,142,423,542,543,572,573],[76,124,141,142,431,543,578],[76,124,141,142,431,543,581],[76,124,141,142,431,543,559,589],[76,124,141,142,431,543,594],[76,124,141,142,572,596],[76,124,141,142,542,543,572,598],[62,76,124,141,142,423,571],[62,76,124,141,142,542,548,550,572,576,577],[62,76,124,141,142,542,548],[76,124,141,142,542,548,575],[62,76,124,141,142,548],[76,124,141,142,423,431],[76,124,141,142,542,551],[62,76,124,141,142,542,551,553,572,580],[76,124,141,142,423,542],[62,76,124,141,142,431,541,546,568],[76,124,141,142,542],[76,124,141,142,558],[76,124,141,142,554],[76,124,141,142,556,558],[76,124,141,142,558,587],[62,76,124,141,142,542,556,558,559,562,563,572,583,584,585,586,588],[62,76,124,141,142,541,542,546,564],[62,76,124,141,142],[76,124,141,142],[62,76,124,141,142,423,542,547,567,572,591,592,593],[76,124,141,142,535],[76,124,141,142,538,539,540],[76,124,141,142,540,542],[76,124,141,142,539],[76,124,141,142,535,544],[62,76,124,141,142,541,544,546],[76,124,141,142,535,542,548],[62,76,124,141,142,541,542,546,548],[76,124,141,142,535,542,551],[62,76,124,141,142,541,542,546],[76,124,141,142,535,554],[76,124,141,142,535,556],[76,124,141,142,535,542,559],[76,124,141,142,542,558],[76,124,141,142,535,558],[62,76,124,141,142,541,542,546,556],[62,76,124,141,142,540,558],[76,124,141,142,535,565],[62,76,124,141,142,540,565],[76,124,141,142,535,568],[76,124,141,142,447,448],[76,121,122,124,141,142],[76,123,124,141,142],[124,141,142],[76,124,129,141,142,159],[76,124,125,130,135,141,142,144,156,167],[76,124,125,126,135,141,142,144],[71,72,73,76,124,141,142],[76,124,127,141,142,168],[76,124,128,129,136,141,142,145],[76,124,129,141,142,156,164],[76,124,130,132,135,141,142,144],[76,123,124,131,141,142],[76,124,132,133,141,142],[76,124,134,135,141,142],[76,123,124,135,141,142],[76,124,135,136,137,141,142,156,167],[76,124,135,136,137,141,142,151,156,159],[76,117,124,132,135,138,141,142,144,156,167],[76,124,135,136,138,139,141,142,144,156,164,167],[76,124,138,140,141,142,156,164,167],[74,75,76,77,78,79,80,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,135,141,142],[76,124,141,142,143,167],[76,124,132,135,141,142,144,156],[76,124,141,142,145],[76,124,141,142,146],[76,123,124,141,142,147],[76,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,141,142,149],[76,124,141,142,150],[76,124,135,141,142,151,152],[76,124,141,142,151,153,168,170],[76,124,136,141,142],[76,124,135,141,142,156,157,159],[76,124,141,142,158,159],[76,124,141,142,156,157],[76,124,141,142,159],[76,124,141,142,160],[76,121,124,141,142,156,161,167],[76,124,135,141,142,162,163],[76,124,141,142,162,163],[76,124,129,141,142,144,156,164],[76,124,141,142,165],[76,124,141,142,144,166],[76,124,138,141,142,150,167],[76,124,129,141,142,168],[76,124,141,142,156,169],[76,124,141,142,143,170],[76,124,141,142,171],[76,117,124,141,142],[76,117,124,135,137,141,142,147,156,159,167,169,170,172],[76,124,141,142,156,173],[62,76,124,141,142,177,179],[62,76,124,141,142,177,178],[62,66,76,124,141,142,176,393,440],[62,66,76,124,141,142,175,393,440],[60,61,76,124,141,142],[76,124,141,142,491,492,495],[76,124,141,142,523],[76,124,141,142,526],[76,124,141,142,492,493,495,496,497],[76,124,141,142,492],[76,124,141,142,492,493,495],[76,124,141,142,492,493],[76,124,141,142,500],[76,124,141,142,487,500,501],[76,124,141,142,487,500],[76,124,141,142,487,494],[76,124,141,142,488],[76,124,141,142,487,488,489,491],[76,124,141,142,487],[76,124,141,142,530,531],[76,124,141,142,530,531,532,533],[76,124,141,142,530,532],[76,124,141,142,530],[68,76,124,141,142],[76,124,141,142,397],[76,124,141,142,404],[76,124,141,142,183,196,197,198,200,357],[76,124,141,142,183,187,189,190,191,192,346,357,359],[76,124,141,142,357],[76,124,141,142,197,213,290,337,353],[76,124,141,142,183],[76,124,141,142,377],[76,124,141,142,357,359,376],[76,124,141,142,276,290,318,445],[76,124,141,142,283,300,337,352],[76,124,141,142,238],[76,124,141,142,341],[76,124,141,142,340,341,342],[76,124,141,142,340],[70,76,124,138,141,142,180,183,190,193,194,195,197,201,269,274,320,328,338,348,357,393],[76,124,141,142,183,199,227,272,357,373,374,445],[76,124,141,142,199,445],[76,124,141,142,272,273,274,357,445],[76,124,141,142,445],[76,124,141,142,183,199,200,445],[76,124,141,142,193,339,345],[76,124,141,142,150,291,353],[76,124,141,142,291,353],[62,76,124,141,142,291],[62,76,124,141,142,270,291,292],[76,124,141,142,218,236,353,429],[76,124,141,142,334,424,425,426,427,428],[76,124,141,142,333],[76,124,141,142,333,334],[76,124,141,142,191,215,216,270],[76,124,141,142,217,218,270],[76,124,141,142,270],[62,76,124,141,142,184,418],[62,76,124,141,142,167],[62,76,124,141,142,199,225],[62,76,124,141,142,199],[76,124,141,142,223,228],[62,76,124,141,142,224,396],[62,66,76,124,138,141,142,174,175,176,393,438,439],[76,124,136,138,141,142,187,213,241,259,270,343,357,358,445],[76,124,141,142,328,344],[76,124,141,142,393],[76,124,141,142,182],[76,124,141,142,150,276,288,309,311,352,353],[76,124,141,142,150,276,288,308,309,310,352,353],[76,124,141,142,302,303,304,305,306,307],[76,124,141,142,304],[76,124,141,142,308],[62,76,124,141,142,224,291,396],[62,76,124,141,142,291,394,396],[62,76,124,141,142,291,396],[76,124,141,142,259,349],[76,124,141,142,349],[76,124,138,141,142,358,396],[76,124,141,142,296],[76,123,124,141,142,295],[76,124,141,142,209,210,212,242,270,283,284,285,287,320,352,355,358],[76,124,141,142,286],[76,124,141,142,210,218,270],[76,124,141,142,283,352],[76,124,141,142,283,292,293,294,296,297,298,299,300,301,312,313,314,315,316,317,352,353,445],[76,124,141,142,281],[76,124,138,141,142,150,187,208,210,212,213,214,218,246,259,268,269,320,348,357,358,359,393,445],[76,124,141,142,352],[76,123,124,141,142,197,212,269,285,300,348,350,351,358],[76,124,141,142,283],[76,123,124,141,142,208,242,262,277,278,279,280,281,282],[76,124,138,141,142,262,263,277,358,359],[76,124,141,142,197,259,269,270,285,348,352,358],[76,124,138,141,142,357,359],[76,124,138,141,142,156,355,358,359],[76,124,138,141,142,150,167,180,187,199,209,210,212,213,214,219,241,242,243,245,246,249,250,252,255,256,257,258,270,347,348,353,355,357,358,359],[76,124,138,141,142,156],[76,124,141,142,183,184,185,187,194,355,356,393,396,445],[76,124,138,141,142,156,167,203,375,377,378,379,445],[76,124,141,142,150,167,180,203,213,242,243,250,259,267,270,348,353,355,360,361,367,373,389,390],[76,124,141,142,193,194,269,328,339,348,357],[76,124,138,141,142,167,184,242,355,357,365],[76,124,141,142,275],[76,124,138,141,142,386,387,388],[76,124,141,142,355,357],[76,124,141,142,187,212,242,347,396],[76,124,138,141,142,150,250,259,355,361,367,369,373,389,392],[76,124,138,141,142,193,328,373,382],[76,124,141,142,183,219,347,357,384],[76,124,138,141,142,199,219,357,368,369,380,381,383,385],[70,76,124,141,142,210,211,212,393,396],[76,124,138,141,142,150,167,187,193,201,209,213,214,242,243,245,246,258,259,267,270,328,347,348,353,354,355,360,361,362,364,366,396],[76,124,138,141,142,156,193,355,367,386,391],[76,124,141,142,323,324,325,326,327],[76,124,141,142,249,251],[76,124,141,142,253],[76,124,141,142,251],[76,124,141,142,253,254],[76,124,138,141,142,187,208,358],[62,76,124,138,141,142,150,182,184,187,209,210,212,213,214,240,355,359,393,396],[76,124,138,141,142,150,167,186,191,242,354,358],[76,124,141,142,277],[76,124,141,142,278],[76,124,141,142,279],[76,124,141,142,202,206],[76,124,138,141,142,187,202,209],[76,124,141,142,205,206],[76,124,141,142,207],[76,124,141,142,202,203],[76,124,141,142,202,220],[76,124,141,142,202],[76,124,141,142,248,249,354],[76,124,141,142,247],[76,124,141,142,203,353,354],[76,124,141,142,244,354],[76,124,141,142,203,353],[76,124,141,142,320],[76,124,141,142,204,209,211,242,270,276,285,288,289,319,355,358],[76,124,141,142,218,229,232,233,234,235,236],[76,124,141,142,336],[76,124,141,142,197,211,212,263,270,283,296,300,329,330,331,332,334,335,338,347,352,357],[76,124,141,142,218],[76,124,141,142,240],[76,124,138,141,142,209,211,221,237,239,241,355,393,396],[76,124,141,142,218,229,230,231,232,233,234,235,236,394],[76,124,141,142,203],[76,124,141,142,263,264,267,348],[76,124,138,141,142,249,357],[76,124,138,141,142],[76,124,141,142,262,283],[76,124,141,142,261],[76,124,141,142,258,263],[76,124,141,142,260,262,357],[76,124,138,141,142,186,263,264,265,266,357,358],[62,76,124,141,142,215,217,270],[76,124,141,142,271],[62,76,124,141,142,184],[62,76,124,141,142,353],[62,70,76,124,141,142,212,214,393,396],[76,124,141,142,184,418,419],[62,76,124,141,142,228],[62,76,124,141,142,150,167,182,222,224,226,227,396],[76,124,141,142,199,353,358],[76,124,141,142,353,363],[62,76,124,136,138,141,142,150,182,228,272,393,394,395],[62,76,124,141,142,175,176,393,440],[62,63,64,65,66,76,124,141,142],[76,124,129,141,142],[76,124,141,142,370,371,372],[76,124,141,142,370],[62,66,76,124,138,140,141,142,150,174,175,176,177,179,180,182,246,308,359,392,396,440],[76,124,141,142,406],[76,124,141,142,408],[76,124,141,142,410],[76,124,141,142,412],[76,124,141,142,414,415,416],[76,124,141,142,420],[67,69,76,124,141,142,398,403,405,407,409,411,413,417,421,423,431,432,434,443,444,445,446],[76,124,141,142,422],[76,124,141,142,430],[76,124,141,142,224],[76,124,141,142,433],[76,123,124,141,142,263,264,265,267,299,353,435,436,437,440,441,442],[76,124,141,142,174],[76,124,141,142,537],[76,124,141,142,465],[76,124,141,142,463,465],[76,124,141,142,454,462,463,464,466,468],[76,124,141,142,452],[76,124,141,142,455,460,465,468],[76,124,141,142,451,468],[76,124,141,142,455,456,459,460,461,468],[76,124,141,142,455,456,457,459,460,468],[76,124,141,142,452,453,454,455,456,460,461,462,464,465,466,468],[76,124,141,142,468],[76,124,141,142,450,452,453,454,455,456,457,459,460,461,462,463,464,465,466,467],[76,124,141,142,450,468],[76,124,141,142,455,457,458,460,461,468],[76,124,141,142,459,468],[76,124,141,142,460,461,465,468],[76,124,141,142,453,463],[76,124,141,142,476,485],[76,124,141,142,475,476],[76,124,141,142,156,174],[76,124,141,142,470,471],[76,124,141,142,469,472],[76,124,141,142,490],[76,89,93,124,141,142,167],[76,89,124,141,142,156,167],[76,84,124,141,142],[76,86,89,124,141,142,164,167],[76,124,141,142,144,164],[76,84,124,141,142,174],[76,86,89,124,141,142,144,167],[76,81,82,85,88,124,135,141,142,156,167],[76,89,96,124,141,142],[76,81,87,124,141,142],[76,89,110,111,124,141,142],[76,85,89,124,141,142,159,167,174],[76,110,124,141,142,174],[76,83,84,124,141,142,174],[76,89,124,141,142],[76,83,84,85,86,87,88,89,90,91,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,124,141,142],[76,89,104,124,141,142],[76,89,96,97,124,141,142],[76,87,89,97,98,124,141,142],[76,88,124,141,142],[76,81,84,89,124,141,142],[76,89,93,97,98,124,141,142],[76,93,124,141,142],[76,87,89,92,124,141,142,167],[76,81,86,89,96,124,141,142],[76,124,141,142,156],[76,84,89,110,124,141,142,172,174],[76,124,141,142,505,506],[76,124,141,142,505],[76,124,141,142,486,505,506,516],[76,124,135,136,138,139,140,141,142,144,156,164,167,173,174,469,476,477,478,479,480,481,482,483,484,485],[76,124,141,142,478,479,480,481],[76,124,141,142,478,479,480],[76,124,141,142,478],[76,124,141,142,479],[76,124,141,142,476],[76,124,141,142,517],[76,124,141,142,498,511,512,535],[76,124,141,142,487,498,502,503,535],[76,124,141,142,527],[76,124,136,141,142,156,486,487,492,498,499,502,504,507,508,509,510,513,514,516,522,535],[76,124,141,142,498,511,512,513,535],[76,124,141,142,486,515],[76,124,141,142,172,520],[76,124,141,142,498,499,502,504,507,535],[76,124,136,141,142,156,486,487,492,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,522,535],[76,124,136,141,142,156,172,486,487,492,495,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,520,521,522,524,525,527,528,529,534,535],[76,124,141,142,473],[76,124,141,142,146,518]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"a305ee2f90e34e9e70aba9a9e9a154ce20c4d5cd1499cd21b8dc3617e1e5c810","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"89e7fd23f6e6ced38596054161f5fb88737018909c6529c946cb349b74b95275","impliedFormat":1},{"version":"115b2ad73fa7d175cd71a5873d984c21593b2a022f1a2036cc39d9f53629e5dc","impliedFormat":1},{"version":"1be330b3a0b00590633f04c3b35db7fa618c9ee079258e2b24c137eb4ffcd728","impliedFormat":1},{"version":"bb7a61dd55dc4b9422d13da3a6bb9cc5e89be888ef23bbcf6558aa9726b89a1c","impliedFormat":1},{"version":"3da0083607976261730c44908eab1b6262f727747ef3230a65ecd0153d9e8639","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"dd721e5707f241e4ef4ab36570d9e2a79f66aad63a339e3cbdbac7d9164d2431","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"3849a7f92d0e11b785f6ae7bedb25d9aad8d1234b3f1cf530a4e7404be26dd0a","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"57d6ac03382e30e9213641ff4f18cf9402bb246b77c13c8e848c0b1ca2b7ef92","impliedFormat":1},{"version":"f040772329d757ecd38479991101ef7bc9bf8d8f4dd8ee5d96fe00aa264f2a2b","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"57e47d02e88abef89d214cdf52b478104dc17997015746e288cbb580beaef266","impliedFormat":1},{"version":"04a2d0bd8166f057cc980608bd5898bfc91198636af3c1eb6cb4eb5e8652fbea","impliedFormat":1},{"version":"376c21ad92ca004531807ea4498f90a740fd04598b45a19335a865408180eddd","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9af0e608929aaf9ce96bd7a7b99c9360636c31d73670e4af09a09950df97841","impliedFormat":1},{"version":"48d37b90a04e753a925228f50304d02c4f95d57bf682f8bb688621c3cd9d32ec","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"b68c4ed987ef5693d3dccd85222d60769463aca404f2ffca1c4c42781dce388e","impliedFormat":1},{"version":"cfb5b5d514eb4ad0ee25f313b197f3baa493eee31f27613facd71efb68206720","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"9715fe982fccf375c88ac4d3cc8f6a126a7b7596be8d60190a0c7d22b45b4be4","impliedFormat":1},{"version":"1fe24e25a00c7dd689cb8c0fb4f1048b4a6d1c50f76aaca2ca5c6cdb44e01442","impliedFormat":1},{"version":"672f293c53a07b8c1c1940797cd5c7984482a0df3dd9c1f14aaee8d3474c2d83","impliedFormat":1},{"version":"0a66cb2511fa8e3e0e6ba9c09923f664a0a00896f486e6f09fc11ff806a12b0c","impliedFormat":1},{"version":"d703f98676a44f90d63b3ffc791faac42c2af0dd2b4a312f4afdb5db471df3de","impliedFormat":1},{"version":"0cfe1d0b90d24f5c105db5a2117192d082f7d048801d22a9ea5c62fae07b80a0","impliedFormat":1},{"version":"ef61792acbfa8c27c9bd113f02731e66229f7d3a169e3c1993b508134f1a58e0","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"414cc05e215b7fc5a4a6ece431985e05e03762c8eb5bf1e0972d477f97832956","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"5c2e5ca7d53236bbf483a81ae283e2695e291fe69490cd139b33fa9e71838a69","impliedFormat":1},{"version":"a73bee51e3820392023252c36348e62dd72e6bae30a345166e9c78360f1aba7e","impliedFormat":1},{"version":"6ea68b3b7d342d1716cc4293813410d3f09ff1d1ca4be14c42e6d51e810962e1","impliedFormat":1},{"version":"c319e82ac16a5a5da9e28dfdefdad72cebb5e1e67cbdcc63cce8ae86be1e454f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a020158a317c07774393974d26723af551e569f1ba4d6524e8e245f10e11b976","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"8bba776476c48b0e319d243f353190f24096057acede3c2f620fee17ff885dba","impliedFormat":1},{"version":"a3abe92070fbd33714bd837806030b39cfb1f8283a98c7c1f55fffeea388809e","impliedFormat":1},{"version":"ceb6696b98a72f2dae802260c5b0940ea338de65edd372ff9e13ab0a410c3a88","impliedFormat":1},{"version":"2cd914e04d403bdc7263074c63168335d44ce9367e8a74f6896c77d4d26a1038","impliedFormat":1},{"version":"ac60bbee0d4235643cc52b57768b22de8c257c12bd8c2039860540cab1fa1d82","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"3bc8605900fd1668f6d93ce8e14386478b6caa6fda41be633ee0fe4d0c716e62","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"9f31420a5040dbfb49ab94bcaaa5103a9a464e607cabe288958f53303f1da32e","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"f11d0dcaa4a1cba6d6513b04ceb31a262f223f56e18b289c0ba3133b4d3cd9a6","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"56013416784a6b754f3855f8f2bf6ce132320679b8a435389aca0361bce4df6b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"9c066f3b46cf016e5d072b464821c5b21cc9adcc44743de0f6c75e2509a357ab","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c51641ab4bfa31b7a50a0ca37edff67f56fab3149881024345b13f2b48b7d2de","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"52abbd5035a97ebfb4240ec8ade2741229a7c26450c84eb73490dc5ea048b911","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"4360ad4de54de2d5c642c4375d5eab0e7fe94ebe8adca907e6c186bbef75a54d","impliedFormat":1},{"version":"c338dff3233675f87a3869417aaea8b8bf590505106d38907dc1d0144f6402ef","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"9c9cae45dc94c2192c7d25f80649414fa13c425d0399a2c7cb2b979e4e50af42","impliedFormat":1},{"version":"068f063c2420b20f8845afadb38a14c640aed6bb01063df224edb24af92b4550","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"b8719d4483ebef35e9cb67cd5677b7e0103cf2ed8973df6aba6fdd02896ddc6e","impliedFormat":1},{"version":"643672ce383e1c58ea665a92c5481f8441edbd3e91db36e535abccbc9035adeb","impliedFormat":1},{"version":"6dd9bcf10678b889842d467706836a0ab42e6c58711e33918ed127073807ee65","impliedFormat":1},{"version":"8fa022ea514ce0ea78ac9b7092a9f97f08ead20c839c779891019e110fce8307","impliedFormat":1},{"version":"c93235337600b786fd7d0ff9c71a00f37ca65c4d63e5d695fc75153be2690f09","impliedFormat":1},{"version":"10179c817a384983f6925f778a2dac2c9427817f7d79e27d3e9b1c8d0564f1f4","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"c0a666b005521f52e2db0b685d659d7ee9b0b60bc0d347dfc5e826c7957bdb83","impliedFormat":1},{"version":"807d38d00ce6ab9395380c0f64e52f2f158cc804ac22745d8f05f0efdec87c33","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"d05fb434f4ba073aed74b6c62eff1723c835de2a963dbb091e000a2decb5a691","impliedFormat":1},{"version":"10e6166be454ddb8c81000019ce1069b476b478c316e7c25965a91904ec5c1e3","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"703989a003790524b4e34a1758941d05c121d5d352bccca55a5cfb0c76bca592","impliedFormat":1},{"version":"a58abf1f5c8feb335475097abeddd32fd71c4dc2065a3d28cf15cacabad9654a","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"671aeae7130038566a8d00affeb1b3e3b131edf93cbcfff6f55ed68f1ca4c1b3","impliedFormat":1},{"version":"f0f05149debcf31b3a717ce8dd16e0323a789905cb9e27239167b604153b8885","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"955c69dde189d5f47a886ed454ff50c69d4d8aaec3a454c9ab9c3551db727861","impliedFormat":1},{"version":"cec8b16ff98600e4f6777d1e1d4ddf815a5556a9c59bc08cc16db4fd4ae2cf00","impliedFormat":1},{"version":"9e21f8e2c0cfea713a4a372f284b60089c0841eb90bf3610539d89dbcd12d65a","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"c226288bda11cee97850f0149cc4ff5a244d42ed3f5a9f6e9b02f1162bf1e3f4","impliedFormat":1},{"version":"210a4ec6fd58f6c0358e68f69501a74aef547c82deb920c1dec7fa04f737915a","impliedFormat":1},{"version":"8eea4cc42d04d26bcbcaf209366956e9f7abaf56b0601c101016bb773730c5fe","impliedFormat":1},{"version":"f5319e38724c54dff74ee734950926a745c203dcce00bb0343cb08fbb2f6b546","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"12b8dfed70961bea1861e5d39e433580e71323abb5d33da6605182ec569db584","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"7e560f533aaf88cf9d3b427dcf6c112dd3f2ee26d610e2587583b6c354c753db","impliedFormat":1},{"version":"71e0082342008e4dfb43202df85ea0986ef8e003c921a1e49999d0234a3019da","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"e71e103fb212e015394def7f1379706fce637fec9f91aa88410a73b7c5cbd4e3","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"794998dc1c5a19ce77a75086fe829fb9c92f2fd07b5631c7d5e0d04fd9bc540c","impliedFormat":1},{"version":"2b0b12d0ee52373b1e7b09226eae8fbf6a2043916b7c19e2c39b15243f32bde2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"bdc5fd605a6d315ded648abf2c691a22d0b0c774b78c15512c40ddf138e51950","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"6cd4b0986c638d92f7204d1407b1cb3e0a79d7a2d23b0f141c1a0829540ce7ef","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"d58265e159fc3cb30aa8878ba5e986a314b1759c824ff66d777b9fe42117231a","impliedFormat":1},{"version":"ff8fccaae640b0bb364340216dcc7423e55b6bb182ca2334837fee38636ad32e","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"59ee66cf96b093b18c90a8f6dbb3f0e3b65c758fba7b8b980af9f2726c32c1a2","impliedFormat":1},{"version":"c590195790d7fa35b4abed577a605d283b8336b9e01fa9bf4ae4be49855940f9","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"026a43d8239b8f12d2fc4fa5a7acbc2ad06dd989d8c71286d791d9f57ca22b78","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"14cf3683955f914b4695e92c93aae5f3fe1e60f3321d712605164bfe53b34334","impliedFormat":1},{"version":"12f0fb50e28b9d48fe5b7580580efe7cc0bd38e4b8c02d21c175aa9a4fd839b0","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"7cd657e359eac7829db5f02c856993e8945ffccc71999cdfb4ab3bf801a1bbc6","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"29c2aa0712786a4a504fce3acd50928f086027276f7490965cb467d2ce638bae","impliedFormat":1},{"version":"f14e63395b54caecc486f00a39953ab00b7e4d428a4e2c38325154b08eb5dcc2","impliedFormat":1},{"version":"e749bbd37dadf82c9833278780527c717226e1e2c9bc7b2576c8ec1c40ec5647","impliedFormat":1},{"version":"46e4e179b295f08d0bd0176fe44cf6c89558c9091d3cb3894f9eaaa42ea1add1","impliedFormat":1},{"version":"08f52a9edaabeda3b2ea19a54730174861ceed637c5ca1c1b0c39459fdc0853e","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"29164fb428c851bc35b632761daad3ae075993a0bf9c43e9e3bc6468b32d9aa5","impliedFormat":1},{"version":"3c01539405051bffccacffd617254c8d0f665cdce00ec568c6f66ccb712b734f","impliedFormat":1},{"version":"ef9021bdfe54f4df005d0b81170bd2da9bfd86ef552cde2a049ba85c9649658f","impliedFormat":1},{"version":"17a1a0d1c492d73017c6e9a8feb79e9c8a2d41ef08b0fe51debc093a0b2e9459","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"96e1caae9b78cde35c62fee46c1ec9fa5f12c16bc1e2ab08d48e5921e29a6958","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"9e0327857503a958348d9e8e9dd57ed155a1e6ec0071eb5eb946fe06ccdf7680","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"01aa917531e116485beca44a14970834687b857757159769c16b228eb1e49c5f","impliedFormat":1},{"version":"397f568f996f8ffcf12d9156342552b0da42f6571eadba6bce61c99e1651977d","impliedFormat":1},{"version":"e2fd426f3cbc5bbff7860378784037c8fa9c1644785eed83c47c902b99b6cda9","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"a52674bc98da7979607e0f44d4c015c59c1b1d264c83fc50ec79ff2cfea06723","impliedFormat":1},{"version":"bcca16e60015db8bbf6bd117e88c5f7269337aebb05fc2b0701ae658a458c9c3","impliedFormat":1},{"version":"5e1246644fab20200cdc7c66348f3c861772669e945f2888ef58b461b81e1cd8","impliedFormat":1},{"version":"eb39550e2485298d91099e8ab2a1f7b32777d9a5ba34e9028ea8df2e64891172","impliedFormat":1},{"version":"e108f38a04a607f9386d68a4c6f3fdae1b712960f11f6482c6f1769bab056c2e","impliedFormat":1},{"version":"a3128a84a9568762a2996df79717d92154d18dd894681fc0ab3a098fa7f8ee3b","affectsGlobalScope":true,"impliedFormat":1},{"version":"347791f3792f436950396dd6171d6450234358001ae7c94ca209f1406566ccbf","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"714d8ebb298c7acc9bd1f34bd479c57d12b73371078a0c5a1883a68b8f1b9389","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"51bf55bb6eb80f11b3aa59fb0a9571565a7ea304a19381f6da5630f4b2e206c4","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"02f8ef78d46c5b27f108dbb56709daa0aff625c20247abb0e6bb67cd73439f9f","impliedFormat":1},{"version":"b7fff2d004c5879cae335db8f954eb1d61242d9f2d28515e67902032723caeab","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"6812502cc640de74782ce9121592ae3765deb1c5c8e795b179736b308dd65e90","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"2b664c3cc544d0e35276e1fb2d4989f7d4b4027ffc64da34ec83a6ccf2e5c528","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"b10bc147143031b250dc36815fd835543f67278245bf2d0a46dca765f215124e","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"1e4c6ac595b6d734c056ac285b9ee50d27a2c7afe7d15bd14ed16210e71593b0","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"3c7b3aecd652169787b3c512d8f274a3511c475f84dcd6cead164e40cad64480","impliedFormat":1},{"version":"9a01f12466488eccd8d9eafc8fecb9926c175a4bf4a8f73a07c3bcf8b3363282","impliedFormat":1},{"version":"b80f624162276f24a4ec78b8e86fbee80ca255938e12f8b58e7a8f1a6937120b","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"b95a6f019095dd1d48fd04965b50dfd63e5743a6e75478343c46d2582a5132bf","impliedFormat":99},{"version":"c2008605e78208cfa9cd70bd29856b72dda7ad89df5dc895920f8e10bcb9cd0a","impliedFormat":99},{"version":"b97cb5616d2ab82a98ec9ada7b9e9cabb1f5da880ec50ea2b8dc5baa4cbf3c16","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"00b0f43b3770f66aa1e105327980c0ff17a868d0e5d9f5689f15f8d6bf4fb1f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"272a7e7dbe05e8aaba1662ef1a16bbd57975cc352648b24e7a61b7798f3a0ad7","affectsGlobalScope":true,"impliedFormat":1},{"version":"a1219ee18b9282b4c6a31f1f0bcc9255b425e99363268ba6752a932cf76662f0","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f2b3bca04d1bfe583daae1e1f798c92ec24bb6693bd88d0a09ba6802dee362a8",{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},"8ab2020159a945e311929abd23acd6b9e2ad9b665bf31a7247f2e12ccad7662d",{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"d2e64a6f25013b099e83bfadb2c388d7bef3e8f3fdb25528225bbc841e7e7e3a","impliedFormat":99},{"version":"369ba5259e66ca8c7d35e3234f7a2a0863a770fdb8266505747c65cf346a0804","impliedFormat":99},{"version":"64d984f55025daf604f670b7dfd090ea765f2098aee871174ef2ee3e94479098","impliedFormat":99},{"version":"f147b6710441cf3ec3234adf63b0593ce5e8c9b692959d21d3babc8454bcf743","impliedFormat":99},{"version":"e96d5373a66c2cfbbc7e6642cf274055aa2c7ff6bd37be7480c66faf9804db6d","impliedFormat":99},{"version":"02bcdd7a76c5c1c485cbf05626d24c86ac8f9a1d8dc31f8924108bbaa4cf3ba9","impliedFormat":99},{"version":"c874ab6feac6e0fdf9142727c9a876065777a5392f14b0bbcf869b1e69eb46b5","impliedFormat":99},{"version":"7c553fc9e34773ddbaabe0fa1367d4b109101d0868a008f11042bee24b5a925d","impliedFormat":99},{"version":"9962ce696fbdce2421d883ca4b062a54f982496625437ae4d3633376c5ad4a80","impliedFormat":99},{"version":"e3ea467c4a7f743f3548c9ed61300591965b1d12c08c8bb9aaff8a002ba95fce","impliedFormat":99},{"version":"4c17183a07a63bea2653fbfc0a942b027160ddbee823024789a415f9589de327","impliedFormat":99},{"version":"3e2203c892297ea44b87470fde51b3d48cfe3eeb6901995de429539462894464","impliedFormat":99},{"version":"c84bf7a4abc5e7fdf45971a71b25b0e0d34ccd5e720a866dd78bb71d60d41a3f","impliedFormat":99},{"version":"e01ea380015ed698c3c0e2ccd0db72f3fc3ef1abc4519f122aa1c1a8d419a505","impliedFormat":99},{"version":"5ada1f8a9580c0f7478fe03ae3e07e958f0b79bdfb9dd50eeb98c1324f40011b","impliedFormat":99},{"version":"a8301dc90b4bd9fba333226ee0f1681aeeff1bd90233a8f647e687cb4b7d3521","impliedFormat":99},{"version":"e3225dc0bec183183509d290f641786245e6652bc3dce755f7ef404060693c35","impliedFormat":99},{"version":"09a03870ed8c55d7453bc9ad684df88965f2f770f987481ca71b8a09be5205bc","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"2cdd50ddc49e2d608ee848fc4ab0db9a2716624fabb4209c7c683d87e54d79c5","impliedFormat":99},{"version":"e431d664338b8470abb1750d699c7dfcebb1a25434559ef85bb96f1e82de5972","impliedFormat":99},{"version":"2c4254139d037c3caca66ce291c1308c1b5092cfcb151eb25980db932dd3b01a","impliedFormat":99},{"version":"970ae00ed018cb96352dc3f37355ef9c2d9f8aa94d7174ccd6d0ed855e462097","impliedFormat":99},{"version":"d2f8dee457ef7660b604226d471d55d927c3051766bdd80353553837492635c3","impliedFormat":99},{"version":"110a503289a2ef76141ffff3ffceb9a1c3662c32748eb9f6777a2bd0866d6fb1","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"310e6b62c493ce991624169a1c1904015769d947be88dc67e00adc7ebebcfa87","impliedFormat":99},{"version":"62fefda288160bf6e435b21cc03d3fbac11193d8d3bd0e82d86623cca7691c29","impliedFormat":99},{"version":"fcc46a8bcbf9bef21023bba1995160a25f0bc590ca3563ec44c315b4f4c1b18a","impliedFormat":99},{"version":"669573548930fb7d0a0761b827e203dc623581e21febf0be80fb02414f217d74","impliedFormat":99},{"version":"f974db5be6d7428044e19c2848e72495a9b56a8d33b6fcab392e7fb5328eb8df","impliedFormat":99},{"version":"aa348c4fb2f8ac77df855f07fb66281c9f6e71746fdff3b13c7932aa7642b788","impliedFormat":99},{"version":"262d4c61b446d096f0615b5acb0a09007b2a425c6a0fd38b66e404e7b6021812","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"0309a01650023994ed96edbd675ea4fdc3779a823ce716ad876cc77afb792b62","impliedFormat":99},{"version":"f13d7beeea58e219daef3a40e0dc4f2bd7d9581ac04cedec236102a12dfd2090","impliedFormat":99},{"version":"48c411efce1848d1ed55de41d7deb93cbf7c04080912fd87aa517ed25ef42639","affectsGlobalScope":true,"impliedFormat":1},{"version":"a094636c05f3e75cb072684dd42cd25a4c1324bec4a866706c85c04cecd49613","affectsGlobalScope":true,"impliedFormat":99},{"version":"fe2d63fcfdde197391b6b70daf7be8c02a60afa90754a5f4a04bdc367f62793d","impliedFormat":99},{"version":"9a3e2c85ec1ab7a0874a19814cc73c691b716282cb727914093089c5a8475955","impliedFormat":99},{"version":"cbdc781d2429935c9c42acd680f2a53a9f633e8de03290ec6ea818e4f7bff19a","impliedFormat":99},{"version":"9f6d9f5dd710922f82f69abf9a324e28122b5f31ae6f6ce78427716db30a377e","impliedFormat":99},{"version":"ac2414a284bdecfd6ab7b87578744ab056cd04dd574b17853cd76830ef5b72f2","impliedFormat":99},{"version":"c3f921bbc9d2e65bd503a56fbc66da910e68467baedb0b9db0cc939e1876c0d7","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"0cc99fbb161d78729d71fad66c6c363e3095862d6277160f29fa960744b785c6","affectsGlobalScope":true,"impliedFormat":99},"129a86faf021cbd7dd75371cef3025e3eb2137128b7cd35ea06b566f3413315c",{"version":"61de421bab744b4e1e105a40e0409221ba5729868cde52fbdbe3e21a1fb2c69c","impliedFormat":99},{"version":"6c84d9410cb39c418e76c74200dc943c8909863efe798df05c552933801dc42f","impliedFormat":99},"296e4d9489ff6aefc93528063dcc384338b4d6e555fbb73cdec2fee8b6744b54",{"version":"3aa67bb15606c55fdd705fc720d1c5550078cb83f21355806a72495e8f59cdfa","signature":"07415405713faaca7975ad1bfbf9b412ba297f47a6923fe9cebafd7f4dcb7395"},"6a9a61013d77df9d48372dd9f6da3b2b7395ca4675b4fb4cfbce80060beb8d86",{"version":"4f0aad57d90b3b60f110236f8aeee57237ed2c083b947e687e4412ee3040b7f6","signature":"d241dd8d3506b3a6df117db8d62114d1d2d4663baaeb0f5e72cc0e38f72725d1"},{"version":"d6cf83ebe1ae55f96a9e79a748723cdea14ad985a72f6585dc53412e32c78a86","signature":"adf2277f61af2e1981a1e63f463cb2a8fc39964908863180a4e52a88482da9e2"},{"version":"0f407ed5c25d2fb2c97a216d175a3efc0fe21fd0faf4de3cca00931016375991","signature":"46f78a03a7c6287bd7eaa736b490ad45be142f90896a3d02a739501b3d5b2de0"},{"version":"ce2c97f3e5b6a222f19f005b6ef4886d89bf4c16bc1903658802d04a552fc9d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdec8f8e76fb1dfd3a6fdca604aae2bf3f51c1110f7a0012dc5604fdbb12f689","signature":"e79eaa521c0e7a44cd476d4c4ec7a2150ffd2800d3f4a92e0c4d32eee476849b"},"af7de680832f9a99aa122282a09b27ce3ca195b913239ed42775e10797136a9d",{"version":"d3b8f898932a894fcf3a2f0137add132d5345f85c97e58cb6974a3b5f80b6127","signature":"fe68f8d72e2ba80e4e6cd7d14dc27c6463905a100d806ff4357dc083b46b60c0"},{"version":"fe03821ef6e674b9bd68b3c957a27e0eec9f2c3da4afd8174eb2a1640b2a0a3b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"306cc4071730e408e1e6df19d616bc3b4bf5dcec55343057834231cea7e2c9b8","signature":"93ea245c6b41d0516d8e9f79df02214396c126cb7129c44a1419f591e9b0149d"},{"version":"ce71392a988633a5400e5c42a22e14144c582f2c099ac74fa6e7327728343da5","signature":"af98905d9b626d3e1e58a247fcf12eeee6dc6ab75fd66f495cfb3f53fc60442f"},{"version":"1341b7ef069321245b63656c887094a74e28ad9a4f0579d55d50b9c8ea227262","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f046a76e6c2b6bc3a9c5cc53b99da7340b63ebd7f84e13a8a53e60f99d7c85a5","signature":"937982033f30be6d1131b84b6e07e898b512dc3c301f8fd13fbbf7fc726fc42f"},{"version":"68448904d6c46e4a04f229602ea4fe77ebe567283b1e370aefe127c03c4ae630","signature":"146b47847e0270185276ad42ff155ebe162ecf46e15dbcf5148818abd6edbb89"},{"version":"a5d5c5023804437603b51acbf146f6d7bfa9585c6addb8e45488f0e14f8ddbcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"59dd6a66b9027174bc2de83cd568fc135fa912f806adc7a2fe2eee15bdb869b8","51d79ddaeeb46de8db2aa5928bf15a830b33ff0b91050c548a2ea82831010b60",{"version":"de1ffa1e3bb7fa3341e883ec6079ba6dd6f73199354948d2f7ded7dc4df74b8c","signature":"e26bccafe194370d7a6064acaa4860cf362e8b05feed9c5c0e938b2d87a7d51a"},{"version":"1dd7fa4fda5354d9a835ff15de140db2664c9ad4bba57f4fa0c8ac7b25b2f0b6","signature":"68cf7e37be5cb67a476fcf97cee522bf6991ff292ce99736568c7cf552c33f12"},{"version":"030d523e29ac43dfe92303dc59be3348b02a6d3a1769e17fde3951bdfc56b464","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e0fc0ac00cc2be442d8ca11f3737d52118bf68c5b54e7857cffe8c663f52b12","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"4caa4cac2a1e939f9a5d8b39dd20a2f318f01881a3f3864a5c5af3a1529db9c1",{"version":"a41537fbd75a9b00364517e9f9a7dedb617a1b7d70529e0ddf573c2f19ef7f17","signature":"73226465e2746e4e6e679adaf07df2c26247bc2020daab13f7fbb7562276eefe"},{"version":"c5a0ffa909f5277c3830ab0ea463558b135ea049b731b3852310e0b64ca3869f","signature":"40d5d8eeb71a7cfb02078d06351f4467bdf5d8dc4d47ba467480eb30b9b73e94"},{"version":"c0efe9f1a7841088fd3b0cdb9c2290a6f5c1fa7a3aebdf3aaf270d344ff56e21","signature":"7175ca141479166d4eb613d64104eeed84093343cca30afd909912737339e111"},{"version":"ff0d9317f266d554abc684692d66c0a4cc76c2ed22248400d7838032dea5d84c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"662ff9b16378365dce8ac7752370c8c8609f11c6f5d53480c5dad6bdc00a467d","signature":"b3f301ee5acbe3a478e4b2fc5c86b1eb021f624afbf2295dbc0c605cd052de16"},"fa452635b5ac56ff89f7c2bef9ae560435d24d8f50f4ba9e661dee9e50475d28","1a017381422d08696696ee73d0b477f65817ceb7db6a862c0ee1274ec8ebf19b",{"version":"ab3408c1be7894e73630304d2c8aebea85542ca23fb96a7011032812bb2f3f90","signature":"ba54342937eb116567785cc93f6ef7c8ce3b041a67bb2ecb579ab28359b06047"},{"version":"d99de358089926db30b5837d01580cce1c517a47112e34bd386e211d6802fe74","signature":"6b55b050c9d01c4cbb1de4a608f41277e50e804ef9be262387e618cd3f75ff53"},{"version":"1ac295e91285a5df3bf0b6b06dd3324451e9e0510c5e9243763f6739fb206f31","signature":"536701639205b9046c7be694d0f58df607a5cefe8bb81b239350d70ed574e2c6"},"532ebc18ccfd0dde7741c8b3063a4d38117516b46b83b92a79587713b8e64d67","1adc127175e94440e797af519f6778fcbbd7965dbff102f9c2bb8059b3650e28",{"version":"e7f7041381a9396b1e52568564b7f90ee87f156e53a2f563f12f8a4043b13325","signature":"9e0e1dc9f88c512d6ca9044e77913ba4a6df74736ee5ea089134a65aa6375cd9"},{"version":"11c4815cba4cd9b80431a6ac938bb837610f865c8211032589b22b11cff37d53","signature":"e0b22866a9bac6f71dd2f508ee1ddd445e17ed71601d1b94ec60013fa934b48e"},{"version":"938a200cab8ef63bd63ef811d7732a0d380cc5331c865d131395e442f73fb338","signature":"db24729f7c24caafaf30635fdc17359f1e9312bd97fdbed0da9d34f00a982190"},{"version":"af5f2422c3e48b5ec5ce234c717528d39c9ff3274cc603aaa79ba1c488ffe573","signature":"a40c98740a3c1d0b71f61f6c9a7337c455d837af954e117bd0f65f0e80598fa7"},{"version":"c722f93b3272613f2645e308b41c9bd2511ceb17a69860422d09f19361542334","signature":"31e2f5b6378057657d9b6ebf2ed6de79026baef70f5463ae397768a7ef76e21d"},{"version":"17cb6cb4f714d0efd0de6e7a5254a6e6b463ced0a8c266724660ff1352da2645","signature":"599c21046dfbaec96c624b6092e7073a3472d2688490bfc8527df9a3496408ec"},{"version":"4cb8ce370dded833aa921768d43d7b5c635363c8bf8e343139df3a5554329354","signature":"c10fe759e992c8f87f199e01729cc58a5c225ecc9a914ddcb86cfc0f912c3ef1"},{"version":"9b198294f6ac383029150d5d784b7ebb2e91eff67517a13462efaca48a27cf0b","signature":"c0f8673c25e3392d2841196ba47799fa401e4135a9f37f75fedb369b79270144"},"1414615c866abf5fa5557c370fd8744a06a31e6c4403c713e17272fa2db15a91","87efe6a08272fd6ad1f8c21c2c55b64cd713a48daf94a12e14ab4030944b7ba3","304fd3f360a445e635986b3264ee8017e6a3631a2a4f6faf0d0bf7d3f98e62eb",{"version":"00c9a5c5fda50a4811bf4db39355b1043ea43e82e5e13e1a02910c6f7fb1bcea","signature":"273f5f74d3126daf5eeb15df72604de561105d1d8a2f5825c88fb09ae65eee8b"},{"version":"3531e6a9e84fa1ba01a8971a55f5b3a74817d6e7122aebfadc764775a0484660","signature":"cfc174c783fc21759487bf9e1d56afab8a6f58526c444c24ec7172160de8b71a"},{"version":"704ba60c7bb6de74ea9ee213ab33b6895f951fb1d5133bb3444f2391927e2c4c","signature":"36699080ba48ae0564aa577db1d8279ff99bd8a98720e8e26e0a88231dffbb8d"},{"version":"eaa57d44de0c8b1ce0c6fb8cf9eec693b15563cfcd71d3501c27aa0615e98667","signature":"a533121eee3903d4cc40d812648fb68cf6f62d670913ef28f5f65a179e4ca6a8"},"187c0cd60326c19be31746d530c8082dcc0b395da934fddd17ad4a356c9a417b",{"version":"d74845f8b6f02aff4b3dd36b99003dd4fe58ac2376ae511f6d92bcb3a153d6aa","signature":"6781825fe0bacb6828c87c307a6126fe3a0ae631d079cb04b8b852804fdaa7bb"},{"version":"a63d1dceca466019e671006f8f071c0bf5596206b98557239ca64f9e58c191e0","signature":"3b31470d2936e80d198c801cc596e0569736a32b31f5544a6453f3934e24fba7"},{"version":"4763b947677a33bd554af8624d932aedea06bcf57a25b11ef6a7e057210358d6","signature":"6cf67db3bb1d66e8d7dbe762fed6bdf98d8236cc313c69a42dffae94476336ab"},{"version":"a63623ab42d5e0f1b159cfcf4d80b4ab2bc324db92a6d05a6077995df1638ba0","signature":"db8a0682e6c1d96fd0116f63cde511bd7b3de4387312aabd7da4bf53d72e0ad6"},"60193090d4e861a6cc875a6f00b0616a024ac586b866dfbdf09e4ca053aa8c65","efff1f1a0df5cb30394a6323bf9fad9532cf4284de8ca3820b8c49d8bd88b463","b9aeaade08803f4f2f5e1fcf82527451afc68b6c93896a749d27d92da239344a","9da27bb4af2cd79a55068f5fa60724e96145ecd54835420f3769c1aeca2a8ebd","56900c830691def7957f3b487b19d91136aa05af0752f83673b2204303c19152","ff25d2789578679f3f1b1f5dcbb5c87a0dad6488fa583cd1fded8ae83c58b1b5",{"version":"30cdf53273913e8ee8b04cabd23d6b53df2f92265a9c88470e5d7c957d95b7ef","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"dc575c9e3b32d510c9a974135f13f83e2b836d8bb482310c40b2495b0a763dd2",{"version":"730539759405584ef46f895820a5723593e9da5fa3a66217debc9bce8aa57ed1","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"39422101b85c49c26174ec094efa2f65e5f4d3d26ae0d5cb1d9078b2ff157d5f","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"abac25862a735319832d29b4daa0eac0af8d63e7a9f6dd0d95dcb687d8cab4a6","6e2133f8ad52b25f2ce9c7ccc30fdd2653214949e08552ee3fbd970716d07dc2","0d7c3b7a0922f5a70e863e5dc809850c9a42d9438ab0ee919657845b3db88c18","a9f7e7f88d04c20cb51988a6c5bceaf00920c6d7034f094106613a6a1f1e3160"],"root":[449,474,519,536,[539,608]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[601,1],[602,2],[603,3],[604,4],[605,5],[606,6],[607,7],[608,8],[600,9],[570,10],[574,11],[579,12],[582,13],[590,14],[595,15],[597,16],[599,17],[572,18],[578,19],[575,20],[576,21],[577,22],[571,23],[580,24],[581,25],[573,26],[596,27],[583,28],[584,29],[587,30],[585,31],[586,29],[588,32],[589,33],[598,34],[546,35],[592,36],[591,36],[593,36],[594,37],[536,38],[541,39],[540,36],[539,36],[543,40],[542,41],[545,42],[544,36],[547,43],[549,44],[548,28],[550,45],[552,46],[551,28],[553,47],[555,48],[554,36],[557,49],[556,28],[560,50],[559,51],[561,52],[558,36],[562,53],[563,54],[564,36],[566,55],[565,36],[567,56],[569,57],[568,28],[449,58],[395,36],[475,36],[121,59],[122,59],[123,60],[76,61],[124,62],[125,63],[126,64],[71,36],[74,65],[72,36],[73,36],[127,66],[128,67],[129,68],[130,69],[131,70],[132,71],[133,71],[134,72],[135,73],[136,74],[137,75],[77,36],[75,36],[138,76],[139,77],[140,78],[174,79],[141,80],[142,36],[143,81],[144,82],[145,83],[146,84],[147,85],[148,86],[149,87],[150,88],[151,89],[152,89],[153,90],[154,36],[155,91],[156,92],[158,93],[157,94],[159,95],[160,96],[161,97],[162,98],[163,99],[164,100],[165,101],[166,102],[167,103],[168,104],[169,105],[170,106],[171,107],[78,36],[79,36],[80,36],[118,108],[119,36],[120,36],[172,109],[173,110],[178,111],[179,112],[177,35],[175,113],[176,114],[60,36],[62,115],[291,35],[522,36],[523,116],[524,117],[527,118],[526,36],[487,36],[498,119],[493,120],[496,121],[511,122],[500,36],[503,123],[502,124],[514,124],[501,125],[525,36],[495,126],[497,126],[489,127],[492,128],[508,127],[494,129],[488,36],[61,36],[482,36],[532,130],[534,131],[533,132],[531,133],[530,36],[69,134],[398,135],[403,9],[405,136],[199,137],[347,138],[374,139],[274,36],[192,36],[197,36],[338,140],[266,141],[198,36],[376,142],[377,143],[319,144],[335,145],[239,146],[342,147],[343,148],[341,149],[340,36],[339,150],[375,151],[200,152],[273,36],[275,153],[195,36],[210,154],[201,155],[214,154],[243,154],[185,154],[346,156],[356,36],[191,36],[297,157],[298,158],[292,159],[426,36],[300,36],[301,159],[293,160],[430,161],[429,162],[425,36],[379,36],[334,163],[333,36],[424,164],[294,35],[217,165],[215,166],[427,36],[428,36],[216,167],[419,168],[422,169],[226,170],[225,171],[224,172],[433,35],[223,173],[261,36],[436,36],[439,36],[438,35],[440,174],[181,36],[344,175],[345,176],[368,36],[190,177],[180,36],[183,178],[313,35],[312,179],[311,180],[302,36],[303,36],[310,36],[305,36],[308,181],[304,36],[306,182],[309,183],[307,182],[196,36],[188,36],[189,154],[397,184],[406,185],[410,186],[350,187],[349,36],[258,36],[441,188],[359,189],[295,190],[296,191],[288,192],[280,36],[286,36],[287,193],[317,194],[281,195],[318,196],[315,197],[314,36],[316,36],[270,198],[351,199],[352,200],[282,201],[283,202],[278,203],[330,204],[358,205],[361,206],[259,207],[186,208],[357,209],[182,139],[380,210],[391,211],[378,36],[390,212],[70,36],[366,213],[246,36],[276,214],[362,36],[205,36],[389,215],[194,36],[249,216],[348,217],[388,36],[382,218],[187,36],[383,219],[385,220],[386,221],[369,36],[387,208],[213,222],[367,223],[392,224],[322,36],[325,36],[323,36],[327,36],[324,36],[326,36],[328,225],[321,36],[252,226],[251,36],[257,227],[253,228],[256,229],[255,229],[254,228],[209,230],[241,231],[355,232],[442,36],[414,233],[416,234],[285,36],[415,235],[353,199],[299,199],[193,36],[242,236],[206,237],[207,238],[208,239],[204,240],[329,240],[220,240],[244,241],[221,241],[203,242],[202,36],[250,243],[248,244],[247,245],[245,246],[354,247],[290,248],[320,249],[289,250],[337,251],[336,252],[332,253],[238,254],[240,255],[237,256],[211,257],[269,36],[402,36],[268,258],[331,36],[260,259],[279,260],[277,261],[262,262],[264,263],[437,36],[263,264],[265,264],[400,36],[399,36],[401,36],[435,36],[267,265],[235,35],[68,36],[218,266],[227,36],[272,267],[212,36],[408,35],[418,268],[234,35],[412,159],[233,269],[394,270],[232,268],[184,36],[420,271],[230,35],[231,35],[222,36],[271,36],[229,272],[228,273],[219,274],[284,88],[360,88],[384,36],[364,275],[363,36],[404,36],[236,35],[396,276],[63,35],[66,277],[67,278],[64,35],[65,36],[381,279],[373,280],[372,36],[371,281],[370,36],[393,282],[407,283],[409,284],[411,285],[413,286],[417,287],[448,288],[421,288],[447,289],[423,290],[431,291],[432,292],[434,293],[443,294],[446,177],[445,36],[444,295],[538,296],[537,36],[466,297],[464,298],[465,299],[453,300],[454,298],[461,301],[452,302],[457,303],[467,36],[458,304],[463,305],[469,306],[468,307],[451,308],[459,309],[460,310],[455,311],[462,297],[456,312],[477,313],[476,314],[365,315],[450,36],[472,316],[471,36],[470,36],[473,317],[512,36],[490,36],[491,318],[58,36],[59,36],[10,36],[11,36],[13,36],[12,36],[2,36],[14,36],[15,36],[16,36],[17,36],[18,36],[19,36],[20,36],[21,36],[3,36],[22,36],[23,36],[4,36],[24,36],[28,36],[25,36],[26,36],[27,36],[29,36],[30,36],[31,36],[5,36],[32,36],[33,36],[34,36],[35,36],[6,36],[39,36],[36,36],[37,36],[38,36],[40,36],[7,36],[41,36],[46,36],[47,36],[42,36],[43,36],[44,36],[45,36],[8,36],[51,36],[48,36],[49,36],[50,36],[52,36],[9,36],[53,36],[54,36],[55,36],[57,36],[56,36],[1,36],[96,319],[106,320],[95,319],[116,321],[87,322],[86,323],[115,295],[109,324],[114,325],[89,326],[103,327],[88,328],[112,329],[84,330],[83,295],[113,331],[85,332],[90,333],[91,36],[94,333],[81,36],[117,334],[107,335],[98,336],[99,337],[101,338],[97,339],[100,340],[110,295],[92,341],[93,342],[102,343],[82,344],[105,335],[104,333],[108,36],[111,345],[509,346],[506,347],[507,346],[510,348],[505,36],[486,349],[483,350],[481,351],[479,352],[478,36],[480,353],[484,36],[485,354],[518,355],[513,356],[504,357],[499,36],[528,358],[515,359],[529,360],[516,361],[521,362],[520,363],[517,364],[535,365],[474,366],[519,367]],"affectedFilesPendingEmit":[601,602,603,604,605,606,607,608,570,574,579,582,590,595,597,599,572,578,575,576,577,571,580,581,573,596,583,584,587,585,586,588,589,598,546,592,591,593,594,536,541,540,543,542,545,544,547,549,548,550,552,551,553,555,554,557,556,560,559,561,558,562,563,564,566,565,567,569,568,474,519],"version":"5.9.3"}
\ No newline at end of file
diff --git a/memory/contracts.md b/memory/contracts.md
index d52cbda..b98f0ce 100644
--- a/memory/contracts.md
+++ b/memory/contracts.md
@@ -41,6 +41,18 @@
- `POST /projects/{id}/chapters/{no}/accept` ← `AcceptRequest{final_text(min1), decisions:[ConflictDecision{conflict_index:int>=0, verdict:"accept"|"ignore"|"manual", note?}]}` → `AcceptResponse{project_id,chapter_no,accepted_version:int,digest_added:bool,decisions_recorded:int,review_id?:uuid}`。**冲突 gate**:裁决的 `conflict_index` 集合须覆盖 `range(len(最近一条 review.conflicts))`,缺判→409 `CONFLICT_UNRESOLVED` + `details.missing_conflict_indices`/`conflict_count`;无留痕或零冲突→直通。digest 提炼 tier=light(`get_digest_gateway`)在事务外(R2),单事务 promote(R4)+digest.append(#4)+set_decisions 末尾一次 commit(R3)。
- 注入缝:`build_gateway_for_tier(session, store, tier)`(原 `build_writer_gateway` 退化为 writer 特例) + `get_review_gateway`/`get_digest_gateway`/`get_review_repo`/`get_digest_append_repo`;测试经 `app.dependency_overrides` 注 mock。
- **@frontend 行动**:`cd apps/web && pnpm gen:api` 重生成客户端含此三端点(T2.6)。
+- **已落(T3.2, 2026-06-18,snake_case)— 伏笔登记/状态端点 + 验收后到期扫描**(挂 `foreshadow.router`,prefix `/projects`,tag `foreshadow`,已注册):
+ - `POST /projects/{id}/foreshadow` ← `ForeshadowRegisterRequest{code(min1),title(min1),planted_at?,content?,expected_close_from?,expected_close_to?,importance?}` → 201 `ForeshadowView{code,title,status,planted_at?,content?,expected_close_from?,expected_close_to?,importance?,links:list,progress:list}`(status=OPEN)。重复 code(DB 唯一 `(project_id,code)`)→ **422** `VALIDATION`+`details{field:"code",code,reason:"duplicate"}`。
+ - `PATCH /projects/{id}/foreshadow/{code}` ← `ForeshadowTransitionRequest{to_status?,progress_entry?}` → 200 `ForeshadowView`。非法转移→422 `VALIDATION`+`details{reason:"invalid_transition"}`;二者都缺→422 `empty_update`;code 不存在→404 `NOT_FOUND`。先转移后追加进展,端点写后 `commit()`(repo 只 flush)。
+ - **错误码取舍**:重复/非法转移映射 `VALIDATION`(422) 非 409——现有唯一 409 码 `CONFLICT_UNRESOLVED` 语义专指未决冲突禁验收,不复用;未动 `packages/shared/errors.py` 契约。若 T3.5/前端需真 409 须先加专用码。
+ - **验收后到期扫描**:`POST .../accept` 单事务 commit 成功后经 `BackgroundTasks` 登记 `run_overdue_scan(session_factory, project_id, chapter_no=刚验收章号)`——**自建独立 session**(请求 session 已关闭)、应用 `current_ch>expected_close_to AND status≠CLOSED→OVERDUE`、有变更才 commit;日志 `foreshadow_overdue_scan`(带 request_id/overdue_count/codes),失败吞不冒泡。§7.4 持久性局限(进程内/重启丢任务)原型接受、不引 jobs 表。
+ - **T3.5 续接**:`GET /projects/{id}/foreshadow?status=` 看板加到本 `foreshadow.router`(注入 `get_foreshadow_repo`→`list_by_status`,建议响应包 `{foreshadow:[...]}`);`POST .../outline` 落**独立** `routers/outline.py`(别混进 foreshadow.py),调 `run_outline`(C6 扩)+`build_gateway_for_tier(...,"analyst")`,逐章 upsert `outline` 表、端点末尾 commit。
+ - **@frontend 行动**:T3.6 前 `pnpm gen:api` 纳入伏笔登记/状态端点(+ T3.5 看板/outline)。
+- **已落(T3.5, 2026-06-18,snake_case)— 伏笔看板 + 大纲生成端点**:
+ - `GET /projects/{id}/foreshadow?status=`(tag `foreshadow`):query `status?` ∈ `OPEN|PARTIAL|CLOSED|OVERDUE`(缺省=全部,非法→422 `VALIDATION`+`details{field:"status",reason:"invalid_status"}`) → 200 `ForeshadowBoardResponse{foreshadow:list[ForeshadowView]}`(按 code 升序)。
+ - `POST /projects/{id}/outline`(tag `outline`,独立 `routers/outline.py`)← `OutlineGenerateRequest{volume:int=1,ge=1}`(M3 简单分卷:整批同卷)→ 200 `OutlineResponse{chapters:list[OutlineChapterView{no,volume,beats:list[str],foreshadow_windows:list[ForeshadowWindowView{code,plant_chapter?,expected_close_from?,expected_close_to?}]}]}`;项目不存在→404 `NOT_FOUND`;无凭据/网关失败→503 `LLM_UNAVAILABLE`(流前 JSON 信封)。调 `run_outline`(analyst 网关 `get_outline_gateway`)→逐章 upsert `outline` 表(写侧 `SqlOutlineWriteRepo`,只 flush)→端点末尾 `commit()`(含 1 条 usage_ledger)。
+ - **beats 存储形**:DB `outline.beats` 是 JSONB dict→写侧包成 `{"beats":[...]}` 落库,API 出参 `OutlineChapterView.beats` 解包成裸 `list[str]`。
+ - **@frontend 行动**:T3.6 `pnpm gen:api` 纳入看板/大纲端点。
## C4 · 编排器接口(LangGraph 写章图) owner @llm 状态: 稳定(M1/T1.3, 2026-06-18;M2 扩四审/验收)
- 来源:`ARCHITECTURE.md §5.2`(图)/ §7.3(SSE)。**M1 仅单 `write` 节点**;并行四审/collect/interrupt(accept) 属 M2。
@@ -64,6 +76,12 @@
- **accept 不在本图**:`interrupt_before=["accept"]`/accept 节点属 T2.4(确定性事务代码,从领域表重读,R3/不变量#5)。
- 消费方行动:T2.5 跑 review 子图 + `normalize_review` + 端点**流耗尽后 `await session.commit()`**(网关 ledger + collect 均只 flush,不提交则记账/留痕静默丢失,同 M1 draft 坑);`GET .../reviews` 用 `review_repo.list_for_chapter`(新→旧)。T2.4 从 `review_repo.list_for_chapter` 读 collect 落的行(`decisions=None`)裁决。
+### C4 扩展(T3.3, 2026-06-18)· 三审齐(foreshadow + pace 并入)
+- `REVIEW_SPECS = (continuity_spec, foreshadow_spec, pace_spec)`;`build_review_graph(..., review_specs=REVIEW_SPECS)` 默认三审齐(文风第四审留 M4)。`run_review`/`make_review_node` 对 spec 泛型(按 `req.output_schema` 产 parsed)。
+- collect 列映射(一次 `review_repo.record(...)` 落齐):continuity→`conflicts`(list);foreshadow→`foreshadow_sug`(list,把 `planted`/`resolved` 扁平成单 list、每条加 `kind:"planted"|"resolved"`);pace→`pace`(dict,整 `{water,hook,beat_map}` 入列);style 留 M4。任一审 incomplete→该列空/None,不阻塞其余(§5.2)。
+- `normalize_review` SSE(向后兼容,`section`/`conflict` 不变)新增:`foreshadow{kind,code?,title,where?,note?}`(每条建议一条)、`pace{water:[{where,reason}],hook:bool,beat_map:[int]}`(一条)。键按字典序确定性遍历(continuityLlmResponse`):`build_outline_request(spec, *, context, user_id, project_id)->LlmRequest`(`output_schema=OutlineResult`,`stream=False`) + `async run_outline(spec, *, context, gateway, user_id, project_id)->OutlineResult`(`gateway.run().parsed`;**只读不写库**;大纲独立生成、非并行审,网关失败**直接上抛**不隔离,parsed 违约抛 `ValueError`)。
+- 消费方:T3.5 端点调 `run_outline` 拿 `OutlineResult` → 逐章 upsert `outline` 表(`beats`/`foreshadow_windows` 入 JSONB,`volume` 由端点分卷逻辑提供,schema 不含 volume);analyst 网关复用 `build_gateway_for_tier(session,store,"analyst")`/`get_review_gateway`;记账闭环同 M2(端点末尾 `commit()`)。T3.6 前端大纲编辑器消费窗口。
+
+### C6 扩展(T3.3, 2026-06-18)· foreshadow-analyst + pace-checker spec
+- `foreshadow_spec`(name="foreshadow", tier="analyst", reads=["foreshadow"], writes=[]只读, output=`ForeshadowReview{planted:[ForeshadowSuggestion],resolved:[ForeshadowSuggestion]}`;`ForeshadowSuggestion{code?,title,where?,note?}`)。
+- `pace_spec`(name="pace", tier="light", reads=["rules"], writes=[]只读, output=`PaceReview{water:[PaceIssue{where,reason}],hook:bool,beat_map:[int]}`)。genre 模板 DSL = pace system_prompt 内描述黄金三章/章末钩子/爽点密度,genre 级模板经 review_context 规则合并(`rules`)注入,**未新建表/repo**。
+- 三审只读,留痕经 collect→review_repo(写库 commit 归端点)。详细图/SSE 见上「C4 扩展(T3.3)」。
## C7 · 前端 ↔ 后端类型契约 owner @backend(产出 OpenAPI) / @frontend(生成) 状态: 待定义
- 机制:FastAPI OpenAPI → `apps/web/lib/api` TS 类型(openapi-typescript/orval)。
diff --git a/memory/decisions.md b/memory/decisions.md
index ca7b604..a508d68 100644
--- a/memory/decisions.md
+++ b/memory/decisions.md
@@ -20,6 +20,37 @@
- 理由 / 取舍:保留既有「注入 client 便于测试」风格,结构化路径同样可注 fake。`ProviderResult.text` 对结构化路径置为 `parsed.model_dump_json()`(便于日志/留痕),程序消费走 `parsed`。
- 影响:契约 C1(`run()` 填 `parsed`;见 contracts 变更日志)、`ProviderResult` 加 `parsed` 字段;T2.2 续审节点据此消费。
+## [2026-06-18] 并行审记账并发安全选 add-only(去网关 ledger flush)— @llm (T3.8)
+- 背景:三审同 superstep 并行共用请求 session,`SqlAlchemyLedgerSink.record` 的 `await flush()` 是唯一让步点→第二/三审 flush 重入「Session is already flushing」→被 `run_review` 吞成 incomplete、foreshadow/pace 静默丢失(T3.7 暴露)。
+- 选择:`record` 改 **add-only**(去 `await flush()`,只 `session.add(row)`)。`session.add()` 同步不让步→并行协程不交错;持久化仍靠端点/事务末尾 `commit()`(自动 flush 待决行)。
+- 理由 / 取舍:最小改动、单 owner(只动 `ledger.py`),不牵动 apps/(@backend)。前提已校验:①并行审区间无 DB 查询触发 autoflush(review_context 取自 state、适配器不碰 session);②draft/review/accept/outline 端点末尾均已 commit。缓冲/独立 session 方案跨 owner、更重,不取。
+- 影响:C1 边界语义不变(commit 责任方未变);凡新增「并行用网关产 usage」路径,sink 必须保持 add-only、不得在并行段 await flush。新增并发回归测试 `test_ledger_concurrency.py`。
+
+## [2026-06-18] 节拍图用定高 CSS 柱 + 文本 sparkline,不引图表库 — @frontend (T3.6)
+- 选择:`beat_map`(int 序列) 按本序列 min..max 线性归一到 7 档 ▁..▇(全相等→居中档,最低柱给 12% 可见高度),渲染定高 CSS 柱 + `aria-label="爽点节拍图 ▁▃▅…"`。静态无动画→天然满足 prefers-reduced-motion。
+- 理由:KISS,避免图表库依赖。影响:T3.7 节拍图 `role="img"` selector。
+
+## [2026-06-18] 大纲 beats 存储形 + 写侧 repo 命名 — @backend (T3.5)
+- 背景:`outline.beats` DB 列是 JSONB dict,但 outliner 产 `list[str]`;读侧 `OutlineRepo`(C5/memory)已占名。
+- 选择:写侧 `SqlOutlineWriteRepo` 把 list 包成 `{"beats":[...]}` 落库(读侧同形 round-trip 一致),API 出参 `OutlineChapterView.beats` 解包成裸 `list[str]`。写侧命名加 `Write` 前缀避撞 C5 读侧(同 DigestAppendRepo/ForeshadowLedgerRepo 先例)。`volume` 由端点提供(schema 无逐章卷号,M3 默认全卷 1)。`get_outline_gateway` 复用 `build_gateway_for_tier(...,"analyst")`、独立缝便于测试 override。
+- 影响:C3 扩(outline 端点);T3.6 渲染 beats 裸 list + 窗口徽标;T3.7 断言 outline 行 beats 列形。
+
+## [2026-06-18] 验收后伏笔到期扫描挂 BackgroundTask + 重复code/非法转移映射 VALIDATION(422) — @backend (T3.2)
+- 背景:§5.5 步骤3 `TODO(M3)` 要验收后置 OVERDUE;登记/状态端点需映射 DB 唯一冲突与非法状态机转移到错误信封。
+- 选择:(1) accept 端点 commit 成功后经 FastAPI BackgroundTasks 登记 `run_overdue_scan`(自建独立 session,因请求 session 已关闭);扫描抽纯 async 函数 + 可注入 `repo_factory`/`session_factory` 缝,单测直接 await 注 fake、不起后台线程。(2) 重复 code(IntegrityError)/`InvalidTransition` 都映射 `ErrorCode.VALIDATION`(422) 非 409——现有 409 码 `CONFLICT_UNRESOLVED` 语义=未决冲突,复用不符;未新增 shared 错误码。
+- 影响:C3 扩(foreshadow 登记/状态端点);§7.4 持久性局限(进程内/重启丢任务)原型接受、不引 jobs 表;T3.5 看板/outline 续接本 router;T3.7 真 pg 断言 OVERDUE。
+
+## [2026-06-18] 伏笔写侧 Repository 加 `Ledger` 前缀避免与读侧同名 — @backend (T3.1)
+- 背景:读侧 `ForeshadowRepo`/`ForeshadowView`/`SqlForeshadowRepo`(`domain/repositories.py`+`memory/`)已被 assemble(C5 稳定)占用且 View 最小(无 importance/links/progress)。M3 写侧账本需更全 View + 写方法。
+- 选择:写侧命名 `ForeshadowLedgerRepo`/`SqlForeshadowLedgerRepo`/`ForeshadowLedgerView`,落 `domain/foreshadow_repo.py`;读侧不动。状态机纯函数落 `domain/foreshadow_state.py`(`ForeshadowStatus` StrEnum + `transition`/`is_overdue`/`apply_overdue_scan`/`InvalidTransition`)。
+- 理由:同 T2.3 `DigestAppendRepo` vs 读侧 `DigestRepo` 先例,读写分离免歧义、不破坏 C5。写方法只 flush 不 commit(commit 归 T3.2 扫描任务 / T3.5 端点)。
+- 影响:T3.2 验收后扫描 / T3.5 看板端点消费 `ForeshadowLedger*`。
+
+## [2026-06-18] 大纲节点不接进 review 图、不做失败隔离 — @llm (T3.4)
+- 背景:续审在 `run_review` 内把网关失败隔离为 `incomplete`(§5.2 任一审不阻塞其余)。
+- 选择:大纲是**独立生成**(不在写章/审稿流水线),`run_outline` 裸函数、网关失败**直接上抛**由 T3.5 端点处理;带 `output_schema` 时 `parsed` 非 `OutlineResult` 抛 `ValueError`(C1 违约即显错,不返回空大纲)。
+- 影响:C6 扩(outliner spec + OutlineResult + run_outline 缝);T3.5 端点调 `run_outline` 持久化 `outline` 表。
+
## [2026-06-18] 审稿页终稿来源:页内可编辑 textarea(无 GET draft 端点)— @frontend (T2.6)
- 背景:审稿/验收都需「终稿」文本,但当前无 `GET .../draft` 端点拉取已存草稿。
- 选择:审稿页用可折叠 textarea 编辑终稿(`initialDraft=''`),重审传它为 `{draft}`(空→后端回退已存草稿),验收 `final_text`=它。
diff --git a/memory/gotchas.md b/memory/gotchas.md
index 93d01d2..c9c83ec 100644
--- a/memory/gotchas.md
+++ b/memory/gotchas.md
@@ -7,6 +7,18 @@
---
+- [2026-06-18] @llm **`SqlAlchemyLedgerSink.record` 改 add-only(T3.8 修,去 `await flush()`)**:原 add+flush 在并行审里 flush 重入炸(见下条)。改为只 `session.add(row)`——`add()` 同步不让步→并行协程不交错;持久化靠端点/事务 `commit()`(自动 flush)。draft/review/accept/outline 端点末尾均已 commit,记账不丢。**凡新增「并行用网关产 usage」的路径,sink 必须保持 add-only,不得在并行段 `await flush`。** 旧 gotcha「record 只 flush 不 commit」措辞已过时——现为「add-only,commit 归调用方」(commit 仍会 flush,故记账语义不变)。
+- [2026-06-18] @qa/@llm **并行审记账撞 session(M3 真 bug,T3.7 暴露→T3.8 修)**:三审同 LangGraph superstep 并行,各自 `gateway.run()`→共用**请求 session** 的 `SqlAlchemyLedgerSink.record`(`session.add`+`await session.flush()`)。`AsyncSession` 非并发安全→第二/三审 flush 撞 `Session is already flushing`→被 `run_review` 失败隔离吞成 `incomplete`→**foreshadow/pace 静默丢失**(SSE 无事件、`chapter_reviews.foreshadow_sug`/`pace` 列空、日志 `review_node_incomplete error='Session is already flushing'`)。M2 单审未触发。根因=并行路径里有 `await flush()`(唯一 await 的 DB-IO)。修向:审稿期记账避免并发 flush(add-only 靠端点 commit / 或缓冲后 collect 串行落 / 或并行审记账用独立 session)。
+- [2026-06-18] @qa **E2E 验「验收后 BackgroundTask 扫描」时序**:httpx `ASGITransport` 下 `await client.post(...)` 会等 ASGI app 协程(含 Starlette background tasks)跑完才返回,故 client 上下文退出后断言稳定不 flaky;必 override `get_session_factory`→`e2e_sm`(真 sessionmaker,同测试 engine/loop),否则默认 `get_sessionmaker()` 另建 engine 绑别的 loop。
+- [2026-06-18] @frontend **审稿 seed 扩成三态**:`useReviewStream.seed` 入参由 `ReviewConflict[]` 改 `ReviewSeed{conflicts,foreshadow,pace}`(进页同时种伏笔建议/节奏留痕,免重审即可看)。`ReviewStreamState` 加 `foreshadow:ForeshadowSuggestion[]`(累加) / `pace:PaceReport|null`(**替换非累加**,对齐后端 collect「pace 整 dict 入列」)。
+- [2026-06-18] @frontend **伏笔 transition 乐观更新只改 status、回滚存快照**:`useForeshadow.transition` 本地先改 status,PATCH 失败 `setItems(snapshot)` 回滚 + 读 `error.details.reason`(duplicate/invalid_transition/empty_update) 映射文案。register **不乐观**(要服务端 code 唯一校验):成功才追加返回行,重复 code→422 友好提示不改 items。大纲页**无 GET 端点**:进页 `initialChapters=[]`,靠 `POST .../outline` 生成填充(无凭据→503 引导去设置)。
+- [2026-06-18] @backend **端点级测「无凭据→LLM_UNAVAILABLE」不要靠真实 `get_*_gateway` dep 解析**——`FakeSession` 不支持 `.execute`,`SqlCredentialStore` 会炸成 500。做法:override 网关 dep 注一个 `raise AppError(LLM_UNAVAILABLE)` 的 async 函数(等价无凭据行为),与 review/accept/outline 测试一致。
+- [2026-06-18] @backend **BackgroundTask 必须自建独立 session**:FastAPI BackgroundTasks 在 response 发回、请求 session 关闭后才跑——复用 `Depends(get_session)` 的 session 已关闭会炸。验收后到期扫描经可注入 `SessionFactory`(=`get_sessionmaker()`),`run_overdue_scan` 内 `async with factory()` 开新 session 自己 commit;再加 `repo_factory` 缝便于单测注 fake、纯函数 await 不起后台线程。**accept 端点新增 `get_session_factory` 依赖→所有 accept 测试 client 需 override 它**(否则 dep 解析会建真 engine)。
+- [2026-06-18] @backend **伏笔登记重复 code / 非法转移 → `VALIDATION`(422) 非 409**:现有唯一 409 码 `CONFLICT_UNRESOLVED` 专指未决冲突禁验收,不复用;`details.reason` ∈ `duplicate`/`invalid_transition`/`empty_update` 供前端区分。register 捕 SQLAlchemy `IntegrityError`→rollback→`AppError(VALIDATION)`;transition 捕 `InvalidTransition`/`LookupError`(404)。
+- [2026-06-18] @llm **三审列类型不齐**:`chapter_reviews.foreshadow_sug` 是 JSONB **list**、`pace` 是 JSONB **dict**——collect 把 `ForeshadowReview{planted,resolved}`(dict) **扁平成单 list**、每条加 `kind:"planted"|"resolved"`;`PaceReview` 整体入 dict 列。贴合既有 DB 列类型、不改 @db。新审种加入须同步:collect 列映射 + `sse._section_result_events` 分支 + `normalize_review` 键名。`sse.py` import `.collect` 的 spec.name 常量做 section 分流(无循环:collect 不 import sse)。
+- [2026-06-18] @llm **三审并行图测试按 `req.output_schema` 路由 parsed**(`SchemaRoutingRunGateway`)——单 `FakeRunGateway` 对所有审返同一 parsed 会让三审拿错 schema。验失败隔离:某 schema 不登记→网关抛 KeyError→`run_review` 隔离为 `incomplete`,无需改 gateway。
+- [2026-06-18] @backend **伏笔 `record_progress` append JSONB 必须新建 list 重赋值**(`row.progress = [*old, entry]`),不可原地 `.append()`——SQLAlchemy 默认不侦测可变 JSONB 原地突变,原地改不脏标记→flush 丢失。`scan_overdue` 仅在有变更时 flush(空扫描零写)。状态机:`transition` 同态(current==to)幂等放行、CLOSED 为终态(离开 CLOSED 全非法抛 `InvalidTransition`);`is_overdue` 严格大于(current==expected_close_to 仍在窗口内不逾期)、无 expected_close_to 永不逾期。
+- [2026-06-18] @llm **orchestrator 内每模块各自声明 `GatewayRun` Protocol**(`review_node.py` 与 `outline_node.py` 各一份,按模块最小依赖)——**不跨模块复用、不在 orchestrator `__init__` 重复导出**(`__init__` 只导出 review_node 那个,避免 re-export 名冲突);outline 的为模块内部用。
- [2026-06-18] @qa **M2 E2E 多档位假适配器**:`config.tier_defaults` writer/analyst/light 默认同 provider(deepseek)→单个假适配器(`provider="deepseek"`)即覆盖三档位;据 `req.output_schema is ContinuityReview`(续审)/否则 digest facts schema 分支返回 `parsed`;三档位用不同 `input_tokens` 区分以断言各自落 `usage_ledger`。三端点记账闭环 = review 端点流末 commit + accept 验收事务末 commit 都把网关 ledger flush 真正提交(M1 ledger bug 在 M2 无复发)。
- [2026-06-18] @qa **E2E 验证「digest 从终稿非草稿」(#4) 手法**:final_text 注入草稿没有的标记串,假 light 适配器把它放进 digest facts 的 `summary`,断言 `chapter_digests.facts["summary"]==标记` 且 `标记 not in draft_text`。accept 409 gate 经 ASGITransport 正常返回(`AppError` 不上抛),断言 `resp.json()["error"]["details"]["missing_conflict_indices"]`(`ErrorCode` StrEnum → `"CONFLICT_UNRESOLVED"`)。
- [2026-06-18] @frontend **审稿历史 `conflicts` 在 OpenAPI 被标松散 `{[k]:unknown}[]`**(后端用 dict/JSONB 列)→ 前端 `lib/review/history.ts` 安全收窄成 `ReviewConflict{type,where,refs,suggestion}`,缺字段给默认、**保序**(顺序=冲突 gate 的 `conflict_index` 身份,不可重排,否则裁决错位)。
diff --git a/packages/agents/tests/test_outliner_spec.py b/packages/agents/tests/test_outliner_spec.py
new file mode 100644
index 0000000..048f9b7
--- /dev/null
+++ b/packages/agents/tests/test_outliner_spec.py
@@ -0,0 +1,117 @@
+"""T3.4 大纲 Agent 契约测试:OutlineResult schema + outliner_spec 声明(C6 扩)。
+
+契约测试——构造符合 schema 的 mock 响应,校验伏笔窗口字段、章节结构、
+声明式 writes(验收/T3.5 才真写库,不变量 #3)。不联网、无 DB。
+"""
+
+from __future__ import annotations
+
+import pytest
+from pydantic import ValidationError
+from ww_agents import (
+ AgentSpec,
+ ForeshadowWindow,
+ OutlineChapter,
+ OutlineResult,
+ outliner_spec,
+)
+
+
+def test_outline_result_parses_mock_response() -> None:
+ # Arrange:模拟网关 instructor 校验后的结构化大纲产出
+ mock = {
+ "chapters": [
+ {
+ "no": 1,
+ "beats": ["主角登场,立下目标", "埋下身世伏笔"],
+ "foreshadow_windows": [
+ {
+ "code": "FS-BIRTH",
+ "plant_chapter": 1,
+ "expected_close_from": 20,
+ "expected_close_to": 30,
+ }
+ ],
+ },
+ {
+ "no": 2,
+ "beats": ["遭遇对手"],
+ "foreshadow_windows": [],
+ },
+ ]
+ }
+
+ # Act
+ outline = OutlineResult.model_validate(mock)
+
+ # Assert
+ assert len(outline.chapters) == 2
+ assert outline.chapters[0].no == 1
+ assert outline.chapters[0].beats == ["主角登场,立下目标", "埋下身世伏笔"]
+ window = outline.chapters[0].foreshadow_windows[0]
+ assert window.code == "FS-BIRTH"
+ assert window.plant_chapter == 1
+ assert window.expected_close_from == 20
+ assert window.expected_close_to == 30
+
+
+def test_outline_chapter_defaults_empty_windows() -> None:
+ chapter = OutlineChapter.model_validate({"no": 3, "beats": ["收束支线"]})
+ assert chapter.foreshadow_windows == []
+
+
+def test_outline_result_defaults_to_empty_chapters() -> None:
+ assert OutlineResult().chapters == []
+
+
+def test_foreshadow_window_optional_bounds_default_none() -> None:
+ window = ForeshadowWindow.model_validate({"code": "FS-X"})
+ assert window.plant_chapter is None
+ assert window.expected_close_from is None
+ assert window.expected_close_to is None
+
+
+def test_foreshadow_window_requires_code() -> None:
+ with pytest.raises(ValidationError):
+ ForeshadowWindow.model_validate({"plant_chapter": 1})
+
+
+def test_outline_chapter_requires_no() -> None:
+ with pytest.raises(ValidationError):
+ OutlineChapter.model_validate({"beats": ["x"]})
+
+
+def test_outliner_spec_is_analyst_tier() -> None:
+ assert outliner_spec.tier == "analyst"
+ assert outliner_spec.name == "outliner"
+
+
+def test_outliner_spec_declares_expected_reads() -> None:
+ assert outliner_spec.reads == [
+ "projects",
+ "foreshadow",
+ "characters",
+ "world_entities",
+ ]
+
+
+def test_outliner_spec_declares_outline_write() -> None:
+ # 声明式 writes(经验收/T3.5 才真写库,不变量 #3)
+ assert outliner_spec.writes == ["outline"]
+
+
+def test_outliner_spec_output_schema_is_outline_result() -> None:
+ assert outliner_spec.output_schema is OutlineResult
+
+
+def test_outliner_spec_has_nonempty_system_prompt() -> None:
+ assert outliner_spec.system_prompt.strip()
+
+
+def test_outliner_spec_is_immutable() -> None:
+ with pytest.raises(ValidationError):
+ outliner_spec.tier = "writer"
+
+
+def test_outliner_spec_is_agent_spec() -> None:
+ assert isinstance(outliner_spec, AgentSpec)
diff --git a/packages/agents/tests/test_review_specs.py b/packages/agents/tests/test_review_specs.py
new file mode 100644
index 0000000..022b8c7
--- /dev/null
+++ b/packages/agents/tests/test_review_specs.py
@@ -0,0 +1,167 @@
+"""T3.3 三审契约测试:foreshadow / pace spec + 输出 schema(C6 扩)。
+
+契约测试——构造符合 schema 的 mock 响应,校验字段、tier、四审只读(writes==[])。
+不联网、无 DB。
+"""
+
+from __future__ import annotations
+
+import pytest
+from pydantic import ValidationError
+from ww_agents import (
+ AgentSpec,
+ ForeshadowReview,
+ ForeshadowSuggestion,
+ PaceIssue,
+ PaceReview,
+ foreshadow_spec,
+ pace_spec,
+)
+
+# ---- ForeshadowReview schema ----
+
+
+def test_foreshadow_review_parses_mock_response() -> None:
+ # Arrange:模拟网关 instructor 校验后的结构化产出
+ mock = {
+ "planted": [
+ {
+ "code": "FS-MARK",
+ "title": "胸口的胎记",
+ "where": "第 2 段主角沐浴时被瞥见",
+ "note": "暗示皇族血统",
+ }
+ ],
+ "resolved": [
+ {
+ "code": "FS-BIRTH",
+ "title": "身世之谜",
+ "where": "第 9 段老者道破来历",
+ "note": "回收第 1 章埋设",
+ }
+ ],
+ }
+
+ # Act
+ review = ForeshadowReview.model_validate(mock)
+
+ # Assert
+ assert len(review.planted) == 1
+ assert review.planted[0].code == "FS-MARK"
+ assert review.resolved[0].code == "FS-BIRTH"
+ assert review.resolved[0].where == "第 9 段老者道破来历"
+
+
+def test_foreshadow_review_defaults_to_empty_lists() -> None:
+ review = ForeshadowReview()
+ assert review.planted == []
+ assert review.resolved == []
+
+
+def test_foreshadow_suggestion_only_title_required() -> None:
+ sug = ForeshadowSuggestion.model_validate({"title": "新埋的悬念"})
+ assert sug.title == "新埋的悬念"
+ assert sug.code is None
+ assert sug.where is None
+ assert sug.note is None
+
+
+def test_foreshadow_suggestion_requires_title() -> None:
+ with pytest.raises(ValidationError):
+ ForeshadowSuggestion.model_validate({"code": "FS-X"})
+
+
+# ---- PaceReview schema ----
+
+
+def test_pace_review_parses_mock_response() -> None:
+ mock = {
+ "water": [{"where": "第 4–6 段反复描写天气", "reason": "信息密度低、与主线无关"}],
+ "hook": True,
+ "beat_map": [1, 3, 2, 0, 4, 5],
+ }
+
+ review = PaceReview.model_validate(mock)
+
+ assert len(review.water) == 1
+ assert review.water[0].reason == "信息密度低、与主线无关"
+ assert review.hook is True
+ assert review.beat_map == [1, 3, 2, 0, 4, 5]
+
+
+def test_pace_review_defaults() -> None:
+ review = PaceReview()
+ assert review.water == []
+ assert review.hook is False
+ assert review.beat_map == []
+
+
+def test_pace_issue_requires_where_and_reason() -> None:
+ with pytest.raises(ValidationError):
+ PaceIssue.model_validate({"where": "x"})
+
+
+# ---- foreshadow_spec 声明 ----
+
+
+def test_foreshadow_spec_is_analyst_tier() -> None:
+ assert foreshadow_spec.tier == "analyst"
+ assert foreshadow_spec.name == "foreshadow"
+
+
+def test_foreshadow_spec_is_read_only() -> None:
+ # 不变量 #3:四审只读
+ assert foreshadow_spec.writes == []
+
+
+def test_foreshadow_spec_reads_foreshadow() -> None:
+ assert foreshadow_spec.reads == ["foreshadow"]
+
+
+def test_foreshadow_spec_output_schema() -> None:
+ assert foreshadow_spec.output_schema is ForeshadowReview
+
+
+def test_foreshadow_spec_has_nonempty_system_prompt() -> None:
+ assert foreshadow_spec.system_prompt.strip()
+
+
+# ---- pace_spec 声明 ----
+
+
+def test_pace_spec_is_light_tier() -> None:
+ # 不变量 #2:节奏审用轻量档,只声明 tier
+ assert pace_spec.tier == "light"
+ assert pace_spec.name == "pace"
+
+
+def test_pace_spec_is_read_only() -> None:
+ assert pace_spec.writes == []
+
+
+def test_pace_spec_reads_rules() -> None:
+ # genre 模板 DSL 经 rules(genre 级)注入
+ assert pace_spec.reads == ["rules"]
+
+
+def test_pace_spec_output_schema() -> None:
+ assert pace_spec.output_schema is PaceReview
+
+
+def test_pace_spec_has_nonempty_system_prompt() -> None:
+ assert pace_spec.system_prompt.strip()
+
+
+# ---- 共性:immutable + 是 AgentSpec ----
+
+
+def test_review_specs_are_agent_specs() -> None:
+ assert isinstance(foreshadow_spec, AgentSpec)
+ assert isinstance(pace_spec, AgentSpec)
+
+
+def test_review_specs_are_immutable() -> None:
+ with pytest.raises(ValidationError):
+ foreshadow_spec.tier = "writer"
+ with pytest.raises(ValidationError):
+ pace_spec.tier = "writer"
diff --git a/packages/agents/ww_agents/__init__.py b/packages/agents/ww_agents/__init__.py
index 23125d1..c053b3a 100644
--- a/packages/agents/ww_agents/__init__.py
+++ b/packages/agents/ww_agents/__init__.py
@@ -1,17 +1,44 @@
"""ww-agents:内置 Agent 声明(AgentSpec)+ 结构化输出 schema(C6)。
-对齐 ARCH §5.1(AgentSpec)/ §5.4(I/O 契约)/ §6.1(冲突分类)。
+对齐 ARCH §5.1(AgentSpec)/ §5.4(I/O 契约)/ §6.1(冲突分类)/ §6.2 §6.4(伏笔/节奏)。
"""
from __future__ import annotations
-from .schemas import Conflict, ConflictType, ContinuityReview
-from .specs import AgentSpec, continuity_spec
+from .schemas import (
+ Conflict,
+ ConflictType,
+ ContinuityReview,
+ ForeshadowReview,
+ ForeshadowSuggestion,
+ ForeshadowWindow,
+ OutlineChapter,
+ OutlineResult,
+ PaceIssue,
+ PaceReview,
+)
+from .specs import (
+ AgentSpec,
+ continuity_spec,
+ foreshadow_spec,
+ outliner_spec,
+ pace_spec,
+)
__all__ = [
"AgentSpec",
"Conflict",
"ConflictType",
"ContinuityReview",
+ "ForeshadowReview",
+ "ForeshadowSuggestion",
+ "ForeshadowWindow",
+ "OutlineChapter",
+ "OutlineResult",
+ "PaceIssue",
+ "PaceReview",
"continuity_spec",
+ "foreshadow_spec",
+ "outliner_spec",
+ "pace_spec",
]
diff --git a/packages/agents/ww_agents/schemas.py b/packages/agents/ww_agents/schemas.py
index cc783b9..848ddb7 100644
--- a/packages/agents/ww_agents/schemas.py
+++ b/packages/agents/ww_agents/schemas.py
@@ -44,3 +44,106 @@ class ContinuityReview(BaseModel):
default_factory=list,
description="检出的一致性冲突;无冲突时为空列表",
)
+
+
+# ---- 大纲 Agent 结构化输出(ARCH §5.4 outliner 行 / §6.2 伏笔窗口)----
+
+
+class ForeshadowWindow(BaseModel):
+ """伏笔回收窗口:关联本章与某条伏笔(ARCH §6.2)。
+
+ 排大纲时把伏笔的「埋设章 → 期望回收区间」绑到具体章节;接近回收窗口的章节
+ 提示作者推进/收束。下界/上界可缺省(仅埋设、尚未排定回收时间)。
+ """
+
+ code: str = Field(description="伏笔编码(关联 foreshadow.code)")
+ plant_chapter: int | None = Field(
+ default=None, description="埋设章号(伏笔在本章/此章首次出现)"
+ )
+ expected_close_from: int | None = Field(default=None, description="期望回收区间下界(章号)")
+ expected_close_to: int | None = Field(default=None, description="期望回收区间上界(章号)")
+
+
+class OutlineChapter(BaseModel):
+ """单章大纲:章号 + 节拍清单 + 关联伏笔窗口。"""
+
+ no: int = Field(description="章号")
+ beats: list[str] = Field(default_factory=list, description="本章节拍/情节要点(有序)")
+ foreshadow_windows: list[ForeshadowWindow] = Field(
+ default_factory=list,
+ description="本章关联的伏笔回收窗口;无则空列表",
+ )
+
+
+class OutlineResult(BaseModel):
+ """大纲结构化产出:分卷分章的章节大纲清单(ARCH §5.4 writes=outline)。
+
+ 声明式 writes(不变量 #3):本结构是排大纲的纯产物,落 `outline` 表经验收/
+ 端点(T3.5),不在 agent 节点直接写库。
+ """
+
+ chapters: list[OutlineChapter] = Field(
+ default_factory=list,
+ description="分章大纲;无则空列表",
+ )
+
+
+# ---- 伏笔续审 Agent 结构化输出(ARCH §5.4 foreshadow-analyst 行 / §6.2)----
+
+
+class ForeshadowSuggestion(BaseModel):
+ """单条伏笔建议:本章新埋线 或 疑似回收(ARCH §6.2)。
+
+ 只读建议——作者在验收裁决里确认后才登记/改状态(不变量 #3);本审不写库。
+ `code` 可缺(新埋线尚无编码时由作者命名);`where` 给本章定位。
+ """
+
+ code: str | None = Field(
+ default=None, description="伏笔编码(关联已登记 foreshadow.code;新埋可缺)"
+ )
+ title: str = Field(description="伏笔标题/一句话描述")
+ where: str | None = Field(default=None, description="本章定位:哪一段埋下/疑似回收")
+ note: str | None = Field(default=None, description="建议说明(埋设要点 / 回收理由)")
+
+
+class ForeshadowReview(BaseModel):
+ """伏笔续审结构化产出:本章新埋 + 疑似回收两组建议(ARCH §5.4 / §6.2)。
+
+ 只读建议清单(不变量 #3);登记/状态变更经验收裁决(作者确认)。
+ """
+
+ planted: list[ForeshadowSuggestion] = Field(
+ default_factory=list,
+ description="本章疑似新埋的伏笔;无则空列表",
+ )
+ resolved: list[ForeshadowSuggestion] = Field(
+ default_factory=list,
+ description="本章疑似回收/收束的伏笔;无则空列表",
+ )
+
+
+# ---- 节奏续审 Agent 结构化输出(ARCH §5.4 pace-checker 行 / §6.4)----
+
+
+class PaceIssue(BaseModel):
+ """单条节奏问题:注水段定位 + 原因(ARCH §6.4)。"""
+
+ where: str = Field(description="本章定位:哪一段疑似注水/拖沓")
+ reason: str = Field(description="判定理由(信息密度低 / 重复 / 偏题等)")
+
+
+class PaceReview(BaseModel):
+ """节奏续审结构化产出:注水段 + 章末钩子 + 爽点节拍图(ARCH §5.4 / §6.4)。
+
+ 只读(不变量 #3)。`beat_map` 是逐段节拍强度序列(整数),供前端 ▁▃▅ 可视化。
+ """
+
+ water: list[PaceIssue] = Field(
+ default_factory=list,
+ description="疑似注水/拖沓段;无则空列表",
+ )
+ hook: bool = Field(default=False, description="章末是否有有效钩子(悬念/反转/期待)")
+ beat_map: list[int] = Field(
+ default_factory=list,
+ description="逐段爽点节拍强度序列(整数,供前端节拍图可视化)",
+ )
diff --git a/packages/agents/ww_agents/specs.py b/packages/agents/ww_agents/specs.py
index 8db5487..baed907 100644
--- a/packages/agents/ww_agents/specs.py
+++ b/packages/agents/ww_agents/specs.py
@@ -12,7 +12,12 @@ from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field
from ww_llm_gateway.types import Tier
-from .schemas import ContinuityReview
+from .schemas import (
+ ContinuityReview,
+ ForeshadowReview,
+ OutlineResult,
+ PaceReview,
+)
class AgentSpec(BaseModel):
@@ -63,3 +68,99 @@ continuity_spec = AgentSpec(
writes=[], # 只读(不变量 #3)
scope="builtin",
)
+
+
+OUTLINER_SYSTEM_PROMPT = """你是长篇连载小说的「大纲架构师」。职责:依据作品立意、\
+人物与世界观,排出分卷分章的章节大纲,并为每条伏笔标注回收窗口。
+
+输入材料:
+- 作品设定(projects:题材、立意、主线、卖点);
+- 已登记伏笔(foreshadow:编码、标题、埋设/期望回收线索);
+- 主要人物(characters)与世界观实体(world_entities)。
+
+产出要求(每章):
+- 章号有序、按卷推进;
+- beats:本章核心节拍/情节要点,推动主线、服务人物弧光;
+- foreshadow_windows:本章关联的伏笔回收窗口——给出伏笔编码、埋设章号、\
+期望回收区间(下界/上界章号)。把每条伏笔的「埋设 → 回收」绑到具体章节。
+
+纪律:
+- 接近某伏笔的回收窗口时,在对应章 beats 里安排推进/收束该伏笔的情节,避免伏笔悬置过久;
+- 你只产结构化大纲(章节 + 节拍 + 伏笔窗口),**不改稿、不写库**(落 outline 表经验收/端点);
+- 伏笔编码须与已登记 foreshadow.code 对应,便于后续按窗口提示与校验。"""
+
+
+outliner_spec = AgentSpec(
+ name="outliner",
+ tier="analyst", # 不变量 #2:只声明档位,不写 model
+ system_prompt=OUTLINER_SYSTEM_PROMPT,
+ input_schema=None, # 注入材料为序列化文本(设定/伏笔/人物/世界观),非结构化入参
+ output_schema=OutlineResult,
+ reads=["projects", "foreshadow", "characters", "world_entities"],
+ writes=["outline"], # 声明式(经验收/T3.5 才真写库,不变量 #3)
+ scope="builtin",
+)
+
+
+FORESHADOW_SYSTEM_PROMPT = """你是长篇连载小说的「伏笔续审」(foreshadow-analyst)。\
+职责:把本章草稿与作品已登记伏笔逐项比对,找出本章**新埋的伏笔**与**疑似回收/收束**\
+的伏笔,产出结构化建议清单。
+
+比对依据(注入材料):
+- 已登记伏笔(foreshadow:编码、标题、状态、期望回收窗口);
+- 本章草稿正文。
+
+产出两组建议:
+- planted(新埋):本章疑似首次埋下的伏笔——给出标题、本章定位、要点说明;\
+若与某已登记编码相关则填 code,否则留空由作者命名。
+- resolved(回收):本章疑似回收/收束已登记伏笔——给出对应 code、本章定位、回收理由。
+
+纪律:
+- 你**只读、只产建议**,不改稿、不写库、不直接登记或改伏笔状态——\
+登记/状态变更经作者在验收时裁决确认(不变量 #3 / #4)。
+- 只报有据可依的;无则两组都返回空列表,不要臆造。
+- 引用具体(伏笔编码、本章段落定位),便于作者就地确认。"""
+
+
+foreshadow_spec = AgentSpec(
+ name="foreshadow",
+ tier="analyst", # 不变量 #2:只声明档位,不写 model
+ system_prompt=FORESHADOW_SYSTEM_PROMPT,
+ input_schema=None, # 注入材料为序列化文本(已登记伏笔 + 草稿),非结构化入参
+ output_schema=ForeshadowReview,
+ reads=["foreshadow"],
+ writes=[], # 只读(不变量 #3)
+ scope="builtin",
+)
+
+
+PACE_SYSTEM_PROMPT = """你是长篇连载小说的「节奏续审」(pace-checker)。职责:按题材的\
+节奏模板审本章草稿,找出注水段、判定章末钩子有无、给出爽点节拍图。
+
+题材节奏模板(genre 级规则,注入材料里带本作题材的具体模板;无则用网文通用基线):
+- 黄金三章:开篇前三章须快速立爽点/钩子/代入感,信息密度高、少铺垫慢热;
+- 章末钩子:每章结尾应留悬念/反转/期待,驱动追更;
+- 爽点密度:按题材基线维持爽点节拍,避免长段平淡注水。
+
+产出:
+- water(注水段):逐段审,标出信息密度低/重复/偏题/拖沓的段落,给本章定位与原因;
+- hook(章末钩子):本章结尾是否存在有效钩子(true/false);
+- beat_map(节拍图):把本章按段切分,给每段一个爽点强度整数(如 0–5),\
+形成逐段强度序列,供前端 ▁▃▅ 可视化节奏起伏。
+
+纪律:
+- 你**只读、只报节奏诊断**,不改稿、不写库(不变量 #3)。
+- 依据题材模板判定,不臆造;无注水段则 water 为空列表。
+- beat_map 长度应与切分段数一致、顺序即正文顺序,便于前端对齐渲染。"""
+
+
+pace_spec = AgentSpec(
+ name="pace",
+ tier="light", # 不变量 #2:只声明档位,不写 model(节奏审用轻量档)
+ system_prompt=PACE_SYSTEM_PROMPT,
+ input_schema=None, # 注入材料为序列化文本(题材模板 + 草稿),非结构化入参
+ output_schema=PaceReview,
+ reads=["rules"], # genre 模板 DSL 经 rules(genre 级)注入,复用 review_context 规则合并
+ writes=[], # 只读(不变量 #3)
+ scope="builtin",
+)
diff --git a/packages/core/tests/fakes_orchestrator.py b/packages/core/tests/fakes_orchestrator.py
index ea8ee51..648c197 100644
--- a/packages/core/tests/fakes_orchestrator.py
+++ b/packages/core/tests/fakes_orchestrator.py
@@ -77,6 +77,29 @@ class FakeRunGateway:
)
+class SchemaRoutingRunGateway:
+ """按 `req.output_schema` 路由返对应 `parsed`——并行三审各拿自己 schema 的产物。
+
+ `by_schema`:{schema 类型: 该 schema 的实例}。命中即回该 parsed;未命中抛 KeyError
+ (测试要保证三审 schema 都登记)。统计每个 schema 的调用次数。
+ """
+
+ def __init__(self, by_schema: dict[type[BaseModel], BaseModel]) -> None:
+ self._by_schema = by_schema
+ self.calls: list[type[BaseModel] | None] = []
+
+ async def run(self, req: LlmRequest) -> LlmResponse:
+ schema = req.output_schema
+ self.calls.append(schema)
+ parsed = self._by_schema[schema] if schema is not None else None
+ return LlmResponse(
+ text=parsed.model_dump_json() if parsed is not None else "",
+ parsed=parsed,
+ usage=_stub_usage(),
+ served_by=ServedBy(provider="fake", model="fake-analyst"),
+ )
+
+
class FailingRunGateway:
"""`run` 抛指定异常——验「某审失败不阻塞其余」+ 未完成占位归一。"""
diff --git a/packages/core/tests/test_foreshadow_repo.py b/packages/core/tests/test_foreshadow_repo.py
new file mode 100644
index 0000000..0a2749e
--- /dev/null
+++ b/packages/core/tests/test_foreshadow_repo.py
@@ -0,0 +1,293 @@
+"""T3.1 伏笔账本写侧 repo 单测(ARCH §6.2)。
+
+纯内存 fake,无 DB:断言 register 唯一、list_by_status 过滤、record_progress append
+不覆盖、transition 走状态机校验、scan_overdue 命中集合、按 project_id 隔离、frozen
+View 不可变。`(project_id, code)` 唯一性是 **DB 级**(models UniqueConstraint),纯 fake
+不引 pg 断言——真实 DB 路径由 T3.7 E2E 覆盖。
+"""
+
+from __future__ import annotations
+
+import uuid
+from dataclasses import dataclass, field
+from typing import Any
+
+import pytest
+from pydantic import ValidationError
+from ww_core.domain.foreshadow_repo import ForeshadowLedgerRepo, ForeshadowLedgerView
+from ww_core.domain.foreshadow_state import (
+ CLOSED,
+ OVERDUE,
+ PARTIAL,
+ InvalidTransition,
+)
+
+PROJECT = uuid.UUID("00000000-0000-0000-0000-000000000001")
+OTHER = uuid.UUID("00000000-0000-0000-0000-000000000002")
+
+
+@dataclass
+class _Row:
+ project_id: uuid.UUID
+ code: str
+ title: str
+ status: str
+ planted_at: int | None
+ content: str | None
+ expected_close_from: int | None
+ expected_close_to: int | None
+ importance: str | None
+ links: list[Any]
+ progress: list[Any]
+
+
+def _view(row: _Row) -> ForeshadowLedgerView:
+ return ForeshadowLedgerView(
+ code=row.code,
+ title=row.title,
+ status=row.status,
+ planted_at=row.planted_at,
+ content=row.content,
+ expected_close_from=row.expected_close_from,
+ expected_close_to=row.expected_close_to,
+ importance=row.importance,
+ links=list(row.links),
+ progress=list(row.progress),
+ )
+
+
+@dataclass
+class FakeForeshadowLedgerRepo:
+ """内存替身——镜像 SqlForeshadowLedgerRepo 语义(按 (project_id, code) 唯一)。"""
+
+ rows: list[_Row] = field(default_factory=list)
+
+ def _find(self, project_id: uuid.UUID, code: str) -> _Row | None:
+ return next((r for r in self.rows if r.project_id == project_id and r.code == code), None)
+
+ async def register(
+ self,
+ project_id: uuid.UUID,
+ *,
+ code: str,
+ title: str,
+ planted_at: int | None = None,
+ content: str | None = None,
+ expected_close_from: int | None = None,
+ expected_close_to: int | None = None,
+ importance: str | None = None,
+ ) -> ForeshadowLedgerView:
+ if self._find(project_id, code) is not None:
+ raise ValueError(f"duplicate foreshadow code: {code}")
+ row = _Row(
+ project_id=project_id,
+ code=code,
+ title=title,
+ status="OPEN",
+ planted_at=planted_at,
+ content=content,
+ expected_close_from=expected_close_from,
+ expected_close_to=expected_close_to,
+ importance=importance,
+ links=[],
+ progress=[],
+ )
+ self.rows.append(row)
+ return _view(row)
+
+ async def get(self, project_id: uuid.UUID, code: str) -> ForeshadowLedgerView | None:
+ row = self._find(project_id, code)
+ return _view(row) if row is not None else None
+
+ async def list_by_status(
+ self, project_id: uuid.UUID, status: str | None = None
+ ) -> list[ForeshadowLedgerView]:
+ return [
+ _view(r)
+ for r in self.rows
+ if r.project_id == project_id and (status is None or r.status == status)
+ ]
+
+ async def transition(
+ self, project_id: uuid.UUID, code: str, *, to_status: str
+ ) -> ForeshadowLedgerView:
+ from ww_core.domain.foreshadow_state import ForeshadowStatus
+ from ww_core.domain.foreshadow_state import transition as apply_transition
+
+ row = self._find(project_id, code)
+ if row is None:
+ raise LookupError(f"foreshadow not found: {code}")
+ row.status = apply_transition(ForeshadowStatus(row.status), ForeshadowStatus(to_status))
+ return _view(row)
+
+ async def record_progress(
+ self, project_id: uuid.UUID, code: str, *, entry: dict[str, Any]
+ ) -> ForeshadowLedgerView:
+ row = self._find(project_id, code)
+ if row is None:
+ raise LookupError(f"foreshadow not found: {code}")
+ row.progress = [*row.progress, dict(entry)]
+ return _view(row)
+
+ async def scan_overdue(
+ self, project_id: uuid.UUID, *, current_chapter: int
+ ) -> list[ForeshadowLedgerView]:
+ from ww_core.domain.foreshadow_state import ForeshadowStatus, apply_overdue_scan
+
+ changed: list[ForeshadowLedgerView] = []
+ for r in self.rows:
+ if r.project_id != project_id:
+ continue
+ new = apply_overdue_scan(
+ ForeshadowStatus(r.status),
+ current_chapter=current_chapter,
+ expected_close_to=r.expected_close_to,
+ )
+ if new != r.status:
+ r.status = new
+ changed.append(_view(r))
+ return changed
+
+
+def _repo() -> FakeForeshadowLedgerRepo:
+ return FakeForeshadowLedgerRepo()
+
+
+# ---- register ----
+
+
+async def test_register_starts_open() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ view = await repo.register(
+ PROJECT, code="F1", title="神秘玉佩", planted_at=3, expected_close_to=20
+ )
+ assert isinstance(view, ForeshadowLedgerView)
+ assert view.code == "F1"
+ assert view.status == "OPEN"
+ assert view.planted_at == 3
+ assert view.expected_close_to == 20
+ assert view.progress == []
+
+
+async def test_register_duplicate_code_rejected() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="F1", title="a")
+ with pytest.raises(ValueError):
+ await repo.register(PROJECT, code="F1", title="b")
+
+
+async def test_register_same_code_other_project_ok() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="F1", title="a")
+ other = await repo.register(OTHER, code="F1", title="b")
+ assert other.code == "F1"
+
+
+# ---- get / list_by_status ----
+
+
+async def test_get_returns_none_when_absent() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ assert await repo.get(PROJECT, "nope") is None
+
+
+async def test_list_by_status_filters() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ b = await repo.register(PROJECT, code="B", title="b")
+ assert b.status == "OPEN"
+ await repo.transition(PROJECT, "B", to_status=PARTIAL)
+
+ opens = await repo.list_by_status(PROJECT, "OPEN")
+ assert {v.code for v in opens} == {"A"}
+ partials = await repo.list_by_status(PROJECT, PARTIAL)
+ assert {v.code for v in partials} == {"B"}
+
+
+async def test_list_by_status_none_returns_all() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ await repo.register(PROJECT, code="B", title="b")
+ assert len(await repo.list_by_status(PROJECT)) == 2
+
+
+async def test_list_by_status_isolated_by_project() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ await repo.register(OTHER, code="B", title="b")
+ rows = await repo.list_by_status(PROJECT)
+ assert len(rows) == 1 and rows[0].code == "A"
+
+
+# ---- transition (走状态机) ----
+
+
+async def test_transition_applies_state_machine() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ v = await repo.transition(PROJECT, "A", to_status=PARTIAL)
+ assert v.status == "PARTIAL"
+ v2 = await repo.transition(PROJECT, "A", to_status=CLOSED)
+ assert v2.status == "CLOSED"
+
+
+async def test_transition_rejects_illegal() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ await repo.transition(PROJECT, "A", to_status=CLOSED)
+ with pytest.raises(InvalidTransition):
+ await repo.transition(PROJECT, "A", to_status=PARTIAL)
+
+
+# ---- record_progress (append-only) ----
+
+
+async def test_record_progress_appends_not_overwrites() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="A", title="a")
+ await repo.record_progress(PROJECT, "A", entry={"chapter": 5, "note": "first hint"})
+ v = await repo.record_progress(PROJECT, "A", entry={"chapter": 9, "note": "second"})
+ assert len(v.progress) == 2
+ assert v.progress[0]["chapter"] == 5
+ assert v.progress[1]["chapter"] == 9
+
+
+# ---- scan_overdue ----
+
+
+async def test_scan_overdue_marks_due_rows() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="DUE", title="d", expected_close_to=10)
+ await repo.register(PROJECT, code="NOTYET", title="n", expected_close_to=30)
+ await repo.register(PROJECT, code="NOWIN", title="w") # 无窗口
+ changed = await repo.scan_overdue(PROJECT, current_chapter=15)
+ assert {v.code for v in changed} == {"DUE"}
+ assert all(v.status == "OVERDUE" for v in changed)
+ # NOTYET / NOWIN 保持 OPEN
+ assert (await repo.get(PROJECT, "NOTYET")).status == "OPEN" # type: ignore[union-attr]
+
+
+async def test_scan_overdue_skips_closed() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(PROJECT, code="C", title="c", expected_close_to=10)
+ await repo.transition(PROJECT, "C", to_status=CLOSED)
+ changed = await repo.scan_overdue(PROJECT, current_chapter=99)
+ assert changed == []
+ assert (await repo.get(PROJECT, "C")).status == "CLOSED" # type: ignore[union-attr]
+
+
+async def test_scan_overdue_isolated_by_project() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ await repo.register(OTHER, code="X", title="x", expected_close_to=5)
+ changed = await repo.scan_overdue(PROJECT, current_chapter=99)
+ assert changed == [] # OTHER 的行不受 PROJECT 扫描影响
+
+
+# ---- frozen View ----
+
+
+async def test_view_is_frozen() -> None:
+ repo: ForeshadowLedgerRepo = _repo()
+ v = await repo.register(PROJECT, code="A", title="a")
+ with pytest.raises(ValidationError):
+ v.status = OVERDUE
diff --git a/packages/core/tests/test_foreshadow_state.py b/packages/core/tests/test_foreshadow_state.py
new file mode 100644
index 0000000..aec52e0
--- /dev/null
+++ b/packages/core/tests/test_foreshadow_state.py
@@ -0,0 +1,143 @@
+"""T3.1 伏笔状态机纯函数单测(ARCH §6.2)。
+
+确定性纯函数,无 IO:覆盖全转移(登记/首次照应/完成回收)+ OVERDUE 到期判据边界
+(current ==/> expected_close_to;CLOSED 不被覆盖;无 expected_close_to 不 OVERDUE)+
+非法转移显式处理。
+"""
+
+from __future__ import annotations
+
+import pytest
+from ww_core.domain.foreshadow_state import (
+ CLOSED,
+ OPEN,
+ OVERDUE,
+ PARTIAL,
+ ForeshadowStatus,
+ InvalidTransition,
+ apply_overdue_scan,
+ is_overdue,
+ transition,
+)
+
+# ---- 合法转移(登记→OPEN→PARTIAL→CLOSED)----
+
+
+def test_open_to_partial() -> None:
+ assert transition(OPEN, PARTIAL) == PARTIAL
+
+
+def test_partial_to_closed() -> None:
+ assert transition(PARTIAL, CLOSED) == CLOSED
+
+
+def test_open_to_closed_direct_recover() -> None:
+ # 一步回收(埋后直接收)也合法
+ assert transition(OPEN, CLOSED) == CLOSED
+
+
+def test_overdue_to_closed_recover_after_due() -> None:
+ # 逾期后仍可回收
+ assert transition(OVERDUE, CLOSED) == CLOSED
+
+
+def test_overdue_to_partial_partial_recover_after_due() -> None:
+ assert transition(OVERDUE, PARTIAL) == PARTIAL
+
+
+# ---- 非法转移显式拒绝 ----
+
+
+def test_closed_cannot_reopen() -> None:
+ with pytest.raises(InvalidTransition):
+ transition(CLOSED, OPEN)
+
+
+def test_closed_cannot_become_overdue_via_transition() -> None:
+ with pytest.raises(InvalidTransition):
+ transition(CLOSED, OVERDUE)
+
+
+def test_closed_cannot_become_partial() -> None:
+ with pytest.raises(InvalidTransition):
+ transition(CLOSED, PARTIAL)
+
+
+def test_partial_cannot_go_back_to_open() -> None:
+ with pytest.raises(InvalidTransition):
+ transition(PARTIAL, OPEN)
+
+
+def test_same_status_is_noop_allowed() -> None:
+ # 幂等:同态转移允许(看板手动设回同值不报错)
+ assert transition(OPEN, OPEN) == OPEN
+ assert transition(PARTIAL, PARTIAL) == PARTIAL
+
+
+# ---- is_overdue 判据边界 ----
+
+
+def test_is_overdue_strictly_past_window() -> None:
+ assert is_overdue(current_chapter=11, expected_close_to=10, status=OPEN) is True
+
+
+def test_is_overdue_at_window_boundary_not_overdue() -> None:
+ # current == expected_close_to:仍在窗口内,未逾期
+ assert is_overdue(current_chapter=10, expected_close_to=10, status=OPEN) is False
+
+
+def test_is_overdue_before_window_not_overdue() -> None:
+ assert is_overdue(current_chapter=5, expected_close_to=10, status=OPEN) is False
+
+
+def test_is_overdue_closed_never_overdue() -> None:
+ assert is_overdue(current_chapter=99, expected_close_to=10, status=CLOSED) is False
+
+
+def test_is_overdue_partial_can_be_overdue() -> None:
+ assert is_overdue(current_chapter=11, expected_close_to=10, status=PARTIAL) is True
+
+
+def test_is_overdue_without_window_never_overdue() -> None:
+ # 无 expected_close_to → 永不逾期
+ assert is_overdue(current_chapter=999, expected_close_to=None, status=OPEN) is False
+
+
+def test_is_overdue_already_overdue_stays() -> None:
+ assert is_overdue(current_chapter=11, expected_close_to=10, status=OVERDUE) is True
+
+
+# ---- apply_overdue_scan ----
+
+
+def test_apply_overdue_scan_sets_overdue_when_due() -> None:
+ new = apply_overdue_scan(OPEN, current_chapter=11, expected_close_to=10)
+ assert new == OVERDUE
+
+
+def test_apply_overdue_scan_keeps_status_when_not_due() -> None:
+ assert apply_overdue_scan(OPEN, current_chapter=10, expected_close_to=10) == OPEN
+
+
+def test_apply_overdue_scan_does_not_touch_closed() -> None:
+ # CLOSED 不被 OVERDUE 覆盖(不变量)
+ assert apply_overdue_scan(CLOSED, current_chapter=99, expected_close_to=10) == CLOSED
+
+
+def test_apply_overdue_scan_no_window_keeps_status() -> None:
+ assert apply_overdue_scan(PARTIAL, current_chapter=99, expected_close_to=None) == PARTIAL
+
+
+def test_apply_overdue_scan_idempotent_on_overdue() -> None:
+ assert apply_overdue_scan(OVERDUE, current_chapter=12, expected_close_to=10) == OVERDUE
+
+
+# ---- 枚举值与 DB 文本一致 ----
+
+
+def test_status_values_match_db_text() -> None:
+ assert OPEN == "OPEN"
+ assert PARTIAL == "PARTIAL"
+ assert CLOSED == "CLOSED"
+ assert OVERDUE == "OVERDUE"
+ assert {s.value for s in ForeshadowStatus} == {"OPEN", "PARTIAL", "CLOSED", "OVERDUE"}
diff --git a/packages/core/tests/test_outline_node.py b/packages/core/tests/test_outline_node.py
new file mode 100644
index 0000000..ea14ffb
--- /dev/null
+++ b/packages/core/tests/test_outline_node.py
@@ -0,0 +1,137 @@
+"""T3.4 大纲节点单测——build_outline_request + run_outline(C6 扩 / ARCH §5.4 §6.2)。
+
+注入 mock 网关(产 `OutlineResult` parsed),无真 LLM、无真 Postgres。
+大纲节点是裸函数(独立生成,不在写章/审稿流水线);只产结构化大纲,**不写库**
+(持久化属 T3.5 验收/端点,不变量 #3)。asyncio_mode=auto。
+"""
+
+from __future__ import annotations
+
+import uuid
+
+import pytest
+from fakes_orchestrator import FailingRunGateway, FakeRunGateway
+from ww_agents import (
+ ForeshadowWindow,
+ OutlineChapter,
+ OutlineResult,
+ outliner_spec,
+)
+from ww_core.orchestrator import build_outline_request, run_outline
+
+PROJECT = uuid.UUID("00000000-0000-0000-0000-000000000001")
+USER = uuid.UUID("00000000-0000-0000-0000-0000000000aa")
+
+CONTEXT = "## 立意\n复仇成长\n## 已开伏笔\nFS-BIRTH: 身世之谜(待第20-30章回收)"
+
+_OUTLINE = OutlineResult(
+ chapters=[
+ OutlineChapter(
+ no=1,
+ beats=["主角登场", "埋身世伏笔"],
+ foreshadow_windows=[
+ ForeshadowWindow(
+ code="FS-BIRTH",
+ plant_chapter=1,
+ expected_close_from=20,
+ expected_close_to=30,
+ )
+ ],
+ ),
+ OutlineChapter(no=2, beats=["遭遇对手"], foreshadow_windows=[]),
+ ]
+)
+
+
+# ---- build_outline_request:tier 不变量、缓存断点、output_schema、run 非 stream ----
+
+
+def test_build_outline_request_uses_spec_tier_and_cached_prompt() -> None:
+ req = build_outline_request(outliner_spec, context=CONTEXT, user_id=USER, project_id=PROJECT)
+
+ assert req.tier == "analyst" # 不变量②:spec 档位透传,不传 model
+ assert req.stream is False # 排大纲用 run() 非 stream()
+ assert len(req.system) == 1
+ assert req.system[0].text == outliner_spec.system_prompt
+ assert req.system[0].cache is True # 不变量#9
+ assert req.input == CONTEXT
+ assert req.output_schema is OutlineResult
+ assert req.scope.user_id == USER
+ assert req.scope.project_id == PROJECT
+
+
+# ---- run_outline:返回结构化大纲含 foreshadow_windows,只读不写库 ----
+
+
+async def test_run_outline_returns_structured_outline() -> None:
+ gateway = FakeRunGateway(_OUTLINE)
+
+ result = await run_outline(
+ outliner_spec, context=CONTEXT, gateway=gateway, user_id=USER, project_id=PROJECT
+ )
+
+ assert isinstance(result, OutlineResult)
+ assert len(result.chapters) == 2
+ assert result.chapters[0].no == 1
+ window = result.chapters[0].foreshadow_windows[0]
+ assert window.code == "FS-BIRTH"
+ assert window.expected_close_to == 30
+ assert gateway.call_count == 1
+
+
+async def test_run_outline_forwards_constructed_request() -> None:
+ gateway = FakeRunGateway(_OUTLINE)
+
+ await run_outline(
+ outliner_spec, context=CONTEXT, gateway=gateway, user_id=USER, project_id=PROJECT
+ )
+
+ req = gateway.last_request
+ assert req is not None
+ assert req.tier == "analyst"
+ assert req.output_schema is OutlineResult
+ assert req.input == CONTEXT
+
+
+async def test_run_outline_raises_when_gateway_fails() -> None:
+ # 大纲是独立生成(非流水线并行审);网关失败直接上抛,端点/T3.5 处理。
+ gateway = FailingRunGateway(RuntimeError("provider down"))
+
+ with pytest.raises(RuntimeError):
+ await run_outline(
+ outliner_spec,
+ context=CONTEXT,
+ gateway=gateway,
+ user_id=USER,
+ project_id=PROJECT,
+ )
+
+
+async def test_run_outline_raises_when_parsed_missing() -> None:
+ # 带 output_schema 时 parsed 必非 None(C1);若网关违约返回 None,明确报错而非静默。
+ class _NoParseGateway:
+ async def run(self, req: object) -> object:
+ from ww_llm_gateway.types import LlmResponse, ServedBy, Usage
+
+ return LlmResponse(
+ text="{}",
+ parsed=None,
+ usage=Usage(
+ provider="fake",
+ model="fake-analyst",
+ input_tokens=1,
+ output_tokens=1,
+ cost_minor=0,
+ currency="USD",
+ ),
+ served_by=ServedBy(provider="fake", model="fake-analyst"),
+ )
+
+ with pytest.raises(ValueError, match="parsed"):
+ await run_outline(
+ outliner_spec,
+ context=CONTEXT,
+ gateway=_NoParseGateway(), # type: ignore[arg-type]
+ user_id=USER,
+ project_id=PROJECT,
+ )
diff --git a/packages/core/tests/test_outline_write_repo.py b/packages/core/tests/test_outline_write_repo.py
new file mode 100644
index 0000000..20b9efb
--- /dev/null
+++ b/packages/core/tests/test_outline_write_repo.py
@@ -0,0 +1,125 @@
+"""T3.5 大纲写侧 repo 单测(ARCH §5.4 / §7.2)。
+
+纯内存 fake,无 DB:断言 upsert 幂等覆盖、按 project_id 隔离、frozen View 不可变、
+`_to_view` 的 beats 包裹/解包 round-trip。`(project_id, chapter_no)` 唯一性是 **DB 级**
+(models UniqueConstraint),纯 fake 不引 pg 断言——真实 DB 路径由 T3.7 E2E 覆盖。
+"""
+
+from __future__ import annotations
+
+import uuid
+from dataclasses import dataclass, field
+from typing import Any
+
+import pytest
+from pydantic import ValidationError
+from ww_core.domain.outline_write_repo import (
+ OutlineWriteRepo,
+ OutlineWriteView,
+ _to_view,
+)
+
+PROJECT = uuid.UUID("00000000-0000-0000-0000-000000000001")
+OTHER = uuid.UUID("00000000-0000-0000-0000-000000000002")
+
+
+@dataclass
+class _Row:
+ """模拟 ORM Outline 行(beats 是 JSONB dict、foreshadow_windows 是 JSONB list)。"""
+
+ project_id: uuid.UUID
+ volume: int
+ chapter_no: int
+ beats: dict[str, Any] | None
+ foreshadow_windows: list[Any] = field(default_factory=list)
+
+
+@dataclass
+class FakeOutlineWriteRepo:
+ """实现 `OutlineWriteRepo` Protocol(内存;幂等覆盖同 (project_id, chapter_no))。"""
+
+ rows: dict[tuple[uuid.UUID, int], _Row] = field(default_factory=dict)
+
+ async def upsert_chapter(
+ self,
+ project_id: uuid.UUID,
+ *,
+ volume: int,
+ chapter_no: int,
+ beats: list[str],
+ foreshadow_windows: list[dict[str, Any]],
+ ) -> OutlineWriteView:
+ row = self.rows.get((project_id, chapter_no))
+ beats_payload = {"beats": list(beats)}
+ windows = [dict(w) for w in foreshadow_windows]
+ if row is None:
+ row = _Row(
+ project_id=project_id,
+ volume=volume,
+ chapter_no=chapter_no,
+ beats=beats_payload,
+ foreshadow_windows=windows,
+ )
+ else:
+ row.volume = volume
+ row.beats = beats_payload
+ row.foreshadow_windows = windows
+ self.rows[(project_id, chapter_no)] = row
+ return _to_view(row) # type: ignore[arg-type]
+
+
+def _repo() -> OutlineWriteRepo:
+ return FakeOutlineWriteRepo()
+
+
+@pytest.mark.asyncio
+async def test_upsert_inserts_new_chapter() -> None:
+ repo = _repo()
+ view = await repo.upsert_chapter(
+ PROJECT,
+ volume=1,
+ chapter_no=1,
+ beats=["a", "b"],
+ foreshadow_windows=[{"code": "F1", "expected_close_to": 10}],
+ )
+ assert view.chapter_no == 1
+ assert view.volume == 1
+ assert view.beats == ["a", "b"]
+ assert view.foreshadow_windows[0]["code"] == "F1"
+
+
+@pytest.mark.asyncio
+async def test_upsert_overwrites_same_chapter() -> None:
+ repo = _repo()
+ await repo.upsert_chapter(PROJECT, volume=1, chapter_no=1, beats=["old"], foreshadow_windows=[])
+ view = await repo.upsert_chapter(
+ PROJECT, volume=2, chapter_no=1, beats=["new"], foreshadow_windows=[]
+ )
+ assert view.beats == ["new"]
+ assert view.volume == 2
+
+
+@pytest.mark.asyncio
+async def test_upsert_isolated_by_project() -> None:
+ repo = FakeOutlineWriteRepo()
+ await repo.upsert_chapter(PROJECT, volume=1, chapter_no=1, beats=["p"], foreshadow_windows=[])
+ await repo.upsert_chapter(OTHER, volume=1, chapter_no=1, beats=["o"], foreshadow_windows=[])
+ assert len(repo.rows) == 2
+
+
+def test_to_view_unpacks_wrapped_beats() -> None:
+ row = _Row(project_id=PROJECT, volume=1, chapter_no=3, beats={"beats": ["x", "y"]})
+ view = _to_view(row) # type: ignore[arg-type]
+ assert view.beats == ["x", "y"]
+
+
+def test_to_view_handles_null_beats() -> None:
+ row = _Row(project_id=PROJECT, volume=1, chapter_no=3, beats=None)
+ view = _to_view(row) # type: ignore[arg-type]
+ assert view.beats == []
+
+
+def test_write_view_is_frozen() -> None:
+ view = OutlineWriteView(chapter_no=1, volume=1, beats=["a"])
+ with pytest.raises(ValidationError):
+ view.volume = 2
diff --git a/packages/core/tests/test_review.py b/packages/core/tests/test_review.py
index 12edc8c..391ffa5 100644
--- a/packages/core/tests/test_review.py
+++ b/packages/core/tests/test_review.py
@@ -13,16 +13,31 @@ from fakes_orchestrator import (
FailingRunGateway,
FakeReviewRepo,
FakeRunGateway,
+ SchemaRoutingRunGateway,
)
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.memory import MemorySaver
-from ww_agents import Conflict, ContinuityReview, continuity_spec
+from ww_agents import (
+ Conflict,
+ ContinuityReview,
+ ForeshadowReview,
+ ForeshadowSuggestion,
+ PaceIssue,
+ PaceReview,
+ continuity_spec,
+ foreshadow_spec,
+ pace_spec,
+)
from ww_core.orchestrator import (
CONTINUITY,
EVENT_CONFLICT,
EVENT_DONE,
EVENT_ERROR,
+ EVENT_FORESHADOW,
+ EVENT_PACE,
EVENT_SECTION,
+ FORESHADOW,
+ PACE,
REVIEW_INCOMPLETE,
REVIEW_OK,
SECTION_DONE,
@@ -33,6 +48,8 @@ from ww_core.orchestrator import (
build_review_request,
collect_reviews,
extract_conflicts,
+ extract_foreshadow_sug,
+ extract_pace,
make_review_node,
normalize_review,
run_review,
@@ -242,3 +259,177 @@ async def test_normalize_review_maps_unexpected_error_to_error_event() -> None:
assert events[-1].event == EVENT_ERROR
assert events[-1].data["code"] == ErrorCode.INTERNAL
assert events[-1].data["request_id"] == "req-9"
+
+
+# ============================================================================
+# T3.3 三审齐:foreshadow + pace 并入并行审 / collect 列映射 / SSE surface
+# ============================================================================
+
+_FORESHADOW = ForeshadowReview(
+ planted=[
+ ForeshadowSuggestion(code="FS-MARK", title="胸口胎记", where="第2段", note="皇族暗示")
+ ],
+ resolved=[ForeshadowSuggestion(code="FS-BIRTH", title="身世", where="第9段", note="回收")],
+)
+_PACE = PaceReview(
+ water=[PaceIssue(where="第4-6段", reason="信息密度低")],
+ hook=True,
+ beat_map=[1, 3, 5, 0, 4],
+)
+
+
+def _three_reviews() -> dict[str, Any]:
+ """三审皆 ok 的 state['reviews'](continuity/foreshadow/pace)。"""
+ return {
+ CONTINUITY: {"status": REVIEW_OK, "result": {"conflicts": [_CONFLICT.model_dump()]}},
+ FORESHADOW: {"status": REVIEW_OK, "result": _FORESHADOW.model_dump()},
+ PACE: {"status": REVIEW_OK, "result": _PACE.model_dump()},
+ }
+
+
+# ---- collect 三审列映射 ----
+
+
+def test_extract_foreshadow_sug_flattens_planted_and_resolved() -> None:
+ sug = extract_foreshadow_sug(_three_reviews())
+
+ assert len(sug) == 2 # 1 planted + 1 resolved 扁平
+ kinds = {s["kind"] for s in sug}
+ assert kinds == {"planted", "resolved"}
+ planted = next(s for s in sug if s["kind"] == "planted")
+ assert planted["code"] == "FS-MARK"
+
+
+def test_extract_pace_returns_dict() -> None:
+ pace = extract_pace(_three_reviews())
+
+ assert pace is not None
+ assert pace["hook"] is True
+ assert pace["beat_map"] == [1, 3, 5, 0, 4]
+
+
+def test_extract_foreshadow_and_pace_empty_when_incomplete() -> None:
+ reviews = {
+ FORESHADOW: {"status": REVIEW_INCOMPLETE, "result": None},
+ PACE: {"status": REVIEW_INCOMPLETE, "result": None},
+ }
+ assert extract_foreshadow_sug(reviews) == []
+ assert extract_pace(reviews) is None
+
+
+async def test_collect_records_three_audits_to_their_columns() -> None:
+ repo = FakeReviewRepo()
+
+ await collect_reviews(_state(_three_reviews()), review_repo=repo)
+
+ assert len(repo.records) == 1 # 一次 record 落齐三审
+ rec = repo.records[0]
+ assert rec["conflicts"][0]["type"] == "能力不符" # continuity→conflicts
+ assert len(rec["foreshadow_sug"]) == 2 # foreshadow→foreshadow_sug(扁平)
+ assert rec["pace"]["hook"] is True # pace→pace
+
+
+# ---- 三审并行图跑通(mock 网关按 schema 路由产三 parsed)----
+
+
+async def test_three_review_graph_runs_all_audits_end_to_end() -> None:
+ gateway = SchemaRoutingRunGateway(
+ {
+ ContinuityReview: ContinuityReview(conflicts=[_CONFLICT]),
+ ForeshadowReview: _FORESHADOW,
+ PaceReview: _PACE,
+ }
+ )
+ repo = FakeReviewRepo()
+ graph = build_review_graph(
+ gateway,
+ repo,
+ review_specs=(continuity_spec, foreshadow_spec, pace_spec),
+ checkpointer=MemorySaver(),
+ )
+ config: RunnableConfig = {"configurable": {"thread_id": "rev-3"}}
+
+ final = await graph.ainvoke(_state(), config=config)
+
+ assert final["reviews"][CONTINUITY]["status"] == REVIEW_OK
+ assert final["reviews"][FORESHADOW]["status"] == REVIEW_OK
+ assert final["reviews"][PACE]["status"] == REVIEW_OK
+ assert len(gateway.calls) == 3 # 三审各调一次网关
+ # collect 一次 record 落齐三列
+ rec = repo.records[0]
+ assert rec["conflicts"] and rec["foreshadow_sug"] and rec["pace"]
+
+
+async def test_three_review_graph_isolates_one_failing_audit() -> None:
+ # foreshadow schema 缺失 → SchemaRoutingRunGateway 抛 KeyError → run_review 隔离为 incomplete
+ gateway = SchemaRoutingRunGateway(
+ {
+ ContinuityReview: ContinuityReview(conflicts=[_CONFLICT]),
+ PaceReview: _PACE,
+ }
+ )
+ repo = FakeReviewRepo()
+ graph = build_review_graph(
+ gateway,
+ repo,
+ review_specs=(continuity_spec, foreshadow_spec, pace_spec),
+ checkpointer=MemorySaver(),
+ )
+ config: RunnableConfig = {"configurable": {"thread_id": "rev-4"}}
+
+ final = await graph.ainvoke(_state(), config=config)
+
+ # foreshadow 失败被隔离,其余两审照常 + collect 照常留痕(foreshadow_sug 空)
+ assert final["reviews"][FORESHADOW]["status"] == REVIEW_INCOMPLETE
+ assert final["reviews"][CONTINUITY]["status"] == REVIEW_OK
+ assert final["reviews"][PACE]["status"] == REVIEW_OK
+ rec = repo.records[0]
+ assert rec["conflicts"] # continuity 落库
+ assert rec["foreshadow_sug"] == [] # 失败列留空,不阻塞其余
+ assert rec["pace"]["hook"] is True # pace 落库
+
+
+# ---- SSE:normalize_review surface 三审(section + conflict + foreshadow + pace + done)----
+
+
+async def test_normalize_review_surfaces_three_audits() -> None:
+ events = [e async for e in normalize_review(_three_reviews())]
+ kinds = [e.event for e in events]
+
+ # 字典序遍历:continuity < foreshadow < pace
+ # continuity: section + 1 conflict
+ # foreshadow: section + 2(planted+resolved);pace: section + 1
+ assert kinds == [
+ EVENT_SECTION,
+ EVENT_CONFLICT,
+ EVENT_SECTION,
+ EVENT_FORESHADOW,
+ EVENT_FORESHADOW,
+ EVENT_SECTION,
+ EVENT_PACE,
+ EVENT_DONE,
+ ]
+ # foreshadow 事件带 kind + code
+ fs_events = [e for e in events if e.event == EVENT_FORESHADOW]
+ assert {e.data["kind"] for e in fs_events} == {"planted", "resolved"}
+ # pace 事件带节拍图
+ pace_evt = next(e for e in events if e.event == EVENT_PACE)
+ assert pace_evt.data["beat_map"] == [1, 3, 5, 0, 4]
+ assert pace_evt.data["hook"] is True
+ assert events[-1].data == {"length": 3} # 三审项
+
+
+async def test_normalize_review_skips_results_for_incomplete_audit() -> None:
+ reviews = {
+ CONTINUITY: {"status": REVIEW_OK, "result": {"conflicts": []}},
+ FORESHADOW: {"status": REVIEW_INCOMPLETE, "result": None},
+ PACE: {"status": REVIEW_OK, "result": _PACE.model_dump()},
+ }
+
+ events = [e async for e in normalize_review(reviews)]
+ kinds = [e.event for e in events]
+
+ # foreshadow incomplete → 仅 section,无 foreshadow 事件
+ assert kinds == [EVENT_SECTION, EVENT_SECTION, EVENT_SECTION, EVENT_PACE, EVENT_DONE]
+ fs_section = next(e for e in events if e.data.get("name") == FORESHADOW)
+ assert fs_section.data["status"] == SECTION_INCOMPLETE
diff --git a/packages/core/ww_core/domain/__init__.py b/packages/core/ww_core/domain/__init__.py
index 129a641..593a95b 100644
--- a/packages/core/ww_core/domain/__init__.py
+++ b/packages/core/ww_core/domain/__init__.py
@@ -9,6 +9,27 @@ from ww_core.domain.chapter_repo import (
SqlChapterRepo,
)
from ww_core.domain.digest_repo import DigestAppendRepo, SqlDigestAppendRepo
+from ww_core.domain.foreshadow_repo import (
+ ForeshadowLedgerRepo,
+ ForeshadowLedgerView,
+ SqlForeshadowLedgerRepo,
+)
+from ww_core.domain.foreshadow_state import (
+ CLOSED,
+ OPEN,
+ OVERDUE,
+ PARTIAL,
+ ForeshadowStatus,
+ InvalidTransition,
+ apply_overdue_scan,
+ is_overdue,
+ transition,
+)
+from ww_core.domain.outline_write_repo import (
+ OutlineWriteRepo,
+ OutlineWriteView,
+ SqlOutlineWriteRepo,
+)
from ww_core.domain.project_repo import (
ProjectCreate,
ProjectRepo,
@@ -26,7 +47,22 @@ __all__ = [
"DigestAppendRepo",
"SqlDigestAppendRepo",
"DigestView",
+ "ForeshadowLedgerRepo",
+ "ForeshadowLedgerView",
+ "SqlForeshadowLedgerRepo",
+ "ForeshadowStatus",
+ "InvalidTransition",
+ "OPEN",
+ "PARTIAL",
+ "CLOSED",
+ "OVERDUE",
+ "apply_overdue_scan",
+ "is_overdue",
+ "transition",
"MemoryRepos",
+ "OutlineWriteRepo",
+ "OutlineWriteView",
+ "SqlOutlineWriteRepo",
"ProjectCreate",
"ProjectRepo",
"ProjectView",
diff --git a/packages/core/ww_core/domain/foreshadow_repo.py b/packages/core/ww_core/domain/foreshadow_repo.py
new file mode 100644
index 0000000..a3f97ab
--- /dev/null
+++ b/packages/core/ww_core/domain/foreshadow_repo.py
@@ -0,0 +1,219 @@
+"""伏笔账本**写侧** Repository(登记/状态机/进展/到期扫描;ARCH §6.2 / PRODUCT_SPEC §4.2)。
+
+读侧(`list_for_codes`,供 assemble 注入伏笔窗口)已在 `ww_core.memory.sql_repositories`
++ `domain.repositories.ForeshadowRepo`/`ForeshadowView` 提供(最小 View,C5 稳定,不动)。
+本模块加**写**能力 + 看板查询,命名加 `Ledger` 前缀避免与读侧同名歧义
+(同 T2.3 `DigestAppendRepo` 区别于读侧 `DigestRepo` 的先例,见 memory/decisions)。
+`ForeshadowLedgerView` 比读侧 View 多 `importance/links/progress`(看板/账本需要)。
+
+**提交边界**:所有写方法**只 `flush()` 不 `commit()`**——提交交调用方:
+- T3.2 验收后扫描挂 BackgroundTask / 接 §5.5 `TODO(M3)`,由那条事务/任务提交;
+- T3.5 登记/状态端点由端点事务提交。
+与 M2 验收-side repos 一致(写库副作用归编排/事务层,见 memory/gotchas)。
+含 nullable 列的唯一约束 `(project_id, code)` 不用 PG `ON CONFLICT`——`register` 仅
+显式 INSERT(重复 code 让 DB 唯一约束/调用方处理),不做 upsert(见 gotcha)。
+"""
+
+from __future__ import annotations
+
+import uuid
+from typing import Any, Protocol
+
+from pydantic import BaseModel, Field
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+from ww_db.models import Foreshadow
+
+from ww_core.domain.foreshadow_state import (
+ ForeshadowStatus,
+ apply_overdue_scan,
+)
+from ww_core.domain.foreshadow_state import (
+ transition as apply_transition,
+)
+
+
+class ForeshadowLedgerView(BaseModel):
+ """伏笔账本只读快照(snake_case,frozen)——看板/账本用,含进展与重要度。"""
+
+ model_config = {"frozen": True}
+
+ code: str
+ title: str
+ status: str
+ planted_at: int | None = None
+ content: str | None = None
+ expected_close_from: int | None = None
+ expected_close_to: int | None = None
+ importance: str | None = None
+ links: list[dict[str, Any]] = Field(default_factory=list)
+ progress: list[dict[str, Any]] = Field(default_factory=list)
+
+
+class ForeshadowLedgerRepo(Protocol):
+ """伏笔账本写侧接口(按 project_id 隔离;写方法只 flush 不 commit)。"""
+
+ async def register(
+ self,
+ project_id: uuid.UUID,
+ *,
+ code: str,
+ title: str,
+ planted_at: int | None = None,
+ content: str | None = None,
+ expected_close_from: int | None = None,
+ expected_close_to: int | None = None,
+ importance: str | None = None,
+ ) -> ForeshadowLedgerView:
+ """登记一条伏笔(status=OPEN);`(project_id, code)` 唯一。"""
+ ...
+
+ async def get(self, project_id: uuid.UUID, code: str) -> ForeshadowLedgerView | None:
+ """按 code 取单条,无则 None。"""
+ ...
+
+ async def list_by_status(
+ self, project_id: uuid.UUID, status: str | None = None
+ ) -> list[ForeshadowLedgerView]:
+ """看板查询:按状态过滤(None=全部)。供 T3.5 `GET /foreshadow?status=`。"""
+ ...
+
+ async def transition(
+ self, project_id: uuid.UUID, code: str, *, to_status: str
+ ) -> ForeshadowLedgerView:
+ """应用状态机校验后写 `status`(非法转移抛 `InvalidTransition`)。"""
+ ...
+
+ async def record_progress(
+ self, project_id: uuid.UUID, code: str, *, entry: dict[str, Any]
+ ) -> ForeshadowLedgerView:
+ """append 一条进展到 `progress` JSONB(append-only,不覆盖历史)。"""
+ ...
+
+ async def scan_overdue(
+ self, project_id: uuid.UUID, *, current_chapter: int
+ ) -> list[ForeshadowLedgerView]:
+ """到期扫描:命中行置 OVERDUE,返回**被改的行**。供 T3.2 验收后扫描。"""
+ ...
+
+
+def _to_view(row: Foreshadow) -> ForeshadowLedgerView:
+ return ForeshadowLedgerView(
+ code=row.code,
+ title=row.title,
+ status=row.status,
+ planted_at=row.planted_at,
+ content=row.content,
+ expected_close_from=row.expected_close_from,
+ expected_close_to=row.expected_close_to,
+ importance=row.importance,
+ links=list(row.links or []),
+ progress=list(row.progress or []),
+ )
+
+
+class SqlForeshadowLedgerRepo:
+ """SQLAlchemy 实现:登记 + 状态机转移 + 进展 append + 到期扫描(只 flush 不 commit)。"""
+
+ def __init__(self, session: AsyncSession) -> None:
+ self._s = session
+
+ async def _find(self, project_id: uuid.UUID, code: str) -> Foreshadow | None:
+ return (
+ await self._s.execute(
+ select(Foreshadow).where(
+ Foreshadow.project_id == project_id,
+ Foreshadow.code == code,
+ )
+ )
+ ).scalar_one_or_none()
+
+ async def register(
+ self,
+ project_id: uuid.UUID,
+ *,
+ code: str,
+ title: str,
+ planted_at: int | None = None,
+ content: str | None = None,
+ expected_close_from: int | None = None,
+ expected_close_to: int | None = None,
+ importance: str | None = None,
+ ) -> ForeshadowLedgerView:
+ # 仅 INSERT;唯一约束 (project_id, code) 由 DB 强制(不做 ON CONFLICT upsert,见模块注释)。
+ row = Foreshadow(
+ project_id=project_id,
+ code=code,
+ title=title,
+ status=ForeshadowStatus.OPEN.value,
+ planted_at=planted_at,
+ content=content,
+ expected_close_from=expected_close_from,
+ expected_close_to=expected_close_to,
+ importance=importance,
+ links=[],
+ progress=[],
+ )
+ self._s.add(row)
+ await self._s.flush()
+ await self._s.refresh(row)
+ return _to_view(row)
+
+ async def get(self, project_id: uuid.UUID, code: str) -> ForeshadowLedgerView | None:
+ row = await self._find(project_id, code)
+ return _to_view(row) if row is not None else None
+
+ async def list_by_status(
+ self, project_id: uuid.UUID, status: str | None = None
+ ) -> list[ForeshadowLedgerView]:
+ stmt = select(Foreshadow).where(Foreshadow.project_id == project_id)
+ if status is not None:
+ stmt = stmt.where(Foreshadow.status == status)
+ rows = (await self._s.execute(stmt.order_by(Foreshadow.code))).scalars()
+ return [_to_view(r) for r in rows]
+
+ async def transition(
+ self, project_id: uuid.UUID, code: str, *, to_status: str
+ ) -> ForeshadowLedgerView:
+ row = await self._find(project_id, code)
+ if row is None:
+ raise LookupError(f"foreshadow not found: {code}")
+ # 状态机校验(非法转移抛 InvalidTransition,事务回滚由调用方控制)。
+ row.status = apply_transition(
+ ForeshadowStatus(row.status), ForeshadowStatus(to_status)
+ ).value
+ await self._s.flush()
+ await self._s.refresh(row)
+ return _to_view(row)
+
+ async def record_progress(
+ self, project_id: uuid.UUID, code: str, *, entry: dict[str, Any]
+ ) -> ForeshadowLedgerView:
+ row = await self._find(project_id, code)
+ if row is None:
+ raise LookupError(f"foreshadow not found: {code}")
+ # 不可变 append:建新 list 重赋值,确保 ORM 侦测 JSONB 变更(避免原地 mutate 不脏标记)。
+ row.progress = [*(row.progress or []), dict(entry)]
+ await self._s.flush()
+ await self._s.refresh(row)
+ return _to_view(row)
+
+ async def scan_overdue(
+ self, project_id: uuid.UUID, *, current_chapter: int
+ ) -> list[ForeshadowLedgerView]:
+ rows = (
+ await self._s.execute(select(Foreshadow).where(Foreshadow.project_id == project_id))
+ ).scalars()
+ changed: list[ForeshadowLedgerView] = []
+ for row in rows:
+ new = apply_overdue_scan(
+ ForeshadowStatus(row.status),
+ current_chapter=current_chapter,
+ expected_close_to=row.expected_close_to,
+ ).value
+ if new != row.status:
+ row.status = new
+ changed.append(_to_view(row))
+ if changed:
+ await self._s.flush()
+ return changed
diff --git a/packages/core/ww_core/domain/foreshadow_state.py b/packages/core/ww_core/domain/foreshadow_state.py
new file mode 100644
index 0000000..42b40b0
--- /dev/null
+++ b/packages/core/ww_core/domain/foreshadow_state.py
@@ -0,0 +1,105 @@
+"""伏笔账本状态机——确定性纯函数(ARCH §6.2 / PRODUCT_SPEC §4.2)。
+
+状态机(§6.2)::
+
+ 登记 首次照应 完成回收
+ ─────▶ OPEN ──────▶ PARTIAL ─────────▶ CLOSED
+ │ │
+ └─────────────┴──▶ OVERDUE (章号 > expected_close_to 且未 CLOSED)
+ (到期扫描置位; 回收后可转 CLOSED)
+
+- 登记 → OPEN(DB server_default)。
+- 首次照应 → PARTIAL。
+- 完成回收 → CLOSED(可从 OPEN/PARTIAL/OVERDUE 一步到达)。
+- 到期判定(M3-d 纯函数,非 Agent):
+ `current_chapter > expected_close_to AND status≠CLOSED → OVERDUE`。
+- **CLOSED 终态**:不被 OVERDUE 覆盖、不可重开(非法转移显式抛 `InvalidTransition`)。
+
+全部为纯函数:不读 DB、不可变、可单测。写库由 `foreshadow_repo` 落地(只 flush)。
+枚举值与 DB `foreshadow.status` 文本严格一致(`StrEnum` → 直接当字符串用)。
+"""
+
+from __future__ import annotations
+
+from enum import StrEnum
+
+
+class ForeshadowStatus(StrEnum):
+ """伏笔状态——值与 DB `status` 文本一致。"""
+
+ OPEN = "OPEN"
+ PARTIAL = "PARTIAL"
+ CLOSED = "CLOSED"
+ OVERDUE = "OVERDUE"
+
+
+# 便捷常量(供 repo / 测试 / 调用方按名引用,避免散落字面量)。
+OPEN = ForeshadowStatus.OPEN
+PARTIAL = ForeshadowStatus.PARTIAL
+CLOSED = ForeshadowStatus.CLOSED
+OVERDUE = ForeshadowStatus.OVERDUE
+
+
+class InvalidTransition(ValueError):
+ """非法状态转移(如 CLOSED 重开)。"""
+
+
+# 合法转移图:from → 允许到达的状态集合(不含同态幂等,单独放行)。
+_ALLOWED: dict[ForeshadowStatus, frozenset[ForeshadowStatus]] = {
+ OPEN: frozenset({PARTIAL, CLOSED, OVERDUE}),
+ PARTIAL: frozenset({CLOSED, OVERDUE}),
+ OVERDUE: frozenset({PARTIAL, CLOSED}), # 逾期后仍可(部分)回收
+ CLOSED: frozenset(), # 终态:不可再转移
+}
+
+
+def transition(current: ForeshadowStatus, to_status: ForeshadowStatus) -> ForeshadowStatus:
+ """应用状态机校验后返回新状态(纯函数)。
+
+ - 同态(current == to_status)幂等放行。
+ - CLOSED 为终态:任何离开 CLOSED 的转移都非法。
+ - 其余按 `_ALLOWED` 校验;非法 → `InvalidTransition`。
+ """
+ if current == to_status:
+ return to_status
+ if to_status not in _ALLOWED[current]:
+ raise InvalidTransition(f"非法伏笔转移: {current.value} → {to_status.value}")
+ return to_status
+
+
+def is_overdue(
+ *,
+ current_chapter: int,
+ expected_close_to: int | None,
+ status: ForeshadowStatus,
+) -> bool:
+ """到期判据(M3-d,§6.2):`current_chapter > expected_close_to AND status≠CLOSED`。
+
+ - 无 `expected_close_to`(未设回收窗口)→ 永不逾期。
+ - CLOSED → 永不逾期(已回收)。
+ - `current_chapter == expected_close_to` → 仍在窗口内,未逾期(严格大于才算)。
+ """
+ if expected_close_to is None:
+ return False
+ if status == CLOSED:
+ return False
+ return current_chapter > expected_close_to
+
+
+def apply_overdue_scan(
+ status: ForeshadowStatus,
+ *,
+ current_chapter: int,
+ expected_close_to: int | None,
+) -> ForeshadowStatus:
+ """到期扫描应用(验收后触发,§6.2):命中判据 → 置 `OVERDUE`,否则保持原状态。
+
+ 幂等:已 OVERDUE 且仍逾期 → 保持 OVERDUE;CLOSED 永不被改写(is_overdue 已排除)。
+ """
+ if is_overdue(
+ current_chapter=current_chapter,
+ expected_close_to=expected_close_to,
+ status=status,
+ ):
+ return OVERDUE
+ return status
diff --git a/packages/core/ww_core/domain/outline_write_repo.py b/packages/core/ww_core/domain/outline_write_repo.py
new file mode 100644
index 0000000..8db5f57
--- /dev/null
+++ b/packages/core/ww_core/domain/outline_write_repo.py
@@ -0,0 +1,109 @@
+"""大纲**写侧** Repository(逐章 upsert;ARCH §5.4 outliner writes=outline / §7.2 POST /outline)。
+
+读侧(`get`,供 assemble 注入本章 beats)已在 `ww_core.memory.sql_repositories.SqlOutlineRepo`
++ `domain.repositories.OutlineRepo`/`OutlineView` 提供(C5 稳定,不动)。本模块加**写**能力,
+命名加 `Write` 前缀避免与读侧 `OutlineRepo` 同名歧义(同 `DigestAppendRepo`/`ForeshadowLedgerRepo`
+区别于读侧的先例,见 memory/decisions)。
+
+**提交边界**:`upsert_chapter` 只 `flush()` 不 `commit()`——提交交端点事务(T3.5 端点末尾
+一次 `commit()`,与网关 ledger 一并落库;与 M2 验收-side repos 一致,见 memory/gotchas)。
+
+唯一约束 `(project_id, chapter_no)`:用**显式 read-modify-write**(按 chapter_no 查→有则改、
+无则插),不用 PG `ON CONFLICT`(保持与项目其它 upsert 路径一致、可纯 fake 单测,不引 pg 依赖)。
+
+**beats 存储形**:DB 列 `outline.beats` 是 JSONB `dict`,而 outliner 产 `list[str]`——
+故包裹成 `{"beats": [...]}` 落库,读侧 `OutlineView.beats` 取到同形 dict(round-trip 一致)。
+`foreshadow_windows` 是 JSONB list,直接落 `[w.model_dump() for w in chapter.foreshadow_windows]`。
+"""
+
+from __future__ import annotations
+
+import uuid
+from typing import Any, Protocol
+
+from pydantic import BaseModel, Field
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+from ww_db.models import Outline
+
+
+class OutlineWriteView(BaseModel):
+ """单章大纲写入后的只读快照(snake_case,frozen)。"""
+
+ model_config = {"frozen": True}
+
+ chapter_no: int
+ volume: int
+ beats: list[str] = Field(default_factory=list)
+ foreshadow_windows: list[dict[str, Any]] = Field(default_factory=list)
+
+
+class OutlineWriteRepo(Protocol):
+ """大纲写侧接口(按 project_id 隔离;唯一 `(project_id, chapter_no)`;只 flush 不 commit)。"""
+
+ async def upsert_chapter(
+ self,
+ project_id: uuid.UUID,
+ *,
+ volume: int,
+ chapter_no: int,
+ beats: list[str],
+ foreshadow_windows: list[dict[str, Any]],
+ ) -> OutlineWriteView:
+ """逐章 upsert:同 `(project_id, chapter_no)` 覆盖,否则插入。只 flush。"""
+ ...
+
+
+def _to_view(row: Outline) -> OutlineWriteView:
+ raw = row.beats or {}
+ beats = list(raw.get("beats", [])) if isinstance(raw, dict) else []
+ return OutlineWriteView(
+ chapter_no=row.chapter_no,
+ volume=row.volume,
+ beats=beats,
+ foreshadow_windows=list(row.foreshadow_windows or []),
+ )
+
+
+class SqlOutlineWriteRepo:
+ """SQLAlchemy 实现:显式 read-modify-write upsert(只 flush 不 commit)。"""
+
+ def __init__(self, session: AsyncSession) -> None:
+ self._s = session
+
+ async def upsert_chapter(
+ self,
+ project_id: uuid.UUID,
+ *,
+ volume: int,
+ chapter_no: int,
+ beats: list[str],
+ foreshadow_windows: list[dict[str, Any]],
+ ) -> OutlineWriteView:
+ row = (
+ await self._s.execute(
+ select(Outline).where(
+ Outline.project_id == project_id,
+ Outline.chapter_no == chapter_no,
+ )
+ )
+ ).scalar_one_or_none()
+ # JSONB 写入建新对象重赋值(避免原地 mutate 不被 ORM 侦测,见 gotcha)。
+ beats_payload = {"beats": list(beats)}
+ windows_payload = [dict(w) for w in foreshadow_windows]
+ if row is None:
+ row = Outline(
+ project_id=project_id,
+ volume=volume,
+ chapter_no=chapter_no,
+ beats=beats_payload,
+ foreshadow_windows=windows_payload,
+ )
+ self._s.add(row)
+ else:
+ row.volume = volume
+ row.beats = beats_payload
+ row.foreshadow_windows = windows_payload
+ await self._s.flush()
+ await self._s.refresh(row)
+ return _to_view(row)
diff --git a/packages/core/ww_core/orchestrator/__init__.py b/packages/core/ww_core/orchestrator/__init__.py
index 16c4914..dad3a25 100644
--- a/packages/core/ww_core/orchestrator/__init__.py
+++ b/packages/core/ww_core/orchestrator/__init__.py
@@ -10,9 +10,13 @@ from __future__ import annotations
from .collect import (
CONTINUITY,
+ FORESHADOW,
+ PACE,
ReviewRecorder,
collect_reviews,
extract_conflicts,
+ extract_foreshadow_sug,
+ extract_pace,
)
from .graph import (
COLLECT_NODE,
@@ -22,6 +26,7 @@ from .graph import (
build_write_graph,
setup_checkpointer,
)
+from .outline_node import build_outline_request, run_outline
from .review_node import (
REVIEW_INCOMPLETE,
REVIEW_OK,
@@ -36,6 +41,8 @@ from .sse import (
EVENT_CONFLICT,
EVENT_DONE,
EVENT_ERROR,
+ EVENT_FORESHADOW,
+ EVENT_PACE,
EVENT_SECTION,
EVENT_TOKEN,
SECTION_DONE,
@@ -45,8 +52,10 @@ from .sse import (
conflict_event,
done_event,
error_event,
+ foreshadow_event,
normalize_deltas,
normalize_review,
+ pace_event,
section_event,
token_event,
)
@@ -59,8 +68,12 @@ __all__ = [
"EVENT_CONFLICT",
"EVENT_DONE",
"EVENT_ERROR",
+ "EVENT_FORESHADOW",
+ "EVENT_PACE",
"EVENT_SECTION",
"EVENT_TOKEN",
+ "FORESHADOW",
+ "PACE",
"REVIEW_INCOMPLETE",
"REVIEW_OK",
"REVIEW_SPECS",
@@ -74,6 +87,7 @@ __all__ = [
"GatewayStream",
"ReviewRecorder",
"SseEvent",
+ "build_outline_request",
"build_review_context",
"build_review_graph",
"build_review_request",
@@ -84,10 +98,15 @@ __all__ = [
"done_event",
"error_event",
"extract_conflicts",
+ "extract_foreshadow_sug",
+ "extract_pace",
+ "foreshadow_event",
"make_review_node",
"merge_reviews",
"normalize_deltas",
"normalize_review",
+ "pace_event",
+ "run_outline",
"run_review",
"section_event",
"setup_checkpointer",
diff --git a/packages/core/ww_core/orchestrator/collect.py b/packages/core/ww_core/orchestrator/collect.py
index 1d334c5..8141cad 100644
--- a/packages/core/ww_core/orchestrator/collect.py
+++ b/packages/core/ww_core/orchestrator/collect.py
@@ -1,17 +1,21 @@
"""collect 节点(C4 扩 / ARCH §5.2 collect 行)——汇总并行审 → 落 `chapter_reviews` 留痕。
-并行四审各把 `{spec.name: {status, result}}` 合并进 `state["reviews"]`(reducer,见 state.py);
-collect 把它们汇总,调 `review_repo.record(...)` append 一行 `chapter_reviews`(真相源留痕)。
+并行审各把 `{spec.name: {status, result}}` 合并进 `state["reviews"]`(reducer,见 state.py);
+collect 把它们汇总,**一次** `review_repo.record(...)` append 一行 `chapter_reviews`(真相源留痕),
+按 spec.name 把各审结果映射到对应列。
不变量 #3:审稿节点只读不写库;**唯一写入点是这里经 `review_repo`**(仍是「留痕」非「生效」,
-AI 产出真正入库经 T2.4 验收事务的裁决)。
+AI 产出真正入库经验收事务的裁决)。
记账边界(gotcha「网关 ledger 只 flush」):审稿经网关产 usage(网关内 `flush` 进同一 session);
-collect 与 review_repo 同样**只 flush 不 commit**——`commit` 归 HTTP 端点(T2.5)/验收事务(T2.4),
-与 draft 端点一致(端点在流耗尽后 `await session.commit()`)。本节点绝不 commit。
+collect 与 review_repo 同样**只 flush 不 commit**——`commit` 归 HTTP 端点 / 验收事务。
+本节点绝不 commit。
-M2 只接 continuity:从 `reviews["continuity"].result.conflicts` 抽冲突落 `conflicts` 列;
-foreshadow/style/pace 列留空(M3/M4 接入后填)。设计成按 spec 名取分项,便于扩展。
+M3 三审齐 → 列映射(一次 record 落齐本章三审):
+- continuity → `conflicts`(冲突清单 list);
+- foreshadow → `foreshadow_sug`(planted/resolved 扁平为建议 list,每条带 `kind` 标记);
+- pace → `pace`(节奏诊断 dict:water/hook/beat_map)。
+style 第四审留 M4。任一审 `incomplete`(网关失败隔离,§5.2)→ 该列留空/None,不阻塞其余。
"""
from __future__ import annotations
@@ -27,6 +31,12 @@ from .state import ChapterState
log = structlog.get_logger(__name__)
CONTINUITY = "continuity"
+FORESHADOW = "foreshadow"
+PACE = "pace"
+
+# foreshadow 建议扁平化时的来源标记(planted=新埋 / resolved=回收)。
+FORESHADOW_KIND_PLANTED = "planted"
+FORESHADOW_KIND_RESOLVED = "resolved"
class ReviewRecorder(Protocol):
@@ -49,17 +59,53 @@ class ReviewRecorder(Protocol):
) -> Any: ...
+def _ok_result(reviews: dict[str, Any], name: str) -> dict[str, Any] | None:
+ """取某审的结果 dict,仅当 `ok` 且有结果;否则 None(incomplete/缺席)。"""
+ entry = reviews.get(name)
+ if not entry or entry.get("status") != REVIEW_OK:
+ return None
+ result = entry.get("result")
+ return result if isinstance(result, dict) else None
+
+
def extract_conflicts(reviews: dict[str, Any]) -> list[dict[str, Any]]:
"""从 continuity 分项抽冲突清单(纯函数)。
仅当该审 `ok` 且有结果时取其 `conflicts`;未完成/缺席 → 空列表(不臆造)。
"""
- entry = reviews.get(CONTINUITY)
- if not entry or entry.get("status") != REVIEW_OK:
+ result = _ok_result(reviews, CONTINUITY)
+ if result is None:
return []
- result = entry.get("result") or {}
- conflicts = result.get("conflicts") or []
- return [dict(c) for c in conflicts]
+ return [dict(c) for c in result.get("conflicts") or []]
+
+
+def extract_foreshadow_sug(reviews: dict[str, Any]) -> list[dict[str, Any]]:
+ """从 foreshadow 分项扁平化建议清单(纯函数)。
+
+ `ForeshadowReview{planted,resolved}` → 单个 list,每条加 `kind` 标记来源
+ (`foreshadow_sug` 列是 JSONB list)。未完成/缺席 → 空列表。
+ """
+ result = _ok_result(reviews, FORESHADOW)
+ if result is None:
+ return []
+ out: list[dict[str, Any]] = []
+ for sug in result.get("planted") or []:
+ out.append({**dict(sug), "kind": FORESHADOW_KIND_PLANTED})
+ for sug in result.get("resolved") or []:
+ out.append({**dict(sug), "kind": FORESHADOW_KIND_RESOLVED})
+ return out
+
+
+def extract_pace(reviews: dict[str, Any]) -> dict[str, Any] | None:
+ """从 pace 分项取节奏诊断 dict(纯函数)。
+
+ `PaceReview{water,hook,beat_map}` 整体入 `pace` 列(JSONB dict)。
+ 未完成/缺席 → None(列留空,§5.2 不阻塞其余)。
+ """
+ result = _ok_result(reviews, PACE)
+ if result is None:
+ return None
+ return dict(result)
async def collect_reviews(
@@ -67,25 +113,32 @@ async def collect_reviews(
*,
review_repo: ReviewRecorder,
) -> dict[str, Any]:
- """collect 节点:汇总并行审 → 抽冲突 → `review_repo.record` 留痕(只 flush 不 commit)。
+ """collect 节点:汇总并行审 → 各列映射 → 一次 `review_repo.record` 留痕(只 flush 不 commit)。
直接调用本函数(注入内存 fake repo)即可单测,无需图运行时。
- `chapter_version=None`:留痕行不绑具体 version(T2.4 验收时再绑/裁决,R3/R4)。
+ `chapter_version=None`:留痕行不绑具体 version(验收时再绑/裁决,R3/R4)。
+ 某审 incomplete → 该列空/None,不阻塞其余审落库(§5.2)。
返回空增量字典——审稿真相已在表,state 不作真相源(不变量 #5)。
"""
reviews = state.get("reviews") or {}
conflicts = extract_conflicts(reviews)
+ foreshadow_sug = extract_foreshadow_sug(reviews)
+ pace = extract_pace(reviews)
await review_repo.record(
state["project_id"],
state["chapter_no"],
chapter_version=None,
conflicts=conflicts,
+ foreshadow_sug=foreshadow_sug,
+ pace=pace,
)
log.info(
"collect_reviews_recorded",
project_id=str(state["project_id"]),
chapter_no=state["chapter_no"],
conflict_count=len(conflicts),
+ foreshadow_sug_count=len(foreshadow_sug),
+ has_pace=pace is not None,
reviews=sorted(reviews.keys()),
)
# 留痕已在表(真相源);不在节点 commit(端点/事务层负责,见模块 docstring)。
diff --git a/packages/core/ww_core/orchestrator/graph.py b/packages/core/ww_core/orchestrator/graph.py
index c5f3753..4df18fd 100644
--- a/packages/core/ww_core/orchestrator/graph.py
+++ b/packages/core/ww_core/orchestrator/graph.py
@@ -15,7 +15,12 @@ from collections.abc import Sequence
from typing import TYPE_CHECKING, Any
from langgraph.graph import END, START, StateGraph
-from ww_agents import AgentSpec, continuity_spec
+from ww_agents import (
+ AgentSpec,
+ continuity_spec,
+ foreshadow_spec,
+ pace_spec,
+)
from .collect import ReviewRecorder, collect_reviews
from .review_node import GatewayRun, run_review
@@ -51,7 +56,8 @@ def build_write_graph(
return builder.compile(checkpointer=checkpointer)
-REVIEW_SPECS: tuple[AgentSpec, ...] = (continuity_spec,)
+# M3:三审齐(continuity + foreshadow + pace)。文风第四审属 M4/T4.2。
+REVIEW_SPECS: tuple[AgentSpec, ...] = (continuity_spec, foreshadow_spec, pace_spec)
def build_review_graph(
@@ -63,9 +69,10 @@ def build_review_graph(
) -> CompiledStateGraph[ChapterState, None, ChapterState, ChapterState]:
"""构建并编译审稿子图:`START →`(各 review spec 并行节点)`→ collect → END`。
- M2 默认仅 continuity;`review_specs` 可扩(M3/M4 加 foreshadow/style/pace 三审),
- 图按这组 spec 循环加并行分支,全部汇入 collect(任一审失败不阻塞,§5.2——失败隔离
- 在 `run_review` 内、标 incomplete 占位)。
+ M3 默认三审齐(continuity + foreshadow + pace);`review_specs` 仍可扩(M4 加 style
+ 第四审),图按这组 spec 循环加并行分支,全部汇入 collect(任一审失败不阻塞,§5.2——
+ 失败隔离在 `run_review` 内、标 incomplete 占位)。`run_review`/`make_review_node` 对 spec
+ 泛型,三审天然支持(按 `spec.output_schema` 产 parsed,无 spec-specific 假设)。
`gateway` / `review_repo` 经闭包绑进节点(langgraph 不收 functools.partial,见 gotcha)。
**accept 不在本图**:本任务只交付到 collect(审稿留痕落 `chapter_reviews`)+ review SSE;
diff --git a/packages/core/ww_core/orchestrator/outline_node.py b/packages/core/ww_core/orchestrator/outline_node.py
new file mode 100644
index 0000000..fecfb31
--- /dev/null
+++ b/packages/core/ww_core/orchestrator/outline_node.py
@@ -0,0 +1,81 @@
+"""大纲节点(C6 扩 / ARCH §5.4 outliner 行 / §6.2 伏笔窗口)。
+
+大纲是**独立生成**——不在写章/审稿流水线(不接进 review 图)。端点(T3.5)直接调
+`run_outline` 拿结构化 `OutlineResult`,再持久化到 `outline` 表(含 foreshadow_windows)。
+
+确定性边界(CLAUDE.md「LangGraph」纪律):
+- 节点逻辑里**无 LLM 非确定性**——不确定性藏在网关后;节点只做
+ `AgentSpec + context → LlmRequest` 的纯构造 + 转发 `Gateway.run` 的 `parsed`。
+- 因此注入 mock 网关(产 `parsed`)即可单测,无需图运行时、无需真 Postgres。
+
+不变量 #2:agent 只声明 `tier`,绝不传具体 model(spec.tier 透传)。
+不变量 #3:节点**只读、不写库**——结构化大纲返回调用方,落库经端点/验收(T3.5)。
+不变量 #9:`spec.system_prompt` 进缓存断点前块(cache=True);注入材料进 `input`(断点后)。
+"""
+
+from __future__ import annotations
+
+import uuid
+from typing import Protocol
+
+import structlog
+from ww_agents import AgentSpec, OutlineResult
+from ww_llm_gateway.types import Block, LlmRequest, LlmResponse, Scope
+
+log = structlog.get_logger(__name__)
+
+
+class GatewayRun(Protocol):
+ """大纲节点对网关的最小依赖——只需 `run`(注入真网关或 mock)。"""
+
+ async def run(self, req: LlmRequest) -> LlmResponse: ...
+
+
+def build_outline_request(
+ spec: AgentSpec,
+ *,
+ context: str,
+ user_id: uuid.UUID,
+ project_id: uuid.UUID,
+) -> LlmRequest:
+ """据 spec + 注入材料构造大纲请求(纯函数)。
+
+ `spec.system_prompt` → `system` 缓存断点前块(cache=True,不变量 #9);
+ `context`(设定/伏笔/人物/世界观的序列化文本)→ `input`(断点后)。
+ 排大纲用 `run()` 非 `stream()`(要结构化产物,非流式正文)。
+ """
+ return LlmRequest(
+ tier=spec.tier, # 不变量 #2:只传档位,不传 model
+ system=[Block(text=spec.system_prompt, cache=True)],
+ input=context,
+ output_schema=spec.output_schema,
+ scope=Scope(user_id=user_id, project_id=project_id),
+ )
+
+
+async def run_outline(
+ spec: AgentSpec,
+ *,
+ context: str,
+ gateway: GatewayRun,
+ user_id: uuid.UUID,
+ project_id: uuid.UUID,
+) -> OutlineResult:
+ """跑大纲生成:构请求 → `gateway.run` → 返回结构化 `OutlineResult`。
+
+ 裸函数(显式 gateway 关键字),注入 mock 网关即可单测,无需图运行时。
+ 大纲是独立生成(非并行审):网关失败直接上抛(端点/T3.5 处理),不静默吞。
+ **只产结构化大纲、不写库**(持久化属 T3.5,不变量 #3)。
+ """
+ req = build_outline_request(spec, context=context, user_id=user_id, project_id=project_id)
+ resp = await gateway.run(req)
+ parsed = resp.parsed
+ if not isinstance(parsed, OutlineResult):
+ # 带 output_schema 时 parsed 必非 None(C1);违约则明确报错而非返回空大纲。
+ log.error(
+ "outline_node_missing_parsed",
+ project_id=str(project_id),
+ parsed_type=type(parsed).__name__,
+ )
+ raise ValueError("gateway returned no parsed OutlineResult for outline request")
+ return parsed
diff --git a/packages/core/ww_core/orchestrator/sse.py b/packages/core/ww_core/orchestrator/sse.py
index 374ccac..4bd619a 100644
--- a/packages/core/ww_core/orchestrator/sse.py
+++ b/packages/core/ww_core/orchestrator/sse.py
@@ -18,6 +18,7 @@ from pydantic import BaseModel
from ww_llm_gateway.types import Delta
from ww_shared import AppError, ErrorCode
+from .collect import CONTINUITY, FORESHADOW, PACE
from .review_node import REVIEW_OK
log = structlog.get_logger(__name__)
@@ -25,7 +26,9 @@ log = structlog.get_logger(__name__)
# §7.3 事件名(全集)。完整清单以 ARCHITECTURE §7.3 为准。
EVENT_TOKEN = "token" # 正文增量(写章 draft)
EVENT_SECTION = "section" # 四审分项开始/完成(审稿 review)
-EVENT_CONFLICT = "conflict" # 冲突命中(审稿 review)
+EVENT_CONFLICT = "conflict" # continuity 冲突命中(审稿 review)
+EVENT_FORESHADOW = "foreshadow" # foreshadow 建议命中(新埋/回收,审稿 review)
+EVENT_PACE = "pace" # pace 节奏诊断(注水/钩子/节拍图,审稿 review)
EVENT_DONE = "done"
EVENT_ERROR = "error"
@@ -72,6 +75,35 @@ def conflict_event(
)
+def foreshadow_event(
+ *,
+ kind: str,
+ code: str | None,
+ title: str,
+ where: str | None,
+ note: str | None,
+) -> SseEvent:
+ """伏笔建议事件(审稿 review)——前端伏笔看板联动。
+
+ `kind` ∈ planted(新埋)/ resolved(回收);作者验收裁决后才登记/改状态(只读建议)。
+ """
+ return SseEvent(
+ event=EVENT_FORESHADOW,
+ data={"kind": kind, "code": code, "title": title, "where": where, "note": note},
+ )
+
+
+def pace_event(*, water: list[dict[str, object]], hook: bool, beat_map: list[int]) -> SseEvent:
+ """节奏诊断事件(审稿 review)——前端节奏报告/节拍图 ▁▃▅ 可视化。
+
+ 一次审产一条:注水段清单 + 章末钩子有无 + 逐段节拍强度序列。
+ """
+ return SseEvent(
+ event=EVENT_PACE,
+ data={"water": water, "hook": hook, "beat_map": beat_map},
+ )
+
+
def error_event(*, code: str, message: str, request_id: str | None = None) -> SseEvent:
"""错误事件,形对齐错误信封 §7.1(`code`/`message`),附 `request_id` 便于贯通排查。"""
return SseEvent(
@@ -122,40 +154,79 @@ async def normalize_deltas(
yield done_event(length=total)
+def _section_result_events(name: str, result: dict[str, Any]) -> list[SseEvent]:
+ """把某审 `result` dict 按审种 surface 为结构化事件(纯函数,便于单测)。
+
+ 畸形 result(非 dict 等)由调用方 try/except 兜成 `error` 事件;这里只按已知形读。
+ """
+ events: list[SseEvent] = []
+ if name == CONTINUITY:
+ for conflict in result.get("conflicts") or []:
+ events.append(
+ conflict_event(
+ type=conflict.get("type", ""),
+ where=conflict.get("where", ""),
+ refs=list(conflict.get("refs") or []),
+ suggestion=conflict.get("suggestion", ""),
+ )
+ )
+ elif name == FORESHADOW:
+ for kind in ("planted", "resolved"):
+ for sug in result.get(kind) or []:
+ events.append(
+ foreshadow_event(
+ kind=kind,
+ code=sug.get("code"),
+ title=sug.get("title", ""),
+ where=sug.get("where"),
+ note=sug.get("note"),
+ )
+ )
+ elif name == PACE:
+ events.append(
+ pace_event(
+ water=[dict(w) for w in result.get("water") or []],
+ hook=bool(result.get("hook", False)),
+ beat_map=[int(b) for b in result.get("beat_map") or []],
+ )
+ )
+ return events
+
+
async def normalize_review(
reviews: Mapping[str, Any],
*,
request_id: str | None = None,
) -> AsyncIterator[SseEvent]:
- """把审稿子图的结构化产出(`state["reviews"]`)归一为 SSE 事件序列。
+ """把审稿子图的结构化产出(`state["reviews"]`)归一为 SSE 事件序列(M3 三审齐)。
- 供 **T2.5** 的 `POST .../review` 端点消费:跑 `build_review_graph` 后拿 `final["reviews"]`
- (形 `{name: {status, result}}`),本函数产 `section`(每审 done/incomplete)+ `conflict`
- (命中冲突,形对齐 C6 `Conflict`)+ `done`。HTTP event-stream 编码归 T2.5,不在此处。
+ 供 `POST .../review` 端点消费:跑 `build_review_graph` 后拿 `final["reviews"]`
+ (形 `{name: {status, result}}`),本函数每审先发 `section`(done/incomplete),再按审种
+ surface 结构化结果:
+ - continuity → 每冲突一条 `conflict`(形对齐 C6 `Conflict`);
+ - foreshadow → 每条建议一条 `foreshadow`(planted/resolved,前端看板联动);
+ - pace → 一条 `pace`(注水/钩子/节拍图,前端节奏报告/▁▃▅)。
+ 末尾 `done{length=审项数}`。HTTP event-stream 编码归端点,不在此处。
错误处理纪律(同 `normalize_deltas`):任何意外 → 一条 `error` 事件后正常收尾,不上抛。
- 某审 `incomplete`(网关失败被隔离,§5.2)→ 发 `section{status:incomplete}`,不发其冲突。
- `reviews` 键按字典序遍历 → 确定性事件顺序(便于单测/前端稳定渲染)。
+ 某审 `incomplete`(网关失败被隔离,§5.2)→ 仅发 `section{status:incomplete}`,不发其结果。
+ `reviews` 键按字典序遍历 → 确定性事件顺序(便于单测/前端稳定渲染)。向后兼容:
+ 既有 `section`/`conflict` 形不变,仅新增 `foreshadow`/`pace`。
"""
try:
section_count = 0
for name in sorted(reviews.keys()):
entry = reviews.get(name) or {}
status = entry.get("status")
- if status == REVIEW_OK:
- yield section_event(name=name, status=SECTION_DONE)
- section_count += 1
- result = entry.get("result") or {}
- for conflict in result.get("conflicts") or []:
- yield conflict_event(
- type=conflict.get("type", ""),
- where=conflict.get("where", ""),
- refs=list(conflict.get("refs") or []),
- suggestion=conflict.get("suggestion", ""),
- )
- else:
+ if status != REVIEW_OK:
yield section_event(name=name, status=SECTION_INCOMPLETE)
section_count += 1
+ continue
+ yield section_event(name=name, status=SECTION_DONE)
+ section_count += 1
+ result = entry.get("result") or {}
+ for event in _section_result_events(name, result):
+ yield event
except Exception as exc: # noqa: BLE001 — 边界兜底:任何意外都归一为 error 事件,不泄异常
log.error("sse_review_unexpected_error", error=str(exc), request_id=request_id)
yield error_event(
diff --git a/packages/llm_gateway/tests/test_ledger_concurrency.py b/packages/llm_gateway/tests/test_ledger_concurrency.py
new file mode 100644
index 0000000..d49bb4f
--- /dev/null
+++ b/packages/llm_gateway/tests/test_ledger_concurrency.py
@@ -0,0 +1,112 @@
+"""T3.8 回归:并行审记账并发安全(add-only,无 flush 重入)。
+
+旧 bug(T3.7 真 pg E2E 暴露):三审作为同一 LangGraph superstep 的并行分支,各自
+`gateway.run()` → 共用**同一请求 session** 的 `SqlAlchemyLedgerSink.record`
+(`session.add` + `await session.flush()`)。`AsyncSession` 非并发安全:`await flush()`
+是并行路径里唯一让出控制权的 DB-IO,第二/三审的 flush 撞上「Session is already
+flushing」(flush 重入)→ 被 `run_review` 失败隔离吞成 `incomplete` → foreshadow/pace
+结果静默丢失。
+
+本测试用一个会**侦测 flush 重入**的假 AsyncSession:`flush` 异步(`await asyncio.sleep`
+让出控制权),若两协程同时在 flush 中即抛 `RuntimeError("Session is already flushing")`
+——精确复刻 SQLAlchemy 的报错。在旧 add-then-flush 实现下,`asyncio.gather` 三并发
+`record` 会触发该重入;修复后 `record` 只 `add`(同步、不让出),不再调 flush,故三行
+全部入 session 且零重入。
+"""
+
+from __future__ import annotations
+
+import asyncio
+import uuid
+
+import pytest
+from ww_llm_gateway.ledger import SqlAlchemyLedgerSink
+from ww_llm_gateway.types import Scope, Usage
+
+
+class _FlushReentryDetectingSession:
+ """假 AsyncSession:记录 `add` 的行;`flush` 异步且侦测并发重入。
+
+ `add` 同步追加(镜像真 session)。`flush` 进入时若已有协程在 flush 中 → 抛
+ `RuntimeError("Session is already flushing")`(复刻 SQLAlchemy 行为),否则
+ `await asyncio.sleep(0)` 让出控制权模拟真实 DB-IO 让步点。
+ """
+
+ def __init__(self) -> None:
+ self.added: list[object] = []
+ self.flush_calls = 0
+ self._flushing = False
+
+ def add(self, row: object) -> None:
+ self.added.append(row)
+
+ async def flush(self) -> None:
+ self.flush_calls += 1
+ if self._flushing:
+ raise RuntimeError("Session is already flushing")
+ self._flushing = True
+ try:
+ await asyncio.sleep(0) # 让出控制权——并行协程在此交错
+ finally:
+ self._flushing = False
+
+
+def _usage(input_tokens: int) -> Usage:
+ return Usage(
+ provider="deepseek",
+ model="deepseek-chat",
+ input_tokens=input_tokens,
+ output_tokens=1,
+ cost_minor=0,
+ currency="USD",
+ )
+
+
+def _scope() -> Scope:
+ return Scope(user_id=uuid.UUID(int=1), project_id=uuid.UUID(int=2))
+
+
+async def test_concurrent_record_adds_all_rows_without_flush_reentry() -> None:
+ """三并发 `record`(模拟三审并行)→ 三行全部入 session,绝不抛 flush 重入。
+
+ 旧 add-then-flush 实现在此会因 `await flush()` 让出控制权而 flush 重入抛错;
+ add-only 实现下 `record` 不让出控制权,三行确定性入 session。
+ """
+ session = _FlushReentryDetectingSession()
+ sink = SqlAlchemyLedgerSink(session) # type: ignore[arg-type]
+
+ await asyncio.gather(
+ sink.record(_scope(), _usage(100)),
+ sink.record(_scope(), _usage(200)),
+ sink.record(_scope(), _usage(300)),
+ )
+
+ # 三审用量全部入 session(一次调用一行,记账不丢)
+ assert len(session.added) == 3
+ input_tokens = sorted(row.input_tokens for row in session.added) # type: ignore[attr-defined]
+ assert input_tokens == [100, 200, 300]
+ # add-only:sink 不在并行路径 flush(持久化靠端点/事务 commit)
+ assert session.flush_calls == 0
+
+
+async def test_record_does_not_flush_so_caller_commit_persists() -> None:
+ """单次 `record` 只 add 不 flush——契约「网关 ledger 只 add、调用方负责 commit」。"""
+ session = _FlushReentryDetectingSession()
+ sink = SqlAlchemyLedgerSink(session) # type: ignore[arg-type]
+
+ await sink.record(_scope(), _usage(42))
+
+ assert len(session.added) == 1
+ assert session.flush_calls == 0
+
+
+@pytest.mark.parametrize("n_concurrent", [2, 5, 10])
+async def test_concurrent_record_scales_without_loss(n_concurrent: int) -> None:
+ """N 并发 `record` 全部落 session,无丢失、无 flush 重入(不论并发度)。"""
+ session = _FlushReentryDetectingSession()
+ sink = SqlAlchemyLedgerSink(session) # type: ignore[arg-type]
+
+ await asyncio.gather(*(sink.record(_scope(), _usage(i + 1)) for i in range(n_concurrent)))
+
+ assert len(session.added) == n_concurrent
+ assert session.flush_calls == 0
diff --git a/packages/llm_gateway/ww_llm_gateway/ledger.py b/packages/llm_gateway/ww_llm_gateway/ledger.py
index 1e03530..8cae3de 100644
--- a/packages/llm_gateway/ww_llm_gateway/ledger.py
+++ b/packages/llm_gateway/ww_llm_gateway/ledger.py
@@ -24,6 +24,19 @@ class SqlAlchemyLedgerSink:
self._session = session
async def record(self, scope: Scope, usage: Usage) -> None:
+ """把一次调用追加进 session(**只 add,不 flush/commit**)。
+
+ 并发安全(T3.8 bugfix):三审作为同一 LangGraph superstep 的并行分支
+ 共用一个请求 session 的本 sink。`AsyncSession` 非并发安全——若在此
+ `await self._session.flush()`,第二/三审的 flush 会撞上「Session is already
+ flushing」(flush 重入),被 `run_review` 失败隔离吞成 `incomplete`,
+ foreshadow/pace 结果静默丢失。`session.add()` 是同步、无 await、不让出
+ 控制权,故并行协程不会在 add 中途交错。
+
+ 持久化靠调用方:每个端点/验收事务在流耗尽/事务末尾 `commit()`(commit
+ 自动 flush 全部待决行),与「网关 ledger 只 add/flush、端点或事务负责
+ commit」的既有边界一致(见 gotcha「网关 ledger 只 flush、调用方必须 commit」)。
+ """
row = UsageLedger(
owner_id=scope.user_id,
project_id=scope.project_id,
@@ -36,4 +49,3 @@ class SqlAlchemyLedgerSink:
currency=usage.currency,
)
self._session.add(row)
- await self._session.flush()
diff --git a/tests/test_m3_e2e.py b/tests/test_m3_e2e.py
new file mode 100644
index 0000000..4c9bb3f
--- /dev/null
+++ b/tests/test_m3_e2e.py
@@ -0,0 +1,584 @@
+"""M3 端到端:伏笔账本/到期提醒 + 大纲(含回收窗口) + 三审(真实 DB,零 token)。
+
+证明 M3 闭环(不变量 #2/#3/#4/#5):
+ 用例 1 · 伏笔 埋设→进展→逾期提醒(验收后扫描)
+ `POST /projects` → `POST .../foreshadow`(埋 F1, expected_close_to=2)
+ → `PATCH .../foreshadow/F1`(追加一条进展) → `PUT .../chapters/5/draft`
+ → `POST .../chapters/5/accept`(零冲突直通) → **验收后 BackgroundTask 到期扫描**置
+ `current_ch(5) > expected_close_to(2) AND status≠CLOSED → OVERDUE` →
+ `GET .../foreshadow?status=OVERDUE` 含 F1 + DB 真源断言(F1.status==OVERDUE、progress 含那条)。
+ 对照:未越界伏笔 F2(expected_close_to=9) 不应 OVERDUE。
+ 用例 2 · 排大纲含伏笔回收窗口
+ `POST .../outline` → analyst 假适配器产 `OutlineResult` → DB `outline` 表逐章行存在
+ (`beats` 形 `{"beats":[...]}`、`foreshadow_windows` JSONB list 含窗口);**再次 POST 幂等
+ 覆盖**(唯一 `(project_id,chapter_no)` 行数不变);`usage_ledger` 含 analyst 那次记账。
+ 用例 3 · 三审 SSE(continuity/foreshadow/pace 三审齐 + 留痕列)
+ `POST .../chapters/N/review` → 解析帧含三审 section + ≥1 `foreshadow`/`pace` 事件;
+ `GET .../reviews` 留痕行 `foreshadow_sug`(list)/`pace`(dict) 列已填。
+
+确定性 & 零成本(同 M1/M2):真实 `Gateway` + 多档位假适配器(据 `req.output_schema`
+分支返回固定 `parsed`,绝不联网)+ 真实 `SqlAlchemyLedgerSink`(用量记账闭环走通)。
+无 pg → skip。
+
+坑(见 memory/gotchas):
+- `LifespanManager` 触发 lifespan → `seed_stub_user`(owner_id FK 依赖它)。
+- `get_sessionmaker` engine 绑定首个 loop → 每 DB 测试清缓存重建、结束 dispose。
+- 网关 ledger / 验收-side repos / outline upsert 均只 flush;端点/验收事务 commit 才落库。
+- **BackgroundTask 自建独立 session**:FastAPI BackgroundTasks 在 response 发回、请求
+ session 关闭后才跑——`get_session_factory` 必被 override 成 `e2e_sm`(真 sessionmaker),
+ 否则 dep 解析会另建真 engine(跨 loop 风险)。httpx `ASGITransport` 下,`await client.post`
+ 调用会等 ASGI app 协程(含 background tasks)跑完才返回——故 client 上下文退出后断言稳定。
+- 多档位假适配器据 `req.output_schema` 路由 parsed(OutlineResult / 三审 schema /
+ ChapterDigestFacts);写章走 stream。
+"""
+
+from __future__ import annotations
+
+import json
+import uuid
+from collections.abc import AsyncIterator, Callable
+from typing import Annotated
+
+import httpx
+import pytest
+from asgi_lifespan import LifespanManager
+from fastapi import Depends
+from sqlalchemy import delete, select
+from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
+from ww_agents import (
+ ContinuityReview,
+ ForeshadowReview,
+ ForeshadowSuggestion,
+ ForeshadowWindow,
+ OutlineChapter,
+ OutlineResult,
+ PaceIssue,
+ PaceReview,
+)
+from ww_api.services.digest_extraction import ChapterDigestFacts
+from ww_db import get_session, get_sessionmaker
+from ww_db.models import (
+ Chapter,
+ ChapterDigest,
+ ChapterReview,
+ Foreshadow,
+ Outline,
+ Project,
+ UsageLedger,
+)
+from ww_llm_gateway import (
+ Gateway,
+ SqlAlchemyLedgerSink,
+ resolve_route,
+)
+from ww_llm_gateway.adapters.base import (
+ Capabilities,
+ ProviderResult,
+ ProviderUsage,
+ StreamChunk,
+)
+from ww_llm_gateway.types import LlmRequest
+
+# 三档位(writer/analyst/light)默认都路由到 deepseek(config.tier_defaults)。
+_PROVIDER = "deepseek"
+
+# 确定性流式写章 token。
+_TOKENS = ["第", "五", "章", ":", "回收之时。"]
+
+# 终稿里可被 digest 追溯的标记串(验证 digest 从终稿提炼,不变量 #4)。
+_FINAL_MARKER = "终稿独有事件:旧伏笔在第五章浮现"
+
+# outliner 假产物:两章,章 1 关联伏笔窗口(回收窗口 code=F1)。
+_OUTLINE_RESULT = OutlineResult(
+ chapters=[
+ OutlineChapter(
+ no=1,
+ beats=["开端:少年入门", "埋线:神秘玉佩"],
+ foreshadow_windows=[
+ ForeshadowWindow(
+ code="F1",
+ plant_chapter=1,
+ expected_close_from=2,
+ expected_close_to=2,
+ )
+ ],
+ ),
+ OutlineChapter(
+ no=2,
+ beats=["试炼:初次交锋"],
+ foreshadow_windows=[],
+ ),
+ ]
+)
+
+# 三审固定产物。
+_FORESHADOW_REVIEW = ForeshadowReview(
+ planted=[ForeshadowSuggestion(code="F9", title="新埋:山门禁制", where="第2段")],
+ resolved=[ForeshadowSuggestion(code="F1", title="疑似回收:玉佩", where="第5段")],
+)
+_PACE_REVIEW = PaceReview(
+ water=[PaceIssue(where="第3段", reason="信息密度低")],
+ hook=True,
+ beat_map=[1, 3, 5, 2],
+)
+
+# 各档位假用量(喂记账;证明各档位调用各自落库)。
+_USAGE = {
+ "writer": ProviderUsage(input_tokens=11, output_tokens=5),
+ "analyst": ProviderUsage(input_tokens=23, output_tokens=7),
+ "light": ProviderUsage(input_tokens=17, output_tokens=9),
+}
+
+
+class _FakeMultiTierAdapter:
+ """实现 `ProviderAdapter` Protocol:按 `req.output_schema` 分支返回 parsed,绝不联网。
+
+ - `stream()`:吐固定写章 token(writer/draft),末尾喂 writer 用量。
+ - `complete()`:据 `req.output_schema` 路由:
+ `OutlineResult`→大纲(analyst);`ContinuityReview`/`ForeshadowReview`/`PaceReview`
+ →三审(analyst/light);`ChapterDigestFacts`→验收 digest(light,追溯终稿)。
+ 每路带 `ProviderUsage` → 网关据此各落一条 usage_ledger。
+ """
+
+ provider = _PROVIDER
+
+ def capabilities(self) -> Capabilities:
+ return Capabilities(structured_output=True)
+
+ async def complete(self, req: LlmRequest, model: str) -> ProviderResult:
+ schema = req.output_schema
+ if schema is OutlineResult:
+ return ProviderResult(
+ text=_OUTLINE_RESULT.model_dump_json(),
+ parsed=_OUTLINE_RESULT,
+ usage=_USAGE["analyst"],
+ )
+ if schema is ContinuityReview:
+ review = ContinuityReview(conflicts=[]) # 零冲突 → 验收 gate 直通
+ return ProviderResult(
+ text=review.model_dump_json(),
+ parsed=review,
+ usage=_USAGE["analyst"],
+ )
+ if schema is ForeshadowReview:
+ return ProviderResult(
+ text=_FORESHADOW_REVIEW.model_dump_json(),
+ parsed=_FORESHADOW_REVIEW,
+ usage=_USAGE["analyst"],
+ )
+ if schema is PaceReview:
+ return ProviderResult(
+ text=_PACE_REVIEW.model_dump_json(),
+ parsed=_PACE_REVIEW,
+ usage=_USAGE["light"],
+ )
+ # 否则视为 digest 提炼(ChapterDigestFacts):把终稿标记塞进 summary(#4)。
+ facts = ChapterDigestFacts(
+ summary=_FINAL_MARKER,
+ events=["玉佩浮现"],
+ locations=["山门"],
+ )
+ return ProviderResult(
+ text=facts.model_dump_json(),
+ parsed=facts,
+ usage=_USAGE["light"],
+ )
+
+ async def stream(self, req: LlmRequest, model: str) -> AsyncIterator[StreamChunk]:
+ for token in _TOKENS:
+ yield StreamChunk(text=token)
+ yield StreamChunk(usage=_USAGE["writer"])
+
+
+@pytest.fixture
+async def e2e_sm() -> AsyncIterator[async_sessionmaker[AsyncSession]]:
+ """真实 DB session 工厂;无 pg 时跳过(每测试清缓存重建 engine、结束 dispose)。"""
+ get_sessionmaker.cache_clear()
+ maker = get_sessionmaker()
+ try:
+ async with maker() as probe:
+ await probe.execute(select(1))
+ except Exception:
+ pytest.skip("postgres not reachable")
+ yield maker
+ await maker.kw["bind"].dispose()
+ get_sessionmaker.cache_clear()
+
+
+def _parse_sse(raw: str) -> list[tuple[str, str]]:
+ """把 text/event-stream 原文解析为 `(event, data)` 帧列表。"""
+ frames: list[tuple[str, str]] = []
+ event: str | None = None
+ data: str | None = None
+ for line in raw.splitlines():
+ if line.startswith("event:"):
+ event = line[len("event:") :].strip()
+ elif line.startswith("data:"):
+ data = line[len("data:") :].strip()
+ elif line == "":
+ if event is not None and data is not None:
+ frames.append((event, data))
+ event, data = None, None
+ if event is not None and data is not None:
+ frames.append((event, data))
+ return frames
+
+
+def _gateway_override_factory(
+ adapter: _FakeMultiTierAdapter,
+) -> Callable[[AsyncSession], Gateway]:
+ """构造依赖覆盖:真实 Gateway + 给定假适配器 + 真实 ledger(请求 session)。
+
+ ledger 用**请求 session**(`Depends(get_session)`,FastAPI 按请求缓存,与端点/验收
+ 事务同一实例)→ 由端点流末 / 验收事务的 `commit()` 落库(同 M1/M2 回归校验)。
+ """
+
+ def _override(session: Annotated[AsyncSession, Depends(get_session)]) -> Gateway:
+ return Gateway(
+ adapters={_PROVIDER: adapter},
+ ledger=SqlAlchemyLedgerSink(session),
+ resolver=resolve_route,
+ )
+
+ return _override
+
+
+async def _cleanup(e2e_sm: async_sessionmaker[AsyncSession], project_uuid: uuid.UUID) -> None:
+ """按 FK 顺序清理(无级联的先删,再删 project;多数子表经 FK CASCADE)。"""
+ async with e2e_sm() as cleanup:
+ await cleanup.execute(delete(UsageLedger).where(UsageLedger.project_id == project_uuid))
+ await cleanup.execute(delete(ChapterDigest).where(ChapterDigest.project_id == project_uuid))
+ await cleanup.execute(delete(ChapterReview).where(ChapterReview.project_id == project_uuid))
+ await cleanup.execute(delete(Outline).where(Outline.project_id == project_uuid))
+ await cleanup.execute(delete(Foreshadow).where(Foreshadow.project_id == project_uuid))
+ await cleanup.execute(delete(Chapter).where(Chapter.project_id == project_uuid))
+ await cleanup.execute(delete(Project).where(Project.id == project_uuid))
+ await cleanup.commit()
+
+
+async def test_m3_foreshadow_plant_progress_overdue_after_accept(
+ e2e_sm: async_sessionmaker[AsyncSession],
+) -> None:
+ """用例 1:伏笔 埋设→进展→验收后到期扫描置 OVERDUE(BackgroundTask)。"""
+ from ww_api.main import create_app
+ from ww_api.services.project_deps import (
+ get_digest_gateway,
+ get_review_gateway,
+ get_session_factory,
+ get_writer_gateway,
+ )
+
+ adapter = _FakeMultiTierAdapter()
+ override = _gateway_override_factory(adapter)
+
+ app = create_app()
+ app.dependency_overrides[get_writer_gateway] = override
+ app.dependency_overrides[get_review_gateway] = override
+ app.dependency_overrides[get_digest_gateway] = override
+ # 到期扫描 BackgroundTask 的独立 session 工厂 → 注入真实 sessionmaker(同测试 engine/loop),
+ # 否则默认 `get_sessionmaker()` 会另建 engine 绑别的 loop(跨 loop 坑)。
+ app.dependency_overrides[get_session_factory] = lambda: e2e_sm
+
+ transport = httpx.ASGITransport(app=app)
+ project_uuid: uuid.UUID | None = None
+ try:
+ async with LifespanManager(app):
+ async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
+ # 1) 立项 → 201。
+ create_resp = await client.post(
+ "/projects",
+ json={"title": "M3 伏笔逾期验证作品", "genre": "玄幻"},
+ )
+ assert create_resp.status_code == 201
+ project_id = create_resp.json()["id"]
+ project_uuid = uuid.UUID(project_id)
+
+ # 2) 埋伏笔 F1(expected_close_to=2 → 验收第5章后应逾期)→ 201, status=OPEN。
+ f1_resp = await client.post(
+ f"/projects/{project_id}/foreshadow",
+ json={
+ "code": "F1",
+ "title": "神秘玉佩",
+ "planted_at": 1,
+ "expected_close_to": 2,
+ },
+ )
+ assert f1_resp.status_code == 201
+ assert f1_resp.json()["status"] == "OPEN"
+
+ # 对照:F2(expected_close_to=9 → 不越界,不应逾期)。
+ f2_resp = await client.post(
+ f"/projects/{project_id}/foreshadow",
+ json={"code": "F2", "title": "远期线索", "expected_close_to": 9},
+ )
+ assert f2_resp.status_code == 201
+
+ # 3) PATCH F1 追加一条进展 → 200, progress 追加。
+ progress_entry = {"chapter": 3, "note": "玉佩发热"}
+ patch_resp = await client.patch(
+ f"/projects/{project_id}/foreshadow/F1",
+ json={"progress_entry": progress_entry},
+ )
+ assert patch_resp.status_code == 200
+ assert patch_resp.json()["progress"][-1] == progress_entry
+
+ # 4) 写第5章草稿(SSE)+ 自动保存。
+ draft_resp = await client.post(f"/projects/{project_id}/chapters/5/draft")
+ assert draft_resp.status_code == 200
+ draft_text = "".join(
+ json.loads(d)["text"]
+ for (ev, d) in _parse_sse(draft_resp.text)
+ if ev == "token"
+ )
+ assert draft_text == "".join(_TOKENS)
+ save_resp = await client.put(
+ f"/projects/{project_id}/chapters/5/draft",
+ json={"text": draft_text},
+ )
+ assert save_resp.status_code == 200
+
+ # 5) 验收第5章(无审稿留痕 → 零冲突直通;final_text 含终稿标记 #4)。
+ final_text = draft_text + "\n\n" + _FINAL_MARKER
+ accept_resp = await client.post(
+ f"/projects/{project_id}/chapters/5/accept",
+ json={"final_text": final_text, "decisions": []},
+ )
+ assert accept_resp.status_code == 200
+ acc = accept_resp.json()
+ assert acc["digest_added"] is True
+ # BackgroundTask 到期扫描在此请求 ASGI 协程内(response 后)跑完才返回。
+
+ # 6a) 端点视角:GET ?status=OVERDUE 含 F1(status==OVERDUE),不含 F2。
+ board_resp = await client.get(
+ f"/projects/{project_id}/foreshadow", params={"status": "OVERDUE"}
+ )
+ assert board_resp.status_code == 200
+ overdue = board_resp.json()["foreshadow"]
+ overdue_codes = {f["code"] for f in overdue}
+ assert "F1" in overdue_codes
+ assert "F2" not in overdue_codes
+ f1_board = next(f for f in overdue if f["code"] == "F1")
+ assert f1_board["status"] == "OVERDUE"
+
+ # 6b) DB 真源断言(经 e2e session)。
+ assert project_uuid is not None
+ async with e2e_sm() as verify:
+ fore_rows = (
+ (
+ await verify.execute(
+ select(Foreshadow)
+ .where(Foreshadow.project_id == project_uuid)
+ .order_by(Foreshadow.code)
+ )
+ )
+ .scalars()
+ .all()
+ )
+ by_code = {r.code: r for r in fore_rows}
+ # F1:验收第5章 > expected_close_to(2) → OVERDUE;progress 含那条进展。
+ assert by_code["F1"].status == "OVERDUE"
+ assert progress_entry in by_code["F1"].progress
+ # F2:expected_close_to(9) 未越界 → 仍 OPEN(对照,扫描不误伤)。
+ assert by_code["F2"].status == "OPEN"
+
+ # 终稿 digest 落库且 summary 含终稿标记(#4:从终稿非草稿提炼)。
+ digest = (
+ await verify.execute(
+ select(ChapterDigest).where(ChapterDigest.project_id == project_uuid)
+ )
+ ).scalar_one()
+ assert digest.facts["summary"] == _FINAL_MARKER
+ assert _FINAL_MARKER not in draft_text
+ finally:
+ if project_uuid is not None:
+ await _cleanup(e2e_sm, project_uuid)
+
+
+async def test_m3_outline_with_foreshadow_windows_upsert_idempotent(
+ e2e_sm: async_sessionmaker[AsyncSession],
+) -> None:
+ """用例 2:排大纲含伏笔回收窗口;逐章落 `outline`;再次 POST 幂等覆盖;记账落库。"""
+ from ww_api.main import create_app
+ from ww_api.services.project_deps import get_outline_gateway
+
+ adapter = _FakeMultiTierAdapter()
+ override = _gateway_override_factory(adapter)
+
+ app = create_app()
+ app.dependency_overrides[get_outline_gateway] = override
+
+ transport = httpx.ASGITransport(app=app)
+ project_uuid: uuid.UUID | None = None
+ try:
+ async with LifespanManager(app):
+ async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
+ create_resp = await client.post("/projects", json={"title": "M3 大纲窗口验证作品"})
+ assert create_resp.status_code == 201
+ project_id = create_resp.json()["id"]
+ project_uuid = uuid.UUID(project_id)
+
+ # 1) 生成大纲(analyst 假适配器产 OutlineResult)→ 200 OutlineResponse。
+ outline_resp = await client.post(
+ f"/projects/{project_id}/outline", json={"volume": 1}
+ )
+ assert outline_resp.status_code == 200
+ chapters = outline_resp.json()["chapters"]
+ assert len(chapters) == 2
+ ch1 = next(c for c in chapters if c["no"] == 1)
+ assert ch1["volume"] == 1
+ assert ch1["beats"] == ["开端:少年入门", "埋线:神秘玉佩"]
+ assert ch1["foreshadow_windows"][0]["code"] == "F1"
+ assert ch1["foreshadow_windows"][0]["expected_close_to"] == 2
+
+ # 2) 再次 POST → 唯一 (project_id, chapter_no) 覆盖,行数不堆叠。
+ outline_resp2 = await client.post(
+ f"/projects/{project_id}/outline", json={"volume": 1}
+ )
+ assert outline_resp2.status_code == 200
+
+ # DB 真源断言。
+ assert project_uuid is not None
+ async with e2e_sm() as verify:
+ outline_rows = (
+ (
+ await verify.execute(
+ select(Outline)
+ .where(Outline.project_id == project_uuid)
+ .order_by(Outline.chapter_no)
+ )
+ )
+ .scalars()
+ .all()
+ )
+ # 幂等:两次 POST 仍是 2 行(每章一行,覆盖非堆叠)。
+ assert len(outline_rows) == 2
+ row1 = outline_rows[0]
+ assert row1.chapter_no == 1
+ assert row1.volume == 1
+ # beats 列形 `{"beats":[...]}`(写侧包裹,见 contracts C3 扩 T3.5)。
+ assert row1.beats == {"beats": ["开端:少年入门", "埋线:神秘玉佩"]}
+ # foreshadow_windows JSONB list 含窗口(code=F1)。
+ assert isinstance(row1.foreshadow_windows, list)
+ assert row1.foreshadow_windows[0]["code"] == "F1"
+ assert row1.foreshadow_windows[0]["expected_close_to"] == 2
+ # 章 2 无窗口。
+ assert outline_rows[1].chapter_no == 2
+ assert outline_rows[1].foreshadow_windows == []
+
+ # usage_ledger:两次大纲生成各 1 条 analyst 调用记账(端点 commit 后落库)。
+ ledger_rows = (
+ (
+ await verify.execute(
+ select(UsageLedger).where(UsageLedger.project_id == project_uuid)
+ )
+ )
+ .scalars()
+ .all()
+ )
+ assert len(ledger_rows) == 2
+ assert all(r.input_tokens == _USAGE["analyst"].input_tokens for r in ledger_rows)
+ assert all(r.provider == _PROVIDER for r in ledger_rows)
+ finally:
+ if project_uuid is not None:
+ await _cleanup(e2e_sm, project_uuid)
+
+
+async def test_m3_three_reviewers_sse_and_review_trace(
+ e2e_sm: async_sessionmaker[AsyncSession],
+) -> None:
+ """用例 3:三审 SSE(continuity/foreshadow/pace 三审齐)+ 留痕列(foreshadow_sug/pace)真落库。
+
+ 三审在同一 LangGraph superstep 并行跑、共用请求 session 记账(T3.8 把
+ `SqlAlchemyLedgerSink.record` 改 add-only:`session.add` 同步不让步 → 并行协程不交错,
+ 持久化靠端点 commit)。故三审齐现真持久化,断言严格正向:
+ - SSE `section` 帧含 continuity/foreshadow/pace 三审,且无 `error` 帧;
+ - SSE 至少各一条 `foreshadow` / `pace` 事件(foreshadow/pace 真发事件);
+ - `GET .../reviews` 留痕行 `foreshadow_sug`(list 非空) / `pace`(dict 非空) 列已填;
+ - DB 真源 `chapter_reviews.foreshadow_sug`/`pace` 列真落库(三审都 done,无静默丢失)。
+ """
+ from ww_api.main import create_app
+ from ww_api.services.project_deps import (
+ get_review_gateway,
+ get_writer_gateway,
+ )
+
+ adapter = _FakeMultiTierAdapter()
+ override = _gateway_override_factory(adapter)
+
+ app = create_app()
+ app.dependency_overrides[get_writer_gateway] = override
+ app.dependency_overrides[get_review_gateway] = override
+
+ transport = httpx.ASGITransport(app=app)
+ project_uuid: uuid.UUID | None = None
+ try:
+ async with LifespanManager(app):
+ async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
+ create_resp = await client.post("/projects", json={"title": "M3 三审验证作品"})
+ assert create_resp.status_code == 201
+ project_id = create_resp.json()["id"]
+ project_uuid = uuid.UUID(project_id)
+
+ # 写第1章草稿 + 自动保存(供续审回退已存草稿)。
+ draft_resp = await client.post(f"/projects/{project_id}/chapters/1/draft")
+ draft_text = "".join(
+ json.loads(d)["text"]
+ for (ev, d) in _parse_sse(draft_resp.text)
+ if ev == "token"
+ )
+ await client.put(
+ f"/projects/{project_id}/chapters/1/draft",
+ json={"text": draft_text},
+ )
+
+ # 续审(SSE):三审 section + ≥1 foreshadow/pace 事件、无 error。
+ review_resp = await client.post(
+ f"/projects/{project_id}/chapters/1/review",
+ json={"draft": draft_text},
+ )
+ assert review_resp.status_code == 200
+ assert review_resp.headers["content-type"].startswith("text/event-stream")
+ rframes = _parse_sse(review_resp.text)
+ assert [d for (ev, d) in rframes if ev == "error"] == []
+ section_names = {
+ json.loads(d).get("name") for (ev, d) in rframes if ev == "section"
+ }
+ assert {"continuity", "foreshadow", "pace"} <= section_names
+ foreshadow_events = [d for (ev, d) in rframes if ev == "foreshadow"]
+ pace_events = [d for (ev, d) in rframes if ev == "pace"]
+ assert len(foreshadow_events) >= 1
+ assert len(pace_events) >= 1
+
+ # GET 历史:留痕行 foreshadow_sug(list) / pace(dict) 列已填。
+ hist_resp = await client.get(f"/projects/{project_id}/chapters/1/reviews")
+ assert hist_resp.status_code == 200
+ latest = hist_resp.json()["reviews"][0]
+ assert isinstance(latest["foreshadow_sug"], list)
+ assert len(latest["foreshadow_sug"]) >= 1
+ assert isinstance(latest["pace"], dict)
+ assert latest["pace"]["hook"] is True
+
+ # DB 真源断言:chapter_reviews 留痕列已填。
+ assert project_uuid is not None
+ async with e2e_sm() as verify:
+ review_rows = (
+ (
+ await verify.execute(
+ select(ChapterReview).where(ChapterReview.project_id == project_uuid)
+ )
+ )
+ .scalars()
+ .all()
+ )
+ assert len(review_rows) == 1
+ row = review_rows[0]
+ assert isinstance(row.foreshadow_sug, list)
+ assert len(row.foreshadow_sug) >= 1
+ # 扁平后每条带 kind:"planted"|"resolved"(见 collect 列映射)。
+ assert {s.get("kind") for s in row.foreshadow_sug} <= {"planted", "resolved"}
+ assert isinstance(row.pace, dict)
+ assert row.pace["hook"] is True
+ assert row.pace["beat_map"] == [1, 3, 5, 2]
+ finally:
+ if project_uuid is not None:
+ await _cleanup(e2e_sm, project_uuid)