@vaur94/agz-memory 0.4.1 → 0.5.1

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 (45) hide show
  1. package/ARCHITECTURE.md +41 -25
  2. package/CHANGELOG.md +76 -0
  3. package/README.md +42 -16
  4. package/README.tr.md +43 -17
  5. package/dist/admin.js +4226 -758
  6. package/dist/core.js +5208 -1023
  7. package/dist/server.js +4888 -905
  8. package/dist/types/admin/quarantine.d.ts +15 -0
  9. package/dist/types/admin/reindex.d.ts +41 -0
  10. package/dist/types/capture/contract.d.ts +7 -5
  11. package/dist/types/capture/redact.d.ts +1 -0
  12. package/dist/types/config.d.ts +1 -0
  13. package/dist/types/context.d.ts +1 -1
  14. package/dist/types/contracts/error.d.ts +16 -0
  15. package/dist/types/contracts/limits.d.ts +18 -0
  16. package/dist/types/contracts/mutation.d.ts +31 -0
  17. package/dist/types/contracts/pagination.d.ts +27 -0
  18. package/dist/types/core.d.ts +6 -2
  19. package/dist/types/db/backup.d.ts +2 -1
  20. package/dist/types/db/health.d.ts +5 -1
  21. package/dist/types/db/legacy-health.d.ts +4 -0
  22. package/dist/types/db/maintenance.d.ts +21 -0
  23. package/dist/types/db/migrations/v011.d.ts +3 -0
  24. package/dist/types/db/schema.d.ts +10 -1
  25. package/dist/types/db.d.ts +12 -1
  26. package/dist/types/hash.d.ts +4 -0
  27. package/dist/types/retrieval/contract.d.ts +15 -3
  28. package/dist/types/security/quarantine-key.d.ts +47 -0
  29. package/dist/types/server.d.ts +1 -1
  30. package/dist/types/store/capture.d.ts +17 -5
  31. package/dist/types/store/outbox.d.ts +15 -3
  32. package/dist/types/store/retrieval.d.ts +2 -2
  33. package/dist/types/store.d.ts +39 -12
  34. package/dist/types/types.d.ts +1 -1
  35. package/dist/types/version.d.ts +1 -1
  36. package/docs/adr/hash-identity-v2.md +57 -0
  37. package/docs/adr/maintenance-lock.md +93 -0
  38. package/docs/backup-restore-runbook.md +36 -16
  39. package/docs/backup-restore-runbook.tr.md +36 -16
  40. package/docs/repository-hardening.md +125 -0
  41. package/docs/review-resolution.md +78 -0
  42. package/docs/schema-v11.md +75 -0
  43. package/package.json +21 -6
  44. package/skills/agz-memory/agz-memory.md +39 -0
  45. package/skills/index.json +9 -0
