Skip to content

fix: Kanban helper syncs only the card's leading link, not trailing date/reference links - #126

Merged
chhoumann merged 3 commits into
masterfrom
chhoumann/80-kanban
Jun 27, 2026
Merged

fix: Kanban helper syncs only the card's leading link, not trailing date/reference links#126
chhoumann merged 3 commits into
masterfrom
chhoumann/80-kanban

Conversation

@chhoumann

Copy link
Copy Markdown
Owner

Summary

Fixes #80 ("Kanban board faulty behavior"). When a Kanban card moves lanes, the
helper updates the linked note's tracked property (e.g. status) to the new lane.

The bug: the helper iterated over every wikilink in the board file and wrote
the card's lane into each linked note. A card line like

- [ ] [[Project A]] @[[2026-06-01]] and see [[Reference Note]]

therefore clobbered the date note (@[[2026-06-01]]) and the reference note with
the lane name - or raised a spurious 'status' not found in <board> notice when
those notes lacked the property (the root of the long-running #45 noise).

A Kanban card is a top-level task whose content begins with its link; only
that leading link identifies the note to sync. The fix scopes updates to that
leading link.

Root cause (restated)

The card's identity was never modeled. updateFilesInBoard looped over
cache.links (the whole file's links) and getTaskHeading reverse-mapped each
link to a lane via a fragile substring search (taskContent.includes(link.original)).
Trailing links and in-text references were indistinguishable from the card itself.

What changed (src/automators/onFileModifyAutomators/kanbanHelper.ts)

  • Leading-link-only card detection. A link is a card link only when it is the
    leading link of a top-level - [ ] task line (only the checkbox + whitespace
    precede it). This excludes @[[date]] links, mid-text references, indented
    sub-checklist items, timestamp-prefixed archive entries, and links inside lane
    headings.
  • Lanes from the metadata cache's headings, not a substring search - so
    ATX-closed (## Done ##Done) and setext lane names resolve correctly
    (verified live).
  • Cache/content drift guard. Card detection is validated against the
    freshly-read board content (the cached link must still sit at its recorded
    position with its recorded text); on a mismatch the link is skipped rather than
    acted on with stale data.
  • Per-card isolation (issue Kanban board faulty behavior #80 fault 1). Each card is processed in its own
    try/catch; an unresolvable link or a throwing property read (e.g. malformed
    YAML in a linked note) no longer aborts syncing of the remaining cards.
  • Clearer notice. The missing-property notice now names the linked file and
    board, and only fires for genuine card notes.

Contract note

A card syncs a note only when the card starts with the link (- [ ] [[Note]] …).
This matches the reporter's explicit intent in #80 ("…the [[Thing]] link is
obviously not meant to be updated" for prose cards) and is the only contract that
fixes #80 without silently clobbering incidental links. A purely descriptive card
like - [ ] Read [[Book]] is intentionally not synced.

Validation

Reproduced-then-fixed live in an isolated Obsidian instance (the 80-kanban
worktree vault), driving the real vault modify-event pipeline.

Before the fix (on today's master, post-#119): moving Project A clobbered
2026-06-01 and Reference Note to In Progress.
After the fix: Project A → In Progress; 2026-06-01 and Reference Note
unchanged.

Generalization proven live (not just the reporter's setup):

Scenario Result
Card under ATX-closed ## Done ## lane Done (not Done ##)
Card after a card whose note lacks the property still updated; accurate notice for the bad one
Rapid moves Shipping → Review → Live (within debounce) final lane Live
Second board tracking a different property (phase) isolated from the first board
A card whose property read throws logged; later cards still sync

Commands run:

pnpm run lint    # 0 errors (16 pre-existing warnings in untouched files)
pnpm run build
pnpm run test    # 71 unit tests pass
pnpm run test:e2e  # 9 live e2e tests pass (2 new Kanban + 7 existing)

Tests

  • __tests__/kanbanHelper.test.ts: regression coverage for the date/reference
    clobbering, prose-then-link and indented sub-items, heading links,
    ATX-closed/setext lanes, stale-cache drift, the throwing-card loop, and
    multi-lane boards - with a fixture builder asserted against the live cache shape.
  • tests/e2e/kanban-helper.test.ts (new): live modify-pipeline regression for the
    no-clobber behavior and the unresolvable-card-doesn't-abort behavior.

Review

A 3-lens adversarial design review ran before coding (folded into the
cache-headings + drift-guard design) and a 3-lens adversarial review ran on the
committed diff. The diff review's one blocking finding - fault 1 still
reachable via a throwing property read - is fixed in commit fae4d5a with live
proof and a regression test.

Out of scope / follow-ups

  • The drift guard validates the card link's identity, not a lane heading renamed
    in place while the cache lags (a transient, self-healing state the 5s modify
    debounce makes negligible). Deriving lanes fully from freshly-read content is a
    reasonable follow-up but would drop the cache's free ATX-closed/setext handling.
  • Kanban board faulty behavior #80 also asks why Kanban doesn't interoperate with Auto Progress - a separate
    automator concern, not addressed here.

Closes #80.

The Kanban helper iterated over every wikilink in a board file and wrote the
card's lane into each linked note's tracked property. Trailing links on a card
line - Kanban "@[[date]]" links and in-text references like "see [[Note]]" -
were therefore clobbered with a lane name (or raised a spurious "property not
found" notice when they lacked the property, the root of #45).

A Kanban card is a top-level task whose content begins with its link
("- [ ] [[Note]] ..."); only that leading link identifies the note to sync.

- Treat a link as a card link only when it is the leading link of a top-level
  task line, verified against the freshly-read board content (which also guards
  against the metadata cache lagging the edit: drift -> skip, never a wrong write).
- Derive lanes from the metadata cache's headings instead of a substring search,
  so ATX-closed ("## Done ##") and setext lane names resolve correctly.
- Keep iterating after an unresolvable card link (#80 fault 1) and keep the
  no-op guard when the property already matches.
- Make the missing-property notice name the linked file and board.

Adds regression coverage for the date/reference clobbering, prose-then-link and
indented sub-items, heading links, ATX-closed/setext lanes, stale-cache drift,
and multi-lane boards, with a fixture builder asserted against the live cache.
Drives the real vault modify-event pipeline in an isolated Obsidian instance:
moving a card updates the leading link's note to the new lane while leaving the
card's "@[[date]]" and "see [[ref]]" links untouched, and an unresolvable card
link no longer aborts updates to later cards.
…e board (#80)

Adversarial review found issue #80's fault 1 still reachable through the throw
path: getPropertiesInFile -> parseFrontmatter -> parseYaml throws on a linked
note with malformed YAML (e.g. a cache/content race), which aborted onFileModify
and skipped every later card. Each card is now processed in its own try/catch and
logged via logMessage (logError re-throws and would re-break this), so a single
failing card never blocks the rest.

Also: drop the unreachable `!fileProperties` branch (getPropertiesInFile always
returns an array), type the file cache as `CachedMetadata | null`, and scope the
card-link guard comment to what it actually guarantees (link identity, not lane
heading drift). Adds a regression test for the throwing-card case.
@chhoumann
chhoumann merged commit 52aec1e into master Jun 27, 2026
7 checks passed
github-actions Bot pushed a commit that referenced this pull request Jul 1, 2026
# [1.9.0](1.8.4...1.9.0) (2026-07-01)

### Bug Fixes

* address review findings from PRs [#119](https://github.com/chhoumann/MetaEdit/issues/119)-[#128](https://github.com/chhoumann/MetaEdit/issues/128) ([adf0b57](adf0b57))
* **api:** preserve reserved keys when cloning values ([#162](#162)) ([cff6076](cff6076))
* **automators:** count only [x]/[X] tasks complete; null-guard Kanban lane display ([#145](#145)) ([21da53a](21da53a))
* **bulk:** reject __proto__/constructor as a bulk property key ([#148](#148)) ([f03eacc](f03eacc))
* **bulk:** serialize bulk frontmatter writes through the controller write queue ([#147](#147)) ([3eb8236](3eb8236))
* **controller:** guard __proto__/constructor in all frontmatter writes ([#159](#159)) ([9d99fae](9d99fae))
* **controller:** make inline-field writes fence-aware ([5f8e713](5f8e713))
* **core,api:** safe command no-op, block-list deletion, presence-based property lookups ([#143](#143)) ([1702d08](1702d08))
* edit YAML lists (incl. tags) as native lists instead of collapsing them to a string ([#94](#94)) ([#128](#128)) ([79f6ee0](79f6ee0)), closes [#51](#51) [#36](#36)
* harden frontmatter metadata writes ([add2e90](add2e90))
* Kanban helper syncs only the card's leading link, not trailing date/reference links ([#126](#126)) ([52aec1e](52aec1e))
* **kanban:** don't write to an ambiguous same-named note ([#158](#158)) ([7f9655b](7f9655b))
* **kanban:** single notice for a card missing the board property ([9b1c88a](9b1c88a))
* parse inline fields behind list/quote markers and brackets ([#122](#122)) ([a62ef21](a62ef21)), closes [#119](#119) [#18](#18) [#121](#121) [#78](#78) [#84](#84)
* **parser:** preserve [[wikilinks]] in multi-value inline edits ([c331adb](c331adb))
* **settings:** serialize Auto Property choice persistence to prevent lost updates ([#154](#154)) ([2066b3e](2066b3e))
* **settings:** settings tab no longer clobbers concurrently-added Auto Property choices ([5759534](5759534))
* **settings:** validate auto-properties before queueing and make rollbacks compare-and-restore ([#156](#156)) ([8cbacdc](8cbacdc)), closes [#154](#154)
* stop inline-field updates appending a stray bracket ([#127](#127)) ([d2617f5](d2617f5)), closes [#67](#67) [#121](#121)
* **suggester:** center row action icons flush-right ([#166](#166)) ([c00795e](c00795e))
* **suggester:** use native icons + tooltips for row actions ([70903cc](70903cc))
* **suggester:** well-formed duplicate Notice, robust modal close, cleaner name suggestions ([#144](#144)) ([915dd6b](915dd6b)), closes [#143](#143)
* **tags:** correct and clarify tag editing ([#142](#142)) ([b837899](b837899)), closes [#49](#49)
* tolerate malformed YAML frontmatter when parsing note metadata ([#132](#132)) ([b835f97](b835f97)), closes [#130](#130)

### Features

* adopt modern Obsidian APIs and raise baseline ([5131664](5131664))
* **api:** append Dataview field instances without replacing ([42c78a8](42c78a8)), closes [#91](#91)
* **api:** expand public integration surface ([#120](#120)) ([bbf0a7a](bbf0a7a))
* assist property-value entry with autocomplete and a native date picker ([#125](#125)) ([cd86235](cd86235)), closes [#61](#61) [#74](#74) [#61](#61) [#61](#61)
* **auto-properties:** description, multi-select, and learn-as-you-go values ([#123](#123)) ([4b24296](4b24296)), closes [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#43](#43) [#30](#30)
* **auto-properties:** paste a list to split into choices ([#139](#139)) ([0245f7a](0245f7a))
* bulk edit metadata across folders and selected notes ([#124](#124)) ([c8d91e2](c8d91e2)), closes [#64](#64) [#20](#20)
* **create:** fluid, type-aware native YAML property creation ([#170](#170)) ([59b4c5d](59b4c5d))
* **editor:** edit properties with Obsidian's native widgets; raise minAppVersion to 1.12.7 ([#168](#168)) ([73b37e0](73b37e0))
* hide file tags from the Edit Meta menu ([#46](#46), [#90](#90)) ([#131](#131)) ([2c07937](2c07937)), closes [#tag](https://github.com/chhoumann/MetaEdit/issues/tag) [#tags](https://github.com/chhoumann/MetaEdit/issues/tags)
* support nested YAML path editing ([e9fce98](e9fce98))
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.9.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kanban board faulty behavior

1 participant