The new cli uses :db/id as its primary id while the existing mcp uses :block/uuid. These changes on list tools cause slow and sometimes breaking interactions for updates. It also causes buggy/inconsistent mcp behavior b/n mcp cli and server as there are different implentations being called. The new cli should use its own thread-api/* fns and replace MCP when it can meet existing functionality - working updates and provide a CLI interface. Also fix mcp.tools dissoc bug which had been fixed in the newer list fns
15 KiB
Logseq CLI Property Type and Upsert Option Unification Implementation Plan
Goal: Add a property type column to list property, add --id update-mode semantics to upsert block/page/tag/property, and remove duplicated --tags or --properties options from upsert block/page in favor of --update-tags or --update-properties.
Architecture: Keep the existing logseq-cli -> transport/invoke -> db-worker-node :thread-api/* contract unchanged and implement behavior changes in CLI parsing, action building, execution, and formatting.
Architecture: Extend property list payload shaping so non-expanded property items include :logseq.property/type, then render a dedicated property table in human output with a TYPE column.
Architecture: Treat --id as an explicit update signal for all upsert entity commands, and keep create paths only when --id is absent.
Tech Stack: ClojureScript, babashka.cli, Promesa, Datascript, logseq-cli command modules, db-worker-node thread APIs.
Related: Builds on docs/agent-guide/044-logseq-cli-upsert-block-page.md and relates to docs/agent-guide/043-logseq-cli-tag-property-management.md.
Problem statement
Current list property human output in /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs uses the same formatter as list tag, so no property-type column is rendered.
Current non-expanded property list items from /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/mcp/tools.cljs are built by minimal-list-item, which does not include :logseq.property/type.
Current upsert block already supports --id and treats it as update mode via update-mode? in /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs, but upsert page, upsert tag, and upsert property do not accept --id.
Current upsert block/page specs include both --tags or --properties and --update-tags or --update-properties in /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs, which duplicates semantics and increases parser and action complexity.
Current parser validation in /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs requires --page for upsert page and requires --name for upsert property, so there is no update-by-id mode for those commands.
Testing Plan
I will use @test-driven-development for all implementation batches.
I will add parser and action RED tests in /Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs for new --id contracts on upsert page, upsert tag, and upsert property.
I will add formatter RED tests in /Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs for the list property TYPE column and its value normalization.
I will add contract RED tests in /Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/mcp_tools_contract_test.cljs to ensure non-expanded property list items carry :logseq.property/type.
I will add integration RED tests in /Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs for update-by-id flows and for rejecting removed --tags or --properties flags on upsert block/page.
I will use @clojure-debug only when failures indicate fixture or async harness issues rather than missing behavior.
NOTE: I will write all tests before I add any implementation behavior.
Current implementation baseline
| Requirement | Current behavior | Gap |
|---|---|---|
list property shows type column. |
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs renders tag and property with the same columns (ID, TITLE, optional IDENT, timestamps). |
No TYPE column in human output, and non-expanded payload currently omits type. |
upsert block/page/tag/property supports --id and --id forces update mode. |
upsert block supports this already, but upsert page/tag/property specs do not expose --id and still depend on page/name creation-first contracts. |
Missing update-by-id mode for three upsert commands. |
upsert block/page removes --tags or --properties and uses --update-tags or --update-properties only. |
Both old and new options are accepted and merged in action building and execution. | Duplicate option surface and duplicate parsing paths remain. |
Target contract
upsert block keeps current --id update-mode behavior and remove legacy create-only --tags or --properties options.
upsert page accepts --id as update mode, and accepts --page only for create mode.
upsert tag accepts --id as update mode, and keeps --name for create mode.
upsert property accepts --id as update mode, and keeps --name for create mode.
upsert tag --id <id> with no additional mutation options is a successful no-op after id lookup and tag-class validation.
upsert page --id <id> --page <name> is invalid and must fail as conflicting selectors.
When --id is provided for any upsert command, create-specific resolution paths must be skipped and the command must fail if the target id does not exist or has the wrong entity class.
upsert block/page should reject --tags and --properties as unknown options after spec cleanup, with guidance to use --update-tags and --update-properties.
Update-by-id failures should use new id-mode specific error codes so scripts can distinguish id lookup and id class mismatch from create-mode validation failures.
Architecture sketch
list property
-> /src/main/logseq/cli/command/list.cljs execute-list-property
-> /src/main/frontend/worker/db_core.cljs :thread-api/cli-list-properties
-> /src/main/logseq/cli/common/mcp/tools.cljs list-properties
-> /src/main/logseq/cli/format.cljs format-list-property (new dedicated formatter)
upsert page/tag/property --id <id>
-> /src/main/logseq/cli/commands.cljs parse + finalize-command
-> /src/main/logseq/cli/command/upsert.cljs update-mode detection and action build
-> transport/invoke :thread-api/pull for id/entity validation
-> transport/invoke :thread-api/apply-outliner-ops for update ops only
Detailed implementation plan
- Add RED parser tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsassertingupsert page --id 10 --update-properties ...parses as:upsert-pageand no longer requires--page. - Add RED parser tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsassertingupsert tag --id 10parses andupsert property --id 10 --type nodeparses. - Add RED parser tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsassertingupsert block --tags ...andupsert page --properties ...fail with:invalid-optionsdue to removed flags. - Add RED parser tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsassertingupsert page --id <id> --page <name>fails with a selector conflict error. - Add RED build-action tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsforupsert page/tag/propertyactions that include:mode :updatewhen--idis present. - Add RED execute tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsverifyingupsert tag --id <id>with no update fields returns:okand no mutation ops. - Add RED execute tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljsverifying update-by-id rejects missing ids and wrong entity classes with new id-mode specific error codes. - Add RED formatter tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljsassertinglist propertyhuman output includesTYPEheader and per-row values. - Add RED contract tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/mcp_tools_contract_test.cljsasserting non-expandedlist-propertiesitems include:logseq.property/type. - Add RED integration tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljsforupsert page --id,upsert tag --id, andupsert property --idupdate mode behavior, including tag no-op behavior. - Add RED integration tests in
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljsverifyingupsert page --id --pagefails with selector conflict andupsert block/pagereject removed--tagsand--propertiesoptions. - Run focused RED commands and verify failures are behavior assertions, not fixture failures.
- Update specs in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsto remove:tagsand:propertiesfrom block/page specs and add:idto page/tag/property specs. - Update option validation in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljssoupsert pageandupsert propertyrequired-field checks are mode-aware instead of unconditional, and add explicit selector-conflict validation forupsert page --id --page. - Refactor
build-page-actionin/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsto support create mode by--pageand update mode by--id. - Refactor
build-tag-actionandbuild-property-actionin/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsto support update mode by--idwith mode-specific required options. - Add shared helper(s) in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsto pull entities by id and validate class/type constraints before updates. - Update
execute-upsert-page,execute-upsert-tag, andexecute-upsert-propertyin/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsso update mode uses id lookup, skips creation paths, and applies only update semantics, withupsert tag --idno-op when no mutation fields are provided. - Introduce explicit id-mode error codes in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljsand/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljsfor id-not-found and id-type-mismatch failures. - Remove all
:tagsand:propertiesaction wiring from page/block upsert flows in/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs, and keep only:update-tagsand:update-properties. - Update
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/mcp/tools.cljsto include:logseq.property/typein non-expanded property list items. - Update
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljsto split property formatting from tag formatting and render a dedicatedTYPEcolumn for:list-property. - Update docs in
/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.mdto remove--tagsor--propertiesfromupsert block/pagedocs, document update-by-id behavior across all upsert commands, and document selector conflict behavior. - Run focused GREEN tests for commands, format, and integration, then run
bb dev:lint-and-test. - Refactor only after GREEN to reduce duplication in upsert mode branching, then rerun focused tests and full suite.
Edge cases
upsert page --id <id> must fail when id points to a block that is not a page entity.
upsert tag --id <id> must fail when id points to a page not tagged with :logseq.class/Tag.
upsert property --id <id> must fail when id points to an entity without :logseq.property/type.
upsert tag --id <id> with no mutation options must return success without issuing mutation ops.
upsert page --id <id> --page <name> must fail with a dedicated selector conflict error.
Update-by-id missing target and class mismatch failures must return new id-mode specific error codes.
upsert block create mode with --blocks or --blocks-file must preserve existing validation behavior after removing --tags and --properties options.
Property type display should remain stable for built-in and custom properties, and missing type values should render as - instead of throwing.
JSON and EDN list outputs should remain backward compatible except for the additive type field on property items.
Verification commands and expected output
| Command | Expected output |
|---|---|
bb dev:test -v logseq.cli.commands-test |
Parser, action, and execute tests for mode switching and option removal pass. |
bb dev:test -v logseq.cli.format-test |
Human formatter tests pass with TYPE column coverage for list property. |
bb dev:test -v logseq.cli.mcp-tools-contract-test |
Contract tests pass with :logseq.property/type present in non-expanded property items. |
bb dev:test -v logseq.cli.integration-test/test-cli-upsert-page-create-and-update-existing |
Existing page upsert flow still passes after mode refactor. |
bb dev:test -v logseq.cli.integration-test |
New --id update-mode and removed-option behavior pass end to end. |
bb dev:lint-and-test |
Full suite passes with exit code 0. |
Testing Details
Tests cover CLI behavior at parser, action, executor, formatter, and end-to-end levels, and they assert entity outcomes instead of internal helper wiring.
Tests verify that update-by-id mode never creates entities and that legacy duplicated options are no longer accepted for block/page upsert.
Tests verify that list property human and structured output both include property-type information in their respective contracts.
Implementation Details
- Keep db-worker-node thread API names unchanged and avoid adding new transport methods.
- Add
--idtoupsert page/tag/propertyspecs in/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs. - Remove
--tagsand--propertiesfromupsert block/pagespecs in/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs. - Make finalize validation in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljsmode-aware for page/property required options. - Rework
build-page-action,build-tag-action, andbuild-property-actionto branch on create vs update mode. - Add id-based entity validation helpers in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs. - Make
upsert tag --idwith no mutation fields a successful no-op after id and class validation. - Reject
upsert page --id --pageas explicit selector conflict. - Add new id-mode specific error codes for id-not-found and id-type-mismatch paths.
- Include
:logseq.property/typein non-expanded property list payload from/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/mcp/tools.cljs. - Split property-specific table rendering in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljsto addTYPEcolumn. - Update
/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.mdcommand reference and examples. - Keep implementation and debugging workflow aligned with
@test-driven-developmentand@clojure-debug.
Question
No open questions.
Decided: upsert tag --id <id> with no additional mutation options is a successful no-op.
Decided: upsert page --id <id> --page <name> is rejected as conflicting selectors.
Decided: update-by-id failures use new id-mode specific error codes.