@@ -0,0 +1,57 @@
1
+ # ADR: Version 2 Tuple Hash Identities
2
+
3
+ Status: Accepted
4
+
5
+ Date: 2026-09-02
6
+
7
+ ## Context
8
+
9
+ Schema 10 hashes several user-controlled tuples by joining strings with NUL bytes. NUL is not rejected by every producer, so two different tuples can serialize to the same byte sequence. JavaScript string length also counts UTF-16 code units rather than UTF-8 bytes, which makes an implicit character-based framing contract unsuitable for persisted identities.
10
+
11
+ The affected values include canonical note hashes, derived-document hashes, OpenCode capture identities, project binding keys, and capture payload hashes. These values survive process restarts and must be deterministic on every supported platform.
12
+
13
+ ## Decision
14
+
15
+ Schema 11 uses one exported helper:
16
+
17
+ ```ts
18
+ hashTuple(domain, version, fields)
19
+ ```
20
+
21
+ The encoder writes the following byte sequence into SHA-256:
22
+
23
+ 1. A fixed `agz-memory/hash-tuple` format marker.
24
+ 2. A length-prefixed UTF-8 domain.
25
+ 3. An unsigned version integer.
26
+ 4. A field count.
27
+ 5. For every field, a one-byte type tag, an unsigned UTF-8/byte payload length, and the payload.
28
+
29
+ `null`, strings, booleans, finite numbers, and byte arrays use distinct tags. Empty strings and `null` are therefore different. String lengths are measured after UTF-8 encoding, never with JavaScript `String.length`. Numbers use a canonical finite representation and unsafe integers are rejected.
30
+
31
+ Each use has a separate domain. The initial version 2 domains are:
32
+
33
+ - `canonical-note`
34
+ - `derived-note`
35
+ - `capture-identity`
36
+ - `capture-payload`
37
+ - `project-binding`
38
+ - `checkpoint-identity`
39
+ - `outbox-operation`
40
+
41
+ Schema 11 recomputes canonical and revision hashes from persisted source fields. Derived hashes are recomputed from `deriveDocument()`. Capture keys are recomputed from strict source identities; a legacy row that cannot satisfy its event-kind identity contract stops migration with a safe row identifier and error code. Different legacy rows mapping to the same version 2 key stop migration; they are never merged or ignored.
42
+
43
+ The capture writer emits `agz-memory.capture/2`. The migration reader accepts `/1` only while migrating persisted schema 10 rows. Runtime ingestion accepts `/2` and independently recomputes the idempotency key before insertion.
44
+
45
+ ## Consequences
46
+
47
+ - NUL and Unicode tuple collision counterexamples no longer collide.
48
+ - Hashes intentionally change during the schema 10 to 11 migration.
49
+ - Version 2 databases cannot be safely written by version 0.4.1; the existing newer-schema guard must reject them before any DDL.
50
+ - Hashes identify content or operation tuples; they are not secret storage and do not replace redaction.
51
+ - Migration produces an aggregate mapping audit without recording note bodies, prompts, credentials, or other private payloads.
52
+
53
+ ## Rejected Alternatives
54
+
55
+ - Delimiter escaping was rejected because every producer would need identical escaping and type/null handling.
56
+ - `JSON.stringify` was rejected because object/key representation and numeric edge cases are not the persisted contract we need.
57
+ - Reusing version 1 hashes was rejected because it preserves the collision class.
@@ -0,0 +1,93 @@
1
+ # ADR: Cross-Process Database Maintenance Gate
2
+
3
+ Status: Accepted
4
+
5
+ Date: 2026-09-02
6
+
7
+ ## Context
8
+
9
+ SQLite WAL coordinates transactions but does not make replacing the database pathname safe while another process holds an open connection. An old connection can continue to use the replaced inode and its WAL after restore. Before schema 11, the migration lock serialized migration owners only; normal MCP and plugin handles did not participate.
10
+
11
+ AGZ Memory must run on Linux, macOS, and Windows under Bun. Bun does not currently expose one portable shared/exclusive advisory-file-lock API for this package, so schema 11 uses a conservative filesystem protocol and fails closed whenever ownership cannot be established.
12
+
13
+ ## Decision
14
+
15
+ Every normal database handle owns a lease for its complete lifetime. Migration, restore, backup publication, and prune use one exclusive maintenance gate associated with the canonical database path.
16
+
17
+ ### Normal Open
18
+
19
+ 1. Resolve and validate the canonical path and parent policy.
20
+ 2. Reject an existing maintenance gate.
21
+ 3. Publish a private lease file by exclusive staging and atomic rename. Its record contains an opaque owner ID, PID, process-start marker, hostname, and creation time. It contains no database content or configured private path.
22
+ 4. Check the maintenance gate again. If it appeared, remove only the caller's verified lease and retry or fail.
23
+ 5. Open and validate SQLite.
24
+ 6. Keep the lease until statements and the SQLite handle are closed.
25
+
26
+ The second gate check closes the race where maintenance creates the gate between the first check and lease publication. Maintenance either observes the published lease, or the opener observes the gate and withdraws.
27
+
28
+ ### Maintenance
29
+
30
+ 1. Atomically create the gate. Only one owner can succeed.
31
+ 2. Validate the gate owner record after publication.
32
+ 3. Enumerate leases. A local lease is stale only when PID liveness and process-start identity prove that its owner is gone or the PID was reused. A remote-host or unverifiable lease remains active and blocks maintenance.
33
+ 4. If any active lease exists, remove only the caller's gate and return `active_database_handles`.
34
+ 5. Perform the operation without exposing a normal handle.
35
+ 6. Verify the installed canonical database before removing the gate.
36
+
37
+ There is no `--force` bypass for active or unverifiable leases. Stale cleanup requires current owner identity checks. Gate and lease deletion never recursively removes an unverified replacement pathname.
38
+
39
+ Migration waiters recheck the canonical schema under a normal lease while they
40
+ still own the migration lock. A waiter that finds the target schema returns that
41
+ handle without creating another maintenance gate. After a successful migration,
42
+ the owner releases the maintenance gate and publishes its normal lease before
43
+ releasing the migration lock. This handoff prevents queued stale observations
44
+ from creating a new gate between migration completion and reopen.
45
+
46
+ An active gate left by a crashed local process is reclaimed in place: an
47
+ exclusive takeover record serializes contenders and atomically replaces the
48
+ stale owner while the gate directory remains continuously present. A reused PID
49
+ is stale only when both process-start markers exist and differ. Remote owners,
50
+ live owners, unavailable markers, malformed records, and missing records remain
51
+ fail-closed.
52
+
53
+ `retain()` atomically persists `state: recovery-required` before returning. Such
54
+ a gate is never reclaimed automatically. A verified restore may take it over
55
+ only with the exact recorded owner ID and
56
+ `RECOVER_RETAINED_MAINTENANCE_GATE`; the restore keeps the gate continuously
57
+ held and validates the installed database before release.
58
+
59
+ ### Restore
60
+
61
+ While the maintenance gate is held and no leases exist:
62
+
63
+ 1. Open the backup and manifest through the validated no-symlink policy.
64
+ 2. Copy the source into a private same-parent staging file while streaming SHA-256 and byte count.
65
+ 3. Validate manifest hash/size, application ID, database UUID/product, schema version/fingerprint, row counts, `integrity_check`, and `foreign_key_check` on the staging inode.
66
+ 4. Checkpoint and preserve the current canonical database.
67
+ 5. Fsync staging and its parent, atomically replace the canonical pathname, and quarantine stale WAL/SHM files.
68
+ 6. Reopen the installed target and repeat identity, fingerprint, count, and health validation.
69
+ 7. On failure, restore the preserved source while still holding the gate. If rollback cannot be verified, retain the gate as a recovery-required marker and fail closed.
70
+
71
+ Backup hashing is streaming. Restore never copies a pathname that was validated and then reopened as the source of truth.
72
+
73
+ ## Platform Policy
74
+
75
+ - The database, backup root, manifest, maintenance gate, lease registry, lock records, and their existing parents must not be symbolic links.
76
+ - Existing components must have the expected file type and private ownership/permissions when the platform exposes those attributes.
77
+ - New files use exclusive creation and private modes.
78
+ - An unsupported no-follow or identity check causes the sensitive operation to fail closed rather than silently weaken the policy.
79
+ - Process-start markers use the strongest local facility available. An unavailable marker never justifies breaking a live lease.
80
+
81
+ ## Consequences
82
+
83
+ - Restore is offline-safe, not an online hot swap.
84
+ - Long-lived MCP/plugin handles explicitly block maintenance until clean shutdown.
85
+ - A crashed owner can be reclaimed only with verifiable stale-owner evidence.
86
+ - The protocol is cooperative against same-user processes; it does not protect a database directory writable by an untrusted account. Unsafe ownership or permissions are rejected.
87
+
88
+ ## Rejected Alternatives
89
+
90
+ - WAL checkpoint alone was rejected because it does not invalidate old file descriptors.
91
+ - PID-only lock files were rejected because PIDs are reused.
92
+ - Unconditional stale timeout and `--force` were rejected because a paused live writer could lose acknowledged writes.
93
+ - Path-only verify-then-copy was rejected because it leaves a TOCTOU window.
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [Türkçe](backup-restore-runbook.tr.md)
4
4
 
5
- This runbook applies to `@vaur94/agz-memory@0.4.1` and SQLite schema v10.
5
+ This runbook applies to `@vaur94/agz-memory@0.5.1` and SQLite schema v11.
6
6
 
7
7
  ## Preconditions
8
8
 
@@ -25,20 +25,20 @@ Do not proceed with a guessed or empty path.
25
25
  Run a read-only health report first:
26
26
 
27
27
  ```sh
28
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
28
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
29
29
  ```
30
30
 
31
31
  `ok` must be `true`. Record `schemaVersion`, row counts, and invariant counts.
32
32
  Then create a standalone verified backup and upgrade:
33
33
 
34
34
  ```sh
35
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
36
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
37
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
35
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup
36
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin upgrade --to 11
37
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
38
38
  ```
39
39
 
40
40
  The upgrade itself creates another verified pre-migration backup when the
41
- database schema is older than v10. Preserve each printed manifest path and
41
+ database schema is older than v11. Preserve each printed manifest path and
42
42
  SHA-256. Do not start a writer if the final report has `ok: false`.
43
43
 
44
44
  ## Verify A Backup
@@ -54,16 +54,16 @@ The manifest format is `agz-memory-backup/1`. `agz-memory-admin restore` verifie
54
54
  that the manifest and database are regular files in the same backup directory,
55
55
  then checks size, SHA-256, SQLite integrity, foreign keys, and row counts.
56
56
 
57
- Final `0.4.1` does not accept prerelease manifest formats. Use the originating
57
+ Final `0.5.1` does not accept prerelease manifest formats. Use the originating
58
58
  prerelease to restore such a backup, run its doctor check, and only then upgrade
59
- that restored database with `0.4.1`.
59
+ that restored database with `0.5.1`.
60
60
 
61
61
  ## Restore Rehearsal
62
62
 
63
63
  Keep all writers stopped. First request a dry run by omitting confirmation:
64
64
 
65
65
  ```sh
66
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
66
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore \
67
67
  "$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json"
68
68
  ```
69
69
 
@@ -71,7 +71,7 @@ Compare `targetPath`, `sourceSchema`, `targetSchema`, row counts, size, and
71
71
  SHA-256 with the recorded backup. Then use the exact manifest hash:
72
72
 
73
73
  ```sh
74
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
74
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore \
75
75
  "$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json" \
76
76
  --sha256 <manifest-database-sha256> \
77
77
  --confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP
@@ -84,9 +84,9 @@ database passes all checks.
84
84
  ## Post-Restore Validation
85
85
 
86
86
  ```sh
87
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
88
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
89
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
87
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
88
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin capture status
89
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin outbox status
90
90
  ```
91
91
 
92
92
  Start only the MCP server and perform read-only `project_list`, `memory_recall`,
@@ -94,6 +94,26 @@ and `memory_read` smoke calls. Compare project/note counts with the manifest.
94
94
  Only after those checks pass should OpenCode be restarted. Keep the plugin in
95
95
  `off` until a separate rollout decision is made.
96
96
 
97
+ ## Retained Maintenance Gate
98
+
99
+ `<database>.maintenance/owner.json` with `state: recovery-required` means a
100
+ previous restore could not verify its rollback. It is never removed
101
+ automatically. Stop every MCP/plugin process, preserve the database, sidecars,
102
+ gate, and restore artifacts, then select a verified backup. Supply the exact
103
+ recorded owner ID only on the restoring command:
104
+
105
+ ```sh
106
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore <manifest> \
107
+ --sha256 <manifest-sha256> \
108
+ --confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP \
109
+ --maintenance-owner <owner-id> \
110
+ --maintenance-confirm RECOVER_RETAINED_MAINTENANCE_GATE
111
+ ```
112
+
113
+ Remote, live, malformed, or otherwise unverifiable owners remain blocked. Never
114
+ delete the gate manually; the recovery restore atomically takes ownership while
115
+ the gate directory remains present.
116
+
97
117
  ## Stale Migration Lock
98
118
 
99
119
  The lock is `<database>.migration.lock/owner.json`. Never remove it while the
@@ -104,7 +124,7 @@ style error first if uncertain. Break only a proven stale lock with the exact
104
124
  owner ID and confirmation:
105
125
 
106
126
  ```sh
107
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin unlock \
127
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin unlock \
108
128
  --owner <owner-id> \
109
129
  --confirm BREAK_STALE_MIGRATION_LOCK
110
130
  ```
@@ -118,13 +138,13 @@ The first command is non-destructive and returns a digest over the exact backup
118
138
  set:
119
139
 
120
140
  ```sh
121
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune
141
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup prune
122
142
  ```
123
143
 
124
144
  Review every listed manifest/database pair. Delete only that unchanged set:
125
145
 
126
146
  ```sh
127
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune \
147
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup prune \
128
148
  --digest <dry-run-digest> \
129
149
  --confirm DELETE_VERIFIED_BACKUPS
130
150
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](backup-restore-runbook.md) | Türkçe
4
4
 
5
- Bu runbook `@vaur94/agz-memory@0.4.1` ve SQLite schema v10 için geçerlidir.
5
+ Bu runbook `@vaur94/agz-memory@0.5.1` ve SQLite schema v11 için geçerlidir.
6
6
 
7
7
  ## Ön Koşullar
8
8
 
@@ -25,19 +25,19 @@ Tahmin edilmiş veya boş bir yolla devam etmeyin.
25
25
  Önce salt-okunur sağlık raporu alın:
26
26
 
27
27
  ```sh
28
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
28
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
29
29
  ```
30
30
 
31
31
  `ok` değeri `true` olmalıdır. `schemaVersion`, satır sayıları ve değişmez kural
32
32
  sayılarını kaydedin. Sonra bağımsız doğrulanmış yedek oluşturup yükseltin:
33
33
 
34
34
  ```sh
35
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup
36
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin upgrade --to 10
37
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
35
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup
36
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin upgrade --to 11
37
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
38
38
  ```
39
39
 
40
- Veritabanı v10'dan eskiyse yükseltme ayrıca değişiklikten önce doğrulanmış yedek
40
+ Veritabanı v11'den eskiyse yükseltme ayrıca değişiklikten önce doğrulanmış yedek
41
41
  oluşturur. Yazdırılan her manifest yolunu ve SHA-256 değerini saklayın. Son rapor
42
42
  `ok: false` ise yazıcı başlatmayın.
43
43
 
@@ -54,16 +54,16 @@ Manifest formatı `agz-memory-backup/1` olur. `agz-memory-admin restore`, manife
54
54
  ile veritabanının aynı yedek dizinindeki normal dosyalar olduğunu doğrular;
55
55
  ardından boyut, SHA-256, SQLite bütünlüğü, foreign key ve satır sayılarını denetler.
56
56
 
57
- Final `0.4.1` ön sürüm manifest formatlarını kabul etmez. Böyle bir yedeği onu
57
+ Final `0.5.1` ön sürüm manifest formatlarını kabul etmez. Böyle bir yedeği onu
58
58
  oluşturan ön sürümle geri yükleyin, o sürümün doctor kontrolünü çalıştırın ve
59
- yalnız bundan sonra geri yüklenen veritabanını `0.4.1` ile yükseltin.
59
+ yalnız bundan sonra geri yüklenen veritabanını `0.5.1` ile yükseltin.
60
60
 
61
61
  ## Geri Yükleme Provası
62
62
 
63
63
  Tüm yazıcıları kapalı tutun. Önce onay vermeden deneme yapın:
64
64
 
65
65
  ```sh
66
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
66
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore \
67
67
  "$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json"
68
68
  ```
69
69
 
@@ -71,7 +71,7 @@ bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
71
71
  değerlerini kaydedilen yedekle karşılaştırın. Sonra tam manifest özetini kullanın:
72
72
 
73
73
  ```sh
74
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin restore \
74
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore \
75
75
  "$OPENCODE_MEMORY_DATABASE_PATH.backup/<backup>.manifest.json" \
76
76
  --sha256 <manifest-database-sha256> \
77
77
  --confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP
@@ -84,9 +84,9 @@ veritabanı tüm kontrollerden geçmeden bunu silmeyin.
84
84
  ## Geri Yükleme Sonrası Doğrulama
85
85
 
86
86
  ```sh
87
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin doctor
88
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin capture status
89
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin outbox status
87
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin doctor
88
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin capture status
89
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin outbox status
90
90
  ```
91
91
 
92
92
  Yalnız MCP sunucusunu başlatın ve salt-okunur `project_list`, `memory_recall` ve
@@ -94,6 +94,26 @@ Yalnız MCP sunucusunu başlatın ve salt-okunur `project_list`, `memory_recall`
94
94
  OpenCode'u ancak bu kontroller geçince yeniden başlatın. Ayrı devreye alma kararı
95
95
  verilene kadar eklentiyi `off` tutun.
96
96
 
97
+ ## Korunan Bakım Kapısı
98
+
99
+ `<database>.maintenance/owner.json` içindeki `state: recovery-required`, önceki
100
+ bir geri yüklemenin geri alma sonucunu doğrulayamadığını gösterir. Bu kapı
101
+ otomatik kaldırılmaz. Tüm MCP/eklenti süreçlerini durdurun; veritabanını, yan
102
+ dosyaları, kapıyı ve geri yükleme kalıntılarını koruyun; ardından doğrulanmış bir
103
+ yedek seçin. Kayıtlı tam sahip kimliğini yalnız geri yükleme komutunda verin:
104
+
105
+ ```sh
106
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin restore <manifest> \
107
+ --sha256 <manifest-sha256> \
108
+ --confirm RESTORE_DATABASE_FROM_VERIFIED_BACKUP \
109
+ --maintenance-owner <owner-id> \
110
+ --maintenance-confirm RECOVER_RETAINED_MAINTENANCE_GATE
111
+ ```
112
+
113
+ Uzak, canlı, bozuk veya başka biçimde doğrulanamayan sahipler engelli kalır.
114
+ Kapıyı elle silmeyin; kurtarma geri yüklemesi kapı dizini kesintisiz yerindeyken
115
+ sahipliği atomik olarak devralır.
116
+
97
117
  ## Eski Geçiş Kilidi
98
118
 
99
119
  Kilit `<database>.migration.lock/owner.json` konumundadır. Kayıtlı süreç yaşıyor
@@ -103,7 +123,7 @@ Sahip dosyasındaki PID, makine ve başlangıç zamanını doğrulayın. Yalnız
103
123
  kanıtlanan kilidi tam sahip ID'si ve onayla kırın:
104
124
 
105
125
  ```sh
106
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin unlock \
126
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin unlock \
107
127
  --owner <owner-id> \
108
128
  --confirm BREAK_STALE_MIGRATION_LOCK
109
129
  ```
@@ -116,14 +136,14 @@ kanıtıdır; veritabanı doğrulamasını atlama izni değildir.
116
136
  İlk komut silme yapmaz ve tam yedek kümesinin özetini döndürür:
117
137
 
118
138
  ```sh
119
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune
139
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup prune
120
140
  ```
121
141
 
122
142
  Listelenen her manifest/veritabanı çiftini inceleyin. Yalnız değişmemiş kümeyi
123
143
  silin:
124
144
 
125
145
  ```sh
126
- bunx --package @vaur94/agz-memory@0.4.1 agz-memory-admin backup prune \
146
+ bunx --package @vaur94/agz-memory@0.5.1 agz-memory-admin backup prune \
127
147
  --digest <dry-run-digest> \
128
148
  --confirm DELETE_VERIFIED_BACKUPS
129
149
  ```
@@ -0,0 +1,125 @@
1
+ # Repository Hardening Checklist
2
+
3
+ Repository: `ugur-murat-alt/agz-memory`
4
+
5
+ Verified on: 2026-09-02
6
+
7
+ The checks below are GitHub repository controls and cannot be enforced by the package source alone. The verification performed for 0.5.0 found that `main` had no classic branch protection, no repository ruleset, no deployment environment, and Actions allowed all actions without repository-level SHA-pinning enforcement. Workflow actions in this repository are nevertheless pinned by commit SHA.
8
+
9
+ ## Required `main` Ruleset
10
+
11
+ GitHub screen: **Settings > Rules > Rulesets > New branch ruleset**
12
+
13
+ Target: default branch, `main`
14
+
15
+ Enable:
16
+
17
+ - Restrict deletions.
18
+ - Block force pushes.
19
+ - Require a pull request before merging.
20
+ - Require at least one approval.
21
+ - Dismiss stale approvals when new commits are pushed.
22
+ - Require review from Code Owners when a `CODEOWNERS` file is introduced.
23
+ - Require conversation resolution.
24
+ - Require status checks to pass.
25
+ - Require branches to be up to date before merging.
26
+ - Require signed commits.
27
+ - Require linear history.
28
+
29
+ Required status checks should include every 0.5.0 CI gate rather than only the aggregate job name:
30
+
31
+ - Linux minimum Bun type/test/build/package gate.
32
+ - Linux current Bun type/test/build/package gate.
33
+ - macOS current Bun gate.
34
+ - Windows current Bun gate.
35
+ - Property/security tests.
36
+ - Multiprocess stress tests.
37
+ - Restore fault tests.
38
+ - Benchmark gate.
39
+ - Packed install and audit.
40
+ - CodeQL.
41
+ - Dependency review for pull requests.
42
+
43
+ Do not grant bypass permission to ordinary contributors. Keep the repository administrator bypass limited to documented emergency recovery.
44
+
45
+ ## Signed Commits and Tags
46
+
47
+ GitHub screen: **Settings > Rules > Rulesets > Require signed commits**
48
+
49
+ Local verification:
50
+
51
+ ```sh
52
+ git log --show-signature -1
53
+ git tag -v v0.5.0
54
+ ```
55
+
56
+ Create the release tag from the reviewed merge commit. Do not move an existing release tag.
57
+
58
+ ## Release Environment
59
+
60
+ GitHub screen: **Settings > Environments > New environment**
61
+
62
+ Name: `npm-release`
63
+
64
+ Configure:
65
+
66
+ - Required reviewer.
67
+ - Deployment branch restricted to protected `main` and version tags.
68
+ - No long-lived npm token when trusted publishing is available.
69
+
70
+ The publish workflow should use npm trusted publishing with OpenID Connect and provenance:
71
+
72
+ ```yaml
73
+ permissions:
74
+ contents: read
75
+ id-token: write
76
+ ```
77
+
78
+ ```sh
79
+ npm publish --provenance --access public
80
+ ```
81
+
82
+ Verify the published package exposes provenance and that npm `gitHead`, Git tag SHA, GitHub Release SHA, and reviewed merge commit all match.
83
+
84
+ ## Actions Policy
85
+
86
+ GitHub screen: **Settings > Actions > General > Actions permissions**
87
+
88
+ Prefer **Allow enterprise actions and select non-enterprise actions** if the account plan supports it. Require full-length commit SHA pinning for third-party actions. Keep workflow token permissions read-only by default and grant narrower write permissions per job.
89
+
90
+ Repository verification commands:
91
+
92
+ ```sh
93
+ gh api repos/ugur-murat-alt/agz-memory/branches/main/protection
94
+ gh api repos/ugur-murat-alt/agz-memory/rulesets
95
+ gh api repos/ugur-murat-alt/agz-memory/actions/permissions
96
+ gh api repos/ugur-murat-alt/agz-memory/environments
97
+ ```
98
+
99
+ Expected release condition:
100
+
101
+ - The protection endpoint or active ruleset reports required pull requests, reviews, signed commits, required status checks, no force push, and no deletion.
102
+ - At least one protected `npm-release` environment exists.
103
+ - Actions policy and workflow files both enforce immutable action versions.
104
+
105
+ ## npm Package Policy
106
+
107
+ - Enable npm two-factor authentication for authorization and writes.
108
+ - Enable trusted publishing for both `@vaur94/agz-memory` and `@vaur94/agz-memory-plugin`.
109
+ - Require provenance on both packages.
110
+ - Deprecate, do not overwrite, a compromised version.
111
+ - Test the packed artifacts together in a clean directory before publish.
112
+ - Verify registry integrity, package contents, imports, exact nine-tool MCP catalog, and inert plugin defaults after publish.
113
+
114
+ ## Release Evidence
115
+
116
+ Attach or link:
117
+
118
+ - Ruleset export or settings screenshots.
119
+ - Required-check list and successful run URLs.
120
+ - Signed merge commit and tag verification.
121
+ - Release environment protection.
122
+ - npm provenance statement for both packages.
123
+ - Pack SHA-256 values and clean-install smoke output.
124
+
125
+ Repository setting changes remain a release blocker until independently verified. Source documentation does not itself close AGZ-064.
@@ -0,0 +1,78 @@
1
+ # AGZ 0.5.0 Review Resolution
2
+
3
+ This record maps every AGZ-001 through AGZ-068 review finding to its 0.5.0
4
+ resolution and durable source or regression evidence. `Fixed` means the source
5
+ change and cited local test or release gate exist in this branch. Repository
6
+ settings are external state; AGZ-064 remains explicitly deferred and blocks a
7
+ release-ready claim until the controls in `repository-hardening.md` are verified.
8
+
9
+ | Finding | Priority | Resolution | Evidence |
10
+ |---|---|---|---|
11
+ | AGZ-001 | P1 | Fixed | Note update and pin compare-and-swap under multiprocess contention: `src/store.ts`, `test/concurrency/note-update-multiprocess.test.ts`. |
12
+ | AGZ-002 | P1 | Fixed | Project deletion binds the immutable ID and confirmed current name in one transaction: `src/store.ts`, `test/concurrency/project-delete-rename.test.ts`. |
13
+ | AGZ-003 | P1 | Fixed | Revision snapshots and provenance use the row returned by the successful mutation: `src/store.ts`, `test/store/lifecycle.test.ts`. |
14
+ | AGZ-004 | P1 | Fixed | Required outbox insertion shares the canonical transaction and cannot be ignored: `src/store.ts`, `test/store/lifecycle.test.ts`. |
15
+ | AGZ-005 | P1 | Fixed | Concurrent project and binding creation distinguishes duplicates from conflicts: `src/store.ts`, `src/store/capture.ts`, `test/concurrency/binding-race.test.ts`. |
16
+ | AGZ-006 | P1 | Fixed | Domain-separated, length-prefixed UTF-8 tuple hashing removes delimiter and Unicode collisions: `src/hash.ts`, `test/capture/capture-contract-v2.test.ts`. |
17
+ | AGZ-007 | P1 | Fixed | Reused capture identities with different source or payload fail as `idempotency_conflict`: `src/store/capture.ts`, `test/capture/capture-contract-v2.test.ts`. |
18
+ | AGZ-008 | P1 | Fixed | Shared MCP/core identities retain the exact nine-tool and fail-closed contract: `src/tools.ts`, `test/contract/mcp-surface.test.ts`. |
19
+ | AGZ-009 | P1 | Fixed | Normal handles publish lifetime database leases before opening SQLite: `src/db/maintenance.ts`, `test/db/maintenance.test.ts`. |
20
+ | AGZ-010 | P1 | Fixed | Maintenance uses one exclusive gate and rejects active or unverifiable handles: `src/db/maintenance.ts`, `test/db/maintenance.test.ts`. |
21
+ | AGZ-011 | P1 | Fixed | Restore refuses to replace a database held by a live writer: `src/db/backup.ts`, `test/db/restore-live-writer.test.ts`. |
22
+ | AGZ-012 | P1 | Fixed | Restore copies and hashes one opened source inode instead of reopening a verified path: `src/db/backup.ts`, `test/db/restore-toctou.test.ts`. |
23
+ | AGZ-013 | P1 | Fixed | Database, backup, sidecar, gate, lease, and manifest paths reject symbolic links: `src/db/maintenance.ts`, `src/db/backup.ts`, `test/db/path-symlink-matrix.test.ts`. |
24
+ | AGZ-014 | P1 | Fixed | `application_id` and `agz_meta` bind product, database UUID, schema, and hash policy: `src/db/schema.ts`, `docs/schema-v11.md`. |
25
+ | AGZ-015 | P1 | Fixed | Exact application-object fingerprint detects same-version schema drift: `src/db/schema.ts`, `test/db/schema-drift-failclosed.test.ts`. |
26
+ | AGZ-016 | P1 | Fixed | Nonempty unsigned databases fail closed while a zero-object database may initialize: `src/db.ts`, `test/db/unrecognized-database.test.ts`. |
27
+ | AGZ-017 | P1 | Fixed | Projection reports truncation into the redaction boundary instead of hiding removed suffixes: `src/capture/projection.ts`, `test/security/redaction-property.test.ts`. |
28
+ | AGZ-018 | P1 | Fixed | Credential/private-key/high-entropy redaction fails closed across boundaries and punctuation: `src/capture/redact.ts`, `test/security/redaction-corpus.test.ts`. |
29
+ | AGZ-019 | P1 | Fixed | Capture event kinds enforce required and forbidden native source identity fields: `src/capture/contract.ts`, `test/capture/capture-contract-v2.test.ts`. |
30
+ | AGZ-020 | P1 | Fixed | Composite foreign keys reject cross-project binding, capture, checkpoint, note, and outbox references: `src/db/schema.ts`, `test/capture/capture-contract-v2.test.ts`. |
31
+ | AGZ-021 | P1 | Fixed | Quarantined events retain no payload and cannot materialize notes: `src/store/capture.ts`, `test/capture/capture.test.ts`. |
32
+ | AGZ-022 | P1 | Fixed | Extraction admits terminal text/status only and excludes tool payloads and reasoning: `packages/opencode-plugin/src/extract.ts`, `test/capture/capture.test.ts`. |
33
+ | AGZ-023 | P1 | Fixed | Checkpoints use `(binding_key, session_id)` so native session IDs can repeat safely: `src/db/schema.ts`, `test/capture/capture-contract-v2.test.ts`. |
34
+ | AGZ-024 | P1 | Fixed | Startup/hourly retention drains bounded terminal, quarantined, and checkpoint backlogs: `src/store/capture.ts`, `test/capture/capture.test.ts`. |
35
+ | AGZ-025 | P2 | Fixed | Reconciliation resumes only after the persisted binding/session checkpoint: `packages/opencode-plugin/src/runtime.ts`, `test/plugin/reconcile-incremental.test.ts`. |
36
+ | AGZ-026 | P2 | Fixed | Reconciliation has bounded global concurrency and coalesces same-session reruns: `packages/opencode-plugin/src/runtime.ts`, `test/plugin/reconcile-backpressure.test.ts`. |
37
+ | AGZ-027 | P1 | Fixed | Shutdown aborts without awaiting hung event, session, or context calls: `packages/opencode-plugin/src/runtime.ts`, `test/plugin/hung-context-stop.test.ts`. |
38
+ | AGZ-028 | P1 | Fixed | Turn opt-out remains effective across every supported hook order: `packages/opencode-plugin/src/runtime.ts`, `test/plugin/optout-hook-permutations.test.ts`. |
39
+ | AGZ-029 | P2 | Fixed | Concurrent opt-out history probes share one bounded request: `packages/opencode-plugin/src/runtime.ts`, `test/plugin/optout-hook-permutations.test.ts`. |
40
+ | AGZ-030 | P2 | Fixed | Reconciliation, terminal-event, and opt-out preflight calls have bounded abortable timeouts: `packages/opencode-plugin/src/runtime.ts`, `packages/opencode-plugin/test/plugin.test.ts`. |
41
+ | AGZ-031 | P2 | Fixed | Runtime errors are reduced to safe codes and do not log payload text: `packages/opencode-plugin/src/runtime.ts`, `packages/opencode-plugin/test/plugin.test.ts`. |
42
+ | AGZ-032 | P1 | Fixed | Location and binding checks fail closed on missing, mismatched, or conflicting identity: `packages/opencode-plugin/src/binding.ts`, `packages/opencode-plugin/test/plugin.test.ts`. |
43
+ | AGZ-033 | P2 | Fixed | First run creates the private default database hierarchy below `HOME`: `src/config.ts`, `test/plugin/first-run-home.test.ts`. |
44
+ | AGZ-034 | P1 | Fixed | Injection remains bounded, escaped, and explicitly marked untrusted: `src/retrieval/formatter.ts`, `test/retrieval/retrieval.test.ts`. |
45
+ | AGZ-035 | P1 | Fixed | Plugin defaults remain off/empty and exact OpenCode version mismatch disables startup: `packages/opencode-plugin/src/config.ts`, `packages/opencode-plugin/test/plugin.test.ts`. |
46
+ | AGZ-036 | P1 | Fixed | Backend hits use the redacted derived-document hash, not canonical note hash: `src/retrieval/derived.ts`, `test/retrieval/hardening.test.ts`. |
47
+ | AGZ-037 | P1 | Fixed | Backend responses have strict keys/types/count bounds and malformed data falls back lexically: `src/retrieval/contract.ts`, `test/retrieval/hardening.test.ts`. |
48
+ | AGZ-038 | P2 | Fixed | One deadline covers backend, canonical validation, graph expansion, and formatting: `src/store/retrieval.ts`, `test/retrieval/hardening.test.ts`. |
49
+ | AGZ-039 | P2 | Fixed | Backend hit validation uses bounded batch SQL rather than per-hit queries: `src/store/retrieval.ts`, `test/retrieval/hardening.test.ts`. |
50
+ | AGZ-040 | P2 | Fixed | Reciprocal-rank fusion is deterministic under channel permutation: `src/retrieval/fusion.ts`, `test/retrieval/hardening.test.ts`. |
51
+ | AGZ-041 | P1 | Fixed | Directed graph retrieval preserves source and target endpoints: `src/store/retrieval.ts`, `test/retrieval/hardening.test.ts`. |
52
+ | AGZ-042 | P2 | Fixed | Context truncation never splits a Unicode code point: `src/retrieval/formatter.ts`, `test/retrieval/hardening.test.ts`. |
53
+ | AGZ-043 | P1 | Fixed | Stale and cross-project backend hits are rejected against canonical rows: `src/store/retrieval.ts`, `test/retrieval/retrieval.test.ts`. |
54
+ | AGZ-044 | P1 | Fixed | Outbox backend calls hard-time out even when `AbortSignal` is ignored: `src/store/outbox.ts`, `test/retrieval/hardening.test.ts`. |
55
+ | AGZ-045 | P1 | Fixed | Heartbeat, lease generation, and fence condition every final outbox transition: `src/store/outbox.ts`, `test/retrieval/hardening.test.ts`. |
56
+ | AGZ-046 | P1 | Fixed | Operation-specific checks and tuple-hashed operation keys reject malformed queue rows: `src/db/schema.ts`, `src/store/outbox.ts`, `test/store/outbox.test.ts`. |
57
+ | AGZ-047 | P1 | Fixed | Each reindex generation queues purge plus a transactional active-note snapshot: `src/admin/index.ts`, `test/retrieval/hardening.test.ts`. |
58
+ | AGZ-048 | P2 | Fixed | Dead work retries explicitly and bounded exponential backoff preserves FIFO: `src/admin/index.ts`, `src/store/outbox.ts`, `test/store/outbox.test.ts`. |
59
+ | AGZ-049 | P1 | Fixed | Project deletion and rebuild enqueue purge so derived stores cannot retain omitted content: `src/store.ts`, `src/admin/index.ts`, `test/store/outbox.test.ts`. |
60
+ | AGZ-050 | P1 | Fixed | Backup publication and migration occur only under the maintenance gate with verified rollback: `src/db.ts`, `src/db/backup.ts`, `test/db/maintenance.test.ts`, `test/db/backup-restore.test.ts`. |
61
+ | AGZ-051 | P2 | Fixed | Batch mutations remain ordered, independently committed, and return every result: `src/tools.ts`, `test/contract/mcp-surface.test.ts`. |
62
+ | AGZ-052 | P1 | Fixed | Schema-10 migration preserves valid legacy bindings, events, tombstones, and orphaned outbox history: `src/db/migrations/v011.ts`, `test/db/migration-v11.test.ts`. |
63
+ | AGZ-053 | P1 | Fixed | Migration verifies counts, revisions, hashes, FTS, foreign keys, identity, and fingerprint before publish: `src/db/migrations/v011.ts`, `src/admin/doctor.ts`, `test/db/migration-v11.test.ts`. |
64
+ | AGZ-054 | P2 | Fixed | Public query and batch inputs retain explicit hard cardinality bounds: `src/tools.ts`, `test/contract/mcp-surface.test.ts`. |
65
+ | AGZ-055 | P1 | Fixed | Semantic query bytes and retrieval card requests clamp to hard limits: `src/store/retrieval.ts`, `test/retrieval/hardening.test.ts`. |
66
+ | AGZ-056 | P2 | Fixed | Store conflicts and lease loss return stable typed error codes rather than raw SQLite errors: `src/store.ts`, `src/store/outbox.ts`, concurrency regressions. |
67
+ | AGZ-057 | P1 | Fixed | Doctor checks database identity, exact fingerprint, tenant references, hashes, revisions, FTS, and operation keys: `src/admin/doctor.ts`, `test/db/migration-v11.test.ts`. |
68
+ | AGZ-058 | P2 | Fixed | Admin reindex/status/retry responses are bounded and payload-free: `src/admin/index.ts`, `test/retrieval/hardening.test.ts`. |
69
+ | AGZ-059 | P2 | Fixed | Retrieval metrics deduplicate rankings and clamp recall, MRR, and NDCG to the unit interval: `benchmark/evaluate.ts`, `test/retrieval/hardening.test.ts`. |
70
+ | AGZ-060 | P2 | Fixed | `benchmark:gate` enforces the documented p99 latency ceiling: `benchmark/run.ts`, `package.json`. |
71
+ | AGZ-061 | P3 | Fixed | Baseline manifest, timings, logs, platform, commit, and benchmark artifacts are committed in `artifacts/baseline/manifest.json` and its listed files. |
72
+ | AGZ-062 | P2 | Fixed | CI covers minimum/current Bun on Linux plus current Bun on macOS and Windows: `.github/workflows/ci.yml`. |
73
+ | AGZ-063 | P2 | Fixed | CI has separate property, stress, restore, benchmark, CodeQL, and dependency-review gates with immutable action pins: `.github/workflows/ci.yml`. |
74
+ | AGZ-064 | P2 | Deferred | `main` protection/ruleset and `npm-release` environment are absent external repository settings; required controls and verification are in `docs/repository-hardening.md`. |
75
+ | AGZ-065 | P2 | Fixed | Package smoke derives both tarball names from the manifest version instead of a stale literal: `.github/workflows/ci.yml`. |
76
+ | AGZ-066 | P3 | Fixed | Runtime ranges, exact plugin compatibility, frozen lock installs, audits, and Dependabot policy are explicit: `package.json`, `packages/opencode-plugin/package.json`, `.github/dependabot.yml`. |
77
+ | AGZ-067 | P3 | Fixed | English/Turkish README and recovery sections are release-verified as mapped contracts: `scripts/verify-release.ts`, `test/release/release-surface.test.ts`. |
78
+ | AGZ-068 | P2 | Fixed | Release verification requires exactly this ordered 68-row record and forbids deferred P0/P1 findings: `scripts/verify-release.ts`, `test/release/release-surface.test.ts`. |