Skip to content

Weave - command updates#22

Merged
T-rav merged 16 commits into
mainfrom
weave
Jun 12, 2025
Merged

Weave - command updates#22
T-rav merged 16 commits into
mainfrom
weave

Conversation

@T-rav

@T-rav T-rav commented Jun 12, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Introduced multi-store annotation system enabling SQLAlchemy models to integrate with Neo4j and Elasticsearch for graph and search capabilities.
    • Added new domain models for unified business interfaces: User, Conversation, and Document, supporting cross-platform data aggregation.
    • Provided automatic synchronization for models across PostgreSQL, Neo4j, and Elasticsearch.
    • Added CLI commands for managing migration tools and enhanced migration workflow with annotation-driven detection and migration generation.
    • Introduced demo scripts and examples for domain composition, multi-store usage, and AI agent integration.
  • Enhancements

    • Expanded documentation to cover new migration commands, annotation usage, and domain model capabilities.
    • Improved database migration commands with dry-run support, rollback, and unified status/history reporting.
    • Updated requirements to include dependencies for multi-store and migration functionality.
    • Refined command references and improved error handling in CLI utilities.
  • Bug Fixes

    • Refined command references and improved error handling in CLI utilities.
  • Refactor

    • Reorganized and replaced legacy domain models with new, business-focused abstractions.
    • Centralized migration logic and removed redundant or specialized migration commands.
  • Tests

    • Added comprehensive unit tests for annotation features and multi-store synchronization.
    • Added GitHub Actions jobs for annotation and MCP client testing.
    • Enhanced test coverage with mocks and asyncio support.
  • Chores

    • Updated version to 0.1.5.
    • Improved .gitignore and configuration placeholders.
    • Added environment variable loading and enhanced error reporting for CLI commands.
  • Removals

    • Removed legacy domain models and tool configurations for Slack and Webcat to streamline the codebase.

@coderabbitai

coderabbitai Bot commented Jun 12, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

This update introduces a new domain composition architecture, replacing the previous monolithic domain models (Person, Channel, Message) with modular, business-focused domain objects (User, Conversation, Document). It adds multi-store annotation support for SQLAlchemy models, enabling seamless integration with Neo4j and Elasticsearch. Extensive documentation, migration workflow enhancements, new CLI commands, and comprehensive example scripts and tests are also included.

Changes

File(s) / Path(s) Change Summary
.gitignore Added pattern to ignore .weave/tools/ directory.
.weave/config.json Updated "webcat" tool URL placeholder to reference service status instead of service list.
.weave/contexts/conversations.yaml, .weave/tools/slack.yaml, .weave/tools/webcat.yaml Deleted context and tool configuration files for conversations, Slack, and Webcat.
README-migrations.md, weave/README.md Updated documentation for unified migration commands, new status/rollback options, and migration tool management.
UPDATED_MODELS_SUMMARY.md, WEAVE_ANNOTATIONS_SUMMARY.md Added detailed summaries of multi-store annotation enhancements and model updates.
domain/__init__.py Refactored exports: removed Person, Channel, Message; added User, Conversation, Document, and related functions.
domain/channels.py, domain/messages.py, domain/person.py Deleted previous domain models for channels, messages, and person.
domain/conversation.py, domain/document.py, domain/user.py Added new business-focused domain models for conversations, documents, and users with multi-source aggregation.
domain/data/insightmesh/*, domain/data/slack/* Enhanced InsightMesh and Slack models with multi-store annotations (Neo4j, Elasticsearch), new business logic methods, auto-sync, and updated fields.
examples/agent_domain_integration.py, examples/domain_composition_demo.py, examples/google_drive_document_demo.py, examples/multi_store_usage.py, examples/updated_models_demo.py Added comprehensive example scripts demonstrating domain composition, multi-store usage, agent integrations, and updated model features.
tests/test_annotations.py Added tests for annotation system and multi-store model integration.
weave/__init__.py Bumped version from 0.1.4 to 0.1.5.
weave/bin/modules/annotations/* Introduced annotation system: decorators and mixins for Neo4j/Elasticsearch integration and synchronization utilities.
weave/bin/modules/annotation_migration_detector.py Added module for detecting annotation changes and generating migration files for Neo4j/Elasticsearch.
weave/bin/modules/cli.py Added --test-mode CLI option for simulating commands.
weave/bin/modules/cli_db.py Refactored migration command logic, added dry-run, improved rollback/status/history, removed tool install/check commands, integrated new tool command group.
weave/bin/modules/cli_db_tools.py New CLI module for managing migration tool installation and status.
weave/bin/modules/cli_migrate.py Enhanced error reporting, environment loading, added annotation migration detection/creation, removed CLI command group.
weave/bin/modules/cli_services.py Renamed service list command to status, updated docstrings accordingly.
weave/requirements.txt Added dependencies for Neo4j, Alembic, psycopg2, SQLAlchemy, Elasticsearch, and requests.

Sequence Diagram(s)

Domain Composition and Multi-Store Sync (High-Level)

sequenceDiagram
    participant User as Domain User
    participant Conversation as Domain Conversation
    participant Document as Domain Document
    participant IM as InsightMesh Data
    participant Slack as Slack Data
    participant GDrive as Google Drive Data
    participant PG as PostgreSQL
    participant Neo4j as Neo4j
    participant ES as Elasticsearch

    User->>IM: Load user data by email/ID
    User->>Slack: Load user data by Slack ID
    User->>User: Aggregate identity, expose business properties

    Conversation->>IM: Load conversation/messages
    Conversation->>Slack: (Optional) Load Slack threads
    Conversation->>Conversation: Aggregate messages, summarize

    Document->>GDrive: Search/retrieve Google Drive docs
    Document->>Slack: Search/retrieve Slack files
    Document->>Document: Aggregate metadata, sharing context

    User->>PG: Save/update via SQLAlchemy
    User->>Neo4j: Sync node/relationships (auto via annotation)
    User->>ES: Sync index (auto via annotation)

    Conversation->>PG: Save/update via SQLAlchemy
    Conversation->>Neo4j: Sync node/relationships
    Conversation->>ES: Sync index

    Document->>PG: Save/update via SQLAlchemy
    Document->>Neo4j: Sync node/relationships
    Document->>ES: Sync index
Loading

Migration Workflow with Annotation Detection

sequenceDiagram
    participant Dev as Developer
    participant Model as Annotated Model
    participant CLI as weave CLI
    participant Detector as AnnotationMigrationDetector
    participant Alembic as Alembic
    participant Neo4j as Neo4j
    participant ES as Elasticsearch

    Dev->>Model: Add/change annotation decorators
    Dev->>CLI: Run `weave db migrate create --auto`
    CLI->>Detector: Scan models for annotation changes
    Detector->>CLI: Return migration instructions
    CLI->>Alembic: Generate/apply SQL migration (if needed)
    CLI->>Neo4j: Generate/apply Cypher migration
    CLI->>ES: Generate/apply Elasticsearch migration
    CLI->>Dev: Report migration status
Loading

Possibly related PRs

  • Kode-Rex/insight-mesh#15: Adds the "webcat" tool configuration and service name mappings in .weave/config.json, directly related to the current PR's update/removal of the "webcat" tool configuration in the same file.

Poem

(\
 ( •_•)
 / >🍃  Hopping through fields of code anew,
 Models now search, graph, and sync for you.
 Domain objects compose, data sources unite,
 Neo4j and ES join the SQLite.
 With agents and docs, and tests that delight,
 This bunny’s domain is shining bright!

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72e6171 and 245f5c8.

📒 Files selected for processing (8)
  • .github/workflows/tests.yml (1 hunks)
  • domain/data/slack/user.py (2 hunks)
  • mcp-server/requirements.txt (1 hunks)
  • mcp-server/test_mcp_mocked.py (1 hunks)
  • run_all_tests.sh (1 hunks)
  • test_mcp.py (1 hunks)
  • weave/bin/modules/annotations/graph.py (1 hunks)
  • weave/bin/modules/annotations/search.py (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@T-rav T-rav merged commit b96ef77 into main Jun 12, 2025
9 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🔭 Outside diff range comments (1)
weave/bin/modules/cli_migrate.py (1)

382-389: ⚠️ Potential issue

Migration templates still contain placeholders – will generate invalid files

Both _generate_neo4j_migration and _generate_elasticsearch_migration return strings with literal {revision_id}, {down_revision}, {create_date}.
Running detect_and_create_annotation_migrations() will therefore create unusable migration scripts.

Recommend injecting concrete values before writing:

-def _generate_neo4j_migration(...):
-    ...
-    return f'''"""
-{message}
-
-Revision ID: neo4j_{{revision_id}}
-Revises: {{down_revision}}
-Create Date: {{create_date}}
-"""
+def _generate_neo4j_migration(..., revision_id: str, down_revision: str = "base"):
+    ...
+    create_date = datetime.datetime.utcnow().isoformat()
+    return f'''"""
+{message}
+
+Revision ID: neo4j_{revision_id}
+Revises: {down_revision}
+Create Date: {create_date}
+"""

Do the same for the Elasticsearch generator and pass revision_id from detect_and_create_annotation_migrations.

Also applies to: 450-455

🧹 Nitpick comments (46)
weave/bin/modules/cli_services.py (1)

179-190: Command rename breaks backward compatibility – add an alias

The previous command was weave service list; changing it to status is reasonable, but existing scripts & tooling will break.
Click allows manual alias registration:

# after defining service_status
service_group.add_command(service_status, 'list')  # legacy alias

That preserves existing behaviour while nudging users toward status in docs.

Also consider renaming the underlying helper list_services to status_services for clarity, or at least leave a TODO.

weave/README.md (1)

108-115: Minor nit – keep command group hierarchy consistent

Documentation introduces weave db tool install/status, but the CLI file names them cli_db_tools.py with subgroup tool.
Double-check that the actual Click registration path is indeed weave db tool …; if the subgroup got named tools in code the examples will 404.

No code change required if already aligned.

domain/__init__.py (3)

4-7: Docstring still InsightMesh-centric

Now that the package exports Slack, Google Drive and cross-platform domain objects, the heading “Domain models for InsightMesh” is misleading.


9-15: Eager imports may slow CLI startup

Importing every heavy domain object at module load time triggers SQLAlchemy configuration, environment discovery, etc., even for scripts that only need a subset. Consider lazy re-exporting, e.g.:

import importlib
def __getattr__(name):
    if name in _EXPORTS:
        return importlib.import_module(f".{_EXPORTS[name]}", __name__)
    raise AttributeError(name)

This keeps the public surface identical while avoiding unnecessary initialisation.


17-32: Unsorted __all__

Long export lists are easier to scan when alphabetised.

weave/bin/modules/cli_db_tools.py (1)

70-113: Duplicated tool-status logic

cli_migrate.check_and_install_tools() already has a complete status routine. Re-implementing it here risks drift. Prefer delegating and printing its returned structure, or refactor shared checks into a util module.

🧰 Tools
🪛 Ruff (0.11.9)

74-74: requests imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


75-75: psycopg2 imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


76-76: alembic imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

domain/data/slack/user.py (1)

40-47: display_name_or_name could include real_name fallback

Current logic omits real_name if both display_name and name are empty. Consider:

return self.display_name or self.name or self.real_name

(consistent with domain/user.py usage).

tests/test_annotations.py (2)

152-154: Prefer truthiness over explicit == True

-assert user.is_active_user() == True
+assert user.is_active_user()
🧰 Tools
🪛 Ruff (0.11.9)

153-153: Avoid equality comparisons to True; use if user.is_active_user(): for truth checks

Replace with user.is_active_user()

(E712)


162-163: Prefer not over == False

-assert bot_user.is_active_user() == False
+assert not bot_user.is_active_user()
🧰 Tools
🪛 Ruff (0.11.9)

162-162: Avoid equality comparisons to False; use if not bot_user.is_active_user(): for false checks

Replace with not bot_user.is_active_user()

(E712)

domain/data/insightmesh/user.py (1)

38-41: is_active_user may return None

If is_active is nullable, return self.is_active may yield None, which is falsy but ambiguous. Consider an explicit boolean cast or default:

return bool(self.is_active)
README-migrations.md (1)

240-245: Duplicate list numbering – renumber for clarity

The “General” best-practices list restarts numbering at 2. and 3., which is confusing to the reader.

-1. **Use smart commands** - `weave db migrate <database>` auto-detects the right tool
-2. **Check tool status** - `weave db tool status` shows available migration tools
-3. **Install missing tools** - `weave db tool install` installs required dependencies
-2. **Check database info** - `weave db info` shows all databases and their types
-3. **Always backup** before running migrations in production
-4. **Test migrations** in development first
-5. **Version control** all migration files
+1. **Use smart commands** – `weave db migrate <database>` auto-detects the right tool  
+2. **Check tool status** – `weave db tool status` shows available migration tools  
+3. **Install missing tools** – `weave db tool install` installs required dependencies  
+4. **Check database info** – `weave db info` shows all databases and their types  
+5. **Always back up** before running migrations in production  
+6. **Test migrations** in development first  
+7. **Version-control** all migration files
domain/data/insightmesh/conversation.py (2)

42-50: display_title may access self.id before flush

If display_title is called on a transient instance (before it’s been persisted and PK generated), self.id will be None, yielding "Conversation None".
Consider a safer fallback:

-return self.title or f"Conversation {self.id}"
+return self.title or (f"Conversation {self.id}" if self.id is not None else "Untitled Conversation")

51-60: get_conversation_summary – missing conversation_metadata

Down-stream consumers may expect metadata; omitting it forces another DB round-trip. Add it unless intentionally excluded.

examples/multi_store_usage.py (2)

10-14: Remove unused imports flagged by Ruff

SlackChannel, SlackBase, and bulk_sync_to_stores are imported but never referenced.

-from domain.data.slack import SlackUser, SlackChannel, SlackBase
-from weave.bin.modules.annotations.sync import bulk_sync_to_stores
+from domain.data.slack import SlackUser
🧰 Tools
🪛 Ruff (0.11.9)

12-12: domain.data.slack.SlackChannel imported but unused

Remove unused import

(F401)


12-12: domain.data.slack.SlackBase imported but unused

Remove unused import

(F401)


13-13: weave.bin.modules.annotations.sync.bulk_sync_to_stores imported but unused

Remove unused import: weave.bin.modules.annotations.sync.bulk_sync_to_stores

(F401)


18-23: Credentials hard-coded in demo connection string

postgresql://postgres:postgres@localhost:5432/slack exposes a password in VCS. Replace with a placeholder or environment variable to avoid copy-paste leaks.

domain/data/slack/channel.py (1)

52-57: display_info – guard against None values

self.num_members may be None; 0 fallback is good, but privacy / status may also be None. Consider defaults to avoid "None" in UI strings.

weave/bin/modules/annotations/README.md (1)

1-200: Documentation looks solid – just a minor nitpick

Great job on the thorough README. Consider adding a short “Limitations / Gotchas” section (e.g. eventual-consistency caveats, Neo4j delete semantics) so newcomers know the current boundaries of the system.

examples/google_drive_document_demo.py (2)

9-13: Remove unused import to keep Ruff/F401 happy

DocumentFormat is imported but never referenced, triggering Ruff’s F401 warning.

-from domain import (
-    Document, DocumentFormat, DocumentSource,
+from domain import (
+    Document, DocumentSource,
🧰 Tools
🪛 Ruff (0.11.9)

10-10: domain.DocumentFormat imported but unused

Remove unused import: domain.DocumentFormat

(F401)


270-273: Avoid spawning a fresh event-loop per demo

asyncio.run() tears down and recreates the event loop on every iteration which is unnecessary and slow. Wrap the demos in a single main() coroutine instead:

-for i, demo in enumerate(demos, 1):
-    asyncio.run(demo())
-    if i < len(demos):
-        print("\n" + "=" * 60 + "\n")
+async def main():
+    for i, demo in enumerate(demos, 1):
+        await demo()
+        if i < len(demos):
+            print("\n" + "=" * 60 + "\n")
+
+if __name__ == "__main__":
+    asyncio.run(main())
weave/bin/modules/annotations/sync.py (2)

7-7: Drop unused typing import

Any isn’t used anywhere in this module.

-from typing import Type, Any
+from typing import Type
🧰 Tools
🪛 Ruff (0.11.9)

7-7: typing.Any imported but unused

Remove unused import: typing.Any

(F401)


81-99: Use yield_per() to avoid loading entire tables in bulk sync

query without batching can exhaust memory on large tables.

-    for instance in query:
+    for instance in query.yield_per(1000):
domain/data/insightmesh/message.py (2)

43-45: Avoid duplicating preview logic

__repr__ re-implements the same slicing done by the content_preview property. Re-use the property to keep logic in one place:

-        content_preview = self.content[:50] + "..." if self.content and len(self.content) > 50 else self.content
-        return f"<Message(id={self.id}, role='{self.role}', content='{content_preview}')>"
+        return f"<Message(id={self.id}, role='{self.role}', content='{self.content_preview}')>"

48-59: Consider @property for role checks

Small readability win:

@property
def is_user(self) -> bool:
    return self.role == 'user'

Then reference msg.is_user instead of msg.is_user_message().

domain/data/insightmesh/context.py (1)

54-59: json.dumps can fail for non-serialisable content

If self.content contains non-JSON-serialisable objects, json.dumps will raise. You may wish to wrap in try/except or use orjson.dumps() with default handlers.

weave/bin/modules/annotations/search.py (4)

5-6: Remove unused imports

functools, Callable, and Type are never referenced here → drop them to keep the module clean and keep Ruff quiet.

🧰 Tools
🪛 Ruff (0.11.9)

5-5: functools imported but unused

Remove unused import: functools

(F401)


6-6: typing.Callable imported but unused

Remove unused import

(F401)


6-6: typing.Type imported but unused

Remove unused import

(F401)


78-83: Expose basic connection tuning

Consider surfacing SSL / auth / timeout parameters (or reading ELASTICSEARCH_USERNAME / PASSWORD env-vars) so the client can be used against secured clusters without editing code.


150-188: Return the list of hits, not the entire hits envelope

search() currently returns response['hits'], which still contains total, max_score, and the nested hits list.
Down-stream callers usually expect just the documents:

-        return response['hits']
+        return response['hits']['hits']

217-247: Fragile SQLAlchemy→ES type mapping

str(column.type).startswith('VARCHAR') … relies on the DB dialect’s __str__ output and breaks for custom types.
Prefer isinstance(column.type, sqlalchemy.String) / sqlalchemy.types.String checks (and include Float, Numeric, etc.).

🧰 Tools
🪛 Ruff (0.11.9)

219-219: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)

weave/bin/modules/annotations/graph.py (3)

5-6: Prune unused imports

functools and Callable are imported but never used.

🧰 Tools
🪛 Ruff (0.11.9)

5-5: functools imported but unused

Remove unused import: functools

(F401)


6-6: typing.Callable imported but unused

Remove unused import: typing.Callable

(F401)


54-59: Duplicate mix-in injection

The guard only checks for sync_to_neo4j; subclasses of an already-decorated parent will reinject methods, overwriting any overrides.
Add an issubclass check or guard on an internal flag to avoid this.


173-175: Static-analysis: avoid unnecessary getattr

config = cls._neo4j_node_config is clearer and a tad faster than getattr with a constant name.

🧰 Tools
🪛 Ruff (0.11.9)

173-173: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)

examples/domain_composition_demo.py (2)

10-10: Remove unused imports

datetime and timedelta are only mentioned inside f-strings, never executed. Drop them or move the examples to real code blocks.

🧰 Tools
🪛 Ruff (0.11.9)

10-10: datetime.datetime imported but unused

Remove unused import

(F401)


10-10: datetime.timedelta imported but unused

Remove unused import

(F401)


178-183: Async main() adds complexity with no awaits

main() is declared async but performs only synchronous I/O. Consider making it a regular function and calling it directly, or actually awaiting real async work.

WEAVE_ANNOTATIONS_SUMMARY.md (1)

126-126: Markdown lint – emphasis used as heading

Line 126 appears as **Applies migrations across all systems**; use a proper heading (###) to satisfy MD036.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

126-126: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

UPDATED_MODELS_SUMMARY.md (1)

41-43: Capitalize “Slack”

Proper noun should be capitalised (“Slack”, not “slack”) to avoid grammar lint warnings.

weave/bin/modules/cli_db.py (3)

24-96: Function far too large – extract helpers

db_migrate_smart() tops 90 statements and >20 locals, triggering R0912/R0914/R0915.
Split the SQL/Neo4j/ES handling (and the dry-run printer) into dedicated private helpers to improve testability and silence the linter.

🧰 Tools
🪛 Ruff (0.11.9)

31-31: .config.get_all_databases imported but unused

Remove unused import: .config.get_all_databases

(F401)


44-44: .config.get_sql_databases imported but unused

Remove unused import

(F401)


44-44: .config.get_graph_databases imported but unused

Remove unused import

(F401)


44-44: .config.get_search_databases imported but unused

Remove unused import

(F401)

🪛 Pylint (3.3.7)

[refactor] 24-24: Too many local variables (23/15)

(R0914)


[refactor] 89-96: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 24-24: Too many branches (25/12)

(R0912)


[refactor] 24-24: Too many statements (90/50)

(R0915)


310-313: Unused import sneaked in

get_database_migration_tool is imported but never used, raising Ruff F401.
Simply drop it:

-from .config import (get_managed_databases, get_database_type, 
-                    get_database_migration_tool)
+from .config import get_managed_databases, get_database_type
🧰 Tools
🪛 Ruff (0.11.9)

311-311: .config.get_database_migration_tool imported but unused

Remove unused import: .config.get_database_migration_tool

(F401)


617-618: Extraneous f-prefix

console.print(f"[blue]💡 Use 'weave db migrate all' ...") has no interpolations → Ruff F541.

-console.print(f"[blue]💡 Use 'weave db migrate all' to run migrations for all databases[/blue]")
+console.print("[blue]💡 Use 'weave db migrate all' to run migrations for all databases[/blue]")
🧰 Tools
🪛 Ruff (0.11.9)

617-617: f-string without any placeholders

Remove extraneous f prefix

(F541)

examples/agent_domain_integration.py (1)

28-29: Remove unused intra-function imports

DocumentType, Document, and Conversation are imported but never referenced, tripping Ruff F401 four times.
Drop them to keep runtime light and avoid circular-import surprises.

-        from domain import Document, DocumentType
+        from domain import Document
...
-        from domain import Document
+        # no import needed, already available above
...
-        from domain import Conversation
+        # import not required here
...
-        from domain import User, Conversation, Document
+        from domain import User, Document

Also applies to: 97-98, 241-242, 263-264

🧰 Tools
🪛 Ruff (0.11.9)

28-28: domain.DocumentType imported but unused

Remove unused import: domain.DocumentType

(F401)

weave/bin/modules/annotation_migration_detector.py (2)

8-16: Prune unused imports

sys, Set, and hashlib are never referenced → Ruff F401.
Delete them to keep the module clean.

🧰 Tools
🪛 Ruff (0.11.9)

10-10: sys imported but unused

Remove unused import: sys

(F401)


12-12: typing.Set imported but unused

Remove unused import: typing.Set

(F401)


15-15: hashlib imported but unused

Remove unused import: hashlib

(F401)


117-119: Lost exception context

The generic except Exception as e: … continue swallows import errors silently and never logs e, making debugging painful.

-            except Exception as e:
-                # Skip modules that can't be imported
-                continue
+            except Exception as exc:
+                print(f"[AnnotationDetector] Skipped {module_path}: {exc}")
+                continue
🧰 Tools
🪛 Ruff (0.11.9)

117-117: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

domain/user.py (2)

9-11: Drop unused symbols

datetime and Union are imported but unused – remove to satisfy Ruff.

🧰 Tools
🪛 Ruff (0.11.9)

9-9: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


10-10: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)


148-155: Simplify is_active property

The elif is redundant after the early return and flagged by Pylint.

-        if self._insightmesh_user and self._insightmesh_user.is_active_user():
-            return True
-        elif self._slack_user and self._slack_user.is_active_user():
-            return True
-        return False
+        return (
+            (self._insightmesh_user and self._insightmesh_user.is_active_user()) or
+            (self._slack_user and self._slack_user.is_active_user())
+        )
🧰 Tools
🪛 Ruff (0.11.9)

150-153: Combine if branches using logical or operator

Combine if branches

(SIM114)

🪛 Pylint (3.3.7)

[refactor] 150-153: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)

domain/conversation.py (1)

9-9: Remove unused Union import

Union is never referenced – drop it to keep the import list lean.

-from typing import Optional, List, Dict, Any, Union
+from typing import Optional, List, Dict, Any
🧰 Tools
🪛 Ruff (0.11.9)

9-9: typing.Union imported but unused

Remove unused import: typing.Union

(F401)

domain/document.py (2)

202-244: Ensure result limit & deduplication when aggregating Slack files

search_by_content can return far more than limit because each Slack message may contain multiple files and you append every one before the final slice. Two side-effects:

  1. Unnecessary memory/CPU.
  2. Potential duplicate documents (same file attached in several messages).

Consider:

seen = set()
for hit in ...:
    for file_data in files:
        fid = file_data.get("id")
        if fid in seen or len(documents) >= limit:
            continue
        seen.add(fid)
        ...

This enforces the limit and prevents duplicates.


414-440: Simplify conditional chain

The early return statements make the subsequent elif blocks redundant. Switching to flat if statements shortens the code and appeases pylint R1705 without changing behaviour.

(Not critical, purely stylistic.)

🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 416-433: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 414-414: Too many return statements (10/6)

(R0911)


[refactor] 438-438: Too many return statements (9/6)

(R0911)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d70bdf4 and 72e6171.

📒 Files selected for processing (43)
  • .gitignore (1 hunks)
  • .weave/config.json (1 hunks)
  • .weave/contexts/conversations.yaml (0 hunks)
  • .weave/tools/slack.yaml (0 hunks)
  • .weave/tools/webcat.yaml (0 hunks)
  • README-migrations.md (5 hunks)
  • UPDATED_MODELS_SUMMARY.md (1 hunks)
  • WEAVE_ANNOTATIONS_SUMMARY.md (1 hunks)
  • domain/__init__.py (1 hunks)
  • domain/channels.py (0 hunks)
  • domain/conversation.py (1 hunks)
  • domain/data/insightmesh/__init__.py (1 hunks)
  • domain/data/insightmesh/context.py (2 hunks)
  • domain/data/insightmesh/conversation.py (2 hunks)
  • domain/data/insightmesh/message.py (1 hunks)
  • domain/data/insightmesh/user.py (2 hunks)
  • domain/data/slack/__init__.py (1 hunks)
  • domain/data/slack/channel.py (2 hunks)
  • domain/data/slack/user.py (2 hunks)
  • domain/document.py (1 hunks)
  • domain/messages.py (0 hunks)
  • domain/person.py (0 hunks)
  • domain/user.py (1 hunks)
  • examples/agent_domain_integration.py (1 hunks)
  • examples/domain_composition_demo.py (1 hunks)
  • examples/google_drive_document_demo.py (1 hunks)
  • examples/multi_store_usage.py (1 hunks)
  • examples/updated_models_demo.py (1 hunks)
  • tests/test_annotations.py (1 hunks)
  • weave/README.md (4 hunks)
  • weave/__init__.py (1 hunks)
  • weave/bin/modules/annotation_migration_detector.py (1 hunks)
  • weave/bin/modules/annotations/README.md (1 hunks)
  • weave/bin/modules/annotations/__init__.py (1 hunks)
  • weave/bin/modules/annotations/graph.py (1 hunks)
  • weave/bin/modules/annotations/search.py (1 hunks)
  • weave/bin/modules/annotations/sync.py (1 hunks)
  • weave/bin/modules/cli.py (1 hunks)
  • weave/bin/modules/cli_db.py (12 hunks)
  • weave/bin/modules/cli_db_tools.py (1 hunks)
  • weave/bin/modules/cli_migrate.py (6 hunks)
  • weave/bin/modules/cli_services.py (1 hunks)
  • weave/requirements.txt (1 hunks)
💤 Files with no reviewable changes (6)
  • .weave/contexts/conversations.yaml
  • .weave/tools/webcat.yaml
  • .weave/tools/slack.yaml
  • domain/person.py
  • domain/messages.py
  • domain/channels.py
🧰 Additional context used
🧬 Code Graph Analysis (9)
domain/data/slack/__init__.py (3)
domain/data/slack/user.py (1)
  • SlackUser (21-50)
domain/data/slack/channel.py (1)
  • SlackChannel (26-56)
weave/bin/modules/annotations/sync.py (1)
  • enable_auto_sync_for_model (69-78)
domain/data/insightmesh/__init__.py (5)
domain/data/insightmesh/user.py (1)
  • InsightMeshUser (21-55)
domain/data/insightmesh/context.py (1)
  • Context (26-78)
domain/data/insightmesh/conversation.py (1)
  • Conversation (26-60)
domain/data/insightmesh/message.py (1)
  • Message (31-77)
weave/bin/modules/annotations/sync.py (1)
  • enable_auto_sync_for_model (69-78)
domain/__init__.py (3)
domain/user.py (2)
  • User (28-256)
  • UserIdentity (19-25)
domain/conversation.py (3)
  • Conversation (40-423)
  • ConversationIdentity (29-37)
  • ConversationType (19-25)
domain/document.py (9)
  • Document (69-586)
  • DocumentIdentity (53-66)
  • DocumentFormat (36-49)
  • DocumentSource (30-33)
  • search_google_docs (590-593)
  • search_slack_files (595-597)
  • get_recent_google_drive_activity (599-601)
  • get_recent_slack_files (603-605)
  • get_recent_document_activity (607-609)
weave/bin/modules/cli_db_tools.py (1)
weave/bin/modules/cli_migrate.py (3)
  • check_and_install_tools (755-813)
  • install_python_dependencies (539-562)
  • install_neo4j_migrations (564-753)
domain/data/insightmesh/user.py (4)
weave/bin/modules/annotations/graph.py (2)
  • neo4j_node (31-61)
  • neo4j_relationship (64-92)
weave/bin/modules/annotations/search.py (1)
  • elasticsearch_index (25-64)
domain/user.py (2)
  • email (139-145)
  • name (130-136)
domain/data/slack/user.py (1)
  • is_active_user (48-50)
domain/data/slack/channel.py (2)
weave/bin/modules/annotations/graph.py (2)
  • neo4j_node (31-61)
  • neo4j_relationship (64-92)
weave/bin/modules/annotations/search.py (1)
  • elasticsearch_index (25-64)
weave/bin/modules/annotations/sync.py (2)
weave/bin/modules/annotations/search.py (2)
  • delete_from_elasticsearch (131-147)
  • sync_to_elasticsearch (115-129)
weave/bin/modules/annotations/graph.py (2)
  • sync_to_neo4j (136-152)
  • sync_relationships_to_neo4j (166-197)
weave/bin/modules/annotations/graph.py (1)
weave/bin/modules/annotations/search.py (1)
  • decorator (44-63)
domain/user.py (3)
domain/data/insightmesh/user.py (2)
  • InsightMeshUser (21-55)
  • display_name (43-45)
domain/data/slack/user.py (1)
  • display_name_or_name (44-46)
domain/conversation.py (2)
  • is_active (253-262)
  • get_loaded_sources (395-397)
🪛 Ruff (0.11.9)
tests/test_annotations.py

153-153: Avoid equality comparisons to True; use if user.is_active_user(): for truth checks

Replace with user.is_active_user()

(E712)


162-162: Avoid equality comparisons to False; use if not bot_user.is_active_user(): for false checks

Replace with not bot_user.is_active_user()

(E712)

examples/multi_store_usage.py

12-12: domain.data.slack.SlackChannel imported but unused

Remove unused import

(F401)


12-12: domain.data.slack.SlackBase imported but unused

Remove unused import

(F401)


13-13: weave.bin.modules.annotations.sync.bulk_sync_to_stores imported but unused

Remove unused import: weave.bin.modules.annotations.sync.bulk_sync_to_stores

(F401)

weave/bin/modules/cli_db_tools.py

65-65: .cli_migrate.check_and_install_tools imported but unused

Remove unused import: .cli_migrate.check_and_install_tools

(F401)


74-74: requests imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


75-75: psycopg2 imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


76-76: alembic imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

domain/data/insightmesh/user.py

8-8: weave.bin.modules.annotations.neo4j_relationship imported but unused

Remove unused import: weave.bin.modules.annotations.neo4j_relationship

(F401)

domain/data/slack/user.py

8-8: weave.bin.modules.annotations.neo4j_relationship imported but unused

Remove unused import: weave.bin.modules.annotations.neo4j_relationship

(F401)

examples/google_drive_document_demo.py

10-10: domain.DocumentFormat imported but unused

Remove unused import: domain.DocumentFormat

(F401)

weave/bin/modules/annotations/sync.py

7-7: typing.Any imported but unused

Remove unused import: typing.Any

(F401)

weave/bin/modules/annotation_migration_detector.py

10-10: sys imported but unused

Remove unused import: sys

(F401)


12-12: typing.Set imported but unused

Remove unused import: typing.Set

(F401)


15-15: hashlib imported but unused

Remove unused import: hashlib

(F401)


117-117: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

examples/domain_composition_demo.py

10-10: datetime.datetime imported but unused

Remove unused import

(F401)


10-10: datetime.timedelta imported but unused

Remove unused import

(F401)

weave/bin/modules/annotations/search.py

5-5: functools imported but unused

Remove unused import: functools

(F401)


6-6: typing.Callable imported but unused

Remove unused import

(F401)


6-6: typing.Type imported but unused

Remove unused import

(F401)


140-147: Use contextlib.suppress(Exception) instead of try-except-pass

(SIM105)


219-219: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)

weave/bin/modules/annotations/graph.py

5-5: functools imported but unused

Remove unused import: functools

(F401)


6-6: typing.Callable imported but unused

Remove unused import: typing.Callable

(F401)


173-173: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)

examples/agent_domain_integration.py

28-28: domain.DocumentType imported but unused

Remove unused import: domain.DocumentType

(F401)


97-97: domain.Document imported but unused

Remove unused import: domain.Document

(F401)


241-241: domain.Conversation imported but unused

Remove unused import: domain.Conversation

(F401)


263-263: domain.Conversation imported but unused

Remove unused import: domain.Conversation

(F401)

domain/user.py

9-9: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


10-10: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)


150-153: Combine if branches using logical or operator

Combine if branches

(SIM114)

weave/bin/modules/cli_db.py

198-201: Use ternary operator action = f'downgrade {revision}' if revision else 'downgrade -1' instead of if-else-block

(SIM108)


311-311: .config.get_database_migration_tool imported but unused

Remove unused import: .config.get_database_migration_tool

(F401)


429-429: pathlib.Path imported but unused

Remove unused import: pathlib.Path

(F401)


617-617: f-string without any placeholders

Remove extraneous f prefix

(F541)

domain/conversation.py

9-9: typing.Union imported but unused

Remove unused import: typing.Union

(F401)

🪛 markdownlint-cli2 (0.17.2)
WEAVE_ANNOTATIONS_SUMMARY.md

126-126: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

🪛 Pylint (3.3.7)
weave/bin/modules/annotation_migration_detector.py

[refactor] 41-41: Too few public methods (1/2)

(R0903)

weave/bin/modules/annotations/search.py

[refactor] 25-25: Too many arguments (7/5)

(R0913)


[refactor] 25-25: Too many positional arguments (7/5)

(R0917)

domain/user.py

[refactor] 83-83: Too many branches (16/12)

(R0912)


[refactor] 132-135: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 141-144: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 150-153: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)

weave/bin/modules/cli_migrate.py

[refactor] 490-493: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)

weave/bin/modules/cli_db.py

[refactor] 24-24: Too many local variables (23/15)

(R0914)


[refactor] 24-24: Too many branches (25/12)

(R0912)


[refactor] 24-24: Too many statements (90/50)

(R0915)


[refactor] 89-96: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)

domain/document.py

[refactor] 53-53: Too many instance attributes (11/7)

(R0902)


[refactor] 253-305: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 416-433: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 414-414: Too many return statements (10/6)

(R0911)


[refactor] 442-457: Unnecessary "elif" after "return", remove the leading "el" from "elif"

(R1705)


[refactor] 438-438: Too many return statements (9/6)

(R0911)

domain/conversation.py

[refactor] 105-105: Too many arguments (6/5)

(R0913)


[refactor] 105-105: Too many positional arguments (6/5)

(R0917)

🪛 LanguageTool
UPDATED_MODELS_SUMMARY.md

[grammar] ~47-~47: Did you mean the communication tool “Slack” (= proper noun, capitalized)?
Context: ... → SlackUser - Elasticsearch: Index slack_channels, searches name, purpose, topi...

(ON_SKYPE)

🔇 Additional comments (7)
.gitignore (1)

191-191: Pattern looks good – no issues
Adding .weave/tools/* to the ignore list is consistent with the tool-directory cleanup and won’t affect build artefacts.

weave/__init__.py (1)

5-5: Remember to propagate the version bump
__version__ is now 0.1.5, but other packaging metadata (e.g. pyproject.toml, setup.cfg, release notes) are not visible here. Double-check that every place publishing/CI relies on is bumped as well and a matching Git tag / changelog entry is created.

.weave/config.json (1)

23-23: Minor placeholder tweak acknowledged
Changing the hint from service listservice status keeps the docs in sync with the CLI rename. No functional impact.

weave/bin/modules/annotations/__init__.py (1)

1-22: LGTM – clear public surface

File re-exports are clean and __all__ is exhaustive.
No immediate issues.

README-migrations.md (1)

43-50: Verify --dry-run flag is implemented at CLI level

Docs introduce weave db migrate all --dry-run, but the flag will confuse users if it is not actually accepted by cli_db.py.
Confirm the option exists (or update docs accordingly).

examples/updated_models_demo.py (1)

1-8: Good explanatory demo script

Purely illustrative, no execution-time side-effects and clear separation of concerns.

domain/conversation.py (1)

236-238: Possible double-count of messages

message_count blindly sums the three internal lists. When _load_cross_platform_messages pulls messages from several InsightMesh conversations the same message could be appended more than once (e.g., if a conversation is processed twice by different filters). Consider de-duplicating on ID or using a set to guarantee accurate counts.

Comment on lines +8 to +12
from weave.bin.modules.annotations.sync import enable_auto_sync_for_model

# Enable automatic synchronization for all models
enable_auto_sync_for_model(SlackUser)
enable_auto_sync_for_model(SlackChannel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid repetition & reduce import-time side effects

  1. Repeating enable_auto_sync_for_model for every model doesn’t scale.
  2. Calling sync at import time can trigger DB/driver initialisation in environments (e.g. migrations, unit tests) where the backing stores are unavailable.

Consider deferring the call or at least deduplicating:

-# Enable automatic synchronization for all models
-enable_auto_sync_for_model(SlackUser)
-enable_auto_sync_for_model(SlackChannel)
+# Enable automatic synchronization for all models defined here
+for _model in (SlackUser, SlackChannel):
+    enable_auto_sync_for_model(_model)

Long-term, a central application bootstrap that opts-in to auto-sync (instead of doing it in __init__) would avoid import-time surprises.


🏁 Script executed:

#!/bin/bash
# Locate all references to enable_auto_sync_for_model
rg -n "enable_auto_sync_for_model" -C4

Length of output: 4398


Deduplicate auto-sync calls & avoid import-time side-effects

The repeated calls to enable_auto_sync_for_model in domain/data/slack/__init__.py don’t scale and will run at import time (which can trigger DB/driver initialization in contexts like migrations or tests).

• File: domain/data/slack/init.py
Lines: 8–12

Suggested patch:

-from weave.bin.modules.annotations.sync import enable_auto_sync_for_model
-
-# Enable automatic synchronization for all models
-enable_auto_sync_for_model(SlackUser)
-enable_auto_sync_for_model(SlackChannel)
+from weave.bin.modules.annotations.sync import enable_auto_sync_for_model
+
+# Enable automatic synchronization for all models defined here
+for model in (SlackUser, SlackChannel):
+    enable_auto_sync_for_model(model)

Long-term, consider moving all auto-sync registration into a central application bootstrap (opt-in) so imports never trigger side effects.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from weave.bin.modules.annotations.sync import enable_auto_sync_for_model
# Enable automatic synchronization for all models
enable_auto_sync_for_model(SlackUser)
enable_auto_sync_for_model(SlackChannel)
from weave.bin.modules.annotations.sync import enable_auto_sync_for_model
# Enable automatic synchronization for all models defined here
for model in (SlackUser, SlackChannel):
enable_auto_sync_for_model(model)
🤖 Prompt for AI Agents
In domain/data/slack/__init__.py around lines 8 to 12, the repeated calls to
enable_auto_sync_for_model for each model cause import-time side effects and do
not scale. Refactor by consolidating these calls into a single function that
registers all models at once, and avoid calling this function at import time.
Instead, defer the execution to a central application bootstrap or an explicit
initialization step to prevent unwanted DB or driver initialization during
imports.

Comment on lines +12 to +16
# Enable automatic synchronization for all models
enable_auto_sync_for_model(InsightMeshUser)
enable_auto_sync_for_model(Context)
enable_auto_sync_for_model(Conversation)
enable_auto_sync_for_model(Message)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Same DRY / bootstrap concern as for Slack package

The per-model calls duplicate logic and run at import time. Mirror the refactor suggested for the Slack package:

-# Enable automatic synchronization for all models
-enable_auto_sync_for_model(InsightMeshUser)
-enable_auto_sync_for_model(Context)
-enable_auto_sync_for_model(Conversation)
-enable_auto_sync_for_model(Message)
+# Enable automatic synchronization for all models in this package
+for _model in (InsightMeshUser, Context, Conversation, Message):
+    enable_auto_sync_for_model(_model)

If auto-sync requires external services, consider gating it behind an environment flag or explicit application bootstrap to prevent accidental side-effects in offline contexts.


🏁 Script executed:

#!/bin/bash
# Search for all usages of enable_auto_sync_for_model across the codebase
rg -n "enable_auto_sync_for_model"

Length of output: 1244


🏁 Script executed:

#!/bin/bash
# Display the first 200 lines of the sync module to review enable_auto_sync_for_model implementation
sed -n '1,200p' weave/bin/modules/annotations/sync.py

Length of output: 3873


Consolidate auto-sync registration into a loop and avoid import-time side-effects

Refactor the per-model calls in domain/data/insightmesh/__init__.py (lines 12–16) to a single loop:

- # Enable automatic synchronization for all models
- enable_auto_sync_for_model(InsightMeshUser)
- enable_auto_sync_for_model(Context)
- enable_auto_sync_for_model(Conversation)
- enable_auto_sync_for_model(Message)
+ # Enable automatic synchronization for all models in this package
+ for model in (InsightMeshUser, Context, Conversation, Message):
+     enable_auto_sync_for_model(model)

• This mirrors the DRY refactor suggested (and needed) for the Slack package in domain/data/slack/__init__.py.
• Since enable_auto_sync_for_model registers SQLAlchemy event listeners at import time, consider gating this behind an environment flag (e.g. ENABLE_AUTO_SYNC) or moving it into an explicit application-startup step to prevent unintended side-effects in offline or testing contexts.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Enable automatic synchronization for all models
enable_auto_sync_for_model(InsightMeshUser)
enable_auto_sync_for_model(Context)
enable_auto_sync_for_model(Conversation)
enable_auto_sync_for_model(Message)
# Enable automatic synchronization for all models in this package
for model in (InsightMeshUser, Context, Conversation, Message):
enable_auto_sync_for_model(model)
🤖 Prompt for AI Agents
In domain/data/insightmesh/__init__.py lines 12 to 16, the calls to
enable_auto_sync_for_model are duplicated for each model and run at import time,
causing potential side-effects. Refactor by consolidating these calls into a
single loop iterating over the models (InsightMeshUser, Context, Conversation,
Message). Additionally, wrap this loop in a conditional that checks an
environment variable like ENABLE_AUTO_SYNC to prevent automatic registration
during offline or testing contexts, ensuring the auto-sync setup occurs only
during explicit application startup.

Comment thread weave/requirements.txt
Comment on lines +4 to +11
# Database packages needed for weave db commands
neo4j-driver==5.28.1
alembic==1.16.1
psycopg2-binary==2.9.10
sqlalchemy==2.0.41
# Additional packages for elasticsearch and other features
elasticsearch==8.9.0
requests>=2.31.0 No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Version-pinning strategy is inconsistent and may cause subtle deployment issues

You pin most of the new dependencies to an exact version (neo4j-driver==5.28.1, alembic==1.16.1, psycopg2-binary==2.9.10, sqlalchemy==2.0.41, elasticsearch==8.9.0) but leave requests unpinned (>=2.31.0).
Mixing hard pins with loose ranges makes reproducible builds harder and may lead to dependency resolution conflicts or security regressions when requests updates downstream transitive packages without notice.

In addition, shipping psycopg2-binary is convenient for local use but discouraged for production as it bundles its own libpq and can lag behind security patches.

Consider:

-psycopg2-binary==2.9.10
+psycopg2==2.9.10  # compile-time build, preferred for production

and decide whether you want to:

  1. Hard-pin everything for perfect repeatability, or
  2. Use compatible-range specifiers consistently (e.g., sqlalchemy>=2.0,<2.1) and rely on a lock-file (pip-tools, poetry.lock, etc.) for determinism.

Failing to adopt a single strategy will bite you once CI and local environments drift.


🏁 Script executed:

#!/bin/bash
set -e
echo "Searching for lock files and other requirement files in the repo:"
find . -maxdepth 2 -type f \( -iname 'poetry.lock' -o -iname 'Pipfile.lock' -o -iname 'requirements*.txt' \)

echo
echo "Showing top lines of each found file:"
for f in $(find . -maxdepth 2 -type f \( -iname 'poetry.lock' -o -iname 'Pipfile.lock' -o -iname 'requirements*.txt' \)); do
  echo "=== $f ==="
  head -n 10 "$f" || true
  echo
done

Length of output: 4460


Inconsistent version-pinning in weave/requirements.txt

Mixing exact pins (==5.28.1, ==1.16.1, etc.) with loose ranges (requests>=2.31.0) makes reproducible installs difficult and can introduce unexpected breakages as dependencies evolve. We also didn’t find any lock file (Pipfile.lock, poetry.lock or similar) in the repo to guarantee a deterministic install.

Please choose one of the following strategies and apply it consistently in weave/requirements.txt (and ideally across all service‐level requirements files):

  1. Exact-pin everything for perfect repeatability.
  2. Use compatible ranges (e.g. sqlalchemy>=2.0,<3.0) plus introduce a lock file (via pip-tools, Poetry, pipenv, etc.) for determinism.

Additionally, for production builds it’s recommended to use the source package of psycopg2 rather than the binary bundle:

- psycopg2-binary==2.9.10
+ psycopg2==2.9.10   # compile-time build, preferred for production

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Database packages needed for weave db commands
neo4j-driver==5.28.1
alembic==1.16.1
psycopg2-binary==2.9.10
sqlalchemy==2.0.41
# Additional packages for elasticsearch and other features
elasticsearch==8.9.0
requests>=2.31.0
# Database packages needed for weave db commands
neo4j-driver==5.28.1
alembic==1.16.1
psycopg2==2.9.10 # compile-time build, preferred for production
sqlalchemy==2.0.41
# Additional packages for elasticsearch and other features
elasticsearch==8.9.0
requests>=2.31.0
🤖 Prompt for AI Agents
In weave/requirements.txt lines 4 to 11, the version-pinning strategy is
inconsistent, mixing exact pins with a loose range for requests, which harms
reproducibility. Choose either to pin all dependencies exactly or use compatible
version ranges consistently for all packages, including requests. Additionally,
replace psycopg2-binary with psycopg2 for production use to avoid bundled libpq
issues. If using compatible ranges, introduce a lock file (e.g., pip-tools,
Poetry) to ensure deterministic installs.

Comment thread weave/bin/modules/cli.py
Comment on lines +30 to +39
@click.option('--test-mode', is_flag=True, help='Enable test mode (simulates commands without executing)')
@click.pass_context
def cli(ctx, verbose, version):
def cli(ctx, verbose, version, test_mode):
"""Weaver: A Rails-like framework for rapidly building and deploying enterprise-grade GenAI applications."""
ctx.ensure_object(dict)
ctx.obj['VERBOSE'] = verbose
ctx.obj['TEST_MODE'] = test_mode or os.getenv('WEAVE_TEST_MODE', '').lower() in ['true', '1', 'yes']

if ctx.obj['TEST_MODE']:
console.print("[yellow]🧪 TEST MODE ENABLED - Commands will be simulated[/yellow]")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Nice addition, but propagate test-mode flag to sub-process helpers

ctx.obj['TEST_MODE'] is set here, yet helper functions (e.g., run_service_up_with_feedback, list_services, etc.) spawn subprocesses directly.
If those helpers do not consult ctx.obj themselves, test-mode will still execute real commands.

If you intend a true dry-run, pass the flag explicitly:

-run_service_up_with_feedback(command, project_name, verbose)
+run_service_up_with_feedback(
+    command, project_name, verbose, dry_run=ctx.obj['TEST_MODE']
+)

(or read WEAVE_TEST_MODE inside the helpers).

Otherwise users might be lulled into a false sense of safety.


🏁 Script executed:

#!/bin/bash
# Locate definition of run_service_up_with_feedback
rg -n "def run_service_up_with_feedback" .

# Locate definition of list_services
rg -n "def list_services" .

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Show first 200 lines of docker_commands.py to inspect run_service_up_with_feedback
echo "=== docker_commands.py (lines 1-200) ==="
sed -n '1,200p' weave/bin/modules/docker_commands.py

# Look for subprocess usage in docker_commands.py
echo "=== subprocess calls in docker_commands.py ==="
grep -n "subprocess" weave/bin/modules/docker_commands.py || echo "None found"

# Show first 300 lines of services.py to inspect list_services
echo "=== services.py (lines 1-300) ==="
sed -n '1,300p' weave/bin/modules/services.py

# Look for subprocess usage in services.py
echo "=== subprocess calls in services.py ==="
grep -n "subprocess" weave/bin/modules/services.py || echo "None found"

Length of output: 23158


Propagate test-mode flag to all subprocess helpers

Although ctx.obj['TEST_MODE'] is set in the CLI, none of the helpers that call subprocess.run (e.g. run_service_up_with_feedback, run_service_restart_with_feedback, run_command, list_services, etc.) actually receive or check this flag—so even in “test mode” they execute real commands.

Recommended changes:

  • Update each helper signature to accept a dry_run: bool = False (or test_mode) parameter.
  • At the top of each helper, short-circuit in dry-run mode, e.g.:
    def run_service_up_with_feedback(command, project_name, verbose=False, dry_run=False):
        if dry_run:
            console.print(f"[bold blue]Dry run:[/bold blue] {' '.join(command)}")
            return True
  • In weave/bin/modules/cli.py, pass the flag from ctx.obj['TEST_MODE'] when invoking helpers:
    - run_service_up_with_feedback(command, project_name, verbose)
    + run_service_up_with_feedback(
    +     command,
    +     project_name,
    +     verbose,
    +     dry_run=ctx.obj['TEST_MODE']
    + )
    
    - list_services(project_name, verbose, debug)
    + list_services(
    +     project_name,
    +     verbose=verbose,
    +     debug=debug,
    +     dry_run=ctx.obj['TEST_MODE']
    + )

This ensures a true dry-run: all subprocess helpers will simulate instead of executing real commands when test mode is enabled.

🤖 Prompt for AI Agents
In weave/bin/modules/cli.py around lines 30 to 39, the test mode flag is set in
the CLI context but not propagated to subprocess helper functions that execute
commands. To fix this, update each helper function that runs subprocesses (e.g.,
run_service_up_with_feedback, list_services) to accept a dry_run or test_mode
boolean parameter. Inside these helpers, add a check to short-circuit and
simulate the command when dry_run is True by printing the command and returning
early. Then, modify the CLI code to pass ctx.obj['TEST_MODE'] as the dry_run
argument when calling these helpers, ensuring all subprocess calls respect the
test mode and do not execute real commands during dry-run.

Comment on lines +65 to +66
from .cli_migrate import check_and_install_tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Unused import triggers Ruff F401

check_and_install_tools is imported here but never referenced inside db_tools_status. Remove the import (or call it) to satisfy static analysis.

-from .cli_migrate import check_and_install_tools
+# (No import needed here)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from .cli_migrate import check_and_install_tools
🧰 Tools
🪛 Ruff (0.11.9)

65-65: .cli_migrate.check_and_install_tools imported but unused

Remove unused import: .cli_migrate.check_and_install_tools

(F401)

🤖 Prompt for AI Agents
In weave/bin/modules/cli_db_tools.py at lines 65 to 66, the import of
check_and_install_tools is unused and triggers a Ruff F401 warning. To fix this,
remove the import statement if the function is not needed, or alternatively,
call check_and_install_tools appropriately within the db_tools_status function
to justify the import.

Comment on lines +155 to +163
"""Resolve target model from class or string reference."""
if isinstance(target_model_ref, str):
# Try to resolve from the same module first
module = self.__class__.__module__
if hasattr(__import__(module, fromlist=[target_model_ref]), target_model_ref):
return getattr(__import__(module, fromlist=[target_model_ref]), target_model_ref)

# Could add more sophisticated resolution logic here
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Target-model resolution is brittle

Using __import__ with string names fails across packages and hides ImportErrors.
Leverage importlib.import_module and explicit error handling; alternatively accept the class object directly to avoid reflection altogether.

🤖 Prompt for AI Agents
In weave/bin/modules/annotations/graph.py around lines 155 to 163, the current
target model resolution uses __import__ with string names, which is brittle and
can hide ImportErrors. Replace __import__ calls with importlib.import_module for
clearer and more reliable imports, and add explicit error handling to catch and
handle ImportErrors properly. Alternatively, modify the method to accept the
class object directly instead of a string to avoid reflection and improve
robustness.

Comment thread domain/user.py
Comment on lines +223 to +229
context['slack'] = {
'display_name': self._slack_user.display_name,
'real_name': self._slack_user.real_name,
'is_admin': self._slack_user.is_admin,
'is_bot': self._slack_user.is_bot,
'team_id': self._slack_user.team_id
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential attribute error on SlackUser.display_name

SlackUser exposes display_name_or_name() (see domain/data/slack/user.py) but not display_name.
Accessing .display_name will raise AttributeError for many records.

-                'display_name': self._slack_user.display_name,
+                'display_name': self._slack_user.display_name_or_name,

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In domain/user.py around lines 223 to 229, the code accesses
self._slack_user.display_name which does not exist and will raise
AttributeError. Replace the use of .display_name with the method
.display_name_or_name() to safely get the display name or fallback name. Update
the context dictionary to call self._slack_user.display_name_or_name() instead
of accessing the non-existent attribute.

Comment thread domain/conversation.py
Comment on lines +198 to +223
if 'insightmesh' in session_factories:
im_session = session_factories['insightmesh']

# Find conversations for these participants
query = im_session.query(InsightMeshConversation).filter(
InsightMeshConversation.user_id.in_(participants)
)

if start_date:
query = query.filter(InsightMeshConversation.created_at >= start_date)
if end_date:
query = query.filter(InsightMeshConversation.created_at <= end_date)
if topic:
query = query.filter(InsightMeshConversation.title.ilike(f'%{topic}%'))

im_conversations = query.all()

# Load messages from these conversations
for conv in im_conversations:
messages = im_session.query(InsightMeshMessage).filter_by(
conversation_id=conv.id
).all()
self._insightmesh_messages.extend(messages)

self._loaded_sources.add('insightmesh')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid an N+1 DB query when loading cross-platform messages

Inside _load_cross_platform_messages you loop over each matched conversation and execute a separate query to fetch its messages. With many conversations this quickly balloons into an N+1 pattern.

Batch-fetch all messages in a single query:

-            im_conversations = query.all()
-            # Load messages from these conversations
-            for conv in im_conversations:
-                messages = im_session.query(InsightMeshMessage).filter_by(
-                    conversation_id=conv.id
-                ).all()
-                self._insightmesh_messages.extend(messages)
+            im_conversations = query.all()
+            if im_conversations:
+                conv_ids = [c.id for c in im_conversations]
+                messages = (
+                    im_session.query(InsightMeshMessage)
+                    .filter(InsightMeshMessage.conversation_id.in_(conv_ids))
+                    .all()
+                )
+                self._insightmesh_messages.extend(messages)

This reduces round-trips and speeds up aggregation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if 'insightmesh' in session_factories:
im_session = session_factories['insightmesh']
# Find conversations for these participants
query = im_session.query(InsightMeshConversation).filter(
InsightMeshConversation.user_id.in_(participants)
)
if start_date:
query = query.filter(InsightMeshConversation.created_at >= start_date)
if end_date:
query = query.filter(InsightMeshConversation.created_at <= end_date)
if topic:
query = query.filter(InsightMeshConversation.title.ilike(f'%{topic}%'))
im_conversations = query.all()
# Load messages from these conversations
for conv in im_conversations:
messages = im_session.query(InsightMeshMessage).filter_by(
conversation_id=conv.id
).all()
self._insightmesh_messages.extend(messages)
self._loaded_sources.add('insightmesh')
if 'insightmesh' in session_factories:
im_session = session_factories['insightmesh']
# Find conversations for these participants
query = im_session.query(InsightMeshConversation).filter(
InsightMeshConversation.user_id.in_(participants)
)
if start_date:
query = query.filter(InsightMeshConversation.created_at >= start_date)
if end_date:
query = query.filter(InsightMeshConversation.created_at <= end_date)
if topic:
query = query.filter(InsightMeshConversation.title.ilike(f'%{topic}%'))
im_conversations = query.all()
if im_conversations:
conv_ids = [c.id for c in im_conversations]
messages = (
im_session.query(InsightMeshMessage)
.filter(InsightMeshMessage.conversation_id.in_(conv_ids))
.all()
)
self._insightmesh_messages.extend(messages)
self._loaded_sources.add('insightmesh')
🤖 Prompt for AI Agents
In domain/conversation.py between lines 198 and 223, the code fetches messages
for each conversation in a loop, causing an N+1 query problem. To fix this,
replace the per-conversation message queries with a single batch query that
fetches all messages for all matched conversations at once using a filter on
conversation_id with the list of conversation IDs. Then extend
self._insightmesh_messages with the results of this batch query, eliminating the
loop and reducing database round-trips.

Comment thread domain/document.py
Comment on lines +14 to +26
try:
from elasticsearch import Elasticsearch
import os

# Initialize Elasticsearch connection
es = Elasticsearch(
hosts=[{
"scheme": "http",
"host": os.getenv("ELASTICSEARCH_HOST", "localhost"),
"port": int(os.getenv("ELASTICSEARCH_PORT", "9200")),
}]
)
except ImportError:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

De-fer Elasticsearch connection to first use

Creating a network connection at import time slows startup and complicates testing. Lazily initialise the client the first time it’s needed:

-try:
-    from elasticsearch import Elasticsearch
-    import os
-    es = Elasticsearch(
-        hosts=[{
-            "scheme": "http",
-            "host": os.getenv("ELASTICSEARCH_HOST", "localhost"),
-            "port": int(os.getenv("ELASTICSEARCH_PORT", "9200")),
-        }]
-    )
-except ImportError:
-    es = None
+import os
+es = None
+
+def _get_es():
+    global es
+    if es is None:
+        from elasticsearch import Elasticsearch  # local import for optional dep
+        es = Elasticsearch(
+            hosts=[{
+                "scheme": "http",
+                "host": os.getenv("ELASTICSEARCH_HOST", "localhost"),
+                "port": int(os.getenv("ELASTICSEARCH_PORT", "9200")),
+            }]
+        )
+    return es

Update call sites (_get_es()) accordingly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
from elasticsearch import Elasticsearch
import os
# Initialize Elasticsearch connection
es = Elasticsearch(
hosts=[{
"scheme": "http",
"host": os.getenv("ELASTICSEARCH_HOST", "localhost"),
"port": int(os.getenv("ELASTICSEARCH_PORT", "9200")),
}]
)
except ImportError:
import os
es = None
def _get_es():
global es
if es is None:
from elasticsearch import Elasticsearch # local import for optional dep
es = Elasticsearch(
hosts=[{
"scheme": "http",
"host": os.getenv("ELASTICSEARCH_HOST", "localhost"),
"port": int(os.getenv("ELASTICSEARCH_PORT", "9200")),
}]
)
return es
🤖 Prompt for AI Agents
In domain/document.py around lines 14 to 26, the Elasticsearch client is being
initialized at import time, which slows startup and complicates testing.
Refactor the code to defer the Elasticsearch connection by wrapping the
initialization inside a function (e.g., _get_es()) that creates and returns the
client only when first called. Update all call sites to use this function
instead of the global client instance.

Comment thread domain/document.py
Comment on lines +478 to +482
def size_mb(self) -> Optional[float]:
"""Get document size in MB."""
if self.identity.size_bytes:
return round(self.identity.size_bytes / (1024 * 1024), 2)
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Robust size conversion

identity.size_bytes can arrive as a string from Elasticsearch. Guard against that and zero-division:

-        if self.identity.size_bytes:
-            return round(self.identity.size_bytes / (1024 * 1024), 2)
+        try:
+            size = int(self.identity.size_bytes)
+        except (TypeError, ValueError):
+            return None
+        return round(size / (1024 * 1024), 2) if size else None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def size_mb(self) -> Optional[float]:
"""Get document size in MB."""
if self.identity.size_bytes:
return round(self.identity.size_bytes / (1024 * 1024), 2)
return None
def size_mb(self) -> Optional[float]:
"""Get document size in MB."""
try:
size = int(self.identity.size_bytes)
except (TypeError, ValueError):
return None
return round(size / (1024 * 1024), 2) if size else None
return None
🤖 Prompt for AI Agents
In domain/document.py around lines 478 to 482, the size_mb method assumes
identity.size_bytes is a number, but it can be a string from Elasticsearch and
may be zero or invalid. Update the method to safely convert size_bytes to a
float or int, handle conversion errors gracefully, and ensure division by zero
is avoided by checking the value before dividing. Return None if size_bytes is
missing, zero, or invalid.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant