mirror of
https://github.com/logseq/logseq.git
synced 2026-05-24 04:34:14 +00:00
enhance(skill): update logseq-review-workflow
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
---
|
||||
name: logseq-review-workflow
|
||||
description: Review Logseq changes with a structured workflow that routes findings through general repository rules, third-party library rules, Logseq module-specific rules, and targeted runtime verification across the web app, desktop app, and Logseq CLI.
|
||||
description: Review Logseq code changes, PRs, patches, commit ranges, or implementation plans through one main-agent orchestration workflow that routes read-only subagents across independent review passes, then deduplicates, validates, and reports findings for the web app, desktop app, and Logseq CLI.
|
||||
---
|
||||
|
||||
# Logseq Review Workflow
|
||||
|
||||
Use this skill when reviewing a Logseq code change, PR, patch, commit range, or implementation plan for correctness, regressions, maintainability, and repository convention compliance.
|
||||
|
||||
## Core principle
|
||||
|
||||
Review the change through three layers:
|
||||
The main agent drives the review. It identifies scope, loads routing rules, launches independent read-only subagents for each review pass, collects candidate findings, validates the actionable ones, deduplicates overlap, and writes the final conclusion.
|
||||
|
||||
Each pass subagent only collects issues. It must not edit files, stage changes, commit, push, or rewrite code. It returns candidate findings, evidence, checks already run, and unresolved questions to the main agent.
|
||||
|
||||
Review every pass through three layers:
|
||||
|
||||
1. **Repository-wide rules** — always apply shared review behavior from `rules/common.md`.
|
||||
2. **Library/runtime/tooling rules** — apply when the diff uses or changes a specific library, language/runtime surface, or tooling dependency.
|
||||
@@ -19,7 +21,7 @@ Use the routing tables below to load the most specific matching rule files.
|
||||
|
||||
Prefer concrete findings backed by code paths, invariants, tests, or runtime behavior. Do not list speculative issues unless you can explain the failure mode and affected scenario.
|
||||
|
||||
Every actionable finding must be validated through at least one real Logseq interaction path before it is included in the final review. Use `logseq-repl`, `logseq-cli`, `chrome`, or `computer-use` depending on the touched surface. Static reasoning and unit tests are useful for narrowing the issue, but they do not replace this per-finding runtime or tool-based validation requirement.
|
||||
Every actionable finding with an executable Logseq behavior path must be validated through at least one real Logseq interaction path before it is included in the final review. Use `logseq-repl`, `logseq-cli`, `chrome`, or `computer-use` depending on the touched surface. For implementation plans, docs-only changes, static configuration errors, missing migrations, schema/protocol invariant breaks, or other findings without an executable behavior path, validate through code paths, invariant evidence, tests, build/lint output, or documented contracts and state why runtime interaction does not apply.
|
||||
|
||||
## Read these first
|
||||
|
||||
@@ -39,7 +41,7 @@ Collect:
|
||||
|
||||
- changed files and touched namespaces
|
||||
- target runtime: renderer, Electron main process, db-worker-node, CLI, mobile, server/worker, tests, docs
|
||||
- changed public contracts: APIs, CLI output, DB schema, protocol messages, migrations, translations, config, or persisted data
|
||||
- changed public contracts: APIs, CLI output, DB schema, `frontend.worker.db.migrate/schema-version->updates`, migration `migrate-updates`, protocol messages, translations, config, or persisted data
|
||||
- dependencies and libraries involved
|
||||
|
||||
### 2. Load matching rule modules
|
||||
@@ -47,6 +49,7 @@ Collect:
|
||||
Always read:
|
||||
|
||||
- [`rules/common.md`](./rules/common.md)
|
||||
- [`rules/passes/subagent-output.md`](./rules/passes/subagent-output.md)
|
||||
|
||||
Then read every matching module below.
|
||||
|
||||
@@ -82,21 +85,49 @@ Then read every matching module below.
|
||||
| Mobile/Capacitor platform behavior | [`rules/modules/mobile.md`](./rules/modules/mobile.md) |
|
||||
| Whiteboard/canvas/tldraw-style interactions | [`rules/modules/whiteboard.md`](./rules/modules/whiteboard.md) |
|
||||
|
||||
### 3. Review in ordered passes
|
||||
### 3. Launch independent pass subagents
|
||||
|
||||
1. **Correctness pass** — identify broken invariants, invalid state transitions, contract mismatches, async races, schema/query errors, and missing migrations.
|
||||
2. **Regression pass** — check compatibility with existing graph data, DB graphs, file graphs, mobile/desktop runtimes, and persisted settings.
|
||||
3. **Failure-mode pass** — check error propagation, logging, cancellation, transaction atomicity, partial writes, and fail-fast behavior.
|
||||
4. **Performance pass** — check query shape, repeated schema construction, large data conversions, render loops, memory retention, and cache invalidation.
|
||||
5. **Test pass** — verify that tests cover the behavior contract and failure cases, not just happy paths.
|
||||
6. **Finding validation pass** — for each candidate actionable finding, run at least one actual check through an appropriate Logseq interaction path:
|
||||
Launch all pass subagents in parallel in one delegation round. Do not run pass subagents serially unless the environment cannot start them in parallel. Give every read-only subagent:
|
||||
|
||||
- the diff, commit range, PR, patch, or changed file list under review
|
||||
- the scope collected in step 1
|
||||
- `rules/common.md`
|
||||
- [`rules/passes/subagent-output.md`](./rules/passes/subagent-output.md)
|
||||
- that pass's rule file
|
||||
- any matching library/module rules relevant to that pass
|
||||
|
||||
Passes:
|
||||
|
||||
| Pass | Rule file |
|
||||
|---|---|
|
||||
| Correctness | [`rules/passes/correctness.md`](./rules/passes/correctness.md) |
|
||||
| Data contract | [`rules/passes/data-contract.md`](./rules/passes/data-contract.md) |
|
||||
| Regression | [`rules/passes/regression.md`](./rules/passes/regression.md) |
|
||||
| Failure mode | [`rules/passes/failure-mode.md`](./rules/passes/failure-mode.md) |
|
||||
| Migration validation | [`rules/passes/migration-validation.md`](./rules/passes/migration-validation.md) |
|
||||
| Performance | [`rules/passes/performance.md`](./rules/passes/performance.md) |
|
||||
| Test coverage | [`rules/passes/test-coverage.md`](./rules/passes/test-coverage.md) |
|
||||
| Repository convention | [`rules/passes/repository-convention.md`](./rules/passes/repository-convention.md) |
|
||||
|
||||
Ask each subagent to inspect only its assigned pass and return only candidate findings, supporting evidence, checks already run, and unresolved questions. While the subagents run in parallel, the main agent may prepare aggregation and validation steps but must wait for every pass report before the final review. If subagents are unavailable or parallel launch is not possible, run each pass locally with the same rule files and state that delegation or parallelism was unavailable in the verification summary.
|
||||
|
||||
### 4. Aggregate pass results
|
||||
|
||||
Wait for all pass subagents before writing the final review. Then:
|
||||
|
||||
1. Merge all candidate findings.
|
||||
2. Deduplicate overlapping reports across passes.
|
||||
3. Discard findings without concrete evidence or turn them into questions.
|
||||
4. Preserve the originating pass name for each retained finding.
|
||||
5. Validate every actionable finding before including it.
|
||||
|
||||
For executable behavior paths, validate with an appropriate Logseq interaction path:
|
||||
- `logseq-repl` for ClojureScript functions, DataScript state, importer/exporter behavior, renderer state, Electron, or worker runtime behavior.
|
||||
- `logseq-cli` for CLI command behavior, db-worker-node graph interactions, command output, and graph mutations.
|
||||
- `chrome` or `computer-use` for UI, keyboard, accessibility, browser-visible rendering, Electron window behavior, and user workflows.
|
||||
Record the exact command, REPL expression, or UI workflow and the observed result. If no actual check can be run, do not present the item as a confirmed finding; either continue investigating, downgrade it to a clearly labeled question, or explain the blocker.
|
||||
7. **Repository-convention pass** — apply naming, i18n, logging, keyword, migration, and command conventions.
|
||||
For findings without an executable behavior path, validate with code-path evidence, schema/protocol invariants, migration evidence, tests, build/lint output, or documented contracts. Record the exact command, REPL expression, UI workflow, or static evidence and the observed result. If no applicable check can be run, do not present the item as a confirmed finding; either continue investigating, downgrade it to a clearly labeled question, or explain the blocker.
|
||||
|
||||
### 4. Run targeted runtime verification
|
||||
### 5. Run targeted runtime verification
|
||||
|
||||
After static code inspection, exercise the reviewed functionality in the runtime surfaces affected by the diff. Keep the scenarios narrow: validate the changed behavior, the likely regression path, or the exact failure mode behind a finding.
|
||||
|
||||
@@ -110,7 +141,7 @@ Prefer isolated test graphs, temporary root directories, or disposable app state
|
||||
|
||||
If a surface is relevant but cannot be exercised, say exactly why. Do not describe unrun checks as verified.
|
||||
|
||||
### 5. Severity guidance
|
||||
### 6. Severity guidance
|
||||
|
||||
Use concise severity labels:
|
||||
|
||||
@@ -119,7 +150,7 @@ Use concise severity labels:
|
||||
- **Minor** — readability, naming, local maintainability, missing small cleanup.
|
||||
- **Question** — unclear intent or missing context that prevents confident review.
|
||||
|
||||
### 6. Finding format
|
||||
### 7. Finding format
|
||||
|
||||
Each finding should include:
|
||||
|
||||
@@ -133,17 +164,22 @@ Each finding should include:
|
||||
|
||||
If there are no findings, say what was reviewed and which rule modules were applied.
|
||||
|
||||
Add a short verification summary after findings or after the no-findings statement. Include the CLI commands, REPL probes, Chrome/browser scenarios, Desktop UI actions, and any relevant checks that could not be run.
|
||||
Add a short verification summary after findings or after the no-findings statement. Include the CLI commands, REPL probes, Chrome/browser scenarios, Desktop UI actions, static evidence, and any relevant checks that could not be run.
|
||||
|
||||
## Review checklist before final response
|
||||
|
||||
- Did you apply `rules/common.md`?
|
||||
- Did you route every touched library/module to its rule file?
|
||||
- Did you launch one read-only subagent for each independent pass in parallel, or state why delegation or parallelism was unavailable?
|
||||
- Did each subagent only collect issues and avoid modifying code?
|
||||
- Did you wait for all pass reports before writing the final review?
|
||||
- Did you deduplicate and validate actionable findings before including them?
|
||||
- Did you run targeted runtime verification after static inspection for every affected web-app, desktop-app, and Logseq CLI surface?
|
||||
- Did you separate verified behavior from checks that could not be run?
|
||||
- Did you distinguish proven issues from questions?
|
||||
- Did you check persisted data, migrations, or protocol compatibility when relevant?
|
||||
- Did you run the migration validation pass and explicitly decide whether the reviewed change requires `frontend.worker.db.migrate/schema-version->updates`, migration `migrate-updates`, a `logseq.db.frontend.schema/version` bump, or migration tests?
|
||||
- Did you check tests and name exact missing test coverage?
|
||||
- Did every actionable finding have at least one actual validation via `logseq-repl`, `logseq-cli`, `chrome`, or `computer-use`, with observed evidence?
|
||||
- Did every actionable finding have applicable validation evidence, using `logseq-repl`, `logseq-cli`, `chrome`, or `computer-use` for executable behavior paths and static/protocol/build evidence for non-executable findings?
|
||||
- Did you avoid asking for broad rewrites when a targeted fix is enough?
|
||||
- Did you mention any verification you could not perform?
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
The rule files are intentionally modular:
|
||||
|
||||
- `common.md` is always applied.
|
||||
- `passes/` files define independent read-only subagent review passes and their output contract.
|
||||
- `libraries/` files apply based on imported libraries, data structures, or runtime tooling.
|
||||
- `modules/` files apply based on Logseq product/runtime area.
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Correctness Pass
|
||||
|
||||
Inspect the reviewed change for broken behavior and invalid state.
|
||||
|
||||
Check:
|
||||
|
||||
- broken invariants and invalid state transitions
|
||||
- incorrect branching, nil handling, ordering, identity, or lifecycle behavior
|
||||
- contract mismatches between callers and callees
|
||||
- async races, cancellation mistakes, stale state, and promise/task misuse
|
||||
- schema, query, transaction, migration, and persistence mistakes
|
||||
- behavior that contradicts documented API, CLI, protocol, or UI contracts
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -0,0 +1,19 @@
|
||||
# Data-contract Pass
|
||||
|
||||
Use this file when running the independent data-contract review pass for `logseq-review-workflow`.
|
||||
|
||||
## Mission
|
||||
|
||||
Inspect the reviewed change for data-contract drift. Identify the canonical data shape proven by current callers, tests, public contracts, persisted data, protocol decoders, DB queries, pull selectors, schemas, or explicit boundary requirements. Flag defensive fallbacks, input-shape guessing, silent coercion, unnecessary normalizers, and compatibility ladders that are not justified by that evidence.
|
||||
|
||||
## Required checks
|
||||
|
||||
- Map every changed data path to its source of truth and canonical shape.
|
||||
- Treat already-contracted domain data as valid. Examples: a valid datom is already a datom; a DB pull result follows its selector; a schema-validated value is already validated; a protocol decoder defines the decoded payload shape.
|
||||
- Do not require normalizers for already-contracted domain data merely to make downstream access defensive or uniform.
|
||||
- Allow normalization only at actual external, persisted, user, CLI, network, or JS/native interop boundaries that must accept multiple shapes.
|
||||
- Require downstream code to use one clear contract after boundary validation or normalization.
|
||||
- Flag broad `or` chains, generic stringification, map/string/keyword guessing, alternate field-name ladders, unchecked dynamic access, and default values that hide missing required data.
|
||||
- Ask for unsupported-shape tests only when the changed code owns boundary validation. Do not ask for invalid-shape tests for data guaranteed by an upstream contract.
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md). If there are no findings, say which changed data paths were inspected and what canonical contracts were confirmed.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Failure-mode Pass
|
||||
|
||||
Inspect how the reviewed change behaves when operations fail or stop halfway.
|
||||
|
||||
Check:
|
||||
|
||||
- error propagation, user-visible errors, logs, and swallowed exceptions
|
||||
- fail-fast behavior versus silent recovery from programmer errors
|
||||
- cancellation, retries, timeouts, and async cleanup
|
||||
- transaction atomicity, partial writes, duplicate writes, and rollback assumptions
|
||||
- file system, network, IPC, worker, and interop failures
|
||||
- resource cleanup, listener cleanup, and state consistency after failure
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -0,0 +1,28 @@
|
||||
# Migration-validation Pass
|
||||
|
||||
Inspect the reviewed change for missing or incorrect frontend DB migrations. Decide whether changed persisted graph state requires updates in `frontend.worker.db.migrate`, especially `schema-version->updates` and the migration maps returned as `:migrate-updates`.
|
||||
|
||||
## Required checks
|
||||
|
||||
- Identify every changed persisted attribute, built-in property, built-in class, schema entry, kv entry, datom shape, or invariant that can already exist in user graphs.
|
||||
- Check whether the change requires a new `frontend.worker.db.migrate/schema-version->updates` entry and a matching `logseq.db.frontend.schema/version` bump.
|
||||
- For new built-in properties or classes, verify that the migration map uses the correct `:properties` or `:classes` entry so `upgrade-version!` returns accurate `:migrate-updates`.
|
||||
- For data rewrites, backfills, or invariant repairs, verify that the migration map uses `:fix` and that the returned tx report includes the intended `:migrate-updates` for sync handling.
|
||||
- For deleted properties, verify `:delete-properties` coverage and check whether the deleted attrs must also be listed in `logseq.db-sync.tx-sanitize/migration-deleted-attrs`.
|
||||
- Check that older graph versions are migrated deterministically without relying on runtime fallback defaults or broad compatibility code.
|
||||
- Check migration tests under `src/test/frontend/worker/migrate_test.cljs` or an equivalent targeted test for the old-version graph state and the post-migration state.
|
||||
|
||||
## Red flags
|
||||
|
||||
- A new built-in property/class or persisted attr is only added to creation-time data, schema, or property definitions, with no `schema-version->updates` entry for existing graphs.
|
||||
- `logseq.db.frontend.schema/version` is bumped without a corresponding migration entry, or a migration entry exceeds the current schema version.
|
||||
- Migration tx data changes existing user graph state but `:migrate-updates` does not describe the migration map that sync consumers need.
|
||||
- Deleted attrs are removed locally but still leak through db-sync sanitization or old local tx replay.
|
||||
- Tests cover newly-created graphs only, not migration from the previous schema version.
|
||||
|
||||
## Suggested validation
|
||||
|
||||
- Static evidence: inspect `src/main/frontend/worker/db/migrate.cljs`, `deps/db/src/logseq/db/frontend/schema.cljs`, built-in property/class definitions, and db-sync sanitization when attrs are deleted.
|
||||
- Test evidence: run a focused migration test with `bb dev:test -v frontend.worker.migrate-test/<test-name>` when a migration finding is actionable.
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md). If there are no findings, state which persisted changes were inspected and why no migration, `migrate-updates`, schema version bump, or migration test is required.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Performance Pass
|
||||
|
||||
Inspect the reviewed change for realistic performance and resource regressions.
|
||||
|
||||
Check:
|
||||
|
||||
- query shape, indexes, pull selectors, repeated scans, and N+1 access
|
||||
- repeated schema construction, parsing, serialization, or large data conversions
|
||||
- render loops, reactive recomputation, cache invalidation, and unnecessary subscriptions
|
||||
- memory retention, listener leaks, unbounded collections, and long-lived references
|
||||
- work that scales poorly with large graphs, many blocks, many pages, or sync history
|
||||
- startup, command latency, import/export, search, and background worker impact
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -0,0 +1,14 @@
|
||||
# Regression Pass
|
||||
|
||||
Inspect the reviewed change for behavior that may break existing users, graphs, runtimes, or persisted data.
|
||||
|
||||
Check:
|
||||
|
||||
- compatibility with existing DB graphs, file graphs, synced graphs, and persisted settings
|
||||
- migration, schema, property, protocol, and config compatibility
|
||||
- changed CLI output, command behavior, user workflows, keyboard flows, or public APIs
|
||||
- renderer, Desktop, Electron main-process, db-worker-node, mobile, and server/runtime differences
|
||||
- removed compatibility that is not justified by a current contract
|
||||
- edge cases in older graph data or partially migrated state
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -0,0 +1,15 @@
|
||||
# Repository-convention Pass
|
||||
|
||||
Inspect the reviewed change for Logseq repository conventions and maintainability.
|
||||
|
||||
Check:
|
||||
|
||||
- root and directory-specific `AGENTS.md` rules
|
||||
- naming, namespace layout, keyword definitions, and built-in property conventions
|
||||
- i18n rules for shipped user-facing text and translation dictionaries
|
||||
- logging, console output, CLI output, and error message conventions
|
||||
- migration placement, db-sync deleted attrs, and schema update rules
|
||||
- dependency, npm interop, Electron/Node/browser boundary conventions
|
||||
- unnecessary compatibility layers, broad rewrites, or unrelated refactors
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -0,0 +1,38 @@
|
||||
# Review Pass Subagent Output
|
||||
|
||||
Use this output contract for every `logseq-review-workflow` pass subagent.
|
||||
|
||||
## Hard constraints
|
||||
|
||||
- Stay read-only: do not edit files, stage changes, commit, push, or rewrite code.
|
||||
- Inspect only the assigned pass. Mention cross-pass concerns as notes, not primary findings.
|
||||
- Report candidate findings only when backed by concrete evidence.
|
||||
- Set each finding's `Category` to the assigned pass type. Do not invent categories.
|
||||
- Include questions when intent or contract ambiguity prevents a confident finding.
|
||||
|
||||
## Output
|
||||
|
||||
```markdown
|
||||
## <Pass Name> Pass Result
|
||||
|
||||
### Candidate findings
|
||||
|
||||
- **Severity:** Blocking | Important | Minor | Question
|
||||
- **Category:** Correctness | Data contract | Regression | Failure mode | Migration validation | Performance | Test coverage | Repository convention
|
||||
- **Location:** `path/to/file.cljs:line`
|
||||
- **Issue:** What is wrong.
|
||||
- **Evidence:** Code path, invariant, test, runtime observation, contract, or command output inspected.
|
||||
- **Impact:** Concrete user, data, runtime, or maintenance impact.
|
||||
- **Suggested validation:** Exact command, REPL probe, CLI workflow, UI workflow, or static check the main agent can use.
|
||||
- **Suggestion:** Smallest actionable fix.
|
||||
|
||||
### Checks run
|
||||
|
||||
- Exact commands, REPL probes, static files, or reasoning paths inspected.
|
||||
|
||||
### Questions
|
||||
|
||||
- Ambiguities that should remain questions unless the main agent finds more evidence.
|
||||
```
|
||||
|
||||
If there are no findings, state the files and behavior areas inspected and why they passed this pass.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Test-coverage Pass
|
||||
|
||||
Inspect whether the reviewed change has useful tests for the behavior it changes.
|
||||
|
||||
Check:
|
||||
|
||||
- tests cover the changed contract, not only happy paths
|
||||
- regression tests exist for fixed bugs or risky behavior
|
||||
- boundary validation is tested when the changed code owns that boundary
|
||||
- tests avoid unsupported-shape cases when upstream contracts already guarantee the shape
|
||||
- unit, CLI E2E, clj-e2e, or runtime probes match the touched surface
|
||||
- assertions verify observable behavior rather than implementation details only
|
||||
- test names, fixtures, and graph setup remain maintainable
|
||||
|
||||
Return results using [`subagent-output.md`](./subagent-output.md).
|
||||
@@ -134,7 +134,7 @@ query_task_status() {
|
||||
|
||||
query_agent_session() {
|
||||
local payload
|
||||
payload="$(run_cli_json query --graph "$graph" --query "[:find ?session . :where [$1 ?attr ?session] [?p :block/name \"agent-session-id\"] [?p :db/ident ?attr]]")"
|
||||
payload="$(run_cli_json query --graph "$graph" --query "[:find ?session . :where [$1 :logseq.property.agent/session-id ?session]]")"
|
||||
python3 - "$root_dir" "$config_path" "$graph" "$cli_path" "$payload" <<'PY'
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
@@ -268,8 +268,7 @@ def read_codex_events(codex_log):
|
||||
def session_query_for_title(title):
|
||||
return (
|
||||
'[:find ?session . :where [?e :block/title "{}"] '
|
||||
'[?p :block/name "agent-session-id"] '
|
||||
"[?p :db/ident ?attr] [?e ?attr ?session]]"
|
||||
"[?e :logseq.property.agent/session-id ?session]]"
|
||||
).format(title)
|
||||
|
||||
|
||||
@@ -759,8 +758,7 @@ def main():
|
||||
session = None
|
||||
query = (
|
||||
'[:find ?session . :where [?e :block/title "{}"] '
|
||||
'[?p :block/name "agent-session-id"] '
|
||||
"[?p :db/ident ?attr] [?e ?attr ?session]]"
|
||||
"[?e :logseq.property.agent/session-id ?session]]"
|
||||
).format(TASK_TITLE)
|
||||
|
||||
while time.time() < deadline:
|
||||
|
||||
5
deps/db/src/logseq/db/frontend/property.cljs
vendored
5
deps/db/src/logseq/db/frontend/property.cljs
vendored
@@ -646,6 +646,11 @@
|
||||
:schema {:type :node
|
||||
:public? false
|
||||
:hide? true}}
|
||||
:logseq.property.agent/session-id {:title "agent session id"
|
||||
:schema {:type :string
|
||||
:db/cardinality :db.cardinality/one
|
||||
:public? false
|
||||
:hide? true}}
|
||||
:logseq.property/used-template {:title "Used template"
|
||||
:schema {:type :node
|
||||
:public? false
|
||||
|
||||
@@ -87,3 +87,16 @@
|
||||
(is (= true (:queryable? property)))
|
||||
(is (contains? db-property/public-built-in-properties :logseq.property/assignee))
|
||||
(is (db-property/logseq-property? :logseq.property/assignee)))))
|
||||
|
||||
(deftest agent-session-id-built-in-property
|
||||
(let [property (get db-property/built-in-properties :logseq.property.agent/session-id)]
|
||||
(testing "schema"
|
||||
(is (= "agent session id" (:title property)))
|
||||
(is (= :string (get-in property [:schema :type])))
|
||||
(is (= :db.cardinality/one (get-in property [:schema :db/cardinality])))
|
||||
(is (= false (get-in property [:schema :public?])))
|
||||
(is (= true (get-in property [:schema :hide?]))))
|
||||
|
||||
(testing "internal built-in logseq property"
|
||||
(is (not (contains? db-property/public-built-in-properties :logseq.property.agent/session-id)))
|
||||
(is (db-property/logseq-property? :logseq.property.agent/session-id)))))
|
||||
|
||||
@@ -84,71 +84,26 @@
|
||||
:error {:code :unknown-command
|
||||
:message (str "unknown agent command: " command)}}))
|
||||
|
||||
(defn- value-ident
|
||||
[value]
|
||||
(cond
|
||||
(keyword? value) value
|
||||
(map? value) (:db/ident value)
|
||||
:else nil))
|
||||
|
||||
(defn- value-title
|
||||
[value]
|
||||
(cond
|
||||
(nil? value) nil
|
||||
(string? value) value
|
||||
(keyword? value) (name value)
|
||||
(map? value) (or (:block/title value)
|
||||
(:name value)
|
||||
(some-> (:db/ident value) name))
|
||||
:else (str value)))
|
||||
|
||||
(defn- value-titles
|
||||
[value]
|
||||
(if (and (sequential? value)
|
||||
(not (string? value)))
|
||||
(keep value-title value)
|
||||
(keep value-title [value])))
|
||||
(def ^:private agent-session-id-property-ident :logseq.property.agent/session-id)
|
||||
|
||||
(defn- task-tag?
|
||||
[tag]
|
||||
(or (= :logseq.class/Task (value-ident tag))
|
||||
(= "Task" (value-title tag))))
|
||||
|
||||
(defn- property-key-name
|
||||
[k]
|
||||
(cond
|
||||
(keyword? k) (name k)
|
||||
(string? k) k
|
||||
:else nil))
|
||||
|
||||
(defn- property-value-by-name
|
||||
[block property-name]
|
||||
(some (fn [[k v]]
|
||||
(when (= property-name (property-key-name k))
|
||||
v))
|
||||
block))
|
||||
(= :logseq.class/Task (:db/ident tag)))
|
||||
|
||||
(defn- assignee-values
|
||||
[block]
|
||||
(->> (concat (mapcat (fn [k]
|
||||
(value-titles (get block k)))
|
||||
["Assignee" :Assignee :assignee :logseq.property/assignee])
|
||||
(value-titles (property-value-by-name block "assignee"))
|
||||
(value-titles (property-value-by-name block "Assignee")))
|
||||
(keep trim-non-empty)
|
||||
(->> (:logseq.property/assignee block)
|
||||
(keep (comp trim-non-empty :block/title))
|
||||
set))
|
||||
|
||||
(defn- agent-session-id
|
||||
(defn- agent-session-id-present?
|
||||
[block]
|
||||
(or (some (fn [k]
|
||||
(trim-non-empty (get block k)))
|
||||
["agent-session-id" :agent-session-id :logseq.property/agent-session-id])
|
||||
(some-> (property-value-by-name block "agent-session-id") trim-non-empty)))
|
||||
(some? (get block agent-session-id-property-ident)))
|
||||
|
||||
(defn routable-task-decision
|
||||
[block agent-name]
|
||||
(let [uuid (:block/uuid block)
|
||||
status-ident (value-ident (:logseq.property/status block))
|
||||
status-ident (get-in block [:logseq.property/status :db/ident])
|
||||
assignees (assignee-values block)]
|
||||
(cond
|
||||
(nil? uuid)
|
||||
@@ -163,7 +118,7 @@
|
||||
(not (contains? assignees agent-name))
|
||||
{:routable? false :reason :assignee-mismatch}
|
||||
|
||||
(seq (agent-session-id block))
|
||||
(agent-session-id-present? block)
|
||||
{:routable? false :reason :already-routed}
|
||||
|
||||
:else
|
||||
@@ -789,10 +744,6 @@
|
||||
{}
|
||||
[:task :comment]))))))
|
||||
|
||||
(defn- first-entity
|
||||
[entities]
|
||||
(first (filter :db/id entities)))
|
||||
|
||||
(defn- first-live-entity
|
||||
[entities]
|
||||
(first (remove ldb/recycled? (filter :db/id entities))))
|
||||
@@ -853,51 +804,14 @@
|
||||
(throw (ex-info "agent bridge agent page not found after create"
|
||||
{:code :agent-registration-failed})))))))))
|
||||
|
||||
(def agent-session-id-property-name "agent-session-id")
|
||||
|
||||
(def agent-session-id-property-schema
|
||||
{:logseq.property/type :default
|
||||
:db/cardinality :db.cardinality/one})
|
||||
|
||||
(def agent-session-id-property-query
|
||||
'[:find [(pull ?p [:db/id :db/ident :block/name :block/title]) ...]
|
||||
:in $ ?property-name
|
||||
:where
|
||||
[?p :block/name ?property-name]
|
||||
[?p :db/ident]])
|
||||
|
||||
(defn- pull-agent-session-id-property
|
||||
[cfg repo]
|
||||
(p/let [properties (transport/invoke cfg :thread-api/q
|
||||
[repo [agent-session-id-property-query
|
||||
(common-util/page-name-sanity-lc agent-session-id-property-name)]])]
|
||||
(first-entity properties)))
|
||||
|
||||
(defn- ensure-agent-session-id-property!
|
||||
[cfg repo]
|
||||
(p/let [existing (pull-agent-session-id-property cfg repo)]
|
||||
(if (:db/ident existing)
|
||||
existing
|
||||
(p/let [_ (transport/invoke cfg :thread-api/apply-outliner-ops
|
||||
[repo [[:upsert-property [nil
|
||||
agent-session-id-property-schema
|
||||
{:property-name agent-session-id-property-name}]]]
|
||||
{}])
|
||||
created (pull-agent-session-id-property cfg repo)]
|
||||
(if (:db/ident created)
|
||||
created
|
||||
(throw (ex-info "agent-session-id property not found after upsert"
|
||||
{:code :agent-session-id-write-failed})))))))
|
||||
|
||||
(defn write-agent-session-id!
|
||||
[cfg repo block-uuid session-id]
|
||||
(p/let [property (ensure-agent-session-id-property! cfg repo)
|
||||
property-ident (:db/ident property)
|
||||
_ (when-not property-ident
|
||||
(throw (ex-info "agent-session-id property ident missing"
|
||||
{:code :agent-session-id-write-failed})))
|
||||
_ (transport/invoke cfg :thread-api/apply-outliner-ops
|
||||
[repo [[:batch-set-property [[block-uuid] property-ident session-id {}]]] {}])]
|
||||
(p/let [_ (transport/invoke cfg :thread-api/apply-outliner-ops
|
||||
[repo [[:batch-set-property [[block-uuid]
|
||||
agent-session-id-property-ident
|
||||
session-id
|
||||
{}]]]
|
||||
{}])]
|
||||
true))
|
||||
|
||||
(def ^:private routable-task-query
|
||||
@@ -906,6 +820,8 @@
|
||||
:block/title
|
||||
{:block/tags [:db/ident :block/title]}
|
||||
{:logseq.property/status [:db/ident :block/title]}
|
||||
{:logseq.property/assignee [:db/id :block/title :block/name :db/ident]}
|
||||
:logseq.property.agent/session-id
|
||||
*]) ...]
|
||||
:in $ ?agent-name
|
||||
:where
|
||||
@@ -932,8 +848,7 @@
|
||||
{:block block
|
||||
:tree-text (or (get-in show-result [:data :message])
|
||||
(:block/title block))}))
|
||||
(filter #(routable-task? % agent-name)
|
||||
(map #(assoc % "Assignee" agent-name) blocks))))))
|
||||
(filter #(routable-task? % agent-name) blocks)))))
|
||||
|
||||
(defn- dry-run-commands
|
||||
[graph agent-name prompt-templates tasks]
|
||||
@@ -1046,6 +961,7 @@
|
||||
{:block/tags [:db/ident :block/title]}
|
||||
{:logseq.property/status [:db/ident :block/title]}
|
||||
{:logseq.property/assignee [:db/id :block/title :block/name :db/ident]}
|
||||
:logseq.property.agent/session-id
|
||||
'*])
|
||||
|
||||
(def ^:private comment-block-selector
|
||||
@@ -1062,10 +978,9 @@
|
||||
(cond-> [:db/id
|
||||
:block/uuid
|
||||
:block/title
|
||||
{:logseq.property/assignee [:db/id :block/title :block/name :db/ident]}
|
||||
'*]
|
||||
session-property-ident
|
||||
(conj {session-property-ident [:db/id :block/title :block/name]})))
|
||||
{:logseq.property/assignee [:db/id :block/title :block/name :db/ident]}]
|
||||
session-property-ident (conj session-property-ident)
|
||||
true (conj '*)))
|
||||
|
||||
(defn- comments-area-selector
|
||||
[session-property-ident]
|
||||
@@ -1088,63 +1003,26 @@
|
||||
[?r :logseq.property.reaction/emoji-id ?emoji-id]
|
||||
[(missing? $ ?r :logseq.property/created-by-ref)]])
|
||||
|
||||
(defn- datom-added?
|
||||
[datom]
|
||||
(cond
|
||||
(map? datom)
|
||||
(not (false? (if (contains? datom :added)
|
||||
(:added datom)
|
||||
(:added? datom))))
|
||||
|
||||
(and (sequential? datom) (<= 5 (count datom)))
|
||||
(not (false? (nth datom 4)))
|
||||
|
||||
:else
|
||||
(not (false? (or (unchecked-get datom "added")
|
||||
(unchecked-get datom "added?")
|
||||
true)))))
|
||||
|
||||
(defn- datom-e
|
||||
[datom]
|
||||
(cond
|
||||
(map? datom) (:e datom)
|
||||
(sequential? datom) (first datom)
|
||||
:else (unchecked-get datom "e")))
|
||||
|
||||
(defn- datom-attr
|
||||
[datom]
|
||||
(cond
|
||||
(map? datom) (:a datom)
|
||||
(sequential? datom) (second datom)
|
||||
:else (unchecked-get datom "a")))
|
||||
|
||||
(defn- datom-value
|
||||
[datom]
|
||||
(cond
|
||||
(map? datom) (:v datom)
|
||||
(sequential? datom) (nth datom 2 nil)
|
||||
:else (unchecked-get datom "v")))
|
||||
|
||||
(defn- unknown-attr-datom?
|
||||
[datom]
|
||||
(let [attr (datom-attr datom)]
|
||||
(and (datom-added? datom)
|
||||
(let [attr (:a datom)]
|
||||
(and (true? (:added datom))
|
||||
(some? attr)
|
||||
(not (keyword? attr)))))
|
||||
|
||||
(defn- direct-assignee-datom?
|
||||
[datom]
|
||||
(and (datom-added? datom)
|
||||
(= assignee-property-ident (datom-attr datom))))
|
||||
(and (true? (:added datom))
|
||||
(= assignee-property-ident (:a datom))))
|
||||
|
||||
(defn- comment-title-datom?
|
||||
[datom agent-name]
|
||||
(and (datom-added? datom)
|
||||
(and (true? (:added datom))
|
||||
(seq agent-name)
|
||||
(= :block/title (datom-attr datom))
|
||||
(string? (datom-value datom))
|
||||
(or (string/includes? (datom-value datom) (str "[[" agent-name "]]"))
|
||||
(string/includes? (datom-value datom) "[["))))
|
||||
(= :block/title (:a datom))
|
||||
(string? (:v datom))
|
||||
(or (string/includes? (:v datom) (str "[[" agent-name "]]"))
|
||||
(string/includes? (:v datom) "[["))))
|
||||
|
||||
(defn- pull-assignee-property
|
||||
[cfg repo]
|
||||
@@ -1158,22 +1036,17 @@
|
||||
(p/let [property (pull-assignee-property cfg repo)
|
||||
property-id (:db/id property)]
|
||||
(concat direct-datoms
|
||||
(filter #(= property-id (datom-attr %)) unknown-attr-datoms)))
|
||||
(filter #(= property-id (:a %)) unknown-attr-datoms)))
|
||||
(p/resolved direct-datoms))))
|
||||
|
||||
(defn- direct-assignee-title
|
||||
[value]
|
||||
(when (or (string? value)
|
||||
(keyword? value)
|
||||
(map? value))
|
||||
(trim-non-empty (value-title value))))
|
||||
(trim-non-empty (:block/title value)))
|
||||
|
||||
(defn- assignee-value-matches?
|
||||
[cfg repo value agent-name]
|
||||
(if-let [title (direct-assignee-title value)]
|
||||
(p/resolved (= agent-name title))
|
||||
(p/let [entity (transport/invoke cfg :thread-api/pull [repo assignee-value-selector value])]
|
||||
(= agent-name (trim-non-empty (value-title entity))))))
|
||||
(p/let [entity (transport/invoke cfg :thread-api/pull [repo assignee-value-selector value])]
|
||||
(= agent-name (direct-assignee-title entity))))
|
||||
|
||||
(defn- pull-task-block
|
||||
[cfg repo block-id]
|
||||
@@ -1205,20 +1078,18 @@
|
||||
|
||||
(defn- comment-tag?
|
||||
[tag]
|
||||
(or (= :logseq.class/Comment (value-ident tag))
|
||||
(= "Comment" (value-title tag))))
|
||||
(= :logseq.class/Comment (:db/ident tag)))
|
||||
|
||||
(defn- comments-area-tag?
|
||||
[tag]
|
||||
(or (= :logseq.class/Comments (value-ident tag))
|
||||
(= "Comments" (value-title tag))))
|
||||
(= :logseq.class/Comments (:db/ident tag)))
|
||||
|
||||
(defn- comment-block?
|
||||
[block agent-name]
|
||||
(and (:block/uuid block)
|
||||
(some comment-tag? (:block/tags block))
|
||||
(or (string/includes? (or (:block/title block) "") (str "[[" agent-name "]]"))
|
||||
(contains? (set (keep value-title (:block/refs block))) agent-name))))
|
||||
(or (string/includes? (:block/title block) (str "[[" agent-name "]]"))
|
||||
(contains? (set (keep :block/title (:block/refs block))) agent-name))))
|
||||
|
||||
(defn- comments-area?
|
||||
[block]
|
||||
@@ -1240,9 +1111,7 @@
|
||||
(defn- ensure-comment-reaction!
|
||||
[cfg repo target-uuid emoji-id]
|
||||
(p/let [existing (transport/invoke cfg :thread-api/q [repo [comment-reaction-query target-uuid emoji-id]])]
|
||||
(when-not (if (sequential? existing)
|
||||
(seq existing)
|
||||
(some? existing))
|
||||
(when-not (some? existing)
|
||||
(transport/invoke cfg :thread-api/apply-outliner-ops
|
||||
[repo [[:toggle-reaction [target-uuid emoji-id nil]]] {}]))))
|
||||
|
||||
@@ -1252,12 +1121,10 @@
|
||||
:request :comment))
|
||||
|
||||
(defn- comment-target-session-id
|
||||
[agent-name session-property-ident block]
|
||||
(when (contains? (assignee-values block) agent-name)
|
||||
(or (agent-session-id block)
|
||||
(some-> (get block session-property-ident)
|
||||
value-title
|
||||
trim-non-empty))))
|
||||
[agent-name block]
|
||||
(if (contains? (assignee-values block) agent-name)
|
||||
(p/resolved (trim-non-empty (get block agent-session-id-property-ident)))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn- route-comment!
|
||||
[cfg {:keys [repo graph agent-name prompt-templates]} comment-block]
|
||||
@@ -1266,8 +1133,7 @@
|
||||
_ (when-not comment-uuid
|
||||
(throw (ex-info "comment block uuid is missing"
|
||||
{:code :agent-comment-uuid-missing})))
|
||||
session-property (pull-agent-session-id-property cfg repo)
|
||||
session-property-ident (:db/ident session-property)
|
||||
session-property-ident agent-session-id-property-ident
|
||||
comments-area (pull-comments-area cfg repo comment-block session-property-ident)
|
||||
_ (when-not (comments-area? comments-area)
|
||||
(throw (ex-info "comment parent is not a comments area"
|
||||
@@ -1285,7 +1151,9 @@
|
||||
:comments-area-tree-text comments-area-tree-text
|
||||
:comment-tree-text comment-tree-text
|
||||
:prompt-template (:comment prompt-templates)})
|
||||
resume-session-id (some #(comment-target-session-id agent-name session-property-ident %) target-blocks)
|
||||
target-session-ids (p/all (mapv #(comment-target-session-id agent-name %)
|
||||
target-blocks))
|
||||
resume-session-id (first (keep identity target-session-ids))
|
||||
command (if resume-session-id
|
||||
(build-codex-resume-command resume-session-id prompt {})
|
||||
(build-codex-command prompt {}))
|
||||
@@ -1333,8 +1201,8 @@
|
||||
|
||||
(defn- route-assignee-datom!
|
||||
[cfg {:keys [repo agent-name] :as opts} datom]
|
||||
(let [block-id (datom-e datom)
|
||||
assignee-value (datom-value datom)]
|
||||
(let [block-id (:e datom)
|
||||
assignee-value (:v datom)]
|
||||
(when block-id
|
||||
(p/let [matches? (assignee-value-matches? cfg repo assignee-value agent-name)]
|
||||
(when matches?
|
||||
@@ -1346,7 +1214,7 @@
|
||||
|
||||
(defn- route-comment-datom!
|
||||
[cfg {:keys [repo agent-name] :as opts} datom]
|
||||
(when-let [block-id (datom-e datom)]
|
||||
(when-let [block-id (:e datom)]
|
||||
(p/let [comment-block (pull-comment-block cfg repo block-id)]
|
||||
(when (comment-block? comment-block agent-name)
|
||||
(route-comment-once! cfg opts comment-block)))))
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
(merge {:db/id 42
|
||||
:block/uuid #uuid "11111111-1111-1111-1111-111111111111"
|
||||
:block/title "Ship the CLI bridge"
|
||||
:block/tags [{:block/title "Task"}]
|
||||
:block/tags [{:db/ident :logseq.class/Task
|
||||
:block/title "Task"}]
|
||||
:logseq.property/status {:db/ident :logseq.property/status.todo}
|
||||
"Assignee" "build-host"}
|
||||
:logseq.property/assignee [{:db/id 101
|
||||
:block/title "build-host"}]}
|
||||
overrides))
|
||||
|
||||
(defn- comment-block
|
||||
@@ -85,14 +87,24 @@
|
||||
"Reply instructions:"]]
|
||||
(is (string/includes? prompt expected))))
|
||||
|
||||
(deftest test-assignee-built-in-property
|
||||
(let [property (get db-property/built-in-properties :logseq.property/assignee)]
|
||||
(is (= "Assignee" (:title property)))
|
||||
(is (= :node (get-in property [:schema :type])))
|
||||
(is (= :many (get-in property [:schema :cardinality])))
|
||||
(is (= true (get-in property [:schema :public?])))
|
||||
(is (= true (:queryable? property)))
|
||||
(is (contains? db-property/public-built-in-properties :logseq.property/assignee))))
|
||||
(deftest test-agent-bridge-built-in-properties
|
||||
(testing "Assignee is public and queryable"
|
||||
(let [property (get db-property/built-in-properties :logseq.property/assignee)]
|
||||
(is (= "Assignee" (:title property)))
|
||||
(is (= :node (get-in property [:schema :type])))
|
||||
(is (= :many (get-in property [:schema :cardinality])))
|
||||
(is (= true (get-in property [:schema :public?])))
|
||||
(is (= true (:queryable? property)))
|
||||
(is (contains? db-property/public-built-in-properties :logseq.property/assignee))))
|
||||
|
||||
(testing "agent session id is an internal built-in property"
|
||||
(let [property (get db-property/built-in-properties :logseq.property.agent/session-id)]
|
||||
(is (= "agent session id" (:title property)))
|
||||
(is (= :string (get-in property [:schema :type])))
|
||||
(is (= :db.cardinality/one (get-in property [:schema :db/cardinality])))
|
||||
(is (= false (get-in property [:schema :public?])))
|
||||
(is (= true (get-in property [:schema :hide?])))
|
||||
(is (not (contains? db-property/public-built-in-properties :logseq.property.agent/session-id))))))
|
||||
|
||||
(deftest test-agent-command-entries
|
||||
(testing "parse agent bridge command surface"
|
||||
@@ -170,8 +182,7 @@
|
||||
|
||||
(testing "routes built-in Assignee node values with many cardinality"
|
||||
(is (true? (agent-command/routable-task?
|
||||
(task-block {"Assignee" nil
|
||||
:logseq.property/assignee [{:db/id 101
|
||||
(task-block {:logseq.property/assignee [{:db/id 101
|
||||
:block/title "build-host"}]})
|
||||
"build-host"))))
|
||||
|
||||
@@ -184,10 +195,11 @@
|
||||
(:reason (agent-command/routable-task-decision (task-block {:logseq.property/status {:db/ident :logseq.property/status.done}})
|
||||
"build-host"))))
|
||||
(is (= :assignee-mismatch
|
||||
(:reason (agent-command/routable-task-decision (task-block {"Assignee" "other-host"})
|
||||
(:reason (agent-command/routable-task-decision (task-block {:logseq.property/assignee [{:db/id 102
|
||||
:block/title "other-host"}]})
|
||||
"build-host"))))
|
||||
(is (= :already-routed
|
||||
(:reason (agent-command/routable-task-decision (task-block {"agent-session-id" "codex-1"})
|
||||
(:reason (agent-command/routable-task-decision (task-block {:logseq.property.agent/session-id "codex-1"})
|
||||
"build-host"))))))
|
||||
|
||||
(deftest test-prompt-and-command
|
||||
@@ -506,7 +518,7 @@
|
||||
{:lookup lookup}))))
|
||||
|
||||
:thread-api/q
|
||||
(p/resolved [])
|
||||
(p/resolved nil)
|
||||
|
||||
:thread-api/apply-outliner-ops
|
||||
(p/resolved {:ok true})
|
||||
@@ -540,7 +552,8 @@
|
||||
:routing-blocks* (atom #{})}
|
||||
{:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title request-comment)}]}))
|
||||
:v (:block/title request-comment)
|
||||
:added true}]}))
|
||||
(p/then (fn [_]
|
||||
(is (not-any? #(= :broad-scan (first %)) @calls))
|
||||
(is (some #(= [:thread-api/apply-outliner-ops
|
||||
@@ -588,7 +601,7 @@
|
||||
(p/rejected (ex-info "unexpected pull"
|
||||
{:lookup lookup}))))
|
||||
:thread-api/q
|
||||
(p/resolved [])
|
||||
(p/resolved nil)
|
||||
:thread-api/apply-outliner-ops
|
||||
(p/resolved {:ok true})
|
||||
(p/rejected (ex-info "unexpected invoke"
|
||||
@@ -615,7 +628,8 @@
|
||||
:routing-blocks* (atom #{})}
|
||||
{:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title request-comment)}]}))
|
||||
:v (:block/title request-comment)
|
||||
:added true}]}))
|
||||
(p/then (fn [_]
|
||||
(is (some #(= :codex (first %)) @calls))
|
||||
(is (some #(= [:thread-api/apply-outliner-ops
|
||||
@@ -636,7 +650,7 @@
|
||||
calls (atom [])
|
||||
request-comment (comment-block {})
|
||||
commented-block (assoc (first (:logseq.property.comments/blocks (comments-area-block {})))
|
||||
"agent-session-id" "existing-session-123"
|
||||
:logseq.property.agent/session-id "existing-session-123"
|
||||
:logseq.property/assignee [{:db/id 101
|
||||
:block/title "build-host"}])
|
||||
comments-area (comments-area-block {:logseq.property.comments/blocks [commented-block]})]
|
||||
@@ -652,7 +666,7 @@
|
||||
(p/rejected (ex-info "unexpected pull"
|
||||
{:lookup lookup}))))
|
||||
:thread-api/q
|
||||
(p/resolved [])
|
||||
(p/resolved nil)
|
||||
:thread-api/apply-outliner-ops
|
||||
(p/resolved {:ok true})
|
||||
(p/rejected (ex-info "unexpected invoke"
|
||||
@@ -679,7 +693,8 @@
|
||||
:routing-blocks* (atom #{})}
|
||||
{:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title request-comment)}]}))
|
||||
:v (:block/title request-comment)
|
||||
:added true}]}))
|
||||
(p/then (fn [_]
|
||||
(let [[_ command] (some #(when (= :codex (first %)) %) @calls)]
|
||||
(is (some? command))
|
||||
@@ -699,7 +714,7 @@
|
||||
calls (atom [])
|
||||
request-comment (comment-block {})
|
||||
commented-block (assoc (first (:logseq.property.comments/blocks (comments-area-block {})))
|
||||
"agent-session-id" "existing-session-123"
|
||||
:logseq.property.agent/session-id "existing-session-123"
|
||||
:logseq.property/assignee [{:db/id 101
|
||||
:block/title "other-host"}])
|
||||
comments-area (comments-area-block {:logseq.property.comments/blocks [commented-block]})]
|
||||
@@ -715,7 +730,7 @@
|
||||
(p/rejected (ex-info "unexpected pull"
|
||||
{:lookup lookup}))))
|
||||
:thread-api/q
|
||||
(p/resolved [])
|
||||
(p/resolved nil)
|
||||
:thread-api/apply-outliner-ops
|
||||
(p/resolved {:ok true})
|
||||
(p/rejected (ex-info "unexpected invoke"
|
||||
@@ -742,7 +757,8 @@
|
||||
:routing-blocks* (atom #{})}
|
||||
{:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title request-comment)}]}))
|
||||
:v (:block/title request-comment)
|
||||
:added true}]}))
|
||||
(p/then (fn [_]
|
||||
(let [[_ command] (some #(when (= :codex (first %)) %) @calls)]
|
||||
(is (some? command))
|
||||
@@ -776,7 +792,7 @@
|
||||
(p/rejected (ex-info "unexpected pull"
|
||||
{:lookup lookup}))))
|
||||
:thread-api/q
|
||||
(p/resolved [])
|
||||
(p/resolved nil)
|
||||
:thread-api/apply-outliner-ops
|
||||
(p/resolved {:ok true})
|
||||
(p/rejected (ex-info "unexpected invoke"
|
||||
@@ -804,7 +820,8 @@
|
||||
:routing-blocks* (atom #{})}
|
||||
{:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title request-comment)}]}))
|
||||
:v (:block/title request-comment)
|
||||
:added true}]}))
|
||||
(p/then (fn [_]
|
||||
(is (fn? @start-on-exit*))
|
||||
(p/let [_ (p/delay 10)]
|
||||
@@ -849,7 +866,8 @@
|
||||
:agent-name "build-host"})
|
||||
(@handler* :sync-db-changes {:tx-data [{:e 50
|
||||
:a :block/title
|
||||
:v (:block/title non-comment)}]})
|
||||
:v (:block/title non-comment)
|
||||
:added true}]})
|
||||
(p/let [_ (p/delay 10)]
|
||||
(is (not-any? #(= :codex (first %)) @calls)))))
|
||||
(p/catch (fn [e]
|
||||
@@ -1055,21 +1073,12 @@
|
||||
(deftest test-write-agent-session-id
|
||||
(async done
|
||||
(let [ops* (atom [])
|
||||
property-query-count* (atom 0)
|
||||
property-ident :user.property/agent-session-id
|
||||
block-uuid (uuid "11111111-1111-1111-1111-111111111111")]
|
||||
(-> (p/with-redefs [transport/invoke (fn [_ method args]
|
||||
(case method
|
||||
:thread-api/q
|
||||
(let [[_ [query & _query-args]] args]
|
||||
(if (= query agent-command/agent-session-id-property-query)
|
||||
(p/resolved (if (= 1 (swap! property-query-count* inc))
|
||||
[]
|
||||
[{:db/id 700
|
||||
:db/ident property-ident
|
||||
:block/title "agent-session-id"}]))
|
||||
(p/rejected (ex-info "unexpected query"
|
||||
{:query query}))))
|
||||
(p/rejected (ex-info "agent session id is built-in and should not be queried"
|
||||
{:args args}))
|
||||
|
||||
:thread-api/apply-outliner-ops
|
||||
(let [[_ ops _] args]
|
||||
@@ -1083,11 +1092,10 @@
|
||||
"logseq_db_demo"
|
||||
block-uuid
|
||||
"session-123")]
|
||||
(is (= [[:upsert-property [nil
|
||||
{:logseq.property/type :default
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:property-name "agent-session-id"}]]
|
||||
[:batch-set-property [[block-uuid] property-ident "session-123" {}]]]
|
||||
(is (= [[:batch-set-property [[block-uuid]
|
||||
:logseq.property.agent/session-id
|
||||
"session-123"
|
||||
{}]]]
|
||||
@ops*))))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))))
|
||||
@@ -1305,7 +1313,21 @@
|
||||
{:close! (fn [] nil)})
|
||||
agent-command/list-routable-tasks (fn [_cfg _repo _agent-name]
|
||||
(swap! broad-scans* inc)
|
||||
(p/resolved []))]
|
||||
(p/resolved []))
|
||||
transport/invoke (fn [_cfg method args]
|
||||
(case method
|
||||
:thread-api/pull
|
||||
(let [[_repo selector lookup] args]
|
||||
(if (and (= selector [:db/id :block/title :block/name])
|
||||
(= lookup 102))
|
||||
(p/resolved {:db/id 102
|
||||
:block/title "other-host"})
|
||||
(p/rejected (ex-info "unexpected pull"
|
||||
{:selector selector
|
||||
:lookup lookup}))))
|
||||
(p/rejected (ex-info "unexpected invoke"
|
||||
{:method method
|
||||
:args args}))))]
|
||||
(do
|
||||
(#'agent-command/listen-forever! {:root-dir "/tmp/logseq"
|
||||
:base-url "http://127.0.0.1:1234"
|
||||
@@ -1316,14 +1338,16 @@
|
||||
(@handler* :rtc-log {:tx-data [{:e 42
|
||||
:a :logseq.property/assignee
|
||||
:v {:db/id 101
|
||||
:block/title "build-host"}}]})
|
||||
:block/title "build-host"}
|
||||
:added true}]})
|
||||
(@handler* :sync-db-changes {:tx-data [{:e 42
|
||||
:a :block/title
|
||||
:v "renamed"}]})
|
||||
:v "renamed"
|
||||
:added true}]})
|
||||
(@handler* :sync-db-changes {:tx-data [{:e 42
|
||||
:a :logseq.property/assignee
|
||||
:v {:db/id 102
|
||||
:block/title "other-host"}}]})
|
||||
:v 102
|
||||
:added true}]})
|
||||
(p/let [_ (p/delay 5)]
|
||||
(is (= 0 @broad-scans*)))))
|
||||
(p/catch (fn [e]
|
||||
@@ -1359,7 +1383,8 @@
|
||||
(@handler* :sync-db-changes {:tx-data [{:e 42
|
||||
:a :logseq.property/assignee
|
||||
:v {:db/id 101
|
||||
:block/title "build-host"}}]})
|
||||
:block/title "build-host"}
|
||||
:added true}]})
|
||||
(p/let [_ (p/delay 5)]
|
||||
(is (= [1] @exit-calls*))
|
||||
(is (= [{:reason :task-processing-failed
|
||||
@@ -1382,8 +1407,7 @@
|
||||
(let [root (temp-root)
|
||||
handler* (atom nil)
|
||||
calls (atom [])
|
||||
block (task-block {"Assignee" nil
|
||||
:logseq.property/assignee [{:db/id 101
|
||||
block (task-block {:logseq.property/assignee [{:db/id 101
|
||||
:block/title "build-host"}]})]
|
||||
(try
|
||||
(-> (p/with-redefs [transport/connect-events! (fn [_cfg handler]
|
||||
@@ -1440,7 +1464,8 @@
|
||||
:agent-name "build-host"})
|
||||
(@handler* :sync-db-changes {:tx-data [{:e 42
|
||||
:a 900
|
||||
:v 101}]})
|
||||
:v 101
|
||||
:added true}]})
|
||||
(p/let [_ (p/delay 5)]
|
||||
(is (not-any? #(= :broad-scan (first %)) @calls))
|
||||
(is (some #(= [:thread-api/pull ["logseq_db_demo" [:db/id :db/ident] :logseq.property/assignee]] %)
|
||||
|
||||
Reference in New Issue
Block a user