claude_memory 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/.mind.mv2.o2N83S +0 -0
  3. data/.claude/CLAUDE.md +1 -0
  4. data/.claude/rules/claude_memory.generated.md +28 -9
  5. data/.claude/settings.local.json +9 -1
  6. data/.claude/skills/check-memory/SKILL.md +77 -0
  7. data/.claude/skills/improve/SKILL.md +532 -0
  8. data/.claude/skills/improve/feature-patterns.md +1221 -0
  9. data/.claude/skills/quality-update/SKILL.md +229 -0
  10. data/.claude/skills/quality-update/implementation-guide.md +346 -0
  11. data/.claude/skills/review-commit/SKILL.md +199 -0
  12. data/.claude/skills/review-for-quality/SKILL.md +154 -0
  13. data/.claude/skills/review-for-quality/expert-checklists.md +79 -0
  14. data/.claude/skills/setup-memory/SKILL.md +168 -0
  15. data/.claude/skills/study-repo/SKILL.md +307 -0
  16. data/.claude/skills/study-repo/analysis-template.md +323 -0
  17. data/.claude/skills/study-repo/focus-examples.md +327 -0
  18. data/CHANGELOG.md +133 -0
  19. data/CLAUDE.md +130 -11
  20. data/README.md +117 -10
  21. data/db/migrations/001_create_initial_schema.rb +117 -0
  22. data/db/migrations/002_add_project_scoping.rb +33 -0
  23. data/db/migrations/003_add_session_metadata.rb +42 -0
  24. data/db/migrations/004_add_fact_embeddings.rb +20 -0
  25. data/db/migrations/005_add_incremental_sync.rb +21 -0
  26. data/db/migrations/006_add_operation_tracking.rb +40 -0
  27. data/db/migrations/007_add_ingestion_metrics.rb +26 -0
  28. data/docs/.claude/mind.mv2.lock +0 -0
  29. data/docs/GETTING_STARTED.md +587 -0
  30. data/docs/RELEASE_NOTES_v0.2.0.md +0 -1
  31. data/docs/RUBY_COMMUNITY_POST_v0.2.0.md +0 -2
  32. data/docs/architecture.md +9 -8
  33. data/docs/auto_init_design.md +230 -0
  34. data/docs/improvements.md +557 -731
  35. data/docs/influence/.gitkeep +13 -0
  36. data/docs/influence/grepai.md +933 -0
  37. data/docs/influence/qmd.md +2195 -0
  38. data/docs/plugin.md +257 -11
  39. data/docs/quality_review.md +472 -1273
  40. data/docs/remaining_improvements.md +330 -0
  41. data/lefthook.yml +13 -0
  42. data/lib/claude_memory/commands/checks/claude_md_check.rb +41 -0
  43. data/lib/claude_memory/commands/checks/database_check.rb +120 -0
  44. data/lib/claude_memory/commands/checks/hooks_check.rb +112 -0
  45. data/lib/claude_memory/commands/checks/reporter.rb +110 -0
  46. data/lib/claude_memory/commands/checks/snapshot_check.rb +30 -0
  47. data/lib/claude_memory/commands/doctor_command.rb +12 -129
  48. data/lib/claude_memory/commands/help_command.rb +1 -0
  49. data/lib/claude_memory/commands/hook_command.rb +9 -2
  50. data/lib/claude_memory/commands/index_command.rb +169 -0
  51. data/lib/claude_memory/commands/ingest_command.rb +1 -1
  52. data/lib/claude_memory/commands/init_command.rb +5 -197
  53. data/lib/claude_memory/commands/initializers/database_ensurer.rb +30 -0
  54. data/lib/claude_memory/commands/initializers/global_initializer.rb +85 -0
  55. data/lib/claude_memory/commands/initializers/hooks_configurator.rb +156 -0
  56. data/lib/claude_memory/commands/initializers/mcp_configurator.rb +56 -0
  57. data/lib/claude_memory/commands/initializers/memory_instructions_writer.rb +135 -0
  58. data/lib/claude_memory/commands/initializers/project_initializer.rb +111 -0
  59. data/lib/claude_memory/commands/recover_command.rb +75 -0
  60. data/lib/claude_memory/commands/registry.rb +5 -1
  61. data/lib/claude_memory/commands/stats_command.rb +239 -0
  62. data/lib/claude_memory/commands/uninstall_command.rb +226 -0
  63. data/lib/claude_memory/core/batch_loader.rb +32 -0
  64. data/lib/claude_memory/core/concept_ranker.rb +73 -0
  65. data/lib/claude_memory/core/embedding_candidate_builder.rb +37 -0
  66. data/lib/claude_memory/core/fact_collector.rb +51 -0
  67. data/lib/claude_memory/core/fact_query_builder.rb +154 -0
  68. data/lib/claude_memory/core/fact_ranker.rb +113 -0
  69. data/lib/claude_memory/core/result_builder.rb +54 -0
  70. data/lib/claude_memory/core/result_sorter.rb +25 -0
  71. data/lib/claude_memory/core/scope_filter.rb +61 -0
  72. data/lib/claude_memory/core/text_builder.rb +29 -0
  73. data/lib/claude_memory/embeddings/generator.rb +161 -0
  74. data/lib/claude_memory/embeddings/similarity.rb +69 -0
  75. data/lib/claude_memory/hook/handler.rb +4 -3
  76. data/lib/claude_memory/index/lexical_fts.rb +7 -2
  77. data/lib/claude_memory/infrastructure/operation_tracker.rb +158 -0
  78. data/lib/claude_memory/infrastructure/schema_validator.rb +206 -0
  79. data/lib/claude_memory/ingest/content_sanitizer.rb +6 -7
  80. data/lib/claude_memory/ingest/ingester.rb +99 -15
  81. data/lib/claude_memory/ingest/metadata_extractor.rb +57 -0
  82. data/lib/claude_memory/ingest/tool_extractor.rb +71 -0
  83. data/lib/claude_memory/mcp/response_formatter.rb +331 -0
  84. data/lib/claude_memory/mcp/server.rb +19 -0
  85. data/lib/claude_memory/mcp/setup_status_analyzer.rb +73 -0
  86. data/lib/claude_memory/mcp/tool_definitions.rb +279 -0
  87. data/lib/claude_memory/mcp/tool_helpers.rb +80 -0
  88. data/lib/claude_memory/mcp/tools.rb +330 -320
  89. data/lib/claude_memory/recall/dual_query_template.rb +63 -0
  90. data/lib/claude_memory/recall.rb +304 -237
  91. data/lib/claude_memory/resolve/resolver.rb +52 -49
  92. data/lib/claude_memory/store/sqlite_store.rb +210 -144
  93. data/lib/claude_memory/store/store_manager.rb +6 -6
  94. data/lib/claude_memory/sweep/sweeper.rb +6 -0
  95. data/lib/claude_memory/version.rb +1 -1
  96. data/lib/claude_memory.rb +35 -3
  97. metadata +71 -11
  98. data/.claude/.mind.mv2.aLCUZd +0 -0
  99. data/.claude/memory.sqlite3 +0 -0
  100. data/.mcp.json +0 -11
  101. /data/docs/{feature_adoption_plan.md → plans/feature_adoption_plan.md} +0 -0
  102. /data/docs/{feature_adoption_plan_revised.md → plans/feature_adoption_plan_revised.md} +0 -0
  103. /data/docs/{plan.md → plans/plan.md} +0 -0
  104. /data/docs/{updated_plan.md → plans/updated_plan.md} +0 -0
data/README.md CHANGED
@@ -17,21 +17,47 @@ It automatically:
17
17
 
18
18
  ## Quick Start
19
19
 
20
- ### Install
20
+ ### 1. Install the Gem
21
21
  ```bash
22
22
  gem install claude_memory
23
23
  ```
24
24
 
25
- ### Initialize
25
+ ### 2. Install the Plugin
26
+
27
+ From within Claude Code, add the marketplace and install the plugin:
28
+
29
+ ```bash
30
+ # Add the marketplace (one-time setup)
31
+ /plugin marketplace add codenamev/claude_memory
32
+
33
+ # Install the plugin
34
+ /plugin install claude-memory
35
+ ```
36
+
37
+ ### 3. Initialize Memory
38
+
39
+ Initialize both global and project-specific memory:
40
+
26
41
  ```bash
27
- # In your project
28
- cd my-project
29
42
  claude-memory init
43
+ ```
44
+
45
+ This creates:
46
+ - **Global database** (`~/.claude/memory.sqlite3`) - User-wide preferences
47
+ - **Project database** (`.claude/memory.sqlite3`) - Project-specific knowledge
48
+
49
+ ### 4. Analyze Your Project (Optional)
50
+
51
+ Bootstrap memory with your project's tech stack:
52
+
53
+ ```
54
+ /claude-memory:analyze
55
+ ```
30
56
 
31
- # Or globally for all projects
32
- claude-memory init --global
57
+ This reads your project files (Gemfile, package.json, etc.) and stores facts about languages, frameworks, tools, and conventions.
33
58
 
34
- # Verify setup
59
+ ### 5. Verify Setup
60
+ ```bash
35
61
  claude-memory doctor
36
62
  ```
37
63
 
@@ -90,9 +116,91 @@ Claude: [uses it during session]
90
116
 
91
117
  Supported tags: `<private>`, `<no-memory>`, `<secret>`
92
118
 
119
+ ## Upgrading
120
+
121
+ Existing users can upgrade seamlessly:
122
+
123
+ ```bash
124
+ gem update claude_memory
125
+ ```
126
+
127
+ All database migrations happen automatically. Run `claude-memory doctor` to verify.
128
+
129
+ See [CHANGELOG.md](CHANGELOG.md) for detailed release notes.
130
+
131
+ ## Troubleshooting
132
+
133
+ ### Check Setup Status
134
+
135
+ If memory tools aren't working, check initialization status:
136
+
137
+ ```
138
+ memory.check_setup
139
+ ```
140
+
141
+ This returns:
142
+ - Initialization status (healthy, needs_upgrade, not_initialized)
143
+ - Version information
144
+ - Missing components
145
+ - Actionable recommendations
146
+
147
+ ### Installation Help
148
+
149
+ Need help getting started? Run:
150
+
151
+ ```
152
+ /setup-memory
153
+ ```
154
+
155
+ This skill provides:
156
+ - Step-by-step installation instructions
157
+ - Common error solutions
158
+ - Post-installation verification
159
+ - Upgrade guidance
160
+
161
+ ### Health Check
162
+
163
+ Verify your ClaudeMemory installation:
164
+
165
+ ```bash
166
+ claude-memory doctor
167
+ ```
168
+
169
+ This checks:
170
+ - Database existence and integrity
171
+ - Schema version compatibility
172
+ - Hooks configuration
173
+ - Snapshot status
174
+ - Stuck operations
175
+ - Orphaned hooks (hooks without MCP configuration)
176
+
177
+ ### Uninstalling
178
+
179
+ To remove ClaudeMemory configuration:
180
+
181
+ ```bash
182
+ # Remove hooks and MCP configuration (keeps databases)
183
+ claude-memory uninstall
184
+
185
+ # Remove everything including databases
186
+ claude-memory uninstall --full
187
+
188
+ # For global uninstall
189
+ claude-memory uninstall --global
190
+ claude-memory uninstall --global --full
191
+ ```
192
+
193
+ The uninstall command removes:
194
+ - Hooks from `.claude/settings.json`
195
+ - MCP server from `.claude.json`
196
+ - ClaudeMemory section from `CLAUDE.md`
197
+ - Databases and generated files (with `--full`)
198
+
199
+ **Note:** The `doctor` command will warn you if orphaned hooks are detected (hooks configured but MCP plugin removed). Run `claude-memory uninstall` to clean them up.
200
+
93
201
  ## Documentation
94
202
 
95
- - 📖 [Getting Started](docs/GETTING_STARTED.md) - Step-by-step onboarding *(coming soon)*
203
+ - 📖 [Getting Started](docs/GETTING_STARTED.md) - Step-by-step onboarding
96
204
  - 💡 [Examples](docs/EXAMPLES.md) - Use cases and workflows
97
205
  - 🔧 [Plugin Setup](docs/PLUGIN.md) - Claude Code integration
98
206
  - 🏗️ [Architecture](docs/architecture.md) - Technical deep dive
@@ -102,7 +210,7 @@ Supported tags: `<private>`, `<no-memory>`, `<secret>`
102
210
 
103
211
  - **Language:** Ruby 3.2+
104
212
  - **Storage:** SQLite3 (no external services)
105
- - **Testing:** 583 examples, 100% core coverage
213
+ - **Testing:** 985 examples, 100% core coverage
106
214
  - **Code Style:** Standard Ruby
107
215
 
108
216
  ```bash
@@ -118,7 +226,6 @@ bundle exec rspec
118
226
 
119
227
  - 🐛 [Report a bug](https://github.com/codenamev/claude_memory/issues)
120
228
  - 💬 [Discussions](https://github.com/codenamev/claude_memory/discussions)
121
- - 📧 Email: valentino@hanamirb.org
122
229
 
123
230
  ## License
124
231
 
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table?(:meta) do
6
+ String :key, primary_key: true
7
+ String :value
8
+ end
9
+
10
+ # Content items store ingested transcript chunks with metadata
11
+ # metadata_json stores extensible session metadata as JSON:
12
+ # {
13
+ # "git_branch": "feature/auth",
14
+ # "cwd": "/path/to/project",
15
+ # "claude_version": "4.5",
16
+ # "tools_used": ["Read", "Edit", "Bash"]
17
+ # }
18
+ create_table?(:content_items) do
19
+ primary_key :id
20
+ String :source, null: false
21
+ String :session_id
22
+ String :transcript_path
23
+ String :occurred_at
24
+ String :ingested_at, null: false
25
+ String :text_hash, null: false
26
+ Integer :byte_len, null: false
27
+ String :raw_text, text: true
28
+ String :metadata_json, text: true # Extensible JSON metadata
29
+ end
30
+
31
+ create_table?(:delta_cursors) do
32
+ primary_key :id
33
+ String :session_id, null: false
34
+ String :transcript_path, null: false
35
+ Integer :last_byte_offset, null: false, default: 0
36
+ String :updated_at, null: false
37
+ unique [:session_id, :transcript_path]
38
+ end
39
+
40
+ create_table?(:entities) do
41
+ primary_key :id
42
+ String :type, null: false
43
+ String :canonical_name, null: false
44
+ String :slug, null: false, unique: true
45
+ String :created_at, null: false
46
+ end
47
+
48
+ create_table?(:entity_aliases) do
49
+ primary_key :id
50
+ foreign_key :entity_id, :entities, null: false
51
+ String :source
52
+ String :alias, null: false
53
+ Float :confidence, default: 1.0
54
+ end
55
+
56
+ create_table?(:facts) do
57
+ primary_key :id
58
+ foreign_key :subject_entity_id, :entities
59
+ String :predicate, null: false
60
+ foreign_key :object_entity_id, :entities
61
+ String :object_literal
62
+ String :datatype
63
+ String :polarity, default: "positive"
64
+ String :valid_from
65
+ String :valid_to
66
+ String :status, default: "active"
67
+ Float :confidence, default: 1.0
68
+ String :created_from
69
+ String :created_at, null: false
70
+ end
71
+
72
+ create_table?(:provenance) do
73
+ primary_key :id
74
+ foreign_key :fact_id, :facts, null: false
75
+ foreign_key :content_item_id, :content_items
76
+ String :quote, text: true
77
+ foreign_key :attribution_entity_id, :entities
78
+ String :strength, default: "stated"
79
+ end
80
+
81
+ create_table?(:fact_links) do
82
+ primary_key :id
83
+ foreign_key :from_fact_id, :facts, null: false
84
+ foreign_key :to_fact_id, :facts, null: false
85
+ String :link_type, null: false
86
+ end
87
+
88
+ create_table?(:conflicts) do
89
+ primary_key :id
90
+ foreign_key :fact_a_id, :facts, null: false
91
+ foreign_key :fact_b_id, :facts, null: false
92
+ String :status, default: "open"
93
+ String :detected_at, null: false
94
+ String :notes, text: true
95
+ end
96
+
97
+ # Indexes
98
+ run "CREATE INDEX IF NOT EXISTS idx_facts_predicate ON facts(predicate)"
99
+ run "CREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(subject_entity_id)"
100
+ run "CREATE INDEX IF NOT EXISTS idx_facts_status ON facts(status)"
101
+ run "CREATE INDEX IF NOT EXISTS idx_provenance_fact ON provenance(fact_id)"
102
+ run "CREATE INDEX IF NOT EXISTS idx_entity_aliases_entity ON entity_aliases(entity_id)"
103
+ run "CREATE INDEX IF NOT EXISTS idx_content_items_session ON content_items(session_id)"
104
+ end
105
+
106
+ down do
107
+ drop_table?(:conflicts)
108
+ drop_table?(:fact_links)
109
+ drop_table?(:provenance)
110
+ drop_table?(:facts)
111
+ drop_table?(:entity_aliases)
112
+ drop_table?(:entities)
113
+ drop_table?(:delta_cursors)
114
+ drop_table?(:content_items)
115
+ drop_table?(:meta)
116
+ end
117
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v2: Add project scoping support
4
+ # - Adds project_path to content_items for tracking content by project
5
+ # - Adds scope (global/project) and project_path to facts
6
+ # - Adds indexes for filtering by scope and project
7
+ Sequel.migration do
8
+ up do
9
+ alter_table(:content_items) do
10
+ add_column :project_path, String
11
+ end
12
+
13
+ alter_table(:facts) do
14
+ add_column :scope, String, default: "project"
15
+ add_column :project_path, String
16
+ add_index :scope, name: :idx_facts_scope
17
+ add_index :project_path, name: :idx_facts_project
18
+ end
19
+ end
20
+
21
+ down do
22
+ alter_table(:facts) do
23
+ drop_index :scope, name: :idx_facts_scope
24
+ drop_index :project_path, name: :idx_facts_project
25
+ drop_column :scope
26
+ drop_column :project_path
27
+ end
28
+
29
+ alter_table(:content_items) do
30
+ drop_column :project_path
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v3: Add session metadata and tool tracking
4
+ # - Adds git_branch, cwd, claude_version, thinking_level to content_items
5
+ # - Creates tool_calls table for tracking tool usage per content item
6
+ # - Adds indexes for efficient querying
7
+ Sequel.migration do
8
+ up do
9
+ alter_table(:content_items) do
10
+ add_column :git_branch, String
11
+ add_column :cwd, String
12
+ add_column :claude_version, String
13
+ add_column :thinking_level, String
14
+ add_index :git_branch, name: :idx_content_items_git_branch
15
+ end
16
+
17
+ create_table?(:tool_calls) do
18
+ primary_key :id
19
+ foreign_key :content_item_id, :content_items, on_delete: :cascade
20
+ String :tool_name, null: false
21
+ String :tool_input, text: true # JSON of input parameters
22
+ String :tool_result, text: true # Truncated result (first 500 chars)
23
+ TrueClass :is_error, default: false
24
+ String :timestamp, null: false
25
+ end
26
+
27
+ run "CREATE INDEX IF NOT EXISTS idx_tool_calls_tool_name ON tool_calls(tool_name)"
28
+ run "CREATE INDEX IF NOT EXISTS idx_tool_calls_content_item ON tool_calls(content_item_id)"
29
+ end
30
+
31
+ down do
32
+ alter_table(:content_items) do
33
+ drop_index :git_branch, name: :idx_content_items_git_branch
34
+ drop_column :git_branch
35
+ drop_column :cwd
36
+ drop_column :claude_version
37
+ drop_column :thinking_level
38
+ end
39
+
40
+ drop_table?(:tool_calls)
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v4: Add embeddings for semantic search
4
+ # - Adds embedding_json column to facts table for vector storage
5
+ # - Uses JSON storage for embeddings instead of sqlite-vec extension
6
+ # - Similarity calculations are done in Ruby using cosine similarity
7
+ # - Future: Could migrate to native vector extension or external vector DB
8
+ Sequel.migration do
9
+ up do
10
+ alter_table(:facts) do
11
+ add_column :embedding_json, String, text: true # JSON array of floats
12
+ end
13
+ end
14
+
15
+ down do
16
+ alter_table(:facts) do
17
+ drop_column :embedding_json
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v5: Add incremental sync support
4
+ # - Adds source_mtime to content_items for tracking file modification times
5
+ # - Enables efficient incremental sync: only re-ingest when source changed
6
+ # - Index for efficient mtime-based lookups
7
+ Sequel.migration do
8
+ up do
9
+ alter_table(:content_items) do
10
+ add_column :source_mtime, String # ISO8601 timestamp of source file mtime
11
+ add_index :source_mtime, name: :idx_content_items_source_mtime
12
+ end
13
+ end
14
+
15
+ down do
16
+ alter_table(:content_items) do
17
+ drop_index :source_mtime, name: :idx_content_items_source_mtime
18
+ drop_column :source_mtime
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v6: Add operation tracking and schema health monitoring
4
+ # - Creates operation_progress table for long-running operations
5
+ # - Creates schema_health table for validation and corruption detection
6
+ # - Enables resumable operations with checkpoint support
7
+ Sequel.migration do
8
+ up do
9
+ create_table?(:operation_progress) do
10
+ primary_key :id
11
+ String :operation_type, null: false # "index_embeddings", "sweep", "distill"
12
+ String :scope, null: false # "global" or "project"
13
+ String :status, null: false # "running", "completed", "failed"
14
+ Integer :total_items
15
+ Integer :processed_items, default: 0
16
+ String :checkpoint_data, text: true # JSON for resumption
17
+ String :started_at, null: false
18
+ String :completed_at
19
+ end
20
+
21
+ run "CREATE INDEX IF NOT EXISTS idx_operation_progress_type ON operation_progress(operation_type)"
22
+ run "CREATE INDEX IF NOT EXISTS idx_operation_progress_status ON operation_progress(status)"
23
+
24
+ create_table?(:schema_health) do
25
+ primary_key :id
26
+ String :checked_at, null: false
27
+ Integer :schema_version, null: false
28
+ String :validation_status, null: false # "healthy", "corrupt", "unknown"
29
+ String :issues_json, text: true # Array of detected problems
30
+ String :table_counts_json, text: true # Snapshot of table row counts
31
+ end
32
+
33
+ run "CREATE INDEX IF NOT EXISTS idx_schema_health_checked_at ON schema_health(checked_at)"
34
+ end
35
+
36
+ down do
37
+ drop_table?(:operation_progress)
38
+ drop_table?(:schema_health)
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration v7: Add ingestion metrics for ROI tracking
4
+ # - Creates ingestion_metrics table to track distillation costs
5
+ # - Tracks token usage (input/output) and facts extracted
6
+ # - Enables ROI analysis: tokens spent per fact extracted
7
+ # - Shows efficiency of memory system over time
8
+ Sequel.migration do
9
+ up do
10
+ create_table?(:ingestion_metrics) do
11
+ primary_key :id
12
+ foreign_key :content_item_id, :content_items, null: false
13
+ Integer :input_tokens # Tokens sent to distillation API
14
+ Integer :output_tokens # Tokens returned from distillation API
15
+ Integer :facts_extracted # Number of facts extracted from this content
16
+ String :created_at, null: false
17
+ end
18
+
19
+ run "CREATE INDEX IF NOT EXISTS idx_ingestion_metrics_content_item ON ingestion_metrics(content_item_id)"
20
+ run "CREATE INDEX IF NOT EXISTS idx_ingestion_metrics_created_at ON ingestion_metrics(created_at)"
21
+ end
22
+
23
+ down do
24
+ drop_table?(:ingestion_metrics)
25
+ end
26
+ end
File without changes