Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a298037
docs(清理): 添加提醒自动清理设计
ezbug Jul 29, 2026
7e6319d
docs(计划): 添加提醒自动清理实施步骤
ezbug Jul 29, 2026
05ddbc5
chore(开发): 忽略隔离工作区和执行账本
ezbug Jul 29, 2026
65887e1
docs(计划): 延后创建 EventKit 测试目标
ezbug Jul 29, 2026
a4c7573
docs(计划): 适配 SDD 任务提取器
ezbug Jul 29, 2026
620d773
feat(清理): 添加提醒候选安全策略
ezbug Jul 29, 2026
2bdd5fb
feat(清理): 验证历史任务真实源
ezbug Jul 29, 2026
b5e8068
test(清理): 覆盖源检查缺失元数据
ezbug Jul 29, 2026
10083cd
feat(清理): 添加双扫描确认状态机
ezbug Jul 29, 2026
2432649
fix(清理): 固定确认间隔下限
ezbug Jul 29, 2026
f57c469
feat(清理): 添加私有候选账本和备份
ezbug Jul 29, 2026
7f651ac
fix(清理): 加固本地备份持久化
ezbug Jul 29, 2026
ea1cac7
fix(清理): 共享本机清理盐
ezbug Jul 29, 2026
5ea2dd4
feat(EventKit): 实现可恢复提醒清理器
ezbug Jul 29, 2026
e17dc65
fix(EventKit): 按原账户恢复提醒列表
ezbug Jul 29, 2026
18e5a1c
fix(EventKit): 加固清理恢复状态机
ezbug Jul 29, 2026
dd89f34
fix(清理): 分离备份版本并保持预演只读
ezbug Jul 29, 2026
2f6eb23
fix(备份): 迁移旧格式并使用用户锁锚点
ezbug Jul 29, 2026
358ab51
fix(清理): 严格校验旧备份并固定锁锚点
ezbug Jul 29, 2026
6c63074
feat(同步): 接入自动清理和恢复命令
ezbug Jul 29, 2026
aaa5bb2
fix(同步): 拒绝冲突模式和歧义列表
ezbug Jul 29, 2026
37f55a1
test(EventKit): 验证清理隔离和恢复闭环
ezbug Jul 29, 2026
fbd24c8
fix(EventKit): 收紧隔离测试和读取超时
ezbug Jul 29, 2026
39862a2
fix(EventKit): 超时后取消提醒读取请求
ezbug Jul 29, 2026
94cf39b
docs(清理): 说明自动删除和恢复边界
ezbug Jul 29, 2026
c0e2ae2
fix(恢复): 只选择真实删除批次
ezbug Jul 29, 2026
cf50f5a
fix(清理): 分离未解析与可恢复备份
ezbug Jul 29, 2026
a90a9a1
fix(运行时): 安全迁移私有目录
ezbug Jul 29, 2026
c421d63
test(命令): 覆盖全部解析模式
ezbug Jul 29, 2026
e29db3f
fix(清理): 同步迁移既有源备份
ezbug Jul 29, 2026
39fdced
feat(sync): 增加 TaskForge Kanban 状态双向同步
ezbug Aug 4, 2026
f77596b
fix(sync): 归一化 planned 状态别名
ezbug Aug 4, 2026
e8ccb69
fix(sync): 记录反向写回失败类别
ezbug Aug 4, 2026
fa4fa23
ci: 仅构建正式产品目标
ezbug Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Run core tests
run: swift run TaskForgeReminderCoreTests
- name: Build release
run: swift build --configuration release
run: swift build --configuration release --product TaskForgeReminderSync
- name: Validate property lists
run: |
plutil -lint Resources/Info.plist
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ __pycache__/
.env
.env.*
Backups/
PruneCandidates.json
PruneBackups/
PruneHashSalt
PruneHashSalt.lock
*.prune-test.json
.worktrees/
.superpowers/
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

All notable changes to this project are documented here.

## Unreleased

- Added automatic cleanup for unfinished, unimportant reminders that are
confirmed absent from both the TaskForge snapshot and durable Vault source.
- Added protection for completed reminders, EventKit priority and six leading
important-title markers.
- Added an unchanged two-scan confirmation window of at least 60 seconds,
private candidate persistence and fail-closed source resolution.
- Added checksummed `0600` pre-deletion backups and
`--restore-last-prune` with a minimum 24-hour restoration grace period.
- Restore selection skips unresolved, zero-deletion and already restored
backups, choosing the newest remaining real deletion batch.
- Added strict read-only `--prune-dry-run` and one-pass `--prune-once`
commands; `--sync` and `--watch` now advance the same pruning state machine
after reverse and forward synchronization.
- Scoped pruning fetches to exactly one configured reminders list, rejecting
same-name ambiguity and leaving all other lists untouched.
- Added a 30-second EventKit fetch timeout with request cancellation and
anonymous, salted pruning logs.
- Preserved the existing TaskForge boundary: reverse completion only marks
source tasks `done`; pruning never deletes TaskForge task lines or files.

## 1.1.0 - 2026-07-29

- Added TaskForge-to-Reminders updates for linked title, date, time and status
Expand Down
72 changes: 38 additions & 34 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,55 @@ TaskForge Reminder Sync is designed to run entirely on the local Mac.
## Data it reads

- TaskForge's local `tasks.v6.bin` cache;
- titles, schedules, status and source metadata for TaskForge tasks;
- reminders in the configured Apple Reminders list;
- Markdown or TaskNotes source files only when a linked reminder is completed.
- the preferences plist entry `flutter.ctl_<fixed-list-id>`;
- titles, status, priority, original schedule and source metadata needed for selected custom-list members;
- reminders only when they carry this tool's marker or match the private index;
- the exact Markdown or TaskNotes source file when an approved reverse write is requested;
- the Vault source referenced by the private index when pruning confirms historical presence.

## Data it writes

- linked reminders in the configured Apple Reminders list;
- completion markers in the matching Vault source task;
- timestamped source-file backups under
`~/Library/Application Support/TaskForgeReminderSync/Backups/`;
- operational logs under `~/Library/Logs/`.
- only the tool-managed Apple status lists and their managed reminders;
- the status of a matching Vault source task during reverse sync;
- verified source-file backups and verified reminder deletion backups;
- private configuration, learned symbols, source references, state history and hashes;
- aggregate operational logs.

## Data it does not send

The application contains no HTTP client, analytics SDK, telemetry, advertising,
crash-reporting service or hosted backend. It does not upload task titles,
Vault paths, reminder contents or source files to this repository or to a
third-party service.
The default private root is:

Apple Reminders may sync through iCloud according to the user's Apple account
and system settings. That synchronization is performed by macOS, not by this
project.
```text
~/Library/Application Support/TaskForgeReminderSync/
```

## Metadata stored in reminders
The root and directories are `0700`; configuration, index, ledger, source backups, reminder
backups and salts are `0600`. The list ID, full source references and reminder mappings are
never committed to Git and are not printed in logs. If permissions, ownership, symlinks,
extended ACLs, schema or hashes are unsafe, the operation fails closed.

Linked reminders contain:
## Data it does not send

- a stable, Base64-encoded TaskForge marker;
- a Base64-encoded source reference used for historical reverse completion;
- a human-readable source type, file path and line number.
The project has no HTTP client, hosted backend, analytics SDK, telemetry, advertising or
crash-reporting service. It does not upload task titles, notes, Vault paths, source files,
personal profiles or list IDs. Apple may sync reminders through iCloud according to the user's
Apple account and macOS settings; that is performed by Apple, not this project.

Anyone who can read the reminder can therefore see its title and source path.
Use a dedicated reminder list and an Apple account you trust.
The tool does not use Computer Use and does not request Calendar, Contacts, Photos, microphone,
camera or location access.

## Permissions
## Reminder notes

- **Reminders full access** is required to create, update and observe reminders.
- **Full Disk Access** may be required by macOS to read TaskForge's sandbox
container or a protected Vault location.
Managed reminder notes contain only a stable TaskForge tool marker and a short source marker.
The marker is intended for management, not secrecy. Full source references, previous status,
source hashes and EventKit mappings remain in the private `0600` index rather than being copied
into reminder notes.

The tool does not request Calendar, Contacts, Photos, microphone, camera or
location access.
## Logs and recovery

## Public repository hygiene
Logs contain status counts, timing and anonymous error categories. They do not contain raw task
IDs, EventKit IDs, titles, notes, list IDs, Vault paths or source paths. Backups are intentionally
sensitive local data because they can contain the fields required to restore a source or
reminder; stop the watcher before manual recovery and preserve their permissions.

Generated build directories, TaskForge cache files, logs, backups, `.env`
files and local plist overrides are excluded by `.gitignore`. Contributors
should still inspect staged files before every push.
Reverse completion changes a source task to `done` and never deletes a TaskForge task. Cleanup
deletes only a managed, low-priority, unfinished reminder after the two-scan gate and verified
backup; ordinary Apple reminders and all TaskForge source tasks are protected.
27 changes: 25 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,37 @@ let package = Package(
.target(
name: "TaskForgeReminderCore"
),
.target(
name: "TaskForgeReminderEventKit",
dependencies: ["TaskForgeReminderCore"]
),
.executableTarget(
name: "TaskForgeReminderSync",
dependencies: ["TaskForgeReminderCore"]
dependencies: [
"TaskForgeReminderCore",
"TaskForgeReminderEventKit"
]
),
.executableTarget(
name: "TaskForgeReminderCoreTests",
dependencies: ["TaskForgeReminderCore"],
dependencies: [
"TaskForgeReminderCore",
"TaskForgeReminderEventKit"
],
path: "Tests/TaskForgeReminderCoreTests"
),
.executableTarget(
name: "TaskForgeReminderCLITests",
dependencies: ["TaskForgeReminderSync"],
path: "Tests/TaskForgeReminderCLITests"
),
.executableTarget(
name: "TaskForgeReminderEventKitTests",
dependencies: [
"TaskForgeReminderCore",
"TaskForgeReminderEventKit"
],
path: "Tests/TaskForgeReminderEventKitTests"
)
]
)
Loading