diff --git a/src/features/team-mode/team-tasklist/store.test.ts b/src/features/team-mode/team-tasklist/store.test.ts
new file mode 100644
index 000000000..f23fa74e6
--- /dev/null
+++ b/src/features/team-mode/team-tasklist/store.test.ts
@@ -0,0 +1,32 @@
+///
+
+import { expect, test } from "bun:test"
+import { readFile } from "node:fs/promises"
+import path from "node:path"
+
+import { getTasksDir, resolveBaseDir } from "../team-registry"
+import { createTask } from "./store"
+import { createTaskInput, createTasklistFixture } from "./test-support"
+
+test("createTask assigns distinct ids during concurrent creation", async () => {
+ // given
+ const fixture = await createTasklistFixture()
+
+ try {
+ // when
+ const [firstTask, secondTask] = await Promise.all([
+ createTask(fixture.teamRunId, createTaskInput({ subject: "first task" }), fixture.config),
+ createTask(fixture.teamRunId, createTaskInput({ subject: "second task" }), fixture.config),
+ ])
+
+ const tasksDirectory = getTasksDir(resolveBaseDir(fixture.config), fixture.teamRunId)
+ const watermarkContent = await readFile(path.join(tasksDirectory, ".highwatermark"), "utf8")
+ const sortedIds = [firstTask.id, secondTask.id].sort((leftId, rightId) => Number(leftId) - Number(rightId))
+
+ // then
+ expect(sortedIds).toEqual(["1", "2"])
+ expect(watermarkContent.trim()).toBe("2")
+ } finally {
+ await fixture.cleanup()
+ }
+})
diff --git a/src/features/team-mode/team-tasklist/store.ts b/src/features/team-mode/team-tasklist/store.ts
new file mode 100644
index 000000000..9a703839b
--- /dev/null
+++ b/src/features/team-mode/team-tasklist/store.ts
@@ -0,0 +1,53 @@
+import { mkdir, readFile } from "node:fs/promises"
+import path from "node:path"
+
+import type { TeamModeConfig } from "../../../config/schema/team-mode"
+import { getTasksDir, resolveBaseDir } from "../team-registry"
+import { atomicWrite, withLock } from "../team-state-store/locks"
+import { TaskSchema } from "../types"
+import type { Task } from "../types"
+
+const HIGH_WATERMARK_FILE = ".highwatermark"
+
+async function readHighWatermark(watermarkPath: string): Promise {
+ try {
+ const watermarkContent = (await readFile(watermarkPath, "utf8")).trim()
+ const parsedWatermark = Number.parseInt(watermarkContent, 10)
+ return Number.isInteger(parsedWatermark) && parsedWatermark >= 0 ? parsedWatermark : 0
+ } catch {
+ await atomicWrite(watermarkPath, "0")
+ return 0
+ }
+}
+
+export async function createTask(
+ teamRunId: string,
+ taskInput: Omit,
+ config: TeamModeConfig,
+): Promise {
+ const tasksDirectory = getTasksDir(resolveBaseDir(config), teamRunId)
+ await mkdir(tasksDirectory, { recursive: true, mode: 0o700 })
+ await mkdir(path.join(tasksDirectory, "claims"), { recursive: true, mode: 0o700 })
+
+ return withLock(path.join(tasksDirectory, ".lock"), async () => {
+ const watermarkPath = path.join(tasksDirectory, HIGH_WATERMARK_FILE)
+ const nextTaskId = (await readHighWatermark(watermarkPath)) + 1
+ await atomicWrite(watermarkPath, String(nextTaskId))
+
+ const now = Date.now()
+ const task = TaskSchema.parse({
+ ...taskInput,
+ version: 1,
+ id: String(nextTaskId),
+ createdAt: now,
+ updatedAt: now,
+ })
+
+ await atomicWrite(
+ path.join(tasksDirectory, `${task.id}.json`),
+ `${JSON.stringify(task, null, 2)}\n`,
+ )
+
+ return task
+ }, { ownerTag: `create-task:${teamRunId}` })
+}