Skip to content

Releases: LUgABeEEE/dsh-global-todo

0.22.0 — the list gets a gate, and the phone's targets are measured

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 31 Oct 03:30

One rule for what reaches the list: no deadline, no list.

  • An agent record without a deadline waits in the inbox. Measured before this change: 354 of
    357 records were written by an agent (~16 a day), and 40 of the 86 open ones said 高 with 34 of
    those having no deadline at all. Three doors were writing with one wide threshold — the per-turn
    guidance had no gate, and the unattended question reused the manual 「抓一下待办」 wording. Now an
    agent-written record without dueAt lands in the inbox (收下 / 丢弃, seven-day sweep) with the
    reason on the record; a person's own entry is never gated.
  • Eight a day, then they wait — nothing is dropped. A per-day allowance on agent-written records
    reaching the list, derived from the records themselves rather than a counter, so a restart, a
    second Host or a sync cannot disagree with it.
  • The level comes from the deadline. No date = P3, within 7 days = P2, within 48 hours = P1,
    overdue = P0. What the model asked for is kept as suggestedPriority; a person's edit locks the
    record and the rule never overrides it again.
  • The inbox is outside the list's numbers, so 全部 / 未完成 mean the list itself.
  • A turn that already recorded something is not asked again — the unattended question is skipped
    when the agent wrote a todo during that turn, and its prompt is now its own, stricter text.
  • The panel explains itself: an inbox row names its reason, and the detail says whether the
    level is derived from the deadline, locked by you, or what the agent suggested.
  • Upgrading does one sweep at first start: unfinished agent records with no deadline that nobody
    touched move to the inbox, and rule-governed levels are recomputed. On this machine that was 68
    records into the inbox, 6 levels changed, the list from 87 to 19 — and every record a person had
    edited stayed exactly where it was.
  • The phone's touch targets are measured now. A probe that walks outward from each control until
    the point stops resolving to it found four the stylesheet had been confidently describing as
    fine: the search field was a 20px box inside a 44px frame, the attachment chips 22px, the
    detail's segmented control 29px, and the bare text buttons 20px. Fixed; npm run check:mobile
    now does that measurement in a real browser over the three phone views.
  • The screenshot baseline is a fixed instant, so the committed images stop drifting every day
    while the CSS stands still.
  • New: docs/RELEASING.md
    — the release procedure with the incident each guard prevents.

Tests: 393, contract checks 101/101, mobile contract green on all three phone views.

Full changelog: CHANGELOG.md

0.21.0 — one list on two machines, and a phone page that folds

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 29 Oct 03:37

One list on two machines, and a phone page that folds.

  • Each machine keeps its own list; they meet in a directory. Pointing storePath at a NAS makes every panel read a network round trip and leaves an offline laptop unable to open its own todos. Instead both machines fold through todos.hub.json in a shared directory (syncDir in 设置 → 多端同步, 0 = manual only). Later updatedAt wins per record, seq is renumbered from the record set, the fold is commutative and idempotent, and a sync that changes nothing writes nothing.
  • A deletion leaves a tombstone. remove used to drop the record, so the next copy from the other machine put it back. Documents now carry trash: [{ id, at, by }]; undo revokes the tombstone instead of letting it delete the restored copy again. Reading the list never touches the share — a stale mount cannot hang the panel, and /health.sync says what the last attempt did.
  • The phone page folds, and the fold is a gesture. At 600px or less the panel measures its own width: a 44px header line, a three-row toolbar (filters / search / grouping), 66px rows, 44px targets, safe areas respected. Scrolling down past 80px folds the search line away — after clearing the 110px it gives back, or the content jumps out from under the thumb — and a 48px flick up unfolds it; either direction is deaf for 300ms after a flip. Folded rows keep their title plus the signals that change what to do next; the open row stays whole. The dispatch summary and auto-collect switch left the phone toolbar for the settings sheet, and dsh-mobile needs no configuration.
  • Grouping by workspace is real. The first mode is the directory the work was written in (originDir, falling back to the session's directory), headed by its last segment with the full path as the tooltip; a workspace group never auto-collapses, and the per-group exemption keys on exactly what the group is. Labels: 工作区 / 时间 / 待办等级.
  • Two silent failures fixed on the way. /sync was registered twice per path; the Host keys exact routes by path alone and throws on the second, and the bundle registers its whole list in one pass — so every route after /sync disappeared while reads kept answering and writes returned a bare 404. And the primary button painted its label with the canvas token (--dsw-alias-bg-base), which a wallpaper theme sets transparent: a white pill with no word on it. Five places fixed, plus a test that forbids surface tokens in color / fill / stroke.
  • Tests: 381, contract matrix 85/85, both screenshot sets regenerated. Schema 2 — every machine sharing a hub needs 0.21.0 or newer, and a 0.20.0 host refuses a version-2 file loudly rather than dropping tombstones.

Full changelog: CHANGELOG.md

0.20.0 — the list and the memory store split the job

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 27 Oct 07:31

The list stops competing with the memory store for the same sentence.

  • A bridge with the memory plugin, as a sentence rather than a wire. When dsh-mnemon is
    installed, the prompt gains one extra section — actions belong to this list, facts belong to
    memory, do not store the same thing twice — and a completed todo's tool result says to
    mnemon_remember the conclusion with the todo id in the body. Both exist only when
    mnemonMemory does: ctx.inject(['systemPrompt','mnemonMemory'], …) gates the section, and
    createTools({ hasMemory }) asks per call. Naming a tool the deployment lacks is the failure the
    tool-group gate already prevents.
  • Two refusals, recorded in docs/DESIGN.md. No reading or writing of its SQLite store (a
    private schema, and its single service is a definition registry, not a "remember" call), and no
    mnemon-source-global-todo package in this repository (a second bundle with a peer dependency;
    this one has to keep working alone). The extension SDK it publishes is the documented path if
    someone wants recall to see open items.
  • Tests: memoryGuidanceFor gating, the completion line with and without a memory plugin, and
    three contract checks that keep the bridge from naming a tool a core deployment does not have.
  • Checked against DSH 0.2.1-rc.3. It adds More menus (browse other plugins, create a plugin) to
    the left sidebar, the conversation view tabs and the right-sidebar guide; fixes New Session losing
    an unsent draft; and changes how the DeepSeek API-key adapter refreshes its model list. None of it
    changes a plugin declaration: packages/boot/hmr/src/package-manifest.ts and
    packages/client/modules/src/client/manifest.ts are byte-identical between rc.2 and rc.3, and the
    sidebar entry keeps registering the same way — the new menu lists entries, it does not change how
    one is declared.
  • The declared range already admits the new line, and now says why. engines.dsh stays
    >=0.2.0-rc.1 <0.3.0; the Host matches with includePrerelease: true (dsh-app-boot's
    satisfies), which is why 0.2.1-rc.3 is inside a range whose lower bound names the 0.2.0
    prerelease. scripts/matrix.mjs gained that case and a warning: "cleaning up" the range into
    ^0.2.1 would refuse both 0.2.1-rc.2 and 0.2.1-rc.3. Both READMEs and the quickstart now
    name the versions that were checked instead of implying one.

0.19.0 — a phone layout, chosen by the panel's measured width

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 25 Oct 11:43
  • A phone layout, chosen by the panel's measured width. At 600px or less the panel is a touch
    surface: 44px controls and inputs, filter chips that scroll sideways instead of losing the tail
    that only appears when it has counts, the row's hand-off collapsed to its icon so meta packs two
    or three per line (the creator attribution moves to the detail), the keyboard hints gone, the
    bottom safe area respected, and type following the shell's --dsh-content-font-size. The
    desktop page is unchanged, and DSH Mobile's phone page needs no configuration — the panel was
    already using measured width for the 960px two-column breakpoint, and this is the same signal
    one step down. Three stable hooks carry it (data-mobile, .gt-btn-label, .gt-meta-by); no
    second render path.
  • The fixture can answer the measurement, which it never could before. tools/preview.mjs
    gives every ref a layout stub sized to --width (and --mobile writes the 390px set into
    preview/mobile/), so the narrow branch is rendered by a script instead of only by a device.
    npm run check:render asserts it on every verify, and npm run screenshots:mobile photographs
    the 390×844 baselines into docs/screenshots/mobile/.
  • A dead writer on the same host is reaped immediately, not after 30 s. 0.18.0 checked the
    lock's age before the holder's pid, so a crashed writer left the list refusing writes until the
    file turned 30 s old — while waiters give up at 5 s. Age now only decides the case with no pid to
    ask (another machine, or an unreadable claim); on this host a dead pid is enough. The test that
    covers it writes a fresh lock file on purpose, because a stale one would pass either way.
  • docs/WINDOWS.md gained the matching three-point checklist for the machine that can actually
    see the difference: exclusive create, no .lock left on disk, and a kill -9 followed by a write.
  • The lock is now findable from wherever somebody would look. Both READMEs name the lock file in
    the data section and say only todos.json is worth backing up; docs/QUICKSTART.md adds it to
    the backup table; docs/ROADMAP.md records it under "landed"; the FAQ gained question 16,
    「一直报 store-busy 怎么办」, with the five checks in the order a person should make them; and
    AGENTS.md says the store must never be written outside store.update, because that is how the
    bug can come back.

0.18.0 — a lock file, so two writers cannot overwrite each other

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 25 Oct 04:32

Two processes writing the same list no longer means one of them silently loses a change.

  • A lock file, not just a promise chain. store.update serialized writers inside one process
    only; two DSH processes (the Web panel plus a desktop or CLI session, or two machines on a synced
    store) each read the list, and the slower rename erased the faster change while both calls returned
    success — the report that prompted this. Every mutation now takes <store>.lock with
    open(…, 'wx') before reading and releases it after the rename (lib/lockfile.js).
  • A crashed writer cannot lock the list forever. The claim records pid + host + a random
    token: on the same host takeover requires a dead pid, across hosts it requires 30 s of age, and a
    reaped-then-retaken lock is never deleted by its previous owner. Takeovers log a warning; waiters
    retry with jittered backoff and give up after five seconds.
  • New code store-busy (HTTP 503; a tool returns { ok: false, reason: "store-busy" }): a write
    that could not happen says so instead of returning success, and the message names the holder when
    it can be read.
  • What it does not cover is now said out loud in docs/FAQ.md and docs/DESIGN.md: an older
    plugin version that does not take the lock, a hand edit, and a sync client writing at an arbitrary
    moment are still last-writer-wins; the panel keeps reporting external changes.
  • Tests: test/lock.test.mjs, including two real child processes adding ten todos each to one file
    (all twenty survive). Checked to fail with the lock disabled, so it pins the bug, not the mechanism.

0.17.1 — the READMEs are one language each

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 23 Oct 17:38

Two READMEs instead of one: Chinese in README.md, English in README.en.md, and the switch to
the other language on the first line of each.

  • Two READMEs, one language each. README.md is the Chinese document — the name GitHub's
    repository page shows and the only one npm renders — and README.en.md is the English one; each
    opens with a one-line switch to the other. The 0.17.0 npm page had an English document with the
    Chinese only reachable through a relative link, and relative links are exactly what npm rewrites
    or drops, so the switch is an absolute GitHub URL (a CJK heading slug is not stable across
    renderers either). README.zh.md becomes a two-line landing page so old links still resolve;
    README.en.md joined files, and test/manifest.test.mjs now reads the Node and DSH floors out
    of the two real documents. AGENTS.md records the convention.

0.17.0 — the confirmation gate is the headline

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 23 Oct 16:49

Documentation and prompt text: the confirmation gate is the headline now, not the third bullet.
Nothing in the write path changed — no field, no route, no validation rule — so upgrading is safe
for existing stores.

  • A release at all, because npm freezes documentation at publish time. The README, docs/ and
    the plugin-page locales in the tarball are a snapshot; leading with the gate only reaches the npm
    page and the installed docs through a version.

  • Both READMEs lead with the gate. It is claim 01 (was 03) and it names both doors: an
    agent's global_todo_done (or a status change to done), and the host closing a dispatched todo
    after a clean turn. A new early section — 「完成确认:没有一条记录能自己变成「完成」」 / Confirmation:
    no record finishes itself
    — carries the two-door table, the two answers and the clean-turn rule;
    the old 0.7 subsection and its row are gone rather than duplicated, and the comparison table gained
    a Finishing row.

  • The landing page, the npm description and the plugin-page locales open with the same sentence:
    page title, meta/og/twitter descriptions, hero lede, claim 01 (was 03), and the compare
    table. package.json gained human-in-the-loop, confirmation and approval, which is how
    someone looking for this actually searches.

  • docs/DESIGN.md documents review for the first time. It lived in lib/model.js and in no
    design doc: there is now a Done is a claim, from either door section, a review field row, and
    the links row finally admits kind: 'image' (0.15 shipped it).

  • The model-facing half says it too. The global_todo_done and global_todo_update
    descriptions call a completion a claim; the done result prints 「这条挂在「待确认」…」; todoLine()
    marks pending records in every rendering, so the model cannot read one as settled; and the injected
    guidance tells the model to reply 「已标记完成,等你确认」 instead of 「已经结束了」. The stale
    「插件也会在你安静下来后自动划掉」 line is gone — that idle-window heuristic was removed in 0.3.0
    and the sentence outlived it by thirteen minor versions, telling the model the opposite of the rule.

0.16.3 — the docs reach npm, and the release order is fixed

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 23 Oct 04:48

[0.16.3] — 2026-10-23

Documentation release: the npm page finally shows the current install instructions.

  • npm's README is frozen at publish time, so the install/per-profile rewrite and the
    「哪些功能还没有 / Not there yet」 sections (README.zh.md, FAQ, ROADMAP) reach GitHub but not the
    npm page until a new version ships. Nothing in lib/ changes.
  • The market index no longer blocks publishing. Its test required the index's newest version
    to equal package.json's — which cannot hold between a version bump and its publish, and
    prepack runs the suite, so the first release after the market index landed could not be
    tagged. The offline rule is now one-directional (the index may not offer a version newer than
    the tree), and staleness moved to npm run market:fresh, which asks the registry. AGENTS.md
    documents the two-step order: publish, then regenerate the index and commit it.

0.16.2 — a date-only deadline is local midnight

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 22 Oct 12:10

[0.16.2] — 2026-10-22

A date-only deadline is local midnight, so a date bound stops swallowing part of its own day.

  • before: / after: with a bare date were anchored at UTC midnight. Date.parse('2026-11-01')
    is UTC midnight by the specification; on +08 that is 08:00 local, so before:2026-11-01 included
    everything due 00:00–08:00 on the bound day, and an after: bound lost the same window in the
    west. Date-only values are now built with the local constructor, matching how dueAt is authored
    (issue #6).
  • This also changes what a bare dueAt: "2026-11-01" stores: it used to become UTC midnight
    and now becomes local midnight — the day the person means, on the machine they are on. Existing
    records are untouched; only new writes differ.
  • The timezone case is pinned in test/tz.test.mjs, which runs the parser in a child process
    with TZ=Asia/Shanghai: CI is UTC, where both anchors are the same number, so the regression
    would otherwise be invisible there. On a platform that ignores TZ the test skips rather than
    claiming to have checked.
  • docs/WINDOWS.md gained two answers from the same round: the date-bound row now states the
    local-midnight contract, and a standing three-question checklist (two-layer semantics, timezone,
    path four states) sits beside the per-feature one, plus the note that ARIA states must be
    verified by attribute, not by searching for the words a screen reader speaks.

0.16.1 — a tag whose CI is green

Choose a tag to compare

@LUgABeEEE LUgABeEEE released this 22 Oct 11:07

[0.16.1] — 2026-10-22

Same code as 0.16.0; the tag now points at a commit whose CI is green.

  • 0.16.0's tag commit had all three CI jobs red. The new before:/after: test asserted the
    order of two todos created in the same test, and CI creates them in the same millisecond, so the
    order it asserted was not the order the default sort produces. The behaviour under test was
    correct; the assertion was wrong. Fixed in 8024a48 — the run after it is green on both platforms.
  • Why a release rather than a moved tag: v0.16.0 is published on npm, and a tag that no
    longer matches what was published is worse than a version bump. 0.16.1 is 0.16.0 plus the
    assertion, so its tag has a green check.