mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Split shared thread capability folders
This commit is contained in:
parent
a14e3d19e7
commit
727f5393af
18 changed files with 102 additions and 53 deletions
18
biome.jsonc
18
biome.jsonc
|
|
@ -6,11 +6,23 @@
|
|||
"useIgnoreFile": true
|
||||
},
|
||||
"plugins": [
|
||||
// App-server protocol and generated binding boundaries.
|
||||
// Shared source-shape boundaries.
|
||||
{
|
||||
"path": "./scripts/lint/no-app-server-root-module-files.grit",
|
||||
"includes": ["**/src/app-server/*.ts"]
|
||||
"path": "./scripts/lint/no-responsibility-root-module-files.grit",
|
||||
"includes": [
|
||||
"**/src/app-server/*.ts",
|
||||
"**/src/app-server/*.tsx",
|
||||
"**/src/domain/*.ts",
|
||||
"**/src/domain/*.tsx",
|
||||
"**/src/features/chat/*.ts",
|
||||
"**/src/features/chat/*.tsx",
|
||||
"**/src/features/threads/*.ts",
|
||||
"**/src/features/threads/*.tsx",
|
||||
"**/src/shared/*.ts",
|
||||
"**/src/shared/*.tsx"
|
||||
]
|
||||
},
|
||||
// App-server protocol and generated binding boundaries.
|
||||
{
|
||||
"path": "./scripts/lint/no-app-server-root-module-imports.grit",
|
||||
"includes": ["**/src/**/*.ts", "**/src/**/*.tsx"]
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
language js
|
||||
|
||||
`$program` where {
|
||||
register_diagnostic(span=$program, message="Keep app-server modules in responsibility subfolders; src/app-server root must not accumulate boundary adapters.", severity="error")
|
||||
}
|
||||
5
scripts/lint/no-responsibility-root-module-files.grit
Normal file
5
scripts/lint/no-responsibility-root-module-files.grit
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
language js
|
||||
|
||||
`$program` where {
|
||||
register_diagnostic(span=$program, message="Keep responsibility-split source roots free of module files; add modules to the matching subfolder instead of the root.", severity="error")
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
type ThreadRenameLifecycleState,
|
||||
threadRenameGenerationStillActive,
|
||||
transitionThreadRenameLifecycleState,
|
||||
} from "../../../threads/rename-lifecycle";
|
||||
} from "../../../threads/list/rename-lifecycle";
|
||||
import type { DisclosureSetAction } from "./actions";
|
||||
import { patchObject } from "./patch";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import type { AppServerClientAccess } from "../../../app-server/connection/clien
|
|||
import { recoverRolloutTokenUsage } from "../../../app-server/services/rollout-token-usage";
|
||||
import { normalizeExplicitThreadName } from "../../../domain/threads/model";
|
||||
import type { LocalIdSource } from "../../../shared/id/local-id";
|
||||
import { createThreadOperations, type ThreadOperations } from "../../threads/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../../threads/thread-title-service";
|
||||
import { createThreadOperations, type ThreadOperations } from "../../threads/workflows/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../../threads/workflows/thread-title-service";
|
||||
import type { ChatServerThreadActions } from "../app-server/actions/threads";
|
||||
import { createChatThreadGoalReadTransport, createChatThreadGoalTransport } from "../app-server/goals/transport";
|
||||
import { createChatThreadHistoryTransport, createChatThreadResumeTransport } from "../app-server/threads/loading-transport";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { h } from "preact";
|
|||
|
||||
import { CLIENT_VERSION } from "../../../../constants";
|
||||
import type { Thread } from "../../../../domain/threads/model";
|
||||
import { threadRowCoreProjection } from "../../../threads/row-projection";
|
||||
import { threadRowCoreProjection } from "../../../threads/list/row-projection";
|
||||
import { connectionDiagnosticSectionsFromState } from "../../application/connection/diagnostic-sections";
|
||||
import { toolInventoryDiagnosticSections } from "../../application/connection/tool-inventory-diagnostic-sections";
|
||||
import type { RuntimeSnapshot } from "../../domain/runtime/snapshot";
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import type { ArchiveExportSettings } from "../../domain/threads/archive-markdow
|
|||
import type { Thread } from "../../domain/threads/model";
|
||||
import type { ObservedResult } from "../../shared/query/observed-result";
|
||||
import { observedInitialError, observedInitialLoading, observedValue } from "../../shared/query/observed-result";
|
||||
import { createThreadOperations, type ThreadOperations } from "../threads/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../threads/thread-title-service";
|
||||
import { createThreadOperations, type ThreadOperations } from "../threads/workflows/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../threads/workflows/thread-title-service";
|
||||
import { isThreadsArchiveConfirmPointer, renderThreadsViewShell, unmountThreadsViewShell } from "./shell.dom";
|
||||
import {
|
||||
type ThreadsGeneratingRenameState,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import {
|
|||
type ThreadRenameActiveState,
|
||||
type ThreadRenameGeneratingState,
|
||||
transitionThreadRenameLifecycleState,
|
||||
} from "../threads/rename-lifecycle";
|
||||
import { type ThreadRowCoreProjection, threadRowCoreProjection } from "../threads/row-projection";
|
||||
} from "../threads/list/rename-lifecycle";
|
||||
import { type ThreadRowCoreProjection, threadRowCoreProjection } from "../threads/list/row-projection";
|
||||
|
||||
type ThreadsLiveStatus = "pending" | "running" | "open";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Thread } from "../../domain/threads/model";
|
||||
import { threadDisplayTitle, threadRenameDraftTitle } from "../../domain/threads/title";
|
||||
import type { Thread } from "../../../domain/threads/model";
|
||||
import { threadDisplayTitle, threadRenameDraftTitle } from "../../../domain/threads/title";
|
||||
import type { ThreadRenameActiveState } from "./rename-lifecycle";
|
||||
|
||||
interface ThreadRowCoreRenameProjection {
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import type { AppServerClientAccess } from "../../app-server/connection/client-access";
|
||||
import type { ThreadCatalogEventSink } from "../../app-server/query/thread-catalog";
|
||||
import { type ArchiveThreadResult, archiveThreadOnAppServer } from "../../app-server/services/thread-archive";
|
||||
import type { ArchiveExportDestination } from "../../app-server/services/thread-archive-markdown";
|
||||
import type { ArchiveExportSettings } from "../../domain/threads/archive-markdown";
|
||||
import { normalizeExplicitThreadName } from "../../domain/threads/model";
|
||||
import type { AppServerClientAccess } from "../../../app-server/connection/client-access";
|
||||
import type { ThreadCatalogEventSink } from "../../../app-server/query/thread-catalog";
|
||||
import { type ArchiveThreadResult, archiveThreadOnAppServer } from "../../../app-server/services/thread-archive";
|
||||
import type { ArchiveExportDestination } from "../../../app-server/services/thread-archive-markdown";
|
||||
import type { ArchiveExportSettings } from "../../../domain/threads/archive-markdown";
|
||||
import { normalizeExplicitThreadName } from "../../../domain/threads/model";
|
||||
|
||||
export interface ThreadOperationsHost {
|
||||
clientAccess: AppServerClientAccess;
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import type { AppServerClientAccess } from "../../app-server/connection/client-access";
|
||||
import { generateThreadTitleWithCodex } from "../../app-server/services/thread-title-generation";
|
||||
import { readCompletedConversationSummariesPage } from "../../app-server/services/threads";
|
||||
import type { ReasoningEffort } from "../../domain/catalog/metadata";
|
||||
import type { AppServerClientAccess } from "../../../app-server/connection/client-access";
|
||||
import { generateThreadTitleWithCodex } from "../../../app-server/services/thread-title-generation";
|
||||
import { readCompletedConversationSummariesPage } from "../../../app-server/services/threads";
|
||||
import type { ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import {
|
||||
findThreadTitleContext,
|
||||
THREAD_TITLE_CONTEXT_UNAVAILABLE_MESSAGE,
|
||||
type ThreadTitleContext,
|
||||
threadTitleContextFromConversationSummary,
|
||||
} from "../../domain/threads/title-generation-model";
|
||||
import type { ThreadConversationSummary } from "../../domain/threads/transcript";
|
||||
} from "../../../domain/threads/title-generation-model";
|
||||
import type { ThreadConversationSummary } from "../../../domain/threads/transcript";
|
||||
|
||||
export interface ThreadTitleServiceHost {
|
||||
codexPath: () => string;
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
createAutoTitleCoordinator,
|
||||
} from "../../../../../src/features/chat/application/threads/auto-title-coordinator";
|
||||
import { threadTitleContextFromMessageStreamItems } from "../../../../../src/features/chat/application/threads/title-context";
|
||||
import { createThreadTitleService } from "../../../../../src/features/threads/thread-title-service";
|
||||
import { createThreadTitleService } from "../../../../../src/features/threads/workflows/thread-title-service";
|
||||
import { DEFAULT_SETTINGS } from "../../../../../src/settings/model";
|
||||
import { deferred } from "../../../../support/async";
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
type ThreadRenameGeneratingState,
|
||||
type ThreadRenameLifecycleState,
|
||||
transitionThreadRenameLifecycleState,
|
||||
} from "../../../src/features/threads/rename-lifecycle";
|
||||
} from "../../../../src/features/threads/list/rename-lifecycle";
|
||||
|
||||
describe("thread rename lifecycle", () => {
|
||||
it("keeps late generation callbacks scoped to the active unchanged generation", () => {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { Thread } from "../../../src/domain/threads/model";
|
||||
import { threadRowCoreProjection } from "../../../src/features/threads/row-projection";
|
||||
import type { Thread } from "../../../../src/domain/threads/model";
|
||||
import { threadRowCoreProjection } from "../../../../src/features/threads/list/row-projection";
|
||||
|
||||
describe("thread row core projection", () => {
|
||||
it("projects shared row title, selection, rename, and archive confirmation state", () => {
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { AppServerClient } from "../../../src/app-server/connection/client";
|
||||
import type { ArchiveThreadResult } from "../../../src/app-server/services/thread-archive";
|
||||
import type { ArchiveExportDestination } from "../../../src/app-server/services/thread-archive-markdown";
|
||||
import { createThreadOperations, type ThreadOperationsHost } from "../../../src/features/threads/thread-operations";
|
||||
import { DEFAULT_SETTINGS } from "../../../src/settings/model";
|
||||
import type { AppServerClient } from "../../../../src/app-server/connection/client";
|
||||
import type { ArchiveThreadResult } from "../../../../src/app-server/services/thread-archive";
|
||||
import type { ArchiveExportDestination } from "../../../../src/app-server/services/thread-archive-markdown";
|
||||
import { createThreadOperations, type ThreadOperationsHost } from "../../../../src/features/threads/workflows/thread-operations";
|
||||
import { DEFAULT_SETTINGS } from "../../../../src/settings/model";
|
||||
|
||||
const archiveMock = vi.hoisted(() => ({
|
||||
archiveThreadOnAppServer: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/app-server/services/thread-archive", () => ({
|
||||
vi.mock("../../../../src/app-server/services/thread-archive", () => ({
|
||||
archiveThreadOnAppServer: archiveMock.archiveThreadOnAppServer,
|
||||
}));
|
||||
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { THREAD_TITLE_CONTEXT_UNAVAILABLE_MESSAGE, type ThreadTitleContext } from "../../../src/domain/threads/title-generation-model";
|
||||
import { THREAD_TITLE_CONTEXT_UNAVAILABLE_MESSAGE, type ThreadTitleContext } from "../../../../src/domain/threads/title-generation-model";
|
||||
import {
|
||||
createThreadTitleService,
|
||||
type ThreadTitleService,
|
||||
type ThreadTitleServiceHost,
|
||||
} from "../../../src/features/threads/thread-title-service";
|
||||
import { DEFAULT_SETTINGS } from "../../../src/settings/model";
|
||||
} from "../../../../src/features/threads/workflows/thread-title-service";
|
||||
import { DEFAULT_SETTINGS } from "../../../../src/settings/model";
|
||||
|
||||
describe("ThreadTitleService", () => {
|
||||
it("generates a title from visible context without saving it", async () => {
|
||||
|
|
@ -7,7 +7,6 @@ import { describe, expect, it } from "vitest";
|
|||
|
||||
const repoRoot = process.cwd();
|
||||
const biomeBin = path.join(repoRoot, "node_modules", ".bin", "biome");
|
||||
const workspaceByPlugins = new Map();
|
||||
const projectPluginByName = new Map(
|
||||
parseJsonc(readFileSync(path.join(repoRoot, "biome.jsonc"), "utf8")).plugins.map((plugin) => {
|
||||
const pluginPath = typeof plugin === "string" ? plugin : plugin.path;
|
||||
|
|
@ -16,8 +15,8 @@ const projectPluginByName = new Map(
|
|||
);
|
||||
const APP_SERVER_PROTOCOL_BOUNDARY_MESSAGE =
|
||||
"Source modules outside root src/app-server must use domain models and app-server services instead of app-server protocol modules. Chat turn-item conversion may consume turn protocol at its app-server boundary; feature state and UI must use Panel-owned models.";
|
||||
const APP_SERVER_ROOT_MODULE_FILE_MESSAGE =
|
||||
"Keep app-server modules in responsibility subfolders; src/app-server root must not accumulate boundary adapters.";
|
||||
const RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE =
|
||||
"Keep responsibility-split source roots free of module files; add modules to the matching subfolder instead of the root.";
|
||||
const APP_SERVER_ROOT_MODULE_IMPORT_MESSAGE =
|
||||
"Import app-server boundary modules from responsibility subfolders, not src/app-server root modules.";
|
||||
const APP_SERVER_SUBFOLDER_ROOT_IMPORT_MESSAGE =
|
||||
|
|
@ -409,6 +408,7 @@ export function timestamp(): number {
|
|||
"no-chat-workspace-boundary-imports.grit",
|
||||
"no-feature-workspace-boundary-imports.grit",
|
||||
"no-workspace-chat-internal-imports.grit",
|
||||
"no-responsibility-root-module-files.grit",
|
||||
"no-chat-host-rendering-layer-imports.grit",
|
||||
"no-chat-panel-runtime-boundary-imports.grit",
|
||||
"no-chat-presentation-outer-layer-imports.grit",
|
||||
|
|
@ -531,6 +531,36 @@ import type { ChatWorkspacePanelSurface } from "../features/chat/host/contracts"
|
|||
import type { CodexChatView } from "../features/chat/host/view.obsidian";
|
||||
|
||||
export type Allowed = ChatWorkspacePanelSurface | CodexChatView;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/domain/mixed-root.ts"),
|
||||
`
|
||||
export const misplaced = true;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/shared/mixed-root.ts"),
|
||||
`
|
||||
export const misplaced = true;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/mixed-root.ts"),
|
||||
`
|
||||
export const misplaced = true;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/threads/mixed-root.ts"),
|
||||
`
|
||||
export const misplaced = true;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/threads/list/rename-lifecycle.ts"),
|
||||
`
|
||||
export const allowed = true;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
|
|
@ -615,6 +645,11 @@ export const value = statusText;
|
|||
"src/features/threads-view/workspace-allowed.ts",
|
||||
"src/workspace/chat-internal-escape.ts",
|
||||
"src/workspace/chat-host-allowed.ts",
|
||||
"src/domain/mixed-root.ts",
|
||||
"src/shared/mixed-root.ts",
|
||||
"src/features/chat/mixed-root.ts",
|
||||
"src/features/threads/mixed-root.ts",
|
||||
"src/features/threads/list/rename-lifecycle.ts",
|
||||
"src/features/chat/panel/outer.tsx",
|
||||
"src/features/chat/panel/allowed.tsx",
|
||||
"src/features/chat/presentation/outer.ts",
|
||||
|
|
@ -644,6 +679,11 @@ export const value = statusText;
|
|||
expect(pluginDiagnostics(report, "src/features/threads-view/workspace-allowed.ts")).toEqual([]);
|
||||
expect(pluginMessages(report, "src/workspace/chat-internal-escape.ts")).toEqual([WORKSPACE_CHAT_INTERNAL_MESSAGE]);
|
||||
expect(pluginDiagnostics(report, "src/workspace/chat-host-allowed.ts")).toEqual([]);
|
||||
expect(pluginMessages(report, "src/domain/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/shared/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/features/chat/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/features/threads/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginDiagnostics(report, "src/features/threads/list/rename-lifecycle.ts")).toEqual([]);
|
||||
expect(pluginMessages(report, "src/features/chat/panel/outer.tsx")).toEqual(
|
||||
Array.from({ length: 3 }, () => CHAT_PANEL_RUNTIME_BOUNDARY_MESSAGE),
|
||||
);
|
||||
|
|
@ -787,7 +827,7 @@ export const value = statusText;
|
|||
|
||||
it("keeps app-server root from becoming a boundary escape hatch", async () => {
|
||||
const cwd = await tempBiomeWorkspace([
|
||||
"no-app-server-root-module-files.grit",
|
||||
"no-responsibility-root-module-files.grit",
|
||||
"no-app-server-root-module-imports.grit",
|
||||
"no-app-server-subfolder-root-imports.grit",
|
||||
]);
|
||||
|
|
@ -836,7 +876,7 @@ export const read = listThreads;
|
|||
cwd,
|
||||
);
|
||||
|
||||
expect(pluginMessages(report, "src/app-server/escape.ts")).toEqual([APP_SERVER_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/app-server/escape.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/features/chat/app-server/root-import.ts")).toEqual([APP_SERVER_ROOT_MODULE_IMPORT_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/app-server/services/root-import.ts")).toEqual([APP_SERVER_SUBFOLDER_ROOT_IMPORT_MESSAGE]);
|
||||
expect(pluginDiagnostics(report, "src/app-server/services/allowed.ts")).toEqual([]);
|
||||
|
|
@ -1310,10 +1350,6 @@ export type AppServerThreadResumeClient = Pick<AppServerClient, "resumeThread">;
|
|||
}
|
||||
|
||||
async function tempBiomeWorkspace(plugins) {
|
||||
const cacheKey = plugins.join("\0");
|
||||
const cached = workspaceByPlugins.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const cwd = await mkdtemp(path.join(tmpdir(), "codex-panel-grit-policy-"));
|
||||
await mkdir(cwd, { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/chat/domain/message-stream"), { recursive: true });
|
||||
|
|
@ -1328,6 +1364,8 @@ async function tempBiomeWorkspace(plugins) {
|
|||
await mkdir(path.join(cwd, "src/features/chat/presentation"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/chat/ui"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/selection-rewrite"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/threads/list"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/threads/workflows"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/features/threads-view"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/settings"), { recursive: true });
|
||||
await mkdir(path.join(cwd, "src/domain/threads"), { recursive: true });
|
||||
|
|
@ -1347,7 +1385,6 @@ async function tempBiomeWorkspace(plugins) {
|
|||
css: { linter: { enabled: true } },
|
||||
}),
|
||||
);
|
||||
workspaceByPlugins.set(cacheKey, cwd);
|
||||
return cwd;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue