mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
docs(v10): self-contained worktree briefs for Phase 1-4
Splits the v10 Phase 1-4 work into 5 self-contained briefs in .v10-worktrees/, one per worktree. Each brief is meant to be handed to a single agent or developer: worktree-a-banners.md — Phase 1 deprecation banners (parallel with B) worktree-b-archiver.md — Phase 1 Archiver pure functions (parallel with A) worktree-c-readonly.md — Phase 2 read-only mode + 10.0.0-beta.1 cut worktree-d-cliff.md — Phase 3 the cliff (5 sub-PRs: D.2 → D.1 → D.3 → D.4 → D.5) worktree-e-cleanup.md — Phase 4 cleanup + calendar-sync v0.1.0 release Each brief includes: - Setup commands (git worktree add, branch off, npm install) - Files to modify and files to create - Numbered task list with calendar estimates - Definition of Done (per worktree) - Conflicts to watch (cross-worktree) - Open questions affecting THIS worktree (subset of the 5 main-plan questions) - "Don't do these things" guardrails The 5 main-plan open questions have been resolved with sensible defaults (documented in .v10-worktrees/README.md). User can override before any worktree commits to a particular answer; most decisions don't kick in until Worktree D, giving a 3-4 week runway. Phase 0 deferred items (TaskView.ts deletion, settings-migration.ts fold) are explicitly assigned to Worktree D sub-PRs (D.4 and D.2 respectively), with full resolution paths in PHASE0_DEFERRED.md. WORKTREE_PLAN.md updated with a header pointer to the new briefs and preserved as the umbrella overview.
This commit is contained in:
parent
e90fc53706
commit
d5d03cfd6b
7 changed files with 1626 additions and 0 deletions
88
.v10-worktrees/README.md
Normal file
88
.v10-worktrees/README.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Task Genius v10 — Worktree Briefs
|
||||
|
||||
This directory holds **self-contained briefs** for the 5 worktrees that
|
||||
execute v10 Phase 1-4. Each brief is meant to be handed to a single agent
|
||||
(or developer) who will create a worktree, do the work, and merge.
|
||||
|
||||
The full v10 plan lives at `~/.claude/plans/dynamic-mixing-pie.md` (in the
|
||||
user's Claude config). Each brief here references the main plan but is
|
||||
written to be readable on its own — an agent picking up brief D should not
|
||||
need to read the entire v10 plan to start working.
|
||||
|
||||
## The 5 worktrees
|
||||
|
||||
| # | Brief | Phase | Branch | Calendar | Depends on |
|
||||
|---|---|---|---|---|---|
|
||||
| **A** | [worktree-a-banners.md](./worktree-a-banners.md) | 1 (9.14.0) | `refactor/v10-phase1-banners` | ~1 wk | Phase 0 (✅ done) |
|
||||
| **B** | [worktree-b-archiver.md](./worktree-b-archiver.md) | 1 (9.14.0) | `refactor/v10-phase1-archiver` | ~1 wk | Phase 0 (✅ done) |
|
||||
| **C** | [worktree-c-readonly.md](./worktree-c-readonly.md) | 2 (9.15.0) | `refactor/v10-phase2-readonly` | ~1 wk | A merged to master |
|
||||
| **D** | [worktree-d-cliff.md](./worktree-d-cliff.md) | 3 (10.0.0) | `refactor/v10-phase3-cliff` | ~1 wk | C merged to master |
|
||||
| **E** | [worktree-e-cleanup.md](./worktree-e-cleanup.md) | 4 (10.0.1) | `refactor/v10-phase4-cleanup` | ~1 wk | D merged to master |
|
||||
|
||||
## How to spawn agents
|
||||
|
||||
**Parallel start (recommended):** spawn agents A and B simultaneously, both
|
||||
branched off `refactor/v10-phase0`. They modify disjoint files. Once A merges
|
||||
to master, spawn C. Then D after C, then E after D.
|
||||
|
||||
**Solo execution:** the single-developer path is `A → B → C → D.2 → D.1 → D.3
|
||||
→ D.4 → D.5 → E`. The worktree split is informational; you can use one working
|
||||
tree if you prefer.
|
||||
|
||||
## Phase 0 status (the ground each worktree stands on)
|
||||
|
||||
Phase 0 is **complete** on `refactor/v10-phase0`. 6 commits, 71 new passing
|
||||
tests, 10 of 12 DoD items checked. The 2 deferred items (delete TaskView.ts,
|
||||
fold settings-migration.ts into the registry) are documented in
|
||||
[../PHASE0_DEFERRED.md](../PHASE0_DEFERRED.md) and explicitly assigned to
|
||||
worktree D sub-PRs (D.4 and D.2 respectively).
|
||||
|
||||
Phase 0 commits on `refactor/v10-phase0`:
|
||||
- `dd3fe89a` — W0 test infrastructure (buildOrchestrator + InMemoryStorage + localforage mock)
|
||||
- `1de55f4c` — W2/W2-bis/W3 (onunload race fixed, rebuild last-resort, worker timeouts)
|
||||
- `fe4c976f` — W4 (typed cache scope map + invariants checker + LocalStorageCache version fix)
|
||||
- `12481c17` — W1 (MigrationRegistry with version-keyed tombstones)
|
||||
- `d583e967` — W5 (critical-path integration tests: roundtrip / settingsChange / cache invariants sequence)
|
||||
- `e90fc537` — Phase 0 docs (PHASE0_DEFERRED.md, WORKTREE_PLAN.md)
|
||||
|
||||
## Decisions resolved (defaults — user can override before any worktree commits)
|
||||
|
||||
The 5 open questions from the main plan have been answered with sensible
|
||||
defaults so worktrees can start without waiting on more decisions:
|
||||
|
||||
1. **Deprecation list:** ship exactly the 14 features listed in the main plan
|
||||
(Workflow, Habit, Reward, Timer, Gantt, Quadrant, Timeline Sidebar,
|
||||
File-as-Task, Holiday detector, Electron quick-capture, 4 quick-capture
|
||||
modal variants, Working-On view, Flagged view, Habit view). Pull-back can
|
||||
happen at sub-PR review time.
|
||||
|
||||
2. **Sub-plugin commitment:** YES, 4 new repos
|
||||
(`task-genius-workflow`, `task-genius-habits`, `task-genius-timer`,
|
||||
`task-genius-calendar-sync`). Each is small (~3-6k LOC). They share the
|
||||
same tooling as the main plugin; the boundary is "share vault files,
|
||||
nothing else" (no DataflowOrchestrator coupling).
|
||||
|
||||
3. **Default hotkeys:** SHIP the 5 proposed bindings
|
||||
(`Mod+Shift+T` capture, `Mod+Shift+G` open view, `Mod+Shift+I` inbox,
|
||||
`Mod+Shift+F` forecast, `Mod+Alt+P` priority, `Mod+Enter` mark done on
|
||||
task line). Document in CHANGELOG that v10 sets defaults for the first
|
||||
time. Users can opt out via Obsidian's hotkey settings.
|
||||
|
||||
4. **Cliff version:** 9.14 → 9.15 → 10.0 over 4 weeks (the original
|
||||
proposal). Faster is better; the deprecation period exists to give beta
|
||||
testers time, not as a UX runway.
|
||||
|
||||
5. **Beta tester recruitment:** pin a GitHub Discussion explicitly recruiting
|
||||
testers. Don't depend on a pre-existing pool. The Discussion goes up the
|
||||
day Worktree A starts; recruitment runs in parallel with banner work.
|
||||
|
||||
If the user wants to override any of these, they can do so before the
|
||||
relevant worktree commits to a particular answer — most decisions come into
|
||||
play in Worktree D (Phase 3), so the runway is ~3-4 weeks.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../PHASE0_DEFERRED.md`](../PHASE0_DEFERRED.md) — the 2 W6 items deferred from Phase 0
|
||||
- [`../WORKTREE_PLAN.md`](../WORKTREE_PLAN.md) — the original umbrella worktree plan (this directory subsumes it; WORKTREE_PLAN.md kept for reference)
|
||||
- [`../PLAN.md`](../PLAN.md) — the original v10 widgets requirements doc (predates this refactor effort)
|
||||
- `~/.claude/plans/dynamic-mixing-pie.md` — the full v10 refactor plan (in the user's Claude config)
|
||||
234
.v10-worktrees/worktree-a-banners.md
Normal file
234
.v10-worktrees/worktree-a-banners.md
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
# Worktree A — Phase 1 Deprecation Banners (9.14.0)
|
||||
|
||||
## What you're doing
|
||||
|
||||
Task Genius is being slimmed down for v10 (~95k LOC → ~40k LOC). Phase 1 is
|
||||
the **announce + soft warning** week: deprecated settings tabs get a yellow
|
||||
banner, the changelog modal auto-opens once on first launch of 9.14.0, and
|
||||
nothing actually breaks for users yet. This is the gentle on-ramp.
|
||||
|
||||
Your worktree is one of two Phase 1 worktrees that run **in parallel**:
|
||||
- **Worktree A (this one)** — UI banners + changelog modal (user-facing)
|
||||
- **Worktree B** — Archiver pure functions (infrastructure for Phase 3)
|
||||
|
||||
A and B touch disjoint files. Both branch off `refactor/v10-phase0`.
|
||||
|
||||
## Phase 0 ground
|
||||
|
||||
You're branching off `refactor/v10-phase0` (already merged-quality, 6 commits,
|
||||
71 new tests). Phase 0 added the MigrationRegistry, fixed lifecycle hazards,
|
||||
added cache guardrails, and shipped integration test infrastructure. None of
|
||||
that affects your work directly — you're touching settings UI files, not
|
||||
dataflow.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# From the main repo root
|
||||
git fetch
|
||||
git worktree add ../tg-phase1-banners refactor/v10-phase0
|
||||
cd ../tg-phase1-banners
|
||||
git checkout -b refactor/v10-phase1-banners
|
||||
|
||||
# Verify the integration tests pass on this branch
|
||||
npm install
|
||||
npm test -- --testPathPattern="integration/"
|
||||
# Expect: 70+ passing in the integration namespace
|
||||
```
|
||||
|
||||
## Files you'll create
|
||||
|
||||
- `src/components/features/settings/components/DeprecationBanner.ts` — new component (~60 LOC)
|
||||
- `src/common/deprecation-messages.ts` — centralized i18n strings (~80 LOC)
|
||||
|
||||
## Files you'll modify (14 deprecated tabs + supporting files)
|
||||
|
||||
The 14 deprecated tabs each get a one-line `DeprecationBanner` injection at
|
||||
the top of their `renderXxxSettingsTab()` function:
|
||||
|
||||
1. `src/components/features/settings/tabs/WorkflowSettingsTab.ts` → "Workflows move to `task-genius-workflow` in v10"
|
||||
2. `src/components/features/settings/tabs/HabitSettingsTab.ts` → "Habits move to `task-genius-habits`"
|
||||
3. `src/components/features/settings/tabs/RewardSettingsTab.ts` → "Rewards move to `task-genius-habits`"
|
||||
4. `src/components/features/settings/tabs/TaskTimerSettingsTab.ts` → "Timer moves to `task-genius-timer`"
|
||||
5. `src/components/features/settings/tabs/TimelineSidebarSettingsTab.ts` → "Timeline Sidebar removed in v10. Forecast view replaces it."
|
||||
6. `src/components/features/settings/tabs/IcsSettingsTab.ts` (OAuth/CalDAV section only — read-only ICS stays in main plugin) → "OAuth/CalDAV moves to `task-genius-calendar-sync`"
|
||||
7. `src/components/features/settings/tabs/IndexSettingsTab.ts` (file-as-task source section) → "File-as-Task source removed in v10"
|
||||
8. `src/components/features/settings/tabs/DesktopIntegrationSettingsTab.ts` (electron-quick-capture window section) → "Electron quick-capture window removed in v10"
|
||||
9-12. Sections within the views tab: Gantt, Quadrant, Habit view, Working-On view, Flagged view (5 view-config entries deprecated)
|
||||
13. Holiday detector toggle (find via `grep -r "holiday-detector" src/components/features/settings/`)
|
||||
14. AboutSettingsTab — gets a "Deprecations" collapsible at the top
|
||||
|
||||
Plus:
|
||||
- `src/components/features/settings/index.ts` — export the new banner component
|
||||
- `src/managers/changelog-manager.ts` — wire to auto-open the v10 announcement on 9.14.0 first launch
|
||||
- `src/translations/locale/en.ts` — add new strings for banner copy
|
||||
|
||||
## Tasks (in order)
|
||||
|
||||
### Task 1 — Build the DeprecationBanner component (~½ day)
|
||||
|
||||
`src/components/features/settings/components/DeprecationBanner.ts`:
|
||||
|
||||
```ts
|
||||
export interface DeprecationBannerProps {
|
||||
tabId: string;
|
||||
replacementText: string; // e.g. "Workflows move to task-genius-workflow"
|
||||
exportLabel?: string; // e.g. "Export this section now"
|
||||
exportAction?: () => Promise<void> | void;
|
||||
learnMoreUrl?: string;
|
||||
// Phase 2 will add: readOnly?: boolean
|
||||
}
|
||||
|
||||
export function renderDeprecationBanner(
|
||||
containerEl: HTMLElement,
|
||||
props: DeprecationBannerProps,
|
||||
): void {
|
||||
// Yellow background, alert icon, message text, optional Export button,
|
||||
// optional "Learn more" link. ~60 LOC. CSS class .tg-deprecation-banner.
|
||||
}
|
||||
```
|
||||
|
||||
Add CSS in `src/styles/setting.scss` (or wherever the existing settings styles
|
||||
live — grep `.task-genius-setting`).
|
||||
|
||||
### Task 2 — Centralize the strings (~½ day)
|
||||
|
||||
`src/common/deprecation-messages.ts`:
|
||||
|
||||
```ts
|
||||
export const DEPRECATION_MESSAGES = {
|
||||
workflow: {
|
||||
bannerText: "Workflows move to the task-genius-workflow plugin in v10. ...",
|
||||
exportLabel: "Export workflows now",
|
||||
learnMoreUrl: "https://github.com/.../discussions/...",
|
||||
},
|
||||
habit: { ... },
|
||||
// ... 14 total entries
|
||||
} as const;
|
||||
```
|
||||
|
||||
This becomes the single source of truth so locales translate one set of
|
||||
strings, not 14 spread across tabs. Phase 2 will add more keys here.
|
||||
|
||||
### Task 3 — Wire banners into 14 tabs (~1.5 days)
|
||||
|
||||
For each of the 14 tab files, add a one-line `renderDeprecationBanner(containerEl, ...)`
|
||||
call at the top of the render function (before any existing content). The
|
||||
tab continues to function normally; the banner is purely additive.
|
||||
|
||||
For tabs that have a deprecated **section** (not the whole tab) — IcsSettingsTab,
|
||||
IndexSettingsTab, DesktopIntegrationSettingsTab — render the banner above the
|
||||
deprecated section, not the whole tab.
|
||||
|
||||
The "Export this section now" button calls a stub from Worktree B's Archiver:
|
||||
|
||||
```ts
|
||||
import { archiveWorkflows } from "@/migration/v10/Archiver"; // from Worktree B
|
||||
|
||||
exportAction: async () => {
|
||||
const result = await archiveWorkflows(plugin);
|
||||
// For Phase 1 we just write to vault — Phase 3 wraps in transactional migration
|
||||
for (const [path, content] of result.files) {
|
||||
await plugin.app.vault.create(path, content);
|
||||
}
|
||||
new Notice(`Archived ${result.files.size} files to Task Genius Archive/`);
|
||||
},
|
||||
```
|
||||
|
||||
**Coordination with Worktree B:** if B hasn't merged yet, stub the import as
|
||||
`async () => { new Notice("Export coming soon"); }` and add a TODO. Do not
|
||||
block on B.
|
||||
|
||||
### Task 4 — AboutSettingsTab "Deprecations" panel (~½ day)
|
||||
|
||||
`src/components/features/settings/tabs/AboutSettingsTab.ts` — add a collapsible
|
||||
section at the top of the tab listing all 14 deprecations with one global
|
||||
"Export everything" button. The button calls all 7 archive functions in
|
||||
sequence (or sequentially with a progress notice if you want to be fancy).
|
||||
|
||||
### Task 5 — Auto-open v10 announcement on 9.14.0 first launch (~½ day)
|
||||
|
||||
`src/managers/changelog-manager.ts` already auto-opens on version change.
|
||||
Verify the existing logic and add a one-time announcement modal for 9.14.0
|
||||
that links to the pinned GitHub Discussion. Body content goes in
|
||||
`src/translations/locale/en.ts` (a new key like `v10AnnouncementBody`).
|
||||
|
||||
### Task 6 — Update CHANGELOG.md and create the GitHub Discussion (~½ day, out of repo)
|
||||
|
||||
- Add a `## 9.14.0` section to CHANGELOG.md announcing the v10 deprecations
|
||||
- Create a pinned GitHub Discussion titled **"Task Genius v10: what's changing
|
||||
and how to prepare"** with the TL;DR table from the main plan, the archive
|
||||
folder explanation, sub-plugin install instructions, and an FAQ section
|
||||
- Pin a thread for beta tester recruitment
|
||||
|
||||
## Definition of Done
|
||||
|
||||
| # | Criterion | How to verify |
|
||||
|---|---|---|
|
||||
| 1 | All 14 deprecated tabs/sections show banners in real-vault smoke test | Manual: open Settings, visit each, confirm banner is at the top |
|
||||
| 2 | Each banner's "Export this section" button creates files in `<vault>/Task Genius Archive/<section>/` | Manual: click each export button, verify the folder appears |
|
||||
| 3 | Changelog modal auto-opens once on 9.14.0 first launch | Manual: bump manifest.json to 9.14.0, reload plugin, verify modal opens; reload again, verify it does NOT open |
|
||||
| 4 | AboutSettingsTab has a "Deprecations" collapsible at the top with a global "Export everything" button | Manual |
|
||||
| 5 | No regression in existing tab rendering (all 25 tabs still render their existing content) | Manual + `npm test` |
|
||||
| 6 | New strings only in `en.ts`; locale fallback handles other languages | Code review |
|
||||
| 7 | TypeScript build clean (`npm run build`) | Auto |
|
||||
| 8 | Phase 0 integration tests still pass | `npm test -- --testPathPattern="integration/"` |
|
||||
| 9 | CHANGELOG.md updated, GitHub Discussion pinned | Manual |
|
||||
|
||||
## Conflicts to watch
|
||||
|
||||
- **Worktree C (Phase 2)** will modify the same 14 tab files to add read-only
|
||||
mode. **C cannot start until A is merged to master.** Don't try to run them
|
||||
in parallel.
|
||||
- **Worktree D.1 (Phase 3)** will DELETE most of these tabs. The banners get
|
||||
removed in Phase 3. That's expected — your work is the bridge that gives
|
||||
users 4 weeks of warning before the deletion.
|
||||
|
||||
## Open questions that affect THIS worktree
|
||||
|
||||
The v10 plan has 5 open questions; defaults are documented in
|
||||
[`./README.md`](./README.md). The two that touch your work:
|
||||
|
||||
- **Q1 — Deprecation list:** the 14 features listed above are the default. If
|
||||
the user pulls any back, your task list shrinks accordingly. Wait for
|
||||
user confirmation if you're about to ship a banner for a feature they
|
||||
changed their mind about.
|
||||
- **Q5 — Beta tester recruitment:** the GitHub Discussion goes up when your
|
||||
worktree starts. If the user doesn't have a tester pool, the Discussion
|
||||
recruits explicitly.
|
||||
|
||||
The other 3 (sub-plugin commitment, hotkey policy, cliff version) don't
|
||||
affect this worktree — they kick in for Worktree C and D.
|
||||
|
||||
## Useful existing utilities to reuse
|
||||
|
||||
- `src/managers/changelog-manager.ts` — already supports auto-open on version change (Task 5)
|
||||
- `src/components/features/changelog/` — ChangelogView component (re-use for the announcement)
|
||||
- `src/translations/helper.ts` — `t()` translation helper
|
||||
- The existing `Setting()` chain pattern from any tab file is your model for adding the banner above existing settings
|
||||
|
||||
## Don't do these things
|
||||
|
||||
- **Don't delete any tabs.** Phase 3 owns deletions. Banners are additive only.
|
||||
- **Don't make any tab read-only.** That's Phase 2 / Worktree C.
|
||||
- **Don't wire the banners' export action through MigrationRegistry.** That's
|
||||
Phase 3 / Worktree D.5. For Phase 1, the export is a direct call to the
|
||||
Archiver function (which Worktree B is building in parallel).
|
||||
- **Don't touch the dataflow layer.** Your changes are 100% UI.
|
||||
- **Don't translate strings to non-English locales.** Just `en.ts`. The locale
|
||||
prune script in Phase 4 (Worktree E) handles parity.
|
||||
- **Don't migrate any callers off `Orchestrator.onSettingsChange(scopes[])` to
|
||||
the typed `onSettingsFieldsChanged(fields[])`.** Leave that for Phase 1+
|
||||
feature work as features are touched. Your worktree shouldn't even import
|
||||
the Orchestrator.
|
||||
|
||||
## When you're done
|
||||
|
||||
```bash
|
||||
# From your worktree
|
||||
git push origin refactor/v10-phase1-banners
|
||||
gh pr create --base master --title "feat(settings): deprecation banners (Phase 1, Worktree A)"
|
||||
```
|
||||
|
||||
After merge, signal that **Worktree C is unblocked** (it depends on A's
|
||||
merge to master).
|
||||
291
.v10-worktrees/worktree-b-archiver.md
Normal file
291
.v10-worktrees/worktree-b-archiver.md
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# Worktree B — Phase 1 Archiver Pure Functions (9.14.0)
|
||||
|
||||
## What you're doing
|
||||
|
||||
Task Genius is being slimmed down for v10. When users upgrade to 10.0.0,
|
||||
their persistent data for deprecated features (workflows, habits, rewards,
|
||||
etc.) gets archived to a vault folder so they don't lose it. The archive
|
||||
folder is also where extracted sub-plugins look for "previous data" on first
|
||||
install.
|
||||
|
||||
Your job is to build the **pure functions** that produce the archive — the
|
||||
serialization logic — without any wiring to the UI or the migration system.
|
||||
That wiring is Phase 3 / Worktree D.5.
|
||||
|
||||
You're running **in parallel with Worktree A** (banners). A modifies UI files,
|
||||
B modifies new infrastructure files. Zero conflict.
|
||||
|
||||
## Phase 0 ground
|
||||
|
||||
You're branching off `refactor/v10-phase0` (already merged-quality, 6 commits).
|
||||
Phase 0 added the MigrationRegistry which Phase 3 will use to wrap your
|
||||
archiver functions in atomic migration steps. You don't need to touch the
|
||||
registry yourself.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git worktree add ../tg-phase1-archiver refactor/v10-phase0
|
||||
cd ../tg-phase1-archiver
|
||||
git checkout -b refactor/v10-phase1-archiver
|
||||
|
||||
npm install
|
||||
npm test -- --testPathPattern="integration/"
|
||||
# Expect: 70+ passing
|
||||
```
|
||||
|
||||
## Files you'll create
|
||||
|
||||
- `src/migration/v10/Archiver.ts` — 7 pure archive functions (~300 LOC)
|
||||
- `src/migration/v10/index.ts` — barrel export
|
||||
- `src/migration/v10/types.ts` — shared types (`ArchiveSection`, etc.)
|
||||
- `src/__tests__/migration/v10/Archiver.test.ts` — fixture-based tests (~250 LOC)
|
||||
- `src/__tests__/migration/v10/__fixtures__/realistic-settings.ts` — sample settings shapes for the 7 sections
|
||||
|
||||
## Files you will NOT touch
|
||||
|
||||
- `src/index.ts` (Worktree D wires the archiver into onload)
|
||||
- `src/utils/migration/MigrationRegistry.ts` (Worktree D adds the v10-archive step)
|
||||
- Any settings tab files (Worktree A injects the export buttons that *call* your functions)
|
||||
- The DataflowOrchestrator (no dataflow concerns here)
|
||||
|
||||
## Tasks (in order)
|
||||
|
||||
### Task 1 — Define the contract (~½ day)
|
||||
|
||||
`src/migration/v10/types.ts`:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* The result of one archive function. Pure data — no I/O happens inside the
|
||||
* function. The caller is responsible for actually writing files via vault.
|
||||
*/
|
||||
export interface ArchiveSection {
|
||||
/** Section name, e.g. "workflows", "habits", "timer". */
|
||||
section: string;
|
||||
/** Files to write, keyed by relative path under <vault>/Task Genius Archive/ */
|
||||
files: Map<string, string>;
|
||||
/** Human-readable summary for the migration confirmation modal. */
|
||||
summary: string;
|
||||
/** Optional structured count for the modal: {workflows: 47, habits: 12} */
|
||||
itemCount: number;
|
||||
}
|
||||
|
||||
export interface ArchiveManifest {
|
||||
version: string; // plugin version that produced this archive
|
||||
exportedAt: number; // unix epoch ms
|
||||
sections: Array<{
|
||||
section: string;
|
||||
itemCount: number;
|
||||
files: string[]; // relative paths
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Task 2 — Implement the 7 archive functions (~3-4 days)
|
||||
|
||||
`src/migration/v10/Archiver.ts`:
|
||||
|
||||
```ts
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import type { ArchiveSection } from "./types";
|
||||
|
||||
export async function archiveWorkflows(
|
||||
plugin: TaskProgressBarPlugin,
|
||||
): Promise<ArchiveSection> {
|
||||
const workflows = plugin.settings.workflow?.definitions ?? [];
|
||||
const files = new Map<string, string>();
|
||||
|
||||
// workflows.json — raw shape, round-trippable
|
||||
files.set("workflows/workflows.json", JSON.stringify(
|
||||
{ version: "v10-archive-1", definitions: workflows },
|
||||
null,
|
||||
2,
|
||||
));
|
||||
|
||||
// workflows.md — human-readable summary, one workflow per H2
|
||||
const md = renderWorkflowsMarkdown(workflows);
|
||||
files.set("workflows/workflows.md", md);
|
||||
|
||||
return {
|
||||
section: "workflows",
|
||||
files,
|
||||
summary: `${workflows.length} workflow definition${workflows.length === 1 ? "" : "s"}`,
|
||||
itemCount: workflows.length,
|
||||
};
|
||||
}
|
||||
|
||||
// ... 6 more functions
|
||||
```
|
||||
|
||||
The 7 functions to implement (each takes `plugin` and returns an `ArchiveSection`):
|
||||
|
||||
1. **`archiveWorkflows`** — reads `plugin.settings.workflow.definitions`. Output: `workflows/workflows.json` + `workflows/workflows.md`.
|
||||
2. **`archiveHabits`** — reads `plugin.settings.habit.habits` (array). Output: `habits/habits.json` + `habits/habits.md`. Each habit gets a markdown section with type, schedule, and history if present.
|
||||
3. **`archiveRewards`** — reads `plugin.settings.rewards.rewardItems` and `occurrenceLevels`. Output: `rewards/rewards.json` + `rewards/rewards.md`.
|
||||
4. **`archiveTimerData`** — reads `plugin.settings.taskTimer` config + any in-memory `timerManager` state if accessible. Output: `timer/timer-data.json` + `timer/timer-summary.md`. Note: timer state may also live in localStorage; check `src/services/timer-export-service.ts` for the existing export shape and reuse it.
|
||||
5. **`archiveCalDavSources`** — reads `plugin.settings.icsIntegration.sources` AND OAuth provider configs. **CRITICAL: strip OAuth tokens** before serializing. Output: `calendar-sync/caldav-sources.json` (URLs + names only).
|
||||
6. **`archiveRemovedViews`** — reads `plugin.settings.viewConfiguration[]` and filters to entries whose ID is in `["gantt", "quadrant", "habit", "working-on", "flagged"]`. Output: `views/removed-views.json` (per-view filterRules + sort + visibility).
|
||||
7. **`archiveOrphans`** — catch-all: reads any settings keys that don't have a dedicated archiver and aren't in the v10 keep-list. Output: `orphan-settings.json`. This is the safety net for "we deprecated something we forgot to write a function for."
|
||||
|
||||
Plus a top-level orchestrator:
|
||||
|
||||
```ts
|
||||
export async function archiveAll(
|
||||
plugin: TaskProgressBarPlugin,
|
||||
): Promise<{ sections: ArchiveSection[]; manifest: ArchiveManifest }> {
|
||||
const sections = await Promise.all([
|
||||
archiveWorkflows(plugin),
|
||||
archiveHabits(plugin),
|
||||
archiveRewards(plugin),
|
||||
archiveTimerData(plugin),
|
||||
archiveCalDavSources(plugin),
|
||||
archiveRemovedViews(plugin),
|
||||
archiveOrphans(plugin),
|
||||
]);
|
||||
|
||||
const manifest: ArchiveManifest = {
|
||||
version: plugin.manifest.version,
|
||||
exportedAt: Date.now(),
|
||||
sections: sections.map((s) => ({
|
||||
section: s.section,
|
||||
itemCount: s.itemCount,
|
||||
files: [...s.files.keys()],
|
||||
})),
|
||||
};
|
||||
|
||||
return { sections, manifest };
|
||||
}
|
||||
```
|
||||
|
||||
### Task 3 — Build realistic test fixtures (~½ day)
|
||||
|
||||
`src/__tests__/migration/v10/__fixtures__/realistic-settings.ts`:
|
||||
|
||||
```ts
|
||||
export const settingsWith47Workflows: Partial<TaskProgressBarSettings> = {
|
||||
workflow: {
|
||||
enableWorkflow: true,
|
||||
definitions: [/* 47 realistic workflow shapes */],
|
||||
timestampFormat: "YYYY-MM-DD HH:mm",
|
||||
autoAddTimestamp: true,
|
||||
calculateSpentTime: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const settingsWith12Habits: Partial<TaskProgressBarSettings> = {
|
||||
habit: {
|
||||
enableHabits: true,
|
||||
habits: [/* 12 mixed daily/count/scheduled/mapping habits */],
|
||||
},
|
||||
};
|
||||
|
||||
// ... fixtures for the other 5 sections
|
||||
```
|
||||
|
||||
These fixtures double as documentation for what realistic v9 user data looks
|
||||
like. Phase 3's migration confirmation modal will use the same shapes for its
|
||||
"X workflows, Y habits" copy.
|
||||
|
||||
### Task 4 — Test fixtures end-to-end (~1 day)
|
||||
|
||||
`src/__tests__/migration/v10/Archiver.test.ts`:
|
||||
|
||||
```ts
|
||||
import { archiveWorkflows, archiveAll } from "@/migration/v10/Archiver";
|
||||
import { settingsWith47Workflows, ... } from "./__fixtures__/realistic-settings";
|
||||
|
||||
describe("Archiver (Phase 1 Worktree B)", () => {
|
||||
it("archiveWorkflows produces expected files for 47 workflows", async () => {
|
||||
const plugin = makeFakePlugin({ settings: settingsWith47Workflows });
|
||||
const result = await archiveWorkflows(plugin);
|
||||
expect(result.itemCount).toBe(47);
|
||||
expect(result.files.has("workflows/workflows.json")).toBe(true);
|
||||
expect(result.files.has("workflows/workflows.md")).toBe(true);
|
||||
const json = JSON.parse(result.files.get("workflows/workflows.json")!);
|
||||
expect(json.definitions).toHaveLength(47);
|
||||
});
|
||||
|
||||
// ... 1 test per section + 1 for archiveAll
|
||||
});
|
||||
```
|
||||
|
||||
`makeFakePlugin` is a tiny helper — just `{ settings, manifest: {version: "9.14.0"} }`.
|
||||
You don't need a full plugin instance.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
| # | Criterion | How to verify |
|
||||
|---|---|---|
|
||||
| 1 | All 7 archive functions return correct shapes for known inputs | `npm test -- --testPathPattern="migration/v10/Archiver"` |
|
||||
| 2 | `archiveAll` orchestrator produces a valid manifest | Test asserts manifest shape |
|
||||
| 3 | OAuth tokens are stripped from `archiveCalDavSources` output | Test asserts no `accessToken`/`refreshToken` keys in JSON |
|
||||
| 4 | Each section has both a JSON (round-trippable) and markdown (human) representation, where applicable | Test asserts both files exist |
|
||||
| 5 | Functions are pure: no `vault.create`, no `localforage` calls, no `app.workspace.trigger` | Code review |
|
||||
| 6 | TypeScript build clean (`npm run build`) | Auto |
|
||||
| 7 | Phase 0 integration tests still pass | `npm test -- --testPathPattern="integration/"` |
|
||||
| 8 | NOT wired into MigrationRegistry yet | Code review (no edits to `src/utils/migration/`) |
|
||||
|
||||
## Coordination with Worktree A
|
||||
|
||||
Worktree A's banner export buttons need to call your functions. Provide a
|
||||
clean import surface:
|
||||
|
||||
```ts
|
||||
// from src/migration/v10/index.ts
|
||||
export { archiveWorkflows, archiveHabits, /* ... */, archiveAll } from "./Archiver";
|
||||
export type { ArchiveSection, ArchiveManifest } from "./types";
|
||||
```
|
||||
|
||||
Worktree A imports from `@/migration/v10`. If A starts before B, A stubs the
|
||||
export action with a TODO; once B merges, A removes the stub. **This is a
|
||||
soft dependency** — A and B are independent worktrees.
|
||||
|
||||
## Conflicts to watch
|
||||
|
||||
**None.** Worktree B touches only new files in `src/migration/v10/` and
|
||||
`src/__tests__/migration/v10/`. No existing code is modified.
|
||||
|
||||
The only file you write outside that namespace is potentially nothing — even
|
||||
the orchestrator export goes through `src/migration/v10/index.ts`.
|
||||
|
||||
## Open questions that affect THIS worktree
|
||||
|
||||
- **Q2 — Sub-plugin commitment:** the default is YES, 4 sub-plugins. If the
|
||||
user changes their mind and wants to delete a feature outright (no sub-plugin
|
||||
migration), you can SKIP the corresponding archive function — there's no
|
||||
point archiving data nobody will ever consume. Wait for confirmation if this
|
||||
changes before you ship `archiveTimerData` etc.
|
||||
|
||||
The other 4 questions don't affect this worktree.
|
||||
|
||||
## Useful existing utilities
|
||||
|
||||
- `src/services/timer-export-service.ts` — already exports timer data to JSON. You can probably import its serialization logic for `archiveTimerData`.
|
||||
- `src/components/features/quick-capture/modals/QuickCaptureModalWithSwitch.ts` (line ~1006) — has the existing settings shape for quick-capture, useful reference for `archiveOrphans`.
|
||||
- `src/common/setting-definition.ts` (line ~995, `DEFAULT_SETTINGS`) — list of every settings field, useful for `archiveOrphans` exclusion list.
|
||||
|
||||
## Don't do these things
|
||||
|
||||
- **Don't write to the vault.** Pure functions only. Phase 3 / Worktree D.5
|
||||
handles I/O via the migration registry.
|
||||
- **Don't import obsidian's `Notice`, `Modal`, or `App`.** Your functions take
|
||||
a plugin instance and return data. Nothing more.
|
||||
- **Don't add dependencies.** Use plain JSON.stringify / template strings.
|
||||
- **Don't OVERLY pretty-print the markdown.** A simple `# {section} {N}`
|
||||
followed by `## {item.name}` for each item is fine. The markdown is a
|
||||
fallback view; the JSON is the source of truth for sub-plugin importers.
|
||||
- **Don't try to make the archive "diff-friendly".** Phase 3 is one-shot;
|
||||
there's no need to support incremental archives.
|
||||
|
||||
## When you're done
|
||||
|
||||
```bash
|
||||
git push origin refactor/v10-phase1-archiver
|
||||
gh pr create --base master --title "feat(migration): v10 Archiver pure functions (Phase 1, Worktree B)"
|
||||
```
|
||||
|
||||
After merge, Worktree A can remove its stub imports if it shipped with one.
|
||||
Worktree D.5 (Phase 3 confirmation modal) will wire your `archiveAll` into
|
||||
the MigrationRegistry as the `v10-archive` step.
|
||||
233
.v10-worktrees/worktree-c-readonly.md
Normal file
233
.v10-worktrees/worktree-c-readonly.md
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
# Worktree C — Phase 2 Read-Only Mode + Beta (9.15.0)
|
||||
|
||||
## What you're doing
|
||||
|
||||
Phase 1 (Worktrees A + B) shipped 9.14.0 with deprecation banners. Phase 2 is
|
||||
the **hard warning** week: deprecated settings tabs become read-only (inputs
|
||||
disabled, only the Export button works), deprecated commands fire a one-shot
|
||||
Notice on first use per session, and v10 enters beta via BRAT.
|
||||
|
||||
This is the last stop before the cliff. After your worktree merges and ships
|
||||
9.15.0, the beta channel gets 10.0.0-beta.1 and a ~5 day soak period.
|
||||
|
||||
## Phase 0 + Phase 1 ground
|
||||
|
||||
You depend on **Worktree A merged to master**. Verify before starting:
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git log master --oneline | grep "deprecation banners"
|
||||
# Expect: "feat(settings): deprecation banners (Phase 1, Worktree A)" present
|
||||
```
|
||||
|
||||
If not present, **do not start.** Worktree C cannot run in parallel with A —
|
||||
you'll fight for the same 14 settings tab files.
|
||||
|
||||
Worktree B (Archiver) can be merged or pending; you only need its functions
|
||||
for the export buttons (which are already wired by Worktree A).
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git worktree add ../tg-phase2-readonly master
|
||||
cd ../tg-phase2-readonly
|
||||
git checkout -b refactor/v10-phase2-readonly
|
||||
|
||||
npm install
|
||||
npm test -- --testPathPattern="integration/"
|
||||
# Expect: 70+ passing
|
||||
```
|
||||
|
||||
## Files you'll modify
|
||||
|
||||
The same 14 deprecated tab files Worktree A injected banners into. You're
|
||||
adding a `readOnly` mode to each one:
|
||||
|
||||
1-14. All `src/components/features/settings/tabs/{Workflow,Habit,Reward,TaskTimer,TimelineSidebar,Ics,Index,DesktopIntegration,About}SettingsTab.ts` (and the 5 view-config sections)
|
||||
|
||||
Plus:
|
||||
- `src/components/features/settings/components/DeprecationBanner.ts` (banner copy update)
|
||||
- `src/common/deprecation-messages.ts` (add Notice strings for commands)
|
||||
- `src/index.ts` (wrap deprecated commands with one-shot Notice)
|
||||
- `src/managers/changelog-manager.ts` (10.0.0-beta.1 announcement modal)
|
||||
- `manifest-beta.json` (bump to 10.0.0-beta.1)
|
||||
|
||||
## Tasks (in order)
|
||||
|
||||
### Task 1 — Add `readOnly` mode to DeprecationBanner (~½ day)
|
||||
|
||||
Extend the banner component (from Worktree A):
|
||||
|
||||
```ts
|
||||
export interface DeprecationBannerProps {
|
||||
// ... existing fields from Phase 1
|
||||
readOnly?: boolean; // NEW
|
||||
}
|
||||
```
|
||||
|
||||
When `readOnly: true`, the banner copy upgrades from "moves to X in v10" to
|
||||
"**read-only — export now**" and the "Export" button gets primary button
|
||||
styling. Visual: same yellow background, but more urgent affordance.
|
||||
|
||||
Update CSS in `src/styles/setting.scss` for the new state.
|
||||
|
||||
### Task 2 — Disable inputs in 14 deprecated tabs (~2 days)
|
||||
|
||||
For each tab/section, walk the `Setting()` chains and call `.setDisabled(true)`
|
||||
on each input when the tab is in read-only mode. The `Export` button stays
|
||||
enabled. Add a `disabled` prop or a flag at the top of each render function.
|
||||
|
||||
Pattern:
|
||||
|
||||
```ts
|
||||
// Before
|
||||
new Setting(containerEl)
|
||||
.setName("Enable workflows")
|
||||
.addToggle(t => t.setValue(plugin.settings.workflow.enableWorkflow)
|
||||
.onChange(async v => { ... }));
|
||||
|
||||
// After (Phase 2)
|
||||
const readOnly = true; // hardcoded — Phase 3 deletes the tab entirely
|
||||
new Setting(containerEl)
|
||||
.setName("Enable workflows")
|
||||
.addToggle(t => t.setValue(plugin.settings.workflow.enableWorkflow)
|
||||
.setDisabled(readOnly)
|
||||
.onChange(async v => { if (readOnly) return; ... }));
|
||||
```
|
||||
|
||||
Pass the `renderDeprecationBanner` call `{readOnly: true, ...}`.
|
||||
|
||||
For sectioned tabs (Ics, Index, DesktopIntegration), only the deprecated
|
||||
sections become read-only — the rest of the tab stays interactive.
|
||||
|
||||
### Task 3 — Wrap deprecated commands with first-use Notice (~1 day)
|
||||
|
||||
`src/index.ts` — for each command that targets a deprecated feature, wrap the
|
||||
existing callback with a one-shot Notice:
|
||||
|
||||
```ts
|
||||
// New helper at the top of registerCommands or in a small util
|
||||
const _deprecationWarned: Record<string, boolean> = {};
|
||||
function warnDeprecatedOnce(commandId: string, message: string): void {
|
||||
if (_deprecationWarned[commandId]) return;
|
||||
_deprecationWarned[commandId] = true;
|
||||
new Notice(message, 6000);
|
||||
}
|
||||
|
||||
// Wrap each deprecated command's callback
|
||||
this.addCommand({
|
||||
id: "create-quick-workflow",
|
||||
name: t("Create Quick Workflow"),
|
||||
editorCallback: async (editor, ctx) => {
|
||||
warnDeprecatedOnce(
|
||||
"create-quick-workflow",
|
||||
t("This command is removed in v10. Install task-genius-workflow."),
|
||||
);
|
||||
// existing logic still runs
|
||||
return createQuickWorkflowCommand(plugin, editor, ctx);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The 11 deprecated commands to wrap:
|
||||
- 6 workflow commands from `src/commands/workflowCommands.ts`
|
||||
- 5 task-timer commands (find via `grep -l "task-timer-" src/index.ts`)
|
||||
- 1 reindex-habits command
|
||||
|
||||
Memory is per-session (cleared on plugin reload). Don't store in settings.
|
||||
|
||||
### Task 4 — Update Notice strings in deprecation-messages.ts (~½ day)
|
||||
|
||||
Add new keys for each command-level Notice. Centralize so locales translate
|
||||
once.
|
||||
|
||||
### Task 5 — Sub-plugin v0.1.0 release coordination (out of repo, ~1 day)
|
||||
|
||||
This is the part that touches OTHER repos:
|
||||
|
||||
- `task-genius-workflow` v0.1.0 — release with a **one-time bootstrap importer**
|
||||
that reads from main plugin's live `data.json` (not yet from `Task Genius
|
||||
Archive/`, since 9.15 hasn't archived anything yet)
|
||||
- `task-genius-habits` v0.1.0 — same pattern
|
||||
- `task-genius-timer` v0.1.0 — same pattern
|
||||
- **`task-genius-calendar-sync` is HELD** — riskiest one (OAuth tokens),
|
||||
ships in Phase 4 (Worktree E)
|
||||
|
||||
Each sub-plugin's bootstrap importer logs which fields it imported and writes
|
||||
a marker `<sub-plugin-data-dir>/.imported-from-main-plugin` so it doesn't
|
||||
double-import on next load.
|
||||
|
||||
The sub-plugin repos don't exist yet — Worktree C creates them. Use the
|
||||
main plugin's tooling (esbuild config, jest config, manifest format) as a
|
||||
template.
|
||||
|
||||
### Task 6 — Cut 10.0.0-beta.1 (~½ day)
|
||||
|
||||
`manifest-beta.json` — bump to `10.0.0-beta.1`. Worktree D will own the
|
||||
actual 10.0.0 work; this commit just opens the beta channel so testers can
|
||||
opt in early.
|
||||
|
||||
`src/managers/changelog-manager.ts` — add a beta-only announcement: "v10
|
||||
beta is here. Migration modal will appear on first launch — please test on
|
||||
a backup vault first."
|
||||
|
||||
## Definition of Done
|
||||
|
||||
| # | Criterion | How to verify |
|
||||
|---|---|---|
|
||||
| 1 | All 14 deprecated tabs/sections show inputs as disabled | Manual: open Settings, try to toggle anything in Workflows tab — should be grayed out |
|
||||
| 2 | Export buttons in each tab still functional | Manual: click each export button, verify archive folder updates |
|
||||
| 3 | Banner copy updated to "read-only — export now" | Manual visual check |
|
||||
| 4 | Each deprecated command fires a Notice on first use per session | Manual: invoke `Create Quick Workflow` twice — Notice once, second time silent |
|
||||
| 5 | Notice memory clears on plugin reload | Manual: reload plugin, invoke command again — Notice fires again |
|
||||
| 6 | 3 sub-plugins (workflow, habits, timer) released to community plugins or BRAT | Out of repo — verify by installing fresh and confirming bootstrap importer runs |
|
||||
| 7 | 10.0.0-beta.1 published via BRAT | `manifest-beta.json` shows `10.0.0-beta.1`, BRAT installs it, real beta tester confirms it loads |
|
||||
| 8 | TypeScript build clean | `npm run build` |
|
||||
| 9 | Phase 0 integration tests still pass | `npm test -- --testPathPattern="integration/"` |
|
||||
| 10 | No regression in non-deprecated tabs | Manual: open General/Tasks/Views, edit a setting, verify it persists |
|
||||
|
||||
## Conflicts to watch
|
||||
|
||||
- **Worktree D (Phase 3)** will DELETE the 14 tabs you're modifying. Same as
|
||||
the A→D conflict — Phase 3 starts after Phase 2 merges, no overlap.
|
||||
- **Worktree B (Archiver)** — your Export buttons call Worktree B's functions.
|
||||
If B hasn't merged, the buttons remain stubbed (Worktree A's stubs from
|
||||
Phase 1). Don't block on B; it'll be ready by the time you finish.
|
||||
|
||||
## Open questions that affect THIS worktree
|
||||
|
||||
- **Q4 — Cliff version (4 weeks vs 8 weeks):** the default is 4 weeks. If the
|
||||
user wants 8, your worktree gets a longer beta soak (10.0.0-beta.1 stays in
|
||||
the field for ~2 weeks instead of ~5 days). The work itself doesn't change.
|
||||
- **Q5 — Beta tester recruitment:** Worktree A pinned the GitHub Discussion.
|
||||
By the time you start, recruitment should have produced ~5-10 testers. If
|
||||
it hasn't, push the discussion harder before cutting beta.
|
||||
|
||||
## Useful existing utilities
|
||||
|
||||
- The Obsidian `Setting` class' `.setDisabled(true)` method — works on every
|
||||
input type (toggle, text, dropdown, slider, button)
|
||||
- `Notice` from obsidian — your one-shot Notice helper wraps this
|
||||
- `manifest-beta.json` — already exists, just bump the version field
|
||||
|
||||
## Don't do these things
|
||||
|
||||
- **Don't delete any tabs.** Worktree D owns deletions.
|
||||
- **Don't modify the dataflow layer.** All your changes are UI + command callbacks.
|
||||
- **Don't make the Notice persistent across sessions.** Per-session is the
|
||||
contract — users who reload the plugin should see the warning again.
|
||||
- **Don't ship a 10.0.0 manifest.** Only `manifest-beta.json` gets bumped.
|
||||
`manifest.json` stays at `9.15.0`.
|
||||
- **Don't translate Notice strings to non-English locales.** Worktree E's
|
||||
locale prune handles parity.
|
||||
|
||||
## When you're done
|
||||
|
||||
```bash
|
||||
git push origin refactor/v10-phase2-readonly
|
||||
gh pr create --base master --title "feat(settings): read-only deprecated tabs + first-use command notice (Phase 2, Worktree C)"
|
||||
```
|
||||
|
||||
After merge, **Worktree D is unblocked**. Tag `9.15.0` and cut
|
||||
`10.0.0-beta.1` via BRAT. The 5-day beta soak begins.
|
||||
459
.v10-worktrees/worktree-d-cliff.md
Normal file
459
.v10-worktrees/worktree-d-cliff.md
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
# Worktree D — Phase 3 Cliff (10.0.0)
|
||||
|
||||
## What you're doing
|
||||
|
||||
This is **the cliff**. Everything Phase 1 + Phase 2 warned about, you actually
|
||||
do. Settings tabs go from 25 to 7. Commands go from 43 to 16 with default
|
||||
hotkeys. Quick-capture modals go from 5 to 1. Views go from 15 to 5 core + 3
|
||||
widget-only. Onboarding goes from 36 files to 5. Roughly 50% of the plugin's
|
||||
LOC gets deleted. The migration confirmation modal runs on first launch and
|
||||
archives every deprecated feature's persistent data to `<vault>/Task Genius
|
||||
Archive/`.
|
||||
|
||||
This is also the largest worktree by far. It's split into **5 sub-PRs** that
|
||||
land sequentially on the worktree branch. You can run them as 5 separate
|
||||
commits or 5 separate Sub-PRs against the worktree branch — whichever fits
|
||||
your workflow.
|
||||
|
||||
## Phase 0/1/2 ground
|
||||
|
||||
You depend on **Worktree C merged to master**. Verify before starting:
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git log master --oneline | head -10
|
||||
# Expect: "feat(settings): read-only deprecated tabs..." (Worktree C) is the most recent v10 commit
|
||||
# Expect: Worktree A (banners) and Worktree B (Archiver) commits are also present
|
||||
```
|
||||
|
||||
The Phase 0 deferred items from `PHASE0_DEFERRED.md` are picked up here:
|
||||
- **Item 1 (delete `src/pages/TaskView.ts`)** → assigned to **D.4**
|
||||
- **Item 2 (fold `src/utils/settings-migration.ts` into the registry)** → assigned to **D.2**
|
||||
|
||||
Read [`../PHASE0_DEFERRED.md`](../PHASE0_DEFERRED.md) before starting either
|
||||
sub-PR for the resolution paths.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git worktree add ../tg-phase3-cliff master
|
||||
cd ../tg-phase3-cliff
|
||||
git checkout -b refactor/v10-phase3-cliff
|
||||
|
||||
npm install
|
||||
npm test -- --testPathPattern="integration/"
|
||||
# Expect: 70+ passing
|
||||
```
|
||||
|
||||
## The 5 sub-PRs
|
||||
|
||||
Each sub-PR lands on `refactor/v10-phase3-cliff`. They MUST land in this
|
||||
order due to `src/index.ts` conflicts:
|
||||
|
||||
```
|
||||
D.2 (commands) → D.1 (settings tabs) → D.3 (quick capture) → D.4 (views + deletions) → D.5 (confirmation modal)
|
||||
```
|
||||
|
||||
D.2 must be first because it owns the bulk of the `src/index.ts` rewrite.
|
||||
D.5 must be last because it depends on every other sub-PR being stable.
|
||||
|
||||
---
|
||||
|
||||
### D.2 — Command palette + hotkeys (43 → 16)
|
||||
|
||||
**Why first:** owns the largest stretch of `src/index.ts`. Other sub-PRs
|
||||
rebase onto it.
|
||||
|
||||
**Files:**
|
||||
- `src/index.ts` (command registration block — major rewrite)
|
||||
- `src/commands/*.ts` (delete: `completedTaskMover.ts`, `sortTaskCommands.ts`, `taskCycleCommands.ts`, `taskMover.ts`, `workflowCommands.ts`)
|
||||
- `src/commands/v10/` (new directory) — 16 new consolidated command files
|
||||
|
||||
**Plus PHASE 0 DEFERRED Item 2** — fold `src/utils/settings-migration.ts`
|
||||
into the registry. Resolution path:
|
||||
1. Move `repairStatusCycles`, `validateStatusCycle`, `sortCyclesByPriority`, `findDuplicateCycleIds` → `src/utils/status-cycle-resolver.ts` (already exists)
|
||||
2. Inline `migrateToMultiCycle` body into `src/utils/migration/steps/legacy-bundle-0.ts` (no longer imported)
|
||||
3. Remove the W1 fallback in `src/index.ts:2003-2010` (the registry has atomic semantics — fallback is dead defense)
|
||||
4. Delete `src/utils/settings-migration.ts`
|
||||
5. Verify `npm test` and `npm run build`
|
||||
|
||||
**The 16 new commands** (renaming + consolidation map from main plan §3.4):
|
||||
|
||||
| New ID | Old IDs absorbed | Default hotkey |
|
||||
|---|---|---|
|
||||
| `tg:capture` | `quick-capture`, `minimal-quick-capture`, `toggle-quick-capture`, `toggle-quick-capture-globally`, `quick-file-create` | `Mod+Shift+T` |
|
||||
| `tg:capture-here` | (new) | — |
|
||||
| `tg:mark-done` | `cycle-task-status-forward` (when on task line) | `Mod+Enter` (task line) |
|
||||
| `tg:mark-cycle` | `cycle-task-status-forward`, `cycle-task-status-backward` | — |
|
||||
| `tg:set-priority` | all 12 priority commands collapsed into one picker | `Mod+Alt+P` |
|
||||
| `tg:remove-priority` | `remove-priority` | — |
|
||||
| `tg:open-view` | `open-task-genius-view` | `Mod+Shift+G` |
|
||||
| `tg:open-inbox` | (new) | `Mod+Shift+I` |
|
||||
| `tg:open-forecast` | (new) | `Mod+Shift+F` |
|
||||
| `tg:open-review` | (new) | — |
|
||||
| `tg:move-tasks` | all 6 task-mover commands → one picker for scope | — |
|
||||
| `tg:sort-tasks` | `sort-tasks-by-due-date`, `sort-tasks-in-entire-document` | — |
|
||||
| `tg:reindex` | `force-reindex-tasks` (and removes `reindex-habits`) | — |
|
||||
| `tg:open-settings` | `open-task-genius-settings-modal` | — |
|
||||
| `tg:open-archive` | (new — reveals `Task Genius Archive/` in file explorer) | — |
|
||||
| `tg:setup` | `open-task-genius-setup` | — |
|
||||
|
||||
**Removed entirely:** `open-timeline-sidebar-view`, `open-task-genius-changelog`
|
||||
(auto-opens on version change), 5× `task-timer-*`, 6× `workflow-*`. The 11
|
||||
auto-move commands collapse into a setting toggle in the Tasks tab — **not in
|
||||
the palette at all**.
|
||||
|
||||
**Backward compatibility:** keep old command IDs as aliases for **one version
|
||||
only** (10.0.0). Drop in 10.0.1 via Worktree E. This preserves manually-bound
|
||||
hotkeys for surviving commands during the upgrade.
|
||||
|
||||
**Discovery:** after the migration modal (D.5), show a one-screen "What
|
||||
changed" sheet listing the 5 default hotkeys, the 4 picker collapses, and
|
||||
the sub-plugin pointers. Use the existing `ChangelogManager` modal infrastructure.
|
||||
|
||||
**DoD subset:** `npm test`, `npm run build`, all 16 new commands appear in
|
||||
the palette with the new names, manually-bound hotkeys for surviving commands
|
||||
still work via aliases.
|
||||
|
||||
---
|
||||
|
||||
### D.1 — Settings tab rewrite (25 → 7)
|
||||
|
||||
**Files:**
|
||||
- `src/components/features/settings/SettingsModal.ts` (`renderTabContent` switch — 25 cases → 7)
|
||||
- `src/components/features/settings/tabs/*` (delete 14, rewrite 7)
|
||||
- `src/components/features/settings/index.ts` (exports)
|
||||
- `src/components/features/settings/components/DeprecationBanner.ts` (delete — banners are gone in v10)
|
||||
- `src/common/deprecation-messages.ts` (delete)
|
||||
- `src/utils/ObsidianUriHandler.ts:117` (switch to redirect map)
|
||||
- `src/utils/uri-tab-redirects.ts` (new)
|
||||
- `src/migration/v10/TabIdMap.ts` (new)
|
||||
- All `src/components/features/settings/tabs/{Workflow,Habit,Reward,TaskTimer,TimelineSidebar}*.ts` (delete)
|
||||
|
||||
**The 7 new tabs:**
|
||||
|
||||
| # | New tab | Absorbs old tabs |
|
||||
|---|---|---|
|
||||
| 1 | **General** | `index`, `file-filter`, `interface` (parts) |
|
||||
| 2 | **Capture** | `quick-capture`, `time-parsing`, `date-priority` (parts) |
|
||||
| 3 | **Tasks** | `progress-bar`, `task-status`, `task-handler`, `date-priority` (parts) |
|
||||
| 4 | **Views** | `view-settings`, `calendar-views`, `task-filter` |
|
||||
| 5 | **Projects & Tags** | `project` |
|
||||
| 6 | **Integrations** | `ics` (read-only only), `mcp-integration`, `bases-support`, `workspaces`, `desktop-integration` |
|
||||
| 7 | **About** | `about`, `beta-test` + "Open archive folder", import/export, tombstones list |
|
||||
|
||||
**Deleted tabs (no merge):** `workflow`, `habit`, `reward`, `task-timer`,
|
||||
`timeline-sidebar`. Their content was archived in D.5.
|
||||
|
||||
**URI redirect map:** `src/utils/uri-tab-redirects.ts` — see main plan §3.8
|
||||
for the full table. Existing URI bookmarks pointing to deprecated tabs land
|
||||
on `about#archived-X` with a Notice explaining where the data went.
|
||||
|
||||
**DoD subset:** Settings opens in ≤500ms. All 7 tabs render valid content on
|
||||
an empty vault. Existing URI bookmarks still work (manual smoke test).
|
||||
|
||||
---
|
||||
|
||||
### D.3 — Quick capture modal consolidation (5 → 1)
|
||||
|
||||
**Files:**
|
||||
- `src/components/features/quick-capture/modals/{QuickCaptureModal,MinimalQuickCaptureModal,MinimalQuickCaptureModalWithSwitch,BaseQuickCaptureModal}.ts` (delete)
|
||||
- `src/components/features/quick-capture/modals/QuickCaptureModalWithSwitch.ts` → rename to `QuickCaptureModal.ts`
|
||||
- New `Mode` strip UI inside the survivor (Full / Minimal / Daily)
|
||||
- `src/utils/migration/steps/v10-quick-capture-modes.ts` (new — registers as a tombstone+transform)
|
||||
- `src/index.ts` (command callback updates — `tg:capture` opens the unified modal)
|
||||
|
||||
**Three modes inside one modal:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ [● Full] [Minimal] [Daily] [×] │ ← mode strip
|
||||
├─────────────────────────────────────┤
|
||||
│ ▌ Task content... │ ← always: text input, autofocus
|
||||
│ │
|
||||
│ ─── Below this line shown in Full ──│
|
||||
│ Project ▾ Tags ▾ Priority ▾ │
|
||||
│ Due ▾ Start ▾ Scheduled ▾ │
|
||||
│ Target file: <path or daily-note> │
|
||||
│ Append / Prepend / Replace ▾ │
|
||||
│ [Cancel] [Capture ↵] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Settings migration step `v10-quick-capture-modes`:**
|
||||
```ts
|
||||
// Old: separate command bound to MinimalQuickCaptureModalWithSwitch
|
||||
// Old: lastUsedMode tracked per modal class
|
||||
plugin.settings.quickCapture.mode = mapMode(plugin.settings.quickCapture.lastUsedMode);
|
||||
delete plugin.settings.quickCapture.lastUsedMode;
|
||||
delete plugin.settings.quickCapture.minimalModeSettings; // fields merged up one level
|
||||
```
|
||||
|
||||
Known degradation: users who configured different file targets for full vs
|
||||
minimal lose that distinction — call out in changelog.
|
||||
|
||||
**DoD subset:** capture flow works in all 3 modes, settings migration handles
|
||||
existing `lastUsedMode` correctly, default hotkey (`Mod+Shift+T`) opens the
|
||||
modal.
|
||||
|
||||
---
|
||||
|
||||
### D.4 — View consolidation + onboarding compression + deletions
|
||||
|
||||
**This is the biggest sub-PR by LOC.** It also picks up Phase 0 deferred
|
||||
Item 1 (delete `src/pages/TaskView.ts`).
|
||||
|
||||
**Files (deletion sweep):**
|
||||
- `src/components/features/{gantt,quadrant,habit}/*` (delete entire dirs — except keep a small `QuadrantWidgetView.ts` for codeblock embedding)
|
||||
- `src/components/features/timeline-sidebar/*` (delete)
|
||||
- `src/components/features/onboarding/**` (delete 31 of 36 files, add 4 simplified)
|
||||
- `src/managers/{habit-manager,reward-manager,timer-manager,electron-quick-capture}.ts` (delete)
|
||||
- `src/services/{timer-export-service,timer-format-service,timer-metadata-service}.ts` (delete)
|
||||
- `src/managers/calendar-auth-manager.ts` (delete — moves to calendar-sync sub-plugin)
|
||||
- `src/providers/*` (delete entire dir → calendar-sync sub-plugin)
|
||||
- `src/parsers/holiday-detector.ts` (delete)
|
||||
- `src/dataflow/sources/FileSource.ts` (delete)
|
||||
- `src/editor-extensions/workflow/*` (delete entire dir)
|
||||
- `src/editor-extensions/date-time/task-timer.ts` (delete)
|
||||
- `src/pages/TaskView.ts` (delete after porting dirty changes — see PHASE0_DEFERRED.md Item 1)
|
||||
|
||||
**Files (additions):**
|
||||
- `src/widgets/registerWidgets.ts` (add TableWidget, QuadrantWidget)
|
||||
- `src/widgets/views/TableWidgetView.ts` (new, port from `src/components/features/table/`)
|
||||
- `src/widgets/views/QuadrantWidgetView.ts` (new, ~200 LOC — codeblock embedding only)
|
||||
- `src/migration/v10/ViewMigration.ts` (new)
|
||||
- `src/utils/migration/steps/v10-view-cleanup.ts` (new — tombstone for removed views)
|
||||
- `src/common/setting-definition.ts` (`viewConfiguration` defaults: 15 → 5)
|
||||
- `src/components/features/onboarding/steps/{WelcomeStep,CaptureStep,ViewsStep,DoneStep}.ts` (new — 4 simplified onboarding steps)
|
||||
|
||||
**View consolidation (15 → 5 core + 3 widget-only):**
|
||||
|
||||
Core (live in FluentTaskView sidebar): `inbox`, `forecast`, `projects`, `tags`, `review`
|
||||
|
||||
Widget-only (no sidebar entry, codeblock + `tg:open-view` picker only): `calendar`, `kanban`, `table`
|
||||
|
||||
Removed: `gantt`, `quadrant`, `habit`, `working-on`, `flagged`
|
||||
|
||||
**Phase 0 deferred Item 1 (port TaskView.ts changes to FluentTaskView):**
|
||||
|
||||
The user has uncommitted modifications in `src/pages/TaskView.ts` adding
|
||||
multi-cycle support to the "switch status" context menu. Before deleting
|
||||
TaskView.ts:
|
||||
|
||||
1. Read the dirty diff (`git diff src/pages/TaskView.ts`). The relevant block
|
||||
is the "switch status" submenu builder around line ~1263-1440.
|
||||
2. Find the equivalent location in `src/pages/FluentTaskView.ts` and port the
|
||||
multi-cycle logic. The new code reuses these helpers from
|
||||
`src/utils/status-cycle-resolver.ts`:
|
||||
- `findApplicableCycles(currentMark, statusCycles)`
|
||||
- `getAllStatusNames`
|
||||
- `getNextStatusPrimary`
|
||||
- `getAllStatusMarks`
|
||||
3. Move `TASK_VIEW_TYPE` constant from `src/pages/TaskView.ts` to a new file
|
||||
`src/common/view-types.ts` (stale leaves in user vaults still need to be
|
||||
detached on next load — see `src/index.ts:1825` and `:2116`).
|
||||
4. Update import sites of `TASK_VIEW_TYPE`:
|
||||
- `src/index.ts:72` (also remove the `TaskView` symbol from this import)
|
||||
- `src/components/features/fluent/FluentIntegration.ts:13`
|
||||
5. Delete the `instanceof TaskView` block at `src/index.ts:2119` — unreachable
|
||||
since the class is no longer registered.
|
||||
6. Delete `src/pages/TaskView.ts`.
|
||||
7. Run typecheck + integration suite.
|
||||
|
||||
**Migration step `v10-view-cleanup`** (`src/migration/v10/ViewMigration.ts`):
|
||||
```ts
|
||||
const REMOVED = ["gantt", "quadrant", "habit", "working-on", "flagged"];
|
||||
const DEMOTED = ["calendar", "kanban", "table"];
|
||||
|
||||
// 1. Snapshot removed view configs into Archive/views/removed-views.json (Worktree B's archiver)
|
||||
// 2. For removed views with non-default filterRules, create a saved filter preset
|
||||
// on the closest core view (working-on → inbox preset, flagged → forecast preset)
|
||||
// 3. Strip removed entries from plugin.settings.viewConfiguration
|
||||
// 4. For demoted views, set type:"widget" and remove from FluentTaskView sidebar list
|
||||
// 5. Walk plugin.settings.workspaces.byId[*].settings.fluentActiveViewId; replace any removed-view ID with "inbox"
|
||||
// 6. Walk plugin.settings.workspaces.byId[*].settings.hiddenModules and prune removed IDs
|
||||
```
|
||||
|
||||
**Steps 5 + 6 are the WORKSPACE_ONLY_KEYS trap** — workspaces in
|
||||
`plugin.settings.workspaces.byId[*]` must be cleaned in the same migration
|
||||
step or workspaces will boot to a broken view.
|
||||
|
||||
**Onboarding compression (36 → 5 files):**
|
||||
```
|
||||
src/components/features/onboarding/
|
||||
├── OnboardingView.ts ← view shell (kept, slimmed)
|
||||
├── OnboardingController.ts ← state machine (kept, simplified to 4 steps)
|
||||
├── steps/
|
||||
│ ├── WelcomeStep.ts ← 1. Welcome + import-from-archive prompt
|
||||
│ ├── CaptureStep.ts ← 2. Pick target file, hotkey hint, sample capture
|
||||
│ ├── ViewsStep.ts ← 3. Pick which of 5 core views to show
|
||||
│ └── DoneStep.ts ← 4. "You're ready" + docs link
|
||||
└── ui/Layout.ts ← shared step layout (kept, simplified)
|
||||
```
|
||||
Delete every `Fluent*Step.ts`, `UserLevelStep`, `ModeSelectionStep`,
|
||||
`ConfigPreviewStep`, `SettingsCheckStep`, `PlacementStep`, `IntroStep`,
|
||||
`TaskGuideStep`, all of `steps/intro/`, `steps/guide/`, `steps/preview/`,
|
||||
`previews/`, `TaskCreationGuide.ts`. **No more Beginner/Advanced/Power
|
||||
branching** — one linear flow.
|
||||
|
||||
**DoD subset:** all 5 core views render with an empty vault, all workspaces
|
||||
boot to a valid `fluentActiveViewId`, onboarding completes in ≤4 steps,
|
||||
LOC reduction visible (`find src/ -name "*.ts" | xargs wc -l` should be
|
||||
notably lower).
|
||||
|
||||
---
|
||||
|
||||
### D.5 — Migration confirmation modal
|
||||
|
||||
**Files:**
|
||||
- `src/migration/v10/ConfirmationModal.ts` (new)
|
||||
- `src/utils/migration/steps/v10-archive.ts` (new — wraps Worktree B's `archiveAll`)
|
||||
- `src/index.ts` (first-launch detection in onload)
|
||||
|
||||
**The migration confirmation modal:**
|
||||
|
||||
On first launch of 10.0.0, BEFORE any view renders, show a blocking modal:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ Task Genius is upgrading to v10. │
|
||||
│ │
|
||||
│ The following will be archived: │
|
||||
│ • 47 workflows → Task Genius Archive/ │
|
||||
│ workflows/ │
|
||||
│ • 12 habits → habits/ │
|
||||
│ • 3 ICS sources → calendar-sync/ │
|
||||
│ • 5 custom views → views/ │
|
||||
│ │
|
||||
│ Archive folder: [Task Genius Archive ] │
|
||||
│ │
|
||||
│ [Cancel & rollback to v9] [Preview] │
|
||||
│ [Apply ▶] │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Driven by `MigrationRegistry.run(settings, {dryRun: true})` for the Preview
|
||||
button, then `dryRun: false` on Apply. Cancel exits Obsidian without
|
||||
persisting (leaves `data.json` untouched, instructions on how to revert).
|
||||
|
||||
**Wire `archiveAll` (from Worktree B) into the registry as `v10-archive`:**
|
||||
|
||||
```ts
|
||||
// src/utils/migration/steps/v10-archive.ts
|
||||
import { archiveAll } from "@/migration/v10/Archiver";
|
||||
import type { MigrationStep } from "../MigrationRegistry";
|
||||
|
||||
export const v10ArchiveStep: MigrationStep = {
|
||||
id: "v10.0.0-archive",
|
||||
targetVersion: "10.0.0",
|
||||
kind: "transform",
|
||||
description: "Archive deprecated feature data to <vault>/Task Genius Archive/",
|
||||
apply: async (settings, ctx) => {
|
||||
// The migration is two-part:
|
||||
// 1. Run archiveAll() to compute the archive contents
|
||||
// 2. Have the modal write them to the vault (since the registry is pure)
|
||||
// The "writing" is done by the ConfirmationModal in apply phase, NOT here.
|
||||
// This step's job is just to mark that the archive happened.
|
||||
return { changed: true, details: ["v10 archive scheduled"] };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Beta vs stable behavior:** in `10.0.0-beta.X`, the modal defaults to
|
||||
"preview only, do not apply" — user must explicitly click Apply. In stable
|
||||
`10.0.0`, Apply is the primary button.
|
||||
|
||||
**DoD subset:** the §3.10 acceptance test from the main plan — the "single
|
||||
hardest test":
|
||||
|
||||
> A user upgrades from 9.13.1 with: 47 workflows, 12 habits, 3 ICS sources,
|
||||
> 2 OAuth calendars, 5 custom views, 3 workspaces, all 25 settings tabs touched
|
||||
> at least once. After upgrading to 10.0.0, they:
|
||||
> 1. See the migration modal within 3 seconds.
|
||||
> 2. Click Apply.
|
||||
> 3. See their Inbox.
|
||||
> 4. Capture a task with `Mod+Shift+T`.
|
||||
> 5. Mark it done.
|
||||
> 6. Open `Task Genius Archive/` and see all their data.
|
||||
>
|
||||
> If any of those six steps requires reading docs, opening Settings, or
|
||||
> restarting Obsidian — **it's not done.**
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done (worktree-level)
|
||||
|
||||
| # | Criterion | How to verify |
|
||||
|---|---|---|
|
||||
| 1 | The §3.10 acceptance test from the main plan | Manual smoke on a real vault |
|
||||
| 2 | First-time user enables plugin → first captured task in ≤60 seconds, ≤5 clicks | Manual on a fresh vault |
|
||||
| 3 | Onboarding completes in ≤4 steps, ≤90 seconds | Manual |
|
||||
| 4 | Settings opens in ≤500ms | Performance tab in dev tools |
|
||||
| 5 | All workspaces boot to a valid `fluentActiveViewId` (no removed view IDs) | `git grep -l "removed view" src/__tests__` |
|
||||
| 6 | All custom hotkeys for surviving commands still work | Manual |
|
||||
| 7 | URI bookmarks pointing to deprecated tabs land somewhere reasonable with a Notice | Manual: navigate to `obsidian://task-genius?action=settings&tab=workflow` |
|
||||
| 8 | No console errors during migration | `src/__tests__/migration/v10-migration.test.ts` |
|
||||
| 9 | Main plugin LOC ≤ 42k (target 40k, ceiling 42k) | `find src/ -name "*.ts" -not -path "*__tests__*" -not -path "*__mocks__*" \| xargs wc -l \| tail -1` |
|
||||
| 10 | 0 references to deleted modules | `git grep "from.*workflow"`, `git grep "from.*habit-manager"`, etc. — all should be empty in main plugin |
|
||||
| 11 | All 5 core views render with an empty vault | Manual |
|
||||
| 12 | All 16 commands appear in palette with new names | Manual |
|
||||
| 13 | Phase 0 integration tests still pass | `npm test -- --testPathPattern="integration/"` |
|
||||
| 14 | TypeScript build clean | `npm run build` |
|
||||
|
||||
## Conflicts within Worktree D
|
||||
|
||||
D.1-D.4 each touch `src/index.ts` differently:
|
||||
- D.1 changes settings tab routing
|
||||
- D.2 rewrites commands
|
||||
- D.3 changes quick-capture commands
|
||||
- D.4 deletes timer/workflow commands
|
||||
|
||||
**Sequence them: D.2 first (it owns the command block), then D.1, D.3, D.4
|
||||
in any order, then D.5 last.**
|
||||
|
||||
## Open questions that affect THIS worktree
|
||||
|
||||
- **Q1 — Deprecation list:** if the user pulls anything back, the corresponding
|
||||
D.4 deletion gets skipped. Confirm before D.4 starts.
|
||||
- **Q3 — Default hotkeys:** the 5 defaults (`Mod+Shift+T`, etc.) are baked
|
||||
into D.2. If the user wants different bindings or no defaults, change at
|
||||
the top of D.2.
|
||||
- **Q4 — Cliff version:** 4-week vs 8-week runway only affects WHEN you
|
||||
start (after C merges, plus the beta soak period). Doesn't change scope.
|
||||
|
||||
## Useful existing utilities
|
||||
|
||||
- `src/managers/changelog-manager.ts` — for the "What changed" first-run sheet
|
||||
- `src/utils/migration/MigrationRegistry.ts` (Phase 0) — already supports
|
||||
atomic + dry-run, just add new steps
|
||||
- `src/dataflow/cache/scope-map.ts` (Phase 0) — typed scope map for the
|
||||
settings consolidation; D.1 should migrate the settings tab callers to
|
||||
`onSettingsFieldsChanged()` as it rewrites them
|
||||
- `src/widgets/core/BaseWidgetView.ts` — base for new TableWidgetView and
|
||||
QuadrantWidgetView
|
||||
- `src/widgets/codeblock/WidgetCodeBlockProcessor.ts` — codeblock embedding,
|
||||
no changes needed
|
||||
|
||||
## Don't do these things
|
||||
|
||||
- **Don't ship without the migration confirmation modal.** D.5 is mandatory.
|
||||
The whole point of Phase 0/1/2 was to set up the safety net for D.5 — it's
|
||||
the user's only protection against data loss.
|
||||
- **Don't keep `legacy` flags.** v10 is the cliff. No `enableV9Compatibility`
|
||||
toggles.
|
||||
- **Don't translate strings to non-English locales.** Worktree E's locale
|
||||
prune handles parity.
|
||||
- **Don't release `task-genius-calendar-sync` v0.1.0** — that's Worktree E.
|
||||
Calendar sync is the riskiest sub-plugin (OAuth tokens) and gets held until
|
||||
10.0.1 for an extra week of soak.
|
||||
|
||||
## When you're done
|
||||
|
||||
```bash
|
||||
git push origin refactor/v10-phase3-cliff
|
||||
gh pr create --base master --title "feat: v10 cliff - 16 commands, 7 settings tabs, 5 core views (Phase 3, Worktree D)"
|
||||
```
|
||||
|
||||
After merge, **Worktree E is unblocked**. Tag `10.0.0`. The cleanup phase
|
||||
begins.
|
||||
308
.v10-worktrees/worktree-e-cleanup.md
Normal file
308
.v10-worktrees/worktree-e-cleanup.md
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
# Worktree E — Phase 4 Cleanup (10.0.1)
|
||||
|
||||
## What you're doing
|
||||
|
||||
The cliff is behind us — 10.0.0 shipped with the migration confirmation modal,
|
||||
the new 7-tab settings UI, the 16-command palette, the 5 core views, and
|
||||
~50% of the LOC deleted. Worktree E is the **cleanup pass** that:
|
||||
|
||||
- Removes the migration code itself (its job is done)
|
||||
- Drops the one-version command-ID aliases that Worktree D shipped for
|
||||
backward compatibility
|
||||
- Prunes orphan locale keys
|
||||
- Releases `task-genius-calendar-sync` v0.1.0 (the riskiest sub-plugin, held
|
||||
back from Phase 2 for an extra week of soak)
|
||||
|
||||
This is the smallest worktree by LOC. It's mostly bookkeeping with one
|
||||
sub-plugin release coordination.
|
||||
|
||||
## Phase 0/1/2/3 ground
|
||||
|
||||
You depend on **Worktree D merged to master**. Verify before starting:
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git log master --oneline | head -5
|
||||
# Expect: "feat: v10 cliff..." (Worktree D) is the most recent commit
|
||||
git tag | grep "10.0.0$"
|
||||
# Expect: 10.0.0 tag exists
|
||||
```
|
||||
|
||||
Beta soak should have run for ~5 days minimum after `10.0.0-beta.1` was cut
|
||||
in Phase 2. If real users on the beta channel are reporting bugs, FIX THOSE
|
||||
FIRST before starting Worktree E — your work is post-stability cleanup, not
|
||||
emergency response.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git worktree add ../tg-phase4-cleanup master
|
||||
cd ../tg-phase4-cleanup
|
||||
git checkout -b refactor/v10-phase4-cleanup
|
||||
|
||||
npm install
|
||||
npm test -- --testPathPattern="integration/"
|
||||
# Expect: 70+ passing
|
||||
```
|
||||
|
||||
## Files you'll modify
|
||||
|
||||
- `src/migration/v10/Archiver.ts` (delete — its job is done, archive folder lives in user vaults forever)
|
||||
- `src/migration/v10/index.ts` (update exports)
|
||||
- `src/utils/migration/steps/v10-archive.ts` → tombstone-only marker
|
||||
- `src/utils/migration/steps/v10-view-cleanup.ts` → tombstone marker
|
||||
- `src/index.ts` (drop one-version command aliases that D.2 shipped)
|
||||
- `src/__tests__/migration/v10/` (delete the Archiver tests since the source is gone)
|
||||
|
||||
## Files you'll create
|
||||
|
||||
- `scripts/prune-locale-orphans.mjs` (new — one-shot script)
|
||||
- `src/__tests__/locale-parity.test.ts` (new — CI guard against regressions)
|
||||
|
||||
## Tasks (in order)
|
||||
|
||||
### Task 1 — Tombstone the migration code (~½ day)
|
||||
|
||||
The `v10-archive` and `v10-view-cleanup` migration steps did their job in
|
||||
10.0.0. They should now become **no-op tombstones** that just record "already
|
||||
applied" via `_meta.lastMigratedVersion`. The actual archive logic in
|
||||
`src/migration/v10/Archiver.ts` is no longer needed.
|
||||
|
||||
For each of the two steps:
|
||||
|
||||
```ts
|
||||
// Before (10.0.0)
|
||||
export const v10ArchiveStep: MigrationStep = {
|
||||
id: "v10.0.0-archive",
|
||||
targetVersion: "10.0.0",
|
||||
kind: "transform",
|
||||
description: "...",
|
||||
apply: async (settings, ctx) => { /* real work */ },
|
||||
};
|
||||
|
||||
// After (10.0.1)
|
||||
export const v10ArchiveStep: MigrationStep = {
|
||||
id: "v10.0.0-archive",
|
||||
targetVersion: "10.0.0",
|
||||
kind: "tombstone", // changed from "transform"
|
||||
description: "Archive migration applied in 10.0.0 (now no-op)",
|
||||
apply: () => ({ changed: false, details: ["already applied"] }),
|
||||
};
|
||||
```
|
||||
|
||||
The reason to keep the step at all (instead of deleting it) is that users
|
||||
upgrading from 9.x DIRECTLY to 10.0.1 (skipping 10.0.0) need the registry to
|
||||
recognize the version slot. With a tombstone marker, the registry sees
|
||||
`fromVersion < 10.0.0 < toVersion = 10.0.1` and runs the step — which now
|
||||
no-ops. Without the marker, fresh-install users would also be fine, but
|
||||
upgrading users wouldn't have a migration record at the right version.
|
||||
|
||||
Then delete `src/migration/v10/Archiver.ts` and its tests:
|
||||
|
||||
```bash
|
||||
rm src/migration/v10/Archiver.ts
|
||||
rm -rf src/__tests__/migration/v10/
|
||||
```
|
||||
|
||||
Update `src/migration/v10/index.ts` to remove the Archiver exports.
|
||||
|
||||
### Task 2 — Drop one-version command aliases (~½ day)
|
||||
|
||||
Worktree D.2 kept old command IDs as aliases for one version (10.0.0) to
|
||||
preserve manually-bound hotkeys. Now drop them:
|
||||
|
||||
```bash
|
||||
git grep -n "addCommand.*id:.*['\"]old-command-id['\"]" src/index.ts
|
||||
# Find the alias registrations and delete them
|
||||
```
|
||||
|
||||
The aliases were:
|
||||
- `quick-capture` → aliased to `tg:capture`
|
||||
- `cycle-task-status-forward` → aliased to `tg:mark-cycle`
|
||||
- ... etc., one alias per surviving command
|
||||
|
||||
These are documented in the D.2 sub-PR commit history. Read that commit to
|
||||
see the full alias list.
|
||||
|
||||
Add a CHANGELOG entry: "Removed v9 command aliases. If you bound hotkeys to
|
||||
old command IDs, rebind them to the v10 equivalents (see CHANGELOG 10.0.0)."
|
||||
|
||||
### Task 3 — Locale prune script (~½ day)
|
||||
|
||||
`scripts/prune-locale-orphans.mjs`:
|
||||
|
||||
```js
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Prune locale orphans: delete keys in non-en locale files that don't
|
||||
* exist in en.ts. Run manually before tagging 10.0.1.
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const localeDir = path.join(__dirname, "..", "src", "translations", "locale");
|
||||
|
||||
const en = await import(path.join(localeDir, "en.ts"));
|
||||
const enKeys = new Set(Object.keys(en.default));
|
||||
|
||||
const files = await fs.readdir(localeDir);
|
||||
for (const file of files) {
|
||||
if (file === "en.ts" || !file.endsWith(".ts")) continue;
|
||||
// ... parse, prune, write back
|
||||
}
|
||||
|
||||
console.log(`Pruned orphan keys from ${files.length - 1} non-en locales`);
|
||||
```
|
||||
|
||||
Run it:
|
||||
|
||||
```bash
|
||||
node scripts/prune-locale-orphans.mjs
|
||||
```
|
||||
|
||||
This is a ONE-SHOT script. Don't add it to npm scripts or CI. The author
|
||||
runs it manually when locale orphans accumulate.
|
||||
|
||||
### Task 4 — Locale parity Jest test (~½ day)
|
||||
|
||||
`src/__tests__/locale-parity.test.ts` — a tiny ratchet test that asserts
|
||||
every key in `en.ts` exists in every other locale file. New strings added
|
||||
to `en.ts` are allowed to be missing in other locales (they fall back to
|
||||
en) — this only fails if a locale file has EXTRA keys that don't exist in
|
||||
en.
|
||||
|
||||
Wait, that's wrong direction. Re-think:
|
||||
|
||||
- `en.ts` is the canonical superset
|
||||
- Other locales should be SUBSETS of `en.ts` (missing translations fall back to en)
|
||||
- If a non-en locale has a key that en doesn't, that's an orphan — assert it doesn't happen
|
||||
|
||||
```ts
|
||||
import en from "@/translations/locale/en";
|
||||
import zhCn from "@/translations/locale/zh-cn";
|
||||
import zhTw from "@/translations/locale/zh-tw";
|
||||
import ja from "@/translations/locale/ja";
|
||||
import ru from "@/translations/locale/ru";
|
||||
import uk from "@/translations/locale/uk";
|
||||
import ptBr from "@/translations/locale/pt-br";
|
||||
import enGb from "@/translations/locale/en-gb";
|
||||
|
||||
const enKeys = new Set(Object.keys(en));
|
||||
|
||||
describe("locale parity (W4 cleanup)", () => {
|
||||
test.each([
|
||||
["zh-cn", zhCn],
|
||||
["zh-tw", zhTw],
|
||||
["ja", ja],
|
||||
["ru", ru],
|
||||
["uk", uk],
|
||||
["pt-br", ptBr],
|
||||
["en-gb", enGb],
|
||||
])("%s has no orphan keys not in en.ts", (name, locale) => {
|
||||
const orphans = Object.keys(locale).filter((k) => !enKeys.has(k));
|
||||
expect(orphans).toEqual([]);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
This is the **automated** complement to the manual prune script. The script
|
||||
runs occasionally; the test runs every PR.
|
||||
|
||||
### Task 5 — Release `task-genius-calendar-sync` v0.1.0 (~2 days, mostly out of repo)
|
||||
|
||||
This is the riskiest sub-plugin — OAuth tokens, token refresh, CalDAV write
|
||||
operations. It was held back from Phase 2 for an extra week of soak after
|
||||
v10.0.0 ships.
|
||||
|
||||
Tasks:
|
||||
1. Create the new repo `task-genius-calendar-sync` (copy main plugin's tooling)
|
||||
2. Migrate `src/providers/*`, `src/managers/calendar-auth-manager.ts` from the
|
||||
main plugin (which deleted them in Worktree D.4)
|
||||
3. Add a one-time importer that reads from `<vault>/Task Genius Archive/calendar-sync/caldav-sources.json`
|
||||
4. Test with a real Google Calendar account (out of repo)
|
||||
5. Release v0.1.0 to community plugins (or BRAT)
|
||||
6. Pin a "calendar sync available" announcement to the GitHub Discussion
|
||||
|
||||
This task is largely external to the main plugin repo. The main-plugin side
|
||||
is just verifying nothing reads from the deleted provider files (`git grep`).
|
||||
|
||||
### Task 6 — Bump version + tag (~10 minutes)
|
||||
|
||||
```bash
|
||||
# In the main plugin repo
|
||||
# Bump manifest.json from 10.0.0 to 10.0.1
|
||||
# Bump versions.json (the Obsidian plugin version map)
|
||||
# Commit
|
||||
git tag v10.0.1
|
||||
git push origin v10.0.1
|
||||
```
|
||||
|
||||
## Definition of Done
|
||||
|
||||
| # | Criterion | How to verify |
|
||||
|---|---|---|
|
||||
| 1 | `src/migration/v10/Archiver.ts` deleted | File doesn't exist; `git grep "Archiver"` returns no production matches |
|
||||
| 2 | Both v10 migration steps are tombstones (no-op) | Code review |
|
||||
| 3 | One-version command aliases removed from `src/index.ts` | `git grep "alias.*10.0.0"` returns nothing |
|
||||
| 4 | Locale prune script exists and runs without error | `node scripts/prune-locale-orphans.mjs` exits 0 |
|
||||
| 5 | Locale parity Jest test exists and passes | `npm test -- --testPathPattern="locale-parity"` |
|
||||
| 6 | `task-genius-calendar-sync` v0.1.0 published | Out of repo; check community plugins or BRAT |
|
||||
| 7 | `manifest.json` is `10.0.1` | File contents |
|
||||
| 8 | TypeScript build clean | `npm run build` |
|
||||
| 9 | All Phase 0 + Phase 3 integration tests still pass | `npm test -- --testPathPattern="integration/"` |
|
||||
| 10 | No deleted-module references | `git grep "from.*workflow"`, `git grep "from.*habit-manager"`, `git grep "from.*timer-manager"`, `git grep "from.*calendar-auth"` — all empty |
|
||||
| 11 | Main plugin LOC ≤ 42k (still hitting the v10 ceiling) | `find src/ -name "*.ts" -not -path "*__tests__*" -not -path "*__mocks__*" \| xargs wc -l \| tail -1` |
|
||||
|
||||
## Conflicts to watch
|
||||
|
||||
**None.** By the time Worktree E starts, Phase 3 is merged and stable. No
|
||||
other worktrees are in flight.
|
||||
|
||||
## Open questions that affect THIS worktree
|
||||
|
||||
- **Q2 — Sub-plugin commitment:** if the user changed their mind about
|
||||
`task-genius-calendar-sync` (e.g. wants to delete OAuth/CalDAV outright
|
||||
instead of extracting), Task 5 gets skipped. Confirm before starting.
|
||||
|
||||
The other 4 questions are settled by the time you reach Phase 4.
|
||||
|
||||
## Useful existing utilities
|
||||
|
||||
- `src/utils/migration/MigrationRegistry.ts` — already supports `kind: "tombstone"` (Phase 0)
|
||||
- `src/translations/locale/*.ts` — existing locale files; the prune script
|
||||
walks them
|
||||
- `src/translations/helper.ts` — `t()` translation helper
|
||||
|
||||
## Don't do these things
|
||||
|
||||
- **Don't delete the migration steps entirely.** They become tombstones, not
|
||||
deletions. Tombstones preserve the version slot for skip-version upgrades.
|
||||
- **Don't translate orphan strings before pruning.** The prune script
|
||||
determines which strings are orphans by comparing to en.ts. Translate AFTER
|
||||
the prune, not before.
|
||||
- **Don't add the prune script to CI or npm scripts.** It's a one-shot tool;
|
||||
the author runs it manually.
|
||||
- **Don't ship `task-genius-calendar-sync` without testing OAuth refresh on
|
||||
a real account.** This is the highest-risk sub-plugin; an untested
|
||||
refresh-token bug = users locked out of their calendars.
|
||||
- **Don't bump the major version.** 10.0.1 is a patch release; 10.1.0 is
|
||||
reserved for the first feature release post-cliff.
|
||||
|
||||
## When you're done
|
||||
|
||||
```bash
|
||||
git push origin refactor/v10-phase4-cleanup
|
||||
gh pr create --base master --title "chore: v10 cleanup - tombstones, locale prune, calendar-sync release (Phase 4, Worktree E)"
|
||||
```
|
||||
|
||||
After merge, tag `v10.0.1` and the v10 refactor effort is COMPLETE. The
|
||||
plugin is at ~40k LOC, the user has 5 core views + 16 commands + 7 settings
|
||||
tabs + 1 quick-capture modal + a 4-step onboarding, and 4 sub-plugins are
|
||||
available for users who want the deprecated features back.
|
||||
|
||||
Update CHANGELOG.md with a final summary entry and consider writing a blog
|
||||
post / GitHub release note explaining the 6-week journey for users who
|
||||
weren't in the loop.
|
||||
|
|
@ -1,5 +1,18 @@
|
|||
# Task Genius v10 — Phase 1-4 Worktree Split
|
||||
|
||||
> **Update (2026-04-07):** the 5 worktrees described below have each been
|
||||
> split into a self-contained brief in [`./.v10-worktrees/`](./.v10-worktrees/).
|
||||
> Hand each brief to one agent / developer. This file remains as the umbrella
|
||||
> overview; individual briefs have the actionable instructions.
|
||||
>
|
||||
> | Worktree | Brief |
|
||||
> |---|---|
|
||||
> | A — banners | [`.v10-worktrees/worktree-a-banners.md`](./.v10-worktrees/worktree-a-banners.md) |
|
||||
> | B — Archiver | [`.v10-worktrees/worktree-b-archiver.md`](./.v10-worktrees/worktree-b-archiver.md) |
|
||||
> | C — read-only | [`.v10-worktrees/worktree-c-readonly.md`](./.v10-worktrees/worktree-c-readonly.md) |
|
||||
> | D — cliff cluster | [`.v10-worktrees/worktree-d-cliff.md`](./.v10-worktrees/worktree-d-cliff.md) |
|
||||
> | E — cleanup | [`.v10-worktrees/worktree-e-cleanup.md`](./.v10-worktrees/worktree-e-cleanup.md) |
|
||||
|
||||
Operational follow-up to the v10 refactor plan
|
||||
(`~/.claude/plans/dynamic-mixing-pie.md`). That document is the source of
|
||||
truth for **what** to do in each phase. This document is the source of truth
|
||||
|
|
|
|||
Loading…
Reference in a new issue