Skip to main content

acshame

  1. ┌─────────────────────────────────────────────┐
    │ Refile: 00.000.0001 - 快速记录              │
    ├─────────────────────────────────────────────┤
    │ [1] personal                                │
    │ [2] work-project                            │
    │ [3] study                                   │
    │ [q] Cancel                                  │
    └─────────────────────────────────────────────┘
             ↓ 按 2
    ┌─────────────────────────────────────────────┐
    │ Refile to: work-project                     │
    ├─────────────────────────────────────────────┤
    │ [20] 项目管理                               │
    │ [21] 开发                                   │
    │ [22] 设计                                   │
    │ [+] 新建 Area                               │
    │ [q] Back                                    │
    └─────────────────────────────────────────────┘
             ↓ 按 20
    ┌─────────────────────────────────────────────┐
    │ Refile to: work-project/20                  │
    ├─────────────────────────────────────────────┤
    │ [001] 任务                                  │
    │ [002] 会议                                  │
    │ [+] 新建 Category                           │
    │ [q] Back                                    │
    └─────────────────────────────────────────────┘
             ↓ 按 001
    ✓ Moved to: work-project/20/001/20.001.0003-task-快速记录.md
    


    **关键设计**:
    - 用户只需选"要去哪个区",**不需要手动输入 ID**
    - 系统自动分配下一个可用 ID
    - 支持快捷键直接跳转(数字键选择)
    - 支持 / 搜索过滤 Namespace/Area/Category

    ### 5.5 引用自动更新(Link Refactoring)

    **问题**:Refile 导致 ID 变化时,其他文件中的引用会断裂。

    
    场景:
    1. Inbox 中有 00.000.0001(想法A)
    2. 笔记 10.002.0005 中写道:"参考 [[00.000.0001]]"
    3. Refile 后,A 变为 20.001.0003
    4. 结果:[[00.000.0001]] 成为 Dead Link
    


    解决方案:自动重构所有引用

    当执行 syn inbox move 且 ID 发生变化时,更新**两种引用形式**:

    
    1. 搜索旧 ID 的引用(使用不同策略):
       - Wiki-style 链接:Ripgrep 搜索 [[00.000.0001]]
       - Frontmatter related:SQLite 索引查询(精确匹配,避免误改)
    2. 自动替换为新 ID
    3. 将所有修改一并加入 Git Commit
    


    **实现逻辑(带安全回滚)**:

    ⚠️ **原子性保证**:Refile 涉及跨多文件修改,必须保证原子性。

    ⚠️ **安全警告**:绝对不能使用 `git reset --hard`!它会清空**所有**未提交的修改,包括与 Refile 无关的用户编辑。

    策略 A:前置检查(推荐)

    在开始 Refile 前,检查工作区是否干净:
  2. ## 5. Inbox 处理与最佳实践

    ### 5.1 Inbox 概念

    - Inbox 是特殊 namespace(`_inbox`),Area 固定为 00
    - 快速捕获的内容默认进入 Inbox,状态为 unprocessed
    - Inbox 中的资源无需完整,支持最小化输入

    ### 5.2 处理操作

    | 操作 | 说明 | 命令 |
    |------|------|------|
    | 归类 (Refile) | 移动到目标 namespace,分配 Area.Category | syn inbox move <id> --to <ns> --type <type> |
    | 转换 | 转换资源类型(如 note → task) | syn inbox convert <id> --type task |
    | 归档 | 原地标记 status=`archived`,不移动文件 | syn inbox archive <id> |
    | 删除 | 永久删除(Git 可恢复) | syn inbox delete <id> |

    **归档说明**:
    - 归档操作**不移动文件**,仅修改 Frontmatter 中的 status 字段为 archived
    - 归档后的资源在列表中默认隐藏,可通过 --include-archived 显示
    - 如需移动到特定归档目录,应使用 syn inbox move --to archive-namespace
    - **`unprocessed` 类型特殊处理**:由于 unprocessed 状态不可变,归档时自动转换为 note 类型并标记 status=archived
    
      syn inbox archive 00.000.0001
      # unprocessed → note (status=archived)
      # 文件重命名:00.000.0001-unprocessed-xxx.md → 00.000.0001-note-xxx.md
      


    ### 5.3 Inbox 处理最佳实践(GTD 风格)

    处理流程决策树:

    
                        ┌─────────────┐
                        │  Inbox 项目  │
                        └──────┬──────┘
                               │
                        ┌──────▼──────┐
                        │ 这是什么?   │
                        │ 需要行动吗? │
                        └──────┬──────┘
                               │
                  ┌────────────┼────────────┐
                  │            │            │
             ┌────▼────┐  ┌────▼────┐  ┌────▼────┐
             │  不需要  │  │ 需要行动 │  │ 参考资料 │
             └────┬────┘  └────┬────┘  └────┬────┘
                  │            │            │
             ┌────▼────┐       │       ┌────▼────┐
             │  删除   │       │       │ 归类为  │
             │  或归档  │       │       │  note   │
             └─────────┘       │       └─────────┘
                               │
                  ┌────────────┼────────────┐
                  │            │            │
             ┌────▼────┐  ┌────▼────┐  ┌────▼────┐
             │ < 2分钟  │  │ 委派他人 │  │ 需要规划 │
             │ 立即做! │  │         │  │         │
             └────┬────┘  └────┬────┘  └────┬────┘
                  │            │            │
             ┌────▼────┐  ┌────▼────┐  ┌────▼────┐
             │ 完成后   │  │ 创建任务 │  │ 创建任务 │
             │ 归档/删除│  │ 分配给   │  │ 设置    │
             └─────────┘  │ 他人     │  │ 截止日期 │
                          └─────────┘  └─────────┘
    


    最佳实践规则:

    1. **2 分钟规则**:如果一件事 2 分钟内能完成,立即做,不要放入任务列表
    2. **每日清空**:每天至少处理一次 Inbox,目标是 Inbox Zero
    3. **快速决策**:每个项目只处理一次,立即决定其去向
    4. **最小化输入**:捕获时只记录关键信息,处理时再补充细节

    处理快捷流程:

    
    # 批量处理 Inbox
    syn inbox process
    
    # 交互式处理(逐项显示,提供操作选项)
    # [m]ove | [c]onvert | [a]rchive | [d]elete | [s]kip
    
    # 快速转换为任务
    syn inbox convert <id> --type task --due tomorrow --priority high
    
    # 快速归类
    syn inbox move <id> --to work-project --type meeting
    


    TUI Inbox 处理快捷键:

    | 快捷键 | 操作 | 说明 |
    |--------|------|------|
    | Enter | 查看详情 | 与全局快捷键一致 |
    | e | 编辑后处理 | 打开编辑器,关闭后弹出处理选项 |
    | m | 移动(Refile) | 层级菜单选择目标位置 |
    | t | 快速转换为 task | 一键转换,保留原位置 |
    | a | 归档 | 标记 status=archived |
    | d | 删除 | 永久删除(Git 可恢复) |

    ### 5.4 Refile 交互优化(类似 Emacs Org-mode)

    m (move) 是最高频操作,TUI 实现层级菜单式 Refile:
  3. ┌─────────────────────────────────────────────┐
    @work-project/20 │ ← 范围搜索
    ├─────────────────────────────────────────────┤
    │ 显示 work-project namespace Area 20 资源 │
    └─────────────────────────────────────────────┘
    
    
    ### 4.5 上下文感知交互
    
    | 视图 | `Enter` | `Space` | `e` |
    |------|---------|---------|-----|
    | Task 列表 | 查看详情 | 切换 status: `todo` → `in-progress` → `done` | 编辑 |
    | Meeting 列表 | 查看详情 | 切换 status: `scheduled` ↔ `completed` | 编辑 |
    | Email 列表 | 查看详情 | 切换 status: `unread` → `read` → `replied` | 编辑 |
    | Note 列表 | 查看详情 | (无活跃状态循环,使用 `a` 键归档) | 编辑 |
    | Calendar (日) | 资源详情 | 同上(根据资源类型) | 新建事项 |
    | Inbox | 查看详情 | (unprocessed 状态不可切换) | 编辑后处理 |
    
    **Space 状态循环**(仅限活跃状态):
    - Task: `todo` → `in-progress` → `done` → `todo`
    - Meeting: `scheduled` ↔ `completed`(二元切换)
    - Email: `unread` → `read` → `replied` → `unread`
    - Note: **无 Space 循环**(仅有 `active`/`archived` 两态,使用 `a` 键切换)
    
    **终态处理**(`archived` / `cancelled`):
    - `archived` 和 `cancelled` 是**终态**,不参与 Space 循环
    - 当资源处于终态时,Space 键**无响应**(或显示提示"资源已归档/取消")
    - 进入/退出终态:使用 `a` 键(归档/取消归档)或编辑 Frontmatter
    
    | 状态 | 类型 | 进入方式 | 退出方式 |
    |------|------|----------|----------|
    | `archived` | task, email | `a` 键归档 | `a` 键取消归档(恢复到 `todo`/`unread`) |
    | `archived` | note | `a` 键归档 | `a` 键取消归档(恢复到 `active`) |
    | `cancelled` | meeting | 编辑 status 字段 | 编辑 status 字段 |
    
    **`a` 键行为**:
    - 活跃状态 → `archived`(归档)
    - `archived` → 恢复到默认活跃状态(task→`todo`, email→`unread`, note→`active`)
    
    ### 4.6 编辑器配置
    
    | 平台 | 默认 | 可选 |
    |------|------|------|
    | Windows | notepad | typora, vscode |
    | Linux | vim | typora |
    | macOS | vim | typora |
    
    ### 4.7 日历视图
    
    **时间来源**:
    - task: `due` / `start_date`
    - meeting: `datetime` / `duration`
    
    **条带说明**:每个条带代表一个资源,高度表示时间跨度,颜色表示类型/优先级。
    
    **交互**:
    - `↑↓` 在同一天的条带间切换选中
    - `←→` 在日期间导航
    - 选中条带时,底部显示资源摘要
    - `Enter` 查看资源详情
    
    

    Calendar - Week (2024-W03) [personal]
    ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┐
    │ Mon 15 │ Tue 16 │ Wed 17 │ Thu 18 │ Fri 19 │ Sat 20 │ Sun 21 │
    ├────────┼────────┼────────┼────────┼────────┼────────┼────────┤
    │ ▓▓▓▓ │ ██ │ ▓▓▓▓▓▓ │ ████ │ ██ │ │ │
    │ ████ │ │ ▓▓▓▓ │ ▓▓ │ │ │ │
    ├────────┴────────┴────────┴────────┴────────┴────────┴────────┤
    [Wed 14:00] 项目评审会议 (meeting) - 14:00~16:00 │
    │ Attendees: @alice, @bob │
    └──────────────────────────────────────────────────────────────┘
    [↑↓] Select [←→] Navigate [Enter] Details [w/m] Week/Month [q] Back

    图例:▓▓ = meeting (紫色) ██ = task (蓝色) ░░ = note (灰色)
    
    
    **跨天显示**:start_date 到 due 的任务、跨天会议,在每天都显示连续条带。
    
    ### 4.8 时间线视图
    
    **时间来源**:Git commit 时间戳(资源的 `modified` 时间)
    
    **条带说明**:每列代表一天,条带高度表示当天活动密度,每个条带对应一次 commit。
    
    **交互**:
    - `←→` 在时间轴上导航
    - `↑↓` 在同一天的 commit 间切换
    - 选中 commit 时,底部显示 commit 信息和涉及的资源
    - `Enter` 跳转到该资源
    
    

    Timeline - Last 7 Days [personal]
    ─────┬──────┬──────┬──────┬──────┬──────┬──────┬─────────
    │ ▓▓ │ ████ │ ██ │ ████ │ ▓▓ │██████│ ██
    │ ████ │ ▓▓ │ ████ │ ██ │ ████ │ ▓▓▓▓ │ ████
    ─────┴──────┴──────┴──────┴──────┴──────┴──────┴─────────
    Mon Tue Wed Thu Fri Sat Sun

    [Sun 14:32] feat(task): add new task for review
    10.001.0015-task-完成项目设计.md
    ──────────────────────────────────────────────────────────
    [↑↓] Select [←→] Navigate [Enter] Resource [d/w/m/y] Scale [q] Back

    图例:▓▓ = meeting ██ = task ░░ = note (颜色同日历视图)
    
    
    ### 4.9 会话持久化(Session Persistence)
    
    系统在 `.synapse/session.json`(不入 Git)中记录当前上下文状态:
    
    ```json
    {
      "last_active_namespace": "work-project",
      "namespaces": {
        "personal": {
          "last_view": "calendar",
          "selected_date": "2024-01-20",
          "recent_files": ["10.001.0001", "10.002.0005"]
        },
        "work-project": {
          "last_view": "list",
          "filter_state": {"status": "todo", "tag": "urgent"},
          "recent_files": ["20.001.0003"]
        }
      },
      "global_history": [
        "syn show 10.001.0001",
        "syn search kubernetes"
      ]
    }
    


    **行为**:
    - 启动 syn 时,自动恢复到上次退出的 Namespace 和 View
    - Ctrl+Tab 可在最近打开的两个文件/视图间快速切换
    - 每个 Namespace 独立记录筛选状态和最近文件

    ---
  4. workspace/
    ├── .synapse/                              # 系统目录
    │   ├── config.yaml                       # 全局配置
    │   ├── index.db                          # SQLite 索引
    │   └── hooks/                            # Git hooks
    ├── personal/                              # namespace
    │   ├── .synapse.yaml                     # namespace 配置
    │   ├── 10/                               # Area 10: 个人管理
    │   │   ├── 001/                          # Category 001: 任务
    │   │   │   ├── 10.001.0001-task-完成报告.md
    │   │   │   └── 10.001.0002-task-学习计划.md
    │   │   └── 002/                          # Category 002: 笔记
    │   │       └── 10.002.0001-note-学习笔记.md
    │   └── 11/                               # Area 11: 会议
    │       └── 001/
    │           └── 11.001.0001-meeting-周会.md
    ├── work-project/
    │   ├── .synapse.yaml
    │   └── 20/
    │       └── 001/
    │           ├── 20.001.0001-meeting-需求评审.md
    │           └── 20.001.0002-task-开发任务.md
    └── _inbox/                                # 特殊 namespace
        └── 00/
            └── 000/                          # Inbox 固定使用 00.000
                ├── 00.000.0001-unprocessed-快速记录.md
                └── 00.000.0002-unprocessed-临时想法.md
    


    **设计说明**:
    - **双层分桶**:Area → Category,避免单目录大量文件
    - 每个 Category 目录最多 9999 个文件,人眼可管理
    - 查询走 SQLite 索引,目录层级不影响性能
    - Area/Category 子目录由系统自动创建

    ---

    ## 4. 用户界面

    ### 4.1 统一命令设计

    CLI 和 TUI 使用统一的命令/操作命名:

    | 操作 | CLI 命令 | TUI 快捷键 | 说明 |
    |------|----------|------------|------|
    | 新建 | syn new [type] | n | 创建新资源 |
    | 列表 | syn list [type] | l | 列表视图 |
    | 查看 | syn show <id> | Enter | 查看详情 |
    | 编辑 | syn edit <id> | e | 编辑资源 |
    | 删除 | syn delete <id> | d | 删除资源 |
    | 搜索 | syn search <query> | / | 搜索过滤 |
    | 日历 | syn calendar | c | 日历视图 |
    | 时间线 | syn timeline | t | 时间线视图 |
    | 图谱 | syn graph | g | 图谱视图 |

    ### 4.2 CLI 命令示例

    
    # TUI 启动
    syn
    
    # 资源操作
    syn new task --title "完成设计" --priority high --due 2024-01-15
    syn new note --title "会议记录"
    syn list task --status todo
    syn show 10.001.0015        # 完整 ID
    syn show 10.1.15            # 短 ID(自动补零)
    syn show 15                 # 最短 ID(当前 Namespace 内唯一)
    syn edit 10.001.0015
    syn delete 10.001.0015
    syn search "kubernetes" --type note --tag devops
    
    # Inbox 操作
    syn inbox list
    syn inbox process
    syn inbox move <id> --to <namespace> --type <type>
    
    # 回顾操作
    syn review daily
    syn review weekly
    
    # Namespace 操作
    syn ns list
    syn ns new my-project
    syn ns switch work-project
    


    ### 4.3 TUI 快捷键

    | 快捷键 | 功能 |
    |--------|------|
    | n | 新建资源 |
    | e | 编辑(外部编辑器) |
    | d | 删除 |
    | a | 归档/取消归档(切换 archived 状态) |
    | / | 搜索/Fuzzy Finder |
    | Enter | 查看详情 |
    | Space | 切换活跃状态(见 §4.5 状态循环) |
    | r | 刷新 |
    | q | 退出/返回 |
    | ? | 帮助 |
    | Ctrl+P | 命令面板 |
    | l | 列表视图 |
    | c | 日历视图 |
    | t | 时间线视图 |
    | g | 图谱视图 |
    | 0 | 切换到 Inbox |
    | 1-9 | 切换 Namespace |

    ### 4.4 命令面板 (Command Palette)

    参考 VS Code / Obsidian 的交互逻辑,区分不同模式:

    | 快捷键 | 模式 | 功能 |
    |--------|------|------|
    | Ctrl+P | 文件跳转 | Fuzzy Search Title/ID |
    | Ctrl+Shift+P> | 命令执行 | 执行命令 |
    | # | Tag 搜索 | 按标签过滤 |
    | @ | 范围搜索 | 按 Namespace/Area 过滤 |

    **交互示例**:
    `
    ┌─────────────────────────────────────────────┐
    │ 设计文档 │ ← 默认:文件跳转
    ├─────────────────────────────────────────────┤
    │ 10.001.0015 - 完成项目设计 │
    │ 10.002.0003 - 设计规范笔记 │
    └─────────────────────────────────────────────┘

    ┌─────────────────────────────────────────────┐
    │ > review │ ← 命令模式
    ├─────────────────────────────────────────────┤
    │ syn review daily │
    │ syn review weekly │
    └─────────────────────────────────────────────┘

    ┌─────────────────────────────────────────────┐
    #urgent │ ← Tag 搜索
    ├─────────────────────────────────────────────┤
    │ 10.001.0015 - 紧急任务 [urgent, work] │
    └─────────────────────────────────────────────┘
  5. def allocate_next_id(namespace: str, area: str, category: str) -> str:
        """
        原子分配下一个可用 ID
        使用 IMMEDIATE 事务模式 + UNIQUE 约束确保并发安全
        """
        max_retries = 3
        for attempt in range(max_retries):
            try:
                # ⚠️ 关键:使用 IMMEDIATE 模式,事务开始时立即获取写锁
                # 这会阻塞其他写事务,避免空 Category 的竞态条件
                cursor.execute("BEGIN IMMEDIATE")
                
                # 1. 查询当前 Category 下最大 ID
                # 同时查询 id_reservations 表(可能有预占但未使用的 ID)
                cursor.execute("""
                    SELECT MAX(id_num) FROM (
                        SELECT CAST(SUBSTR(jd_number, -4) AS INTEGER) as id_num
                        FROM resources 
                        WHERE namespace = ? AND jd_number LIKE ?
                        UNION ALL
                        SELECT CAST(SUBSTR(jd_number, -4) AS INTEGER) as id_num
                        FROM id_reservations
                        WHERE namespace = ? AND jd_number LIKE ?
                    )
                """, (namespace, f"{area}.{category}.%", 
                      namespace, f"{area}.{category}.%"))
                
                row = cursor.fetchone()
                max_id = (row[0] if row else None) or 0
                next_id = max_id + 1
                
                if next_id > 9999:
                    cursor.execute("ROLLBACK")
                    raise OverflowError(f"Category {area}.{category} ID exhausted (max 9999)")
                
                # 2. 预占 ID(UNIQUE 约束是最后一道防线)
                new_jd = f"{area}.{category}.{next_id:04d}"
                cursor.execute("""
                    INSERT INTO id_reservations (jd_number, namespace, reserved_at)
                    VALUES (?, ?, datetime('now'))
                """, (new_jd, namespace))
                
                cursor.execute("COMMIT")
                return new_jd
                
            except sqlite3.IntegrityError:
                # UNIQUE 约束冲突,说明有并发分配,重试
                cursor.execute("ROLLBACK")
                if attempt == max_retries - 1:
                    raise AllocationError(f"Failed to allocate ID after {max_retries} retries")
                continue  # 重试
    
    def release_reservation(jd_number: str):
        """创建失败时释放预占的 ID"""
        cursor.execute("DELETE FROM id_reservations WHERE jd_number = ?", (jd_number,))
    


    **id_reservations 表结构**:

    
    CREATE TABLE id_reservations (
        jd_number TEXT PRIMARY KEY,   -- ⚠️ UNIQUE 约束,防止重复分配
        namespace TEXT NOT NULL,
        reserved_at TEXT NOT NULL,
        INDEX idx_namespace_jd (namespace, jd_number)
    );
    
    -- 定期清理过期预占(超过 1 小时未使用)
    DELETE FROM id_reservations 
    WHERE reserved_at < datetime('now', '-1 hour');
    


    **并发控制机制**:

    | 层级 | 机制 | 作用 |
    |------|------|------|
    | 1 | BEGIN IMMEDIATE | 事务级写锁,阻塞其他写事务 |
    | 2 | 查询含 id_reservations | 感知其他进程的预占 |
    | 3 | jd_number PRIMARY KEY | UNIQUE 约束,最后防线 |
    | 4 | 重试机制 | 冲突时自动重试 |
    
    
    **批量导入优化**:
    
    ```bash
    # 批量模式:一次性预占 N 个 ID,减少锁竞争
    syn import --batch /path/to/files/ --count 100
    
    # 内部实现:单次事务分配 100 个连续 ID
    


    **ID 耗尽处理**:
    - 每个 Category 最多 9999 个资源
    - 接近上限(>9000)时 CLI/TUI 显示警告
    - 建议用户创建新 Category 或清理归档资源

    ### 3.2 短 ID 支持

    为降低 CLI 输入摩擦,支持 Git 风格的短 ID:

    | 输入方式 | 示例 | 匹配规则 |
    |----------|------|----------|
    | 完整 ID | 10.001.0015 | 精确匹配 |
    | 短 ID | 10.1.15 | 自动补零 → 10.001.0015 |
    | 最短 ID | 15 | 当前 Namespace 内唯一匹配 |
    | 标题模糊 | 设计文档 | Fuzzy Finder 模糊搜索 |

    
    syn show 15           # 当前 Namespace 内唯一,直接匹配
    syn show 10.1.15      # 自动补零
    syn show 设计         # Fuzzy Finder
    


    **歧义处理**:当短 ID 匹配到多个结果时:
    - **CLI**:报错并列出候选项,用户需输入更精确的 ID
    - **TUI**:弹窗让用户选择

    
    $ syn show 15
    Error: Ambiguous ID '15'. Did you mean:
      [1] 10.001.0015 - 完成项目设计 (task)
      [2] 20.001.0015 - 会议纪要 (meeting)
    Use full ID or choose: syn show 10.001.0015
    


    **Shell 自动补全**:CLI 必须实现 Tab completion(支持 Bash/Zsh/Fish)。

    ### 3.3 目录结构(双层分桶)
  6. # Synapse (syn) - 需求规格说明书

    > Synapse - 神经突触,象征知识连接与传递。缩写 syn 简短易记,与知识管理高度契合。

    ---

    ## 1. 引言

    ### 1.1 系统愿景

    Synapse 是一个基于 Python 3 构建的命令行/TUI 工作流管理工具,提供统一的知识与任务管理平台。

    核心理念: "快速捕获、清晰组织、经常回顾、有效执行" (CORE)

    ### 1.2 核心原则

    | 原则 | 描述 |
    |------|------|
    | 快速捕获 | 最小化录入摩擦,新内容默认进入 Inbox |
    | 清晰组织 | Johnny.Decimal 变体 + k8s 风格资源管理 |
    | 经常回顾 | GTD、周期性和智能回顾模式 |
    | 有效执行 | 任务跟踪、状态管理、自动化工作流 |

    ### 1.3 目标用户

    - **个人用户**:知识管理、任务追踪
    - **团队用户**:协作、项目管理
    - 支持个人使用,可扩展到团队协作

    ### 1.4 技术栈

    | 组件 | 选型 | 说明 |
    |------|------|------|
    | TUI | Textual | CSS 布局,鼠标支持,适合 k9s 风格 |
    | 存储 | Markdown | 仅 .md 文件,禁止二进制 |
    | 索引 | SQLite + FTS5 | Sidecar Index,加速查询 |
    | 搜索 | Ripgrep | 全文搜索,高性能 |
    | 文件监听 | watchdog | 实时检测外部修改 |
    | 版本控制 | Git | 本地 + 远程 |
    | 图谱 | Nebula Graph | GraphRAG 就绪(优先级低) |
    | 自动化 | 本地 Hook + GitHub Actions | 本地优先,远程通知 |

    ---

    ## 2. 资源管理

    ### 2.1 资源标识

    **文件路径**(双层分桶:Area → Category):
    
    {namespace}/{Area}/{Category}/{Area.Category.ID}-{resource-type}-{title}.md
    


    **示例**:
    
    personal/10/001/10.001.0001-task-完成报告.md
    personal/10/002/10.002.0001-note-学习笔记.md
    work-project/20/001/20.001.0001-meeting-需求评审.md
    _inbox/00/000/00.000.0001-unprocessed-快速记录.md
    


    | 组件 | 说明 |
    |------|------|
    | namespace | 用户自定义(个人/项目名称),对应目录 |
    | Area | JD 顶级分类(00-99),对应子目录 |
    | Category | Area 内分类(000-999),对应子目录 |
    | ID | Category 内序号(0001-9999) |
    | resource-type | 资源类型(task, note, meeting 等) |
    | title | 资源标题(可包含中文) |

    ### 2.2 资源操作规范

    任何资源被操作时(CRUD):
    - 自动触发 Git 提交
    - 更新 modified 时间戳
    - commit message 遵循 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
    - 可选使用 LiteLLM 自动生成 commit message

    **commit 类型**:`feat` | fix | docs | refactor | chore | archive

    ### 2.3 Namespace 管理

    Namespace 作为项目/领域的管理容器:
    - 对应目录:`{workspace}/{namespace}/`
    - 可包含配置文件 .synapse.yaml
    - 支持自定义资源类型和 SOP

    ### 2.4 预设资源类型

    #### 2.4.1 类型与字段定义

    | 类型 | 必填字段 | 可选字段 |
    |------|----------|----------|
    | task | title | status, priority, due, start_date, assignee, blocked_by |
    | note | title | status, tags, links |
    | meeting | title, datetime | status, duration, location, attendees, action_items |
    | email | subject | status, from_addr, to_addr, cc, body |
    | contact | name | organization, email, phone, custom_fields |
    | unprocessed | (无) | status*, title, content |

    > \* unprocessedstatus 字段为系统固定值,不可由用户修改(见 §2.4.2)。

    **contact 支持自定义字段**:IP、MAC、微信号等任意键值对。

    #### 2.4.2 状态定义

    | 类型 | 状态字段 | 可选值 | 默认值 |
    |------|----------|--------|--------|
    | task | status | todo, in-progress, done, archived | todo |
    | meeting | status | scheduled, completed, cancelled | scheduled |
    | email | status | unread, read, replied, archived | unread |
    | note | status | active, archived | active |
    | contact | (无状态) | - | - |
    | unprocessed | (固定) | unprocessed | unprocessed |

    > **说明**:`unprocessed` 类型状态不可变,归档时自动转换为 note 类型(见 §5.2)。

    #### 2.4.3 优先级定义(仅 task)

    | 优先级 | 含义 | TUI 颜色 |
    |--------|------|----------|
    | high | 紧急重要 | 🔴 红色 |
    | medium | 一般 | 🟡 黄色 |
    | low | 可延后 | 🟢 绿色 |

    #### 2.4.4 类型颜色标识

    | 类型 | TUI 颜色 | 说明 |
    |------|----------|------|
    | task | 蓝色 | 待办任务 |
    | note | 灰色 | 笔记 |
    | meeting | 紫色 | 会议 |
    | email | 青色 | 邮件 |
    | contact | 橙色 | 联系人 |
    | unprocessed | 白色/默认 | 未处理 |

    ---

    ## 3. Johnny.Decimal 组织结构

    ### 3.1 编号规则

    采用标准 JD 三级结构,便于分桶管理:

    
    Area.Category.ID
     │      │     │
     │      │     └─ 4位数字 (0001-9999)
     │      └─ 3位数字 (000-999)
     └─ 2位数字 (00-99)
    


    **示例**:`10.001.0015` = Area 10 > Category 001 > 第 15 项

    #### 3.1.1 ID 分配器与并发控制

    **问题**:快速连续创建文件(如脚本批量导入)时,获取"下一个可用 ID"存在竞态条件。

    
    # 并发场景示例
    $ for i in {1..100}; do syn new task --title "Task $i" & done
    # 如果无锁机制,多个进程可能拿到相同的 ID
    


    解决方案:SQLite IMMEDIATE 事务 + UNIQUE 约束

    ⚠️ 为什么 `FOR UPDATE` 在空 Category 不起作用?
    - FOR UPDATE 只能锁定 SELECT 返回的行
    - 空 Category 的 MAX() 查询返回零行,无行可锁
    - 多个进程可能同时得到 max_id = 0`,都尝试分配 `0001

    正确方案:BEGIN IMMEDIATE + UNIQUE 约束双重保障
  7. 将alb的timeout设置为60秒,以在Spring WebClient的idletime中从alb取消连接,从而引发socket closed issue
    将WebClient上的maxIdleTime设置为59秒,以便在60秒之前关闭idle time
  8. acshame
    https://dev.to/aws-builders/kubernetes-503-errors-with-aws-alb-possible-causes-and-solutions-1ddh
    ## Intermittent 503 Error Analysis

    ### Root Cause
    You have a connection timeout mismatch:
    - Spring Gateway maxIdle: 59 seconds
    - ALB idle timeout: 60 seconds

    ### Why This Causes 503

    Timeline of the problem:
    1. At 59s: Spring Gateway closes the idle connection
    2. At 60s: ALB still thinks the connection is open
    3. New request arrives → ALB tries to use the closed connection
    4. Result: 503 Service Unavailable

    ### The Rule
    Backend timeout must be GREATER than load balancer timeout

    ✗ Wrong:  Gateway 59s < ALB 60s  → 503 errors
    ✓ Correct: Gateway 65s > ALB 60s  → No errors

    ### Solution

    Option 1: Increase Spring Gateway timeout (Recommended)
    spring:
      cloud:
        gateway:
          httpclient:
            pool:
              max-idle-time: 65s  # Must be > 60s

    Option 2: Decrease ALB timeout
    # Set ALB to 55 seconds
    alb.ingress.kubernetes.io/load-balancer-attributes: 
      idle_timeout.timeout_seconds=55

    ### Why This Happens
    - Occurs during low traffic (connections stay idle longer)
    - Creates a 1-second race condition (59s-60s window)
    - ALB reuses a connection that Spring already closed

    ### Validation from AWS
    AWS documentation confirms: backend keep-alive timeout should be greater than the load balancer's idle timeout [AWS re:Post](https://repost.aws/knowledge-center/eks-http-504-errors) to prevent exactly this issue.

    Your diagnosis is 100% correct! This is a classic connection pool timing problem.
  9. 我现在有个服务部署在eks pod 中,通过alb 对外开放。网络和服务都运行正常,但是偶尔出现 503 service unavailable。

    怀疑是 intermitten 503 由于
    服务端 spring gateway maxidle 59s ,但是elb idle timeout 60s 导致的。


    你帮我分析我的怀疑是否合理,然后若是合理请搜索类似的案例