跳转到内容

Project Management

Structured project management for FreeAnima, based on the Unified Entity Model.

Model note (v0.2 simplified): Milestones and required completion criteria were removed. Projects keep schedule + terminal status; optional background notes live in entity content (edited in the project edit dialog only). Tasks link to projects via body.project_id only.

v1 delivers: project folder tree, project entity, and task ownership migration. OKR, cross-entity links, Gantt/Kanban, and file assets are [v2+] (see Non-goals (v1)).

project_folder (organizational tree, independent from task_list folders)
└── project (leaf — cannot nest)
└── task_item (via body.project_id)
task module (/tasks) — lists, smart lists, backlog tasks (body.project_id empty)

Project folders and task-list folders are separate trees. The task module remains the Backlog / ad-hoc task pool for items not assigned to a project.


Independent folder hierarchy for project management only — not shared with task-list folders (task_list.is_folder).

RuleDetail
NestingUnlimited depth; pure organization
No lifecycleNo start/end dates or terminal status
Parentbody.parent_id → another project_folder entity id, or null at root
Cycle preventionSame as task-list folders — nesting must not form cycles
DeleteRecursively removes sub-folders; contained projects get folder_id: null (projects are not deleted)

[v2+] Folder-level Gantt / Kanban views aggregating all projects under the folder.

Example layout:

FreeAnima (product tag or top folder name)
├── 0.8
│ └── Ship 0.8.5 (project)
└── 0.9
└── Memory pipeline v2 (project)

RuleDetail
Leaf nodeProjects cannot nest. Use project_folder for grouping
ScheduleRequired start_at and end_at (or explicit end condition encoded as end_at) at create time
BackgroundOptional entity content — project background / notes; not shown in list or task detail panes
Terminal statuscompleted / cancelled / on_hold (plus default active)

Edit background notes only in the project edit dialog (ProjectEditorDialog). The create dialog collects title + schedule only; the main project view shows dates in the header and tasks in the list — not content.

Not a project:

  • Ongoing product maintenance (no defined end)
  • Ad-hoc chores such as “clean fridge compressor” (no planned schedule — stays in the task module)
  • task_list folders or multiple task lists (those belong to the task module, not project management)

When a project reaches a terminal state or is deleted, task handling is explicit:

EventTask behavior (v1 default)
status → completedDefault: keep in project (project_id retained). Explicit release_tasks: true moves pending tasks to the default list (Inbox).
status → cancelledSame as completed
status → on_holdTasks keep project_id; project UI read-only or degraded edit
Delete projectAll tasks move to the default list (is_default Inbox): project_id cleared, list_id set

Inactive projects (on_hold / completed / cancelled) are hidden from the project sidebar by default; toggle 显示非活跃 (same pattern as archived task lists).

Project archive semantics use project.status (and optional future archived_at) — do not reuse task_list.body.closed. Task-list archive (closed: true) remains: sidebar hidden, mutations forbidden (清单已归档).

[v2+] Archived project context marks weak reference chains; file co-archive is user-explicit.

TypeDescriptionOwnership
TaskSmallest execution unit; pending / completedBelongs to project when body.project_id is set
Project materialsFiles/docs not tied to a task[v2+] — file entity + project reference
Linked entitiesDiary, notes, clips[v2+] — weak refs via future relationship layer

The task module is the existing /tasks Shell route: multiple task_lists, folder tree, Smart Lists, and archived lists.

RuleDetail
Scope”Not in any project” means task_item.body.project_id is null
Multi-listUnassigned tasks may live in any normal list (including the default list is_default, e.g. Inbox) — not a single Backlog list
VisibilityTasks with project_id set are hidden from task-module list views
Default listThe lazy-created default list remains a normal list inside the task module — it is not a sync inbox between task module and projects
No sync inboxThere is no auto two-way sync or “inbox” that requires tidying on both sides. Tasks belong to either the task module or a project at any moment

Smart Lists and archived lists follow the same module; see Smart Lists.


A task belongs to one side at a time: either the task module (Backlog) or exactly one project. Ownership is mutually exclusive on the body fields.

ActionEffect
Drag / move task from Backlog → projectSet project_id; clear list_id; task disappears from task-module views and list counts
Move task from project → listSet list_id (user picks list); clear project_id — there is no “restore previous list” / 移回清单
Move task project A → project BDirect transfer; list_id stays null
Global searchMatches all tasks; result shows归属 Backlog / {list name} or Project / {project title}; click navigates to the owning surface
FieldTypeRule
list_idnumber | nullRequired when in Backlog (project_id null). Null when in a project — do not retain last list id
project_idnumber | nullSet when task is in a project; null when in Backlog

Invariants:

  • Exactly one of list_id / project_id is non-null
  • Task-module visibility ⇔ project_id IS NULL (and list_id set)
  • Project-module visibility ⇔ project_id = {current project} (and list_id null)
  • list_id must not point at a folder (task_list.is_folder)
  • List item_count counts only backlog tasks (project_id null) for that list_id

Pomodoro focus (pomodoro_task_focus.body.task_item_id) is unchanged — focus is by task id regardless of project归属.


Existing smart_list entities and built-in presets (Today, Tomorrow, etc.) remain in the task module.

RuleDetail
Default scopeSmart List queries include only tasks with project_id null, unless the filter explicitly includes project_id
v1Do not mix in-project tasks into Smart List results (consistent with Backlog-only task-module views)
[v2+]Presets filtered by project_id or cross-project views

Aligned with entity-model Shell scope:

  • project_folder and project live in the current subject’s default private world
  • Habitat RPC methods accept optional subject_kind: user | agent (default user, same as tasks)
  • v1: no cross user/agent world sharing for projects

Surfacev1 behavior
task.search / entity_searchInclude project_id, project_title, list_name in results
ToolSet projectCRUD for folders and projects (load via toolset_load); project_create / project_patch accept optional content for background notes
ToolSet tasktask_create / task_update support project_id; task_list / task_search filter by project_id (mutually exclusive with list_id; default list is Backlog only)

OKR sits above projects: an Objective splits into Key Results; KRs land via one or more projects. OKR answers why and how much; projects answer how and when.

Product is a ongoing namespace (e.g. “FreeAnima”) — not a project, does not end or archive. v1: use project.body.product_tag?: string or top-level folder naming; no separate product entity.


[v2+] Linked entities (external references)

Section titled “[v2+] Linked entities (external references)”

Projects may weak-link independent entities:

EntityRelationNotes
DiaryWeak refdiary_entry exists today; link table or relationship layer [v2+]
NoteWeak refNot implemented
ClipWeak refNot implemented
File / photoAttribution refFile module not implemented; delete project does not delete files

Same diary may link to multiple projects. Requires future memory_references / relationship table per entity-model.


Files and photos are global assets with project as one reference entry. Deleting a project does not delete files. Archive behavior for references is user-explicit.


AspectDecision
Folder treesSeparateproject_folder vs task_list.is_folder
Task moduleRetained — Backlog + ad-hoc pool
ConvergenceIf Backlog usage shrinks, reconsider later; do not pre-merge modules

RouteLayout
/projectsFolder tree + project task list + task detail pane (responsive ThreeColumnLayout); no milestone column or milestone dialogs
/tasksUnchanged; hides project_id tasks

Project background notes (entity.content) are edited in the project edit dialog only — not in the sidebar, project header, or task list.

Both inherit Shell User / Agent toggle via subject_kind.


  • Existing tasks: project_id absent / null — behavior unchanged
  • Simplified model migration: drops milestone entities, clears task_item.body.milestone_id, removes project.body.completion_criteria; when entity.content is empty, backfills from former completion_criteria
  • New components registered in entity component index

Explicit out of scope for the first implementation:

  • OKR entities and KR → project mapping
  • Gantt / Kanban (including folder-level cross-project views)
  • Notes, clips, file/photo library and project material uploads
  • Cross-entity reference UI and archived reference context
  • Habitat admin surfaces for projects
  • Cross user/agent world project sharing

Entity type: content. Text fields use entity columns where noted.

LocationFieldTypeNotes
entitytitlestringFolder name
bodyparent_idnumber | nullParent folder entity id
bodysort_ordernumberSibling order
LocationFieldTypeNotes
entitytitlestringProject name
entitycontentstringOptional background / notes (edit dialog only in UI)
bodyfolder_idnumber | nullParent folder
bodystart_atstringISO 8601
bodyend_atstringISO 8601
bodystatusenumactive | completed | cancelled | on_hold
bodyproduct_tagstringOptional v1 product label
bodysort_ordernumberAmong siblings in folder
LocationFieldTypeNotes
bodyproject_idnumber | nullSet when in project
bodylist_idnumberExisting; see Task ownership

卫星壳 Project UI 走 IndexedDB 快照 + outbox(与 Diary / Task 同级)。写 RPC 支持可选 client_op_id 幂等;详见 offline-platform.md


All methods optional subject_kind: user | agent (default user). Transport: POST|WS /rpc/v1.

MethodPurpose
projectfolder.listList folder tree (include nested or flat with parent_id)
projectfolder.createCreate folder (parent_id?, title, sort_order?)
projectfolder.patchRename, reparent, reorder
projectfolder.deleteDelete folder tree; projects → folder_id: null
MethodPurpose
project.listList projects (folder_id?, status?)
project.statsPer-project task_count for sidebar badges (folder_id?, status?)
project.createCreate project (required title, start_at, end_at; optional content, folder_id, …)
project.getProject detail
project.patchUpdate fields (including optional content), terminal status, or release_tasks side-effect
project.deleteDelete project; tasks move to default list (Inbox)

Task item Habitat methods(归属拆分)

Section titled “Task item Habitat methods(归属拆分)”
MethodPurpose
tasklist.item.list任务模块列任务(清单 / Backlog;默认排除项目内)
tasklist.item.create任务模块建任务(只认 list_id,可省略→收件箱)
project.item.list项目模块列任务(必填 project_id
project.item.create项目模块建任务(只认 project_id
task.moveToProject显式移入项目(清空 list_id
task.moveToList显式移回清单(清空 project_id
task.patch仅内容字段(标题/优先级/截止等;不含归属)
task.search跨归属搜索;结果含 project/list 归属
task.complete / uncomplete / delete按 id 共享操作

Implementation target: src/features/project/ (domain + habitat + ui/spa

  • plugin.ts); schemas under src/host/core/db/schema/entity/components/; SAP frames under src/shared/rpc-contract/frames/.

See also entity-model — Project module.