Compare commits

..
80 Commits
Author SHA1 Message Date
bvandeusen b12d9ba396 Scribe is renamed Fabled Grimoire
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m3s
CI & Build / Python tests (push) Successful in 2m6s
CI & Build / Build & push image (push) Successful in 14s
2026-10-22 14:20:16 -04:00
bvandeusen b69bc47e65 Merge pull request 'Backups carry the Profile page (user_profiles)' (#211) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m7s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 23s
2026-10-15 20:00:32 -04:00
bvandeusen cef173c0a6 Scheduled backups to a folder or S3 bucket, and forge tokens sealed at rest (milestone 523)
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m11s
CI & Build / Python tests (push) Successful in 1m57s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Build & push image (push) Successful in 14s
2026-10-15 18:19:26 -04:00
bvandeusen a9bfe13631 Merge pull request 'Reply shapes ride the moments (milestone 500, steps 1–5)' (#209) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 17s
2026-10-09 16:40:04 -04:00
bvandeusen f15542d6cb Merge pull request 'Merge dev → main: family precedents say their reason once; the repeat trigger compares like with like' (#208) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / integration (push) Successful in 1m37s
CI & Build / TypeScript typecheck (push) Successful in 58s
CI & Build / Python tests (push) Successful in 2m20s
CI & Build / Build & push image (push) Successful in 22s
2026-10-06 19:00:18 -04:00
bvandeusen b3c0cc5548 Merge pull request 'Merge dev → main: a family idea links to the rule topic holding its norms' (#207) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / integration (push) Successful in 1m26s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / Python tests (push) Successful in 2m10s
CI & Build / Build & push image (push) Successful in 1m7s
2026-10-06 18:00:52 -04:00
bvandeusen ecbadf51ce Merge pull request 'Merge dev: family canon, milestone 463 steps 1-6' (#206) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m13s
CI & Build / Python tests (push) Successful in 1m57s
CI & Build / Build & push image (push) Successful in 30s
2026-10-06 14:11:28 -04:00
bvandeusen d4de0b9b54 Merge pull request 'Merge dev: scope-then-rank searches (#4958, #4961), CI gate on integration, milestone 456 steps 4-6' (#205) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m52s
CI & Build / Build & push image (push) Successful in 18s
2026-10-05 22:23:48 -04:00
bvandeusen 25c1c778f1 Merge pull request 'Moments: skills teach moments, and a mount that misfires proposes its own removal (milestone 458 steps 8, 7b)' (#204) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 26s
2026-10-05 18:29:53 -04:00
bvandeusen 2b52afcd72 Merge pull request 'Mount the corpus by proposal: a pass and an open-after-moment signal (milestone 458 step 7)' (#203) from dev into main
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 17s
CI & Build / Python tests (push) Successful in 1m59s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Build & push image (push) Successful in 22s
2026-10-05 16:31:42 -04:00
bvandeusen 2db9e0c0c1 Merge pull request 'Skills and processes declare their moments, and the UI for moments (milestone 458 steps 5–6)' (#202) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m9s
CI & Build / Python tests (push) Successful in 1m59s
CI & Build / Build & push image (push) Successful in 19s
2026-10-05 15:41:56 -04:00
bvandeusen 9fc2df080d Merge pull request 'Rules mount on moments, and one retrieval pipeline (milestones 458 steps 1–4, 456 steps 1–3)' (#201) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m0s
CI & Build / Python tests (push) Successful in 1m54s
CI & Build / Build & push image (push) Successful in 19s
2026-10-05 12:41:56 -04:00
bvandeusen 802ead748b Merge pull request 'Rules never-opened advice per corpus (#4798); a lesson's name is one line (#4797)' (#200) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 53s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m45s
CI & Build / Build & push image (push) Successful in 22s
2026-10-03 23:11:18 -04:00
bvandeusen 8b9ff0c5c1 Merge pull request 'Auto-inject looks up records named by number (#4796)' (#199) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m47s
CI & Build / Build & push image (push) Successful in 18s
2026-10-03 22:16:01 -04:00
bvandeusen a19c344770 Merge pull request 'No heading-only chunks, agent-message skip, refusals reach the agent (#4784, #4785, #4794)' (#198) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m14s
CI & Build / Python tests (push) Successful in 2m2s
CI & Build / Build & push image (push) Successful in 18s
2026-10-03 21:47:03 -04:00
bvandeusen c389ed61d2 Merge pull request 'The review drops records written after the call it re-runs (#4773)' (#197) from dev into main
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m59s
CI & Build / integration (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 23s
2026-10-03 20:37:08 -04:00
bvandeusen c50c1beb83 Merge pull request 'A review pass judges whether injected lines related (#4772)' (#196) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / integration (push) Successful in 1m19s
CI & Build / Python tests (push) Successful in 2m6s
CI & Build / Build & push image (push) Successful in 19s
2026-10-03 18:17:49 -04:00
bvandeusen b4c637a7df Merge pull request 'Rulings readout and the prior-art 500 fix' (#195) from dev into main
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 59s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m43s
CI & Build / Build & push image (push) Successful in 20s
2026-10-03 08:57:36 -04:00
bvandeusen a24f6b6b41 Merge pull request 'Rulings reach the work they govern — milestone 444 steps 3-4' (#194) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m11s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 20s
2026-10-03 08:29:25 -04:00
bvandeusen 6477c20664 Merge pull request 'Milestone 444 steps 1-2: an operator's ruling lives on the System it governs, and code is read as behaviour, not intent' (#193) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m46s
CI & Build / Build & push image (push) Successful in 18s
2026-10-02 19:46:50 -04:00
bvandeusen f94ff9e8fc Merge pull request 'The web lesson editor records which rule a lesson is an instance of (milestone 440, #4658)' (#192) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m43s
CI & Build / Build & push image (push) Successful in 17s
2026-10-01 16:54:34 -04:00
bvandeusen 6024230fa9 Merge pull request 'Milestone 440 steps 4, 5, 7: rules surface through their lessons, convergence named at the write, both records show the link' (#191) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m50s
CI & Build / Build & push image (push) Successful in 19s
2026-10-01 16:06:03 -04:00
bvandeusen c08655914f Merge pull request 'Soft links: a lesson and a rule arriving together are proposed as a link (milestone 440 step 3, #4637)' (#190) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 21s
2026-10-01 14:59:30 -04:00
bvandeusen 6ba40c9e10 Merge pull request 'Lessons point at rules (milestone 440 steps 1–2) + the away-from-home usage readout' (#189) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 58s
CI & Build / integration (push) Successful in 57s
CI & Build / Python tests (push) Successful in 1m40s
CI & Build / Build & push image (push) Successful in 21s
2026-10-01 13:48:01 -04:00
bvandeusen d7641e57a8 Merge pull request 'Shapes are judged when they are written (milestone 439 steps 1–6) + #4608 fixes' (#188) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 56s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / Python tests (push) Successful in 1m45s
CI & Build / Build & push image (push) Successful in 17s
2026-10-01 08:55:10 -04:00
bvandeusen 33c0f2c95d Merge pull request 'Server instructions orient the workflow; using-scribe splits into reference files (#4389, #4398)' (#187) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m46s
CI & Build / Build & push image (push) Successful in 14s
2026-09-24 11:30:02 -04:00
bvandeusen 779efb2e74 Merge pull request 'dev → main: MCP SDK v2 port, create-gate thresholds in Settings' (#186) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m38s
CI & Build / Build & push image (push) Successful in 16s
2026-09-24 07:31:51 -04:00
bvandeusen f385d9075b Merge pull request 'dev → main: task claims (milestone 381), embedding model stamp, dedup copy band, sweep-shared.css' (#185) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 13s
2026-09-24 06:57:12 -04:00
bvandeusen eb6912c722 Merge pull request 'Snippets gain notes; when_to_use is the situation it is ranked on (#4378)' (#184) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 54s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m43s
CI & Build / Build & push image (push) Successful in 17s
CI & Build / Plugin hooks (push) Successful in 12s
2026-09-23 18:00:35 -04:00
bvandeusen 14734ebe46 Merge pull request 'dev → main: auto-inject reads the conversation, startup names its failure, menu lines carry metadata, titles are names' (#183) from dev into main
CI & Build / integration (push) Successful in 50s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 17s
2026-09-23 16:57:42 -04:00
bvandeusen bf2871ffb4 Merge pull request 'Shape ledger: a semantic miss is not evidence; floors sized for the reader (#4208, #4306)' (#182) from dev into main
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 18s
2026-09-22 09:50:33 -04:00
bvandeusen 3530ed5e87 Merge pull request 'dev → main: the meaning check reads new shapes first, and its miss withdraws an early flag (#4208)' (#181) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / integration (push) Successful in 50s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
2026-09-22 08:14:50 -04:00
bvandeusen cc791de0e5 Merge pull request 'dev → main: rule overlap check, design-guidance write arm, usage chip seam, divergence meaning gate' (#180) from dev into main
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 51s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 16s
2026-09-22 07:48:53 -04:00
bvandeusen 05a55dc566 Merge pull request #179 from bvandeusen/dev
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
CI & Build / integration (push) Successful in 50s
fix(lessons): a multi-paragraph trigger no longer grows a copy of itself on every edit (#4272)
2026-09-21 16:52:23 -04:00
bvandeusen a928171943 Merge pull request 'fix(systems): System tagging works from whichever door wrote the record, as whoever wrote it (#4249)' (#178) from dev into main
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m43s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / Build & push image (push) Successful in 14s
2026-09-21 16:13:10 -04:00
bvandeusen fa6c4e1ef8 Merge pull request 'fix(telemetry): silence is only an answer where the ledger was watching (#4268)' (#177) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / Python tests (push) Successful in 1m39s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Build & push image (push) Successful in 15s
2026-09-21 15:26:33 -04:00
bvandeusen c1c5debef8 Merge pull request 'fix: a backfill reads text when it embeds, not when it scanned (#4264)' (#176) from dev into main
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Successful in 1m39s
CI & Build / Python lint (push) Successful in 4s
CI & Build / Build & push image (push) Successful in 15s
2026-09-21 14:06:34 -04:00
bvandeusen 888379ada2 Merge pull request 'Retrieval: the passage that matched, every kind searchable, work logs and charters findable' (#175) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m3s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 13s
2026-09-21 12:39:18 -04:00
bvandeusen b2ff93abea Merge pull request 'Rule outcomes, the contract hint, and four extractor/backup fixes' (#174) from dev into main
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m35s
CI & Build / Build & push image (push) Successful in 17s
2026-09-21 06:31:10 -04:00
bvandeusen e2de5ad5f6 Merge pull request #173: the ledger stops asserting on thin evidence, and the agent is the judge
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m3s
CI & Build / Python tests (push) Successful in 1m47s
CI & Build / Build & push image (push) Successful in 14s
2026-09-20 23:07:04 -04:00
bvandeusen 92ffd14462 Merge pull request 'retrieval_telemetry says what is wrong (#3431)' (#172) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 50s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 17s
2026-09-20 21:03:53 -04:00
bvandeusen 8c916da058 Merge pull request 'The hooks need no jq, and the prompt boundary retrieves against prompts' (#171) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 14s
2026-09-20 18:50:03 -04:00
bvandeusen 0c8e109602 Merge pull request 'Lessons: the accretion reflex, and telemetry that can see across projects (#4195, #4196, #3735)' (#170) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m0s
CI & Build / Python tests (push) Successful in 1m43s
CI & Build / Build & push image (push) Successful in 15s
2026-09-19 23:36:31 -04:00
bvandeusen b131e2b04c Merge pull request 'Lessons step 7 — a lesson is readable, writable and browsable by a human (#3734)' (#169) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m35s
CI & Build / Build & push image (push) Successful in 15s
2026-09-19 16:12:39 -04:00
bvandeusen 1135445e3f Merge pull request 'A rule proposal has five answers, and three of them route' (#168) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 50s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 15s
2026-09-19 13:34:51 -04:00
bvandeusen 25d8655ad4 Merge pull request 'Lessons reach the session that needs them, every kind is a full citizen, and a slow disk no longer takes the instance down' (#167) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 48s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m30s
CI & Build / Build & push image (push) Successful in 14s
2026-09-19 12:02:16 -04:00
bvandeusen adeaec88c5 Merge pull request #166 from bvandeusen/dev
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m32s
CI & Build / Build & push image (push) Successful in 13s
Plugin reply shape, preferences UI, cited-record status, and the lesson kind

Merged to ship the plugin: the marketplace checkout tracks main, so
#4153's reply-shape rewrite and #4154's "a record you only mention is a
record to read" could not reach a session from dev. Manifest moves
2026.09.18.0436 -> 2026.09.18.1606, so the executing cache refreshes.

Riding along: #4154 (next_step on milestone summaries, status in the
injected kind marker), #3895 (preferences writable + drift visible), and
milestone 385 steps 2-3 (#3729, #3730) which are inert until a lesson can
be written in step 4.

CI 7023 green on 1361ed7, all six jobs.
2026-09-18 16:33:56 -04:00
bvandeusen 3ac77261a1 Merge pull request #165 from bvandeusen/dev
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 49s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 13s
A recognized retrieval miss has a route, and the record comes first (#4133)

Closes milestone 416 at 9/9. CI 6999 green on 104c1d6.
2026-09-18 00:48:49 -04:00
bvandeusen f324be6b5a Merge pull request 'The wide net becomes a pull, and a tuned number carries the space it was measured in' (#164) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 51s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m41s
CI & Build / Build & push image (push) Successful in 19s
2026-09-17 18:50:50 -04:00
bvandeusen d9e512e9b4 Every ledger is cleared on a compact, and a retrieval floor becomes something the model maintains (#163)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 48s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m32s
CI & Build / Build & push image (push) Successful in 16s
2026-09-17 12:01:42 -04:00
bvandeusen 91cfa8da97 Merge pull request 'A rule must say when it applies; the ledger says what was read, not what was shown' (#162) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 49s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 14s
2026-09-16 17:50:08 -04:00
bvandeusen 8f3f36e6fc Merge pull request 'A directory names its project; a compaction is told what to keep; list rows say what a record is' (#161) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 49s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m32s
CI & Build / Build & push image (push) Successful in 30s
2026-09-16 08:57:45 -04:00
bvandeusen 0ac250194f Merge pull request 'Plan gate default 0.80, and an "Approval requested" reply section' (#160) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Failing after 15s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
2026-09-15 15:23:01 -04:00
bvandeusen c2c92a8421 Merge pull request 'Milestone 415: an existing plan is found before a new one is made' (#159) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m38s
CI & Build / Build & push image (push) Successful in 18s
2026-09-15 14:23:31 -04:00
bvandeusen 790abdb68c Merge pull request 'Milestone 414 steps 1–3: a rule is global or belongs to one project, and retrieval honours which; #3191 read-scope fix' (#158) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m0s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 16s
2026-09-15 12:49:19 -04:00
bvandeusen b0346acd00 Merge pull request 'enter_project becomes a small primer instead of a 222k-character dump' (#157) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 47s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m31s
CI & Build / Build & push image (push) Successful in 15s
2026-09-14 22:12:17 -04:00
bvandeusen 0b52f894d8 Merge pull request 'Reply shapes reach the reply, a Stop hook checks completion reports, and hook pipes keep their output' (#156) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / integration (push) Successful in 48s
CI & Build / TypeScript typecheck (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m42s
CI & Build / Build & push image (push) Successful in 15s
2026-09-14 18:57:06 -04:00
bvandeusen d3375be77f Merge pull request 'Guidance by ownership, placement and report-back, and no more guessed record ids' (#155) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 14s
2026-09-14 14:16:14 -04:00
bvandeusen b7e648226b Merge pull request 'The two new retrieval bars get their Settings controls' (#154) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m29s
CI & Build / Build & push image (push) Successful in 16s
2026-09-11 20:58:50 -04:00
bvandeusen de9431ec39 Merge pull request 'Migration 0099 matched inside words and mangled rows it rewrote' (#153) from dev into main
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 56s
CI & Build / TypeScript typecheck (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m33s
CI & Build / Build & push image (push) Successful in 14s
2026-09-11 20:18:02 -04:00
bvandeusen a552deb689 Merge pull request 'Retire the always-on tier — every rule arrives by retrieval (milestone 394)' (#152) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m29s
CI & Build / Build & push image (push) Successful in 13s
2026-09-11 16:43:20 -04:00
bvandeusen 6da39e0298 Merge pull request 'The prompt-boundary rule arm, and a slot a preference cannot lose (#3852, #3894)' (#151) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m25s
CI & Build / Build & push image (push) Successful in 26s
2026-09-11 11:11:59 -04:00
bvandeusen 5f77fc29c1 Merge pull request 'Preferences — a rule kind that Scribe keeps up to date (#3849 steps 1–3)' (#150) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 58s
CI & Build / integration (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m27s
CI & Build / Build & push image (push) Successful in 24s
2026-09-10 23:34:16 -04:00
bvandeusen d8017f9b96 Merge pull request 'A surfaced rule stops going permanently quiet (#3750, #3751, #3752)' (#149) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 43s
CI & Build / integration (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m18s
CI & Build / Build & push image (push) Successful in 24s
2026-09-09 22:44:28 -04:00
bvandeusen a9fa8bddb6 Merge pull request 'Record WHAT the bar turned away, not only how close it came (#3807)' (#148) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 26s
CI & Build / integration (push) Successful in 47s
CI & Build / Python tests (push) Successful in 1m25s
CI & Build / Build & push image (push) Successful in 24s
2026-09-09 21:34:56 -04:00
bvandeusen cd910e32a0 Merge pull request 'A search that never ran is not a decline' (#147) from dev into main
CI & Build / Python lint (push) Successful in 6s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 42s
CI & Build / integration (push) Successful in 49s
CI & Build / Python tests (push) Successful in 1m22s
CI & Build / Build & push image (push) Successful in 16s
2026-09-09 13:36:43 -04:00
bvandeusen 1c4ace5199 Merge pull request 'write_path stops reporting records it withheld itself as near misses' (#146) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 34s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m8s
CI & Build / Build & push image (push) Successful in 14s
2026-09-09 00:44:44 -04:00
bvandeusen 6495236569 Merge pull request 'A repeat is not a rejection, and a compaction is not knowledge' (#145) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / integration (push) Successful in 42s
CI & Build / Python tests (push) Successful in 1m9s
CI & Build / Build & push image (push) Successful in 15s
2026-09-09 00:13:01 -04:00
bvandeusen 53df6742cb Merge pull request 'Milestone 379 closes: the readout can now say what it did not measure' (#144) from dev into main
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / Python tests (push) Successful in 1m3s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 36s
CI & Build / integration (push) Successful in 39s
CI & Build / Build & push image (push) Successful in 15s
2026-09-08 16:06:14 -04:00
bvandeusen be08edcd7e Merge pull request 'Telemetry coverage, and the rule arms stop filtering to one tier' (#143) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / integration (push) Successful in 34s
CI & Build / TypeScript typecheck (push) Successful in 24s
CI & Build / Python tests (push) Successful in 1m4s
CI & Build / Build & push image (push) Successful in 13s
2026-09-08 11:11:40 -04:00
bvandeusen 4a85220aea Merge pull request 'feat(rules): the rule arms stop filtering the corpus to one tier (#3702)' (#142) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 39s
CI & Build / integration (push) Successful in 40s
CI & Build / Python tests (push) Successful in 1m13s
CI & Build / Build & push image (push) Successful in 19s
2026-09-08 00:31:12 -04:00
bvandeusen 14ff41faf5 Merge pull request 'feat(rules): rule creation becomes propose-then-approve (#3557)' (#141) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 22s
CI & Build / integration (push) Successful in 30s
CI & Build / Python tests (push) Successful in 1m2s
CI & Build / Build & push image (push) Successful in 19s
2026-09-04 22:01:47 -04:00
bvandeusen 67df41ae00 Merge pull request 'feat(rules): the instruction surfaces say to RETRIEVE a rule, not only to receive one (#3523)' (#140) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 32s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / TypeScript typecheck (push) Successful in 3m18s
CI & Build / Build & push image (push) Successful in 16s
2026-09-03 22:07:10 -04:00
bvandeusen 0915c48bb0 Merge pull request 'feat(telemetry): tell a ranker decline from a repeat before the observation window opens (#3497)' (#139) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 30s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / TypeScript typecheck (push) Successful in 5m20s
CI & Build / Build & push image (push) Successful in 17s
2026-09-03 21:23:01 -04:00
bvandeusen aea7b63b62 Merge pull request 'fix(telemetry): both rule arms logged only their hits, so the clear-rate could only read 100% (#3497)' (#138) from dev into main
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 30s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 17s
2026-09-03 07:19:08 -04:00
bvandeusen 5b02908dfd dev → main: rules become measurable at the preload, and retrievable at the tool call (#137)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 17s
2026-09-02 23:41:09 -04:00
bvandeusen 34cd389371 dev → main: rule usage telemetry, the plugin's derived version (#136)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 23s
CI & Build / integration (push) Successful in 30s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 16s
2026-09-02 18:52:20 -04:00
bvandeusen b267037911 Two milestones: a note can carry its own check (317), and a rule keeps what it used to say (323) (#135)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 38s
CI & Build / integration (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Successful in 29s
2026-08-31 00:01:15 -04:00
bvandeusen 93d660b710 Tool disambiguators, kind badges, and a badge layer that clears AA (#3123, #3124, #3132)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 31s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m17s
CI & Build / Build & push image (push) Successful in 15s
2026-08-27 21:28:48 -04:00
bvandeusen 056c7c75da A task's kind is correctable — the Kind select stops lying (#3129)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 15s
2026-08-27 18:05:16 -04:00
8 changed files with 9 additions and 259 deletions
+2 -9
View File
@@ -41,13 +41,6 @@ row carries a status:
nothing. Rows, never prose — a consumer list in a note or verification
detail cannot be sorted, queried, or diffed.
Rows are keyed by repo and path, so a move in the tree is a move in the
ledger. When a file or directory moves, run `move_shapes(project_id,
from_path, to_path)` before the next refresh. For a repo renamed on its
forge, run `bind_repo(new_url, project_id, moved_from=old_url)`. Otherwise
the refresh marks every judged row vanished and lists the same shapes again,
unjudged.
## The writer judges; audits check that it happened
The ledger is filled from your answers, given while you still know them. When
@@ -164,8 +157,8 @@ Three questions the ledger answers mechanically:
it), `variant` with the why if the departure is deliberate. The write-path
hint asks the same question in-band the moment such a shape is written.
- **History** — `shape_history(project_id, path, symbol?)`: the current rows
plus every `classified` / `vanished` / `reappeared` / `drifted` / `moved`
event with its commit — "instance of #N from <date>, re-judged variant of #M because
plus every `classified` / `vanished` / `reappeared` / `drifted` event with
its commit — "instance of #N from <date>, re-judged variant of #M because
R, vanished at C". Rows, not recollection.
- **Recheck** — `list_shapes(project_id, flag="recheck")`: judged
instances/variants whose body changed since judged. The judgment stands;
+1 -1
View File
@@ -200,7 +200,7 @@ _WRITE_TOOLS = frozenset({
"merge_snippets", "unmerge_snippet",
"create_process", "update_process", "delete_process",
"classify_shapes", "classify_shapes_by_rule", "confirm_shape_proposals",
"refresh_pattern_coverage", "move_shapes",
"refresh_pattern_coverage",
# design systems
"create_design_system", "update_design_system", "delete_design_system",
"create_design_token", "update_design_token", "delete_design_token",
+2 -31
View File
@@ -263,8 +263,7 @@ async def shape_history(
snippet_id, who, why — one per judgment, so a shape that was an instance
of #N and later a variant of #M shows both), `vanished`, `reappeared`,
`drifted` (the body moved under a judgment; see list_shapes flag=
"recheck"), `moved` (move_shapes carried the row; the event's `path`
is the one it left). Each event carries the commit the tree was read at.
"recheck"). Each event carries the commit the tree was read at.
Read it as a timeline: "instance of #N from <first classified at>,
re-judged variant of #M at <at> because <reason>, vanished at <commit>".
@@ -399,38 +398,10 @@ async def refresh_pattern_coverage(project_id: int) -> dict:
}
async def move_shapes(project_id: int, from_path: str, to_path: str) -> dict:
"""Carry the judgments of a file or directory that moved in the tree.
Rows are keyed by path. After `git mv src/old src/new`, the next coverage
refresh would mark every row under src/old vanished and list the same
shapes again under src/new, unclassified, with every judgment left
behind. Run this once the move is committed, before that refresh:
each row under `from_path` is re-keyed to the same place under
`to_path`, in every repo bound to the project, keeping its judgment,
uses edges and history, and gaining a `moved` event (shape_history).
A row already at the new path is replaced when nobody judged it (the
refresh ran first, or the sync stamped it `scoped`); a judged one stands,
and the old row is left behind and listed in `kept`. The refresh that
follows compares bodies as always: a judged shape whose body changed in
the move comes back flagged `recheck`.
Args:
project_id: The project whose ledger moves. Requires write access.
from_path: The file or directory the shapes were found at.
to_path: Where it lives now. Neither path may contain the other.
Returns {"moved": N, "replaced": N, "kept": [{path, symbol, kind}]}.
"""
uid = current_user_id()
return await shape_ledger_svc.move_shapes(uid, project_id, from_path, to_path)
def register(mcp) -> None:
for fn in (
classify_shapes, classify_shapes_by_rule, list_shapes,
refresh_pattern_coverage, confirm_shape_proposals, shape_history,
stamps_to_review, move_shapes,
stamps_to_review,
):
mcp.tool(name=fn.__name__)(fn)
+3 -4
View File
@@ -311,9 +311,8 @@ class CodeShapeConsumer(Base):
# What a shape's history records. Not "appeared" — first_seen and
# created_at already say that on the row; history is for what CHANGED.
# A `moved` event carries the path the shape left; the row says where it went.
SHAPE_EVENTS = ("classified", "vanished", "reappeared", "drifted", "moved")
# created_at already say that on the row; history is for what CHANGED:
SHAPE_EVENTS = ("classified", "vanished", "reappeared", "drifted")
class CodeShapeEvent(Base):
@@ -322,7 +321,7 @@ class CodeShapeEvent(Base):
"We used #N here from <date>, #M replaced it at commit C, reason R" is a
question the ledger row alone cannot answer once it has moved on; this
table keeps each judgment (status, snippet, who, why, at which commit)
and each presence change (vanished / reappeared / drifted / moved) as it
and each presence change (vanished / reappeared / drifted) as it
happened. Denormalised path/symbol/kind so a directory's history reads
without joining; `snippet_id` is deliberately FK-free — history outlives
the snippet it names, which is the point.
-79
View File
@@ -170,85 +170,6 @@ async def sync_repo_shapes(
await session.commit()
def _under(path: str, prefix: str) -> bool:
return path == prefix or path.startswith(prefix + "/")
async def move_shapes(
user_id: int, project_id: int, from_path: str, to_path: str,
) -> dict:
"""Carry the ledger rows under ``from_path`` (a file or a directory) to
the same place under ``to_path``, in every repo of the project.
Rows are keyed by path, so a directory moved in the tree would otherwise
vanish every judged row at the next sync and start the same shapes again,
unclassified, under the new path. A moved row is the same row — its id,
judgment, uses edges and history ride along — and a `moved` event
records the path it left. A row already at the target (the sync ran
first, or a provisional judgment landed there) is replaced when nobody
judged it; a judged one stands, and the old row is left where it is and
reported in ``kept``. Bodies are not compared here: the next sync does
that as always, and asks for a recheck where a judged body changed.
"""
from sqlalchemy import or_
from grimoire.services import access
old = (from_path or "").strip().strip("/")
new = (to_path or "").strip().strip("/")
if not old or not new:
raise ValueError("from_path and to_path are both required")
if _under(old, new) or _under(new, old):
raise ValueError(f"`{old}` and `{new}` overlap; move to a path outside the source")
if not await access.can_write_project(user_id, project_id):
raise ValueError(f"project {project_id} not found or no write access")
now = datetime.now(timezone.utc)
async with async_session() as session:
def beneath(prefix: str):
return select(CodeShape).where(
CodeShape.project_id == project_id,
or_(CodeShape.path == prefix, CodeShape.path.like(prefix + "/%")),
)
# LIKE reads `_` as a wildcard; the prefix test is the real filter.
sources = [
r for r in (await session.execute(beneath(old))).scalars().all()
if _under(r.path, old)
]
if not sources:
raise ValueError(f"project {project_id} has no shapes under `{old}`")
targets = {
(r.repo_key, r.path, r.symbol, r.kind): r
for r in (await session.execute(beneath(new))).scalars().all()
}
moved, replaced, kept = 0, 0, []
for row in sources:
dest = new + row.path[len(old):]
there = targets.get((row.repo_key, dest, row.symbol, row.kind))
if there is not None:
if there.status not in _MECHANICAL_TODO:
kept.append({"path": row.path, "symbol": row.symbol, "kind": row.kind})
continue
# The target is the live sighting when the sync got there
# first; the moved row takes over its presence.
row.vanished_at = there.vanished_at
row.last_seen_commit = there.last_seen_commit or row.last_seen_commit
await session.delete(there)
await session.flush()
replaced += 1
session.add(CodeShapeEvent(
shape_id=row.id, project_id=project_id,
path=row.path, symbol=row.symbol, kind=row.kind,
event="moved", status=row.status, snippet_id=row.snippet_id,
classified_by=row.classified_by, reason=row.reason,
commit=row.last_seen_commit or "", at=now,
))
row.path = dest
moved += 1
await session.commit()
return {"moved": moved, "replaced": replaced, "kept": kept}
def _event(row: CodeShape, event: str, at: datetime, *, commit: str = "") -> CodeShapeEvent:
"""A history row for a state change on ``row`` — status/snippet/by/reason
are the row's CURRENT values, which for `classified` is the judgment
-126
View File
@@ -1,126 +0,0 @@
"""Real-Postgres integration tests for moving a directory's shape judgments.
Ledger rows are keyed by path. A directory moved in the tree has to carry its
rows to the new path, or the next sync vanishes every judgment and lists the
same shapes again, unjudged.
"""
import pytest
import pytest_asyncio
from sqlalchemy import select
from grimoire.models import async_session
from grimoire.models.code_shape import CodeShape, CodeShapeEvent
from grimoire.models.project import Project
from grimoire.services.shape_ledger import classify_shapes, move_shapes, sync_repo_shapes
from tests.helpers import ensure_user
pytestmark = [pytest.mark.integration, pytest.mark.usefixtures("_dispose_engine")]
REPO = "git.example.com/alice/widget"
OLD_TREE = [
("src/old/app.py", "sym", "make_app"),
("src/old/sub/util.py", "sym", "helper"),
("src/older/keep.py", "sym", "untouched"),
]
NEW_TREE = [
("src/new/app.py", "sym", "make_app"),
("src/new/sub/util.py", "sym", "helper"),
("src/older/keep.py", "sym", "untouched"),
]
@pytest_asyncio.fixture
async def seeded():
"""A project whose ledger was synced before the move, one shape judged."""
async with async_session() as s:
owner = await ensure_user(s, "shape_move_owner")
other = await ensure_user(s, "shape_move_other")
project = Project(user_id=owner.id, title="Moved directory")
s.add(project)
await s.flush()
ids = {"owner": owner.id, "other": other.id, "pid": project.id}
await s.commit()
await sync_repo_shapes(ids["pid"], REPO, OLD_TREE, seen_marker="c1")
await classify_shapes(ids["owner"], ids["pid"], [
{"path": "src/old/sub/util.py", "symbol": "helper", "status": "exempt",
"reason": "one caller"},
])
return ids
async def _ledger(pid: int) -> dict[tuple[str, str], tuple[str, bool]]:
async with async_session() as s:
rows = await s.execute(select(CodeShape).where(CodeShape.project_id == pid))
return {
(r.path, r.symbol): (r.status, r.vanished_at is None)
for r in rows.scalars()
}
@pytest.mark.integration
async def test_the_judgments_move_and_the_next_sync_finds_them(seeded):
pid = seeded["pid"]
result = await move_shapes(seeded["owner"], pid, "src/old/", "src/new")
assert result == {"moved": 2, "replaced": 0, "kept": []}
await sync_repo_shapes(pid, REPO, NEW_TREE, seen_marker="c2")
assert await _ledger(pid) == {
("src/new/app.py", "make_app"): ("unclassified", True),
("src/new/sub/util.py", "helper"): ("exempt", True),
("src/older/keep.py", "untouched"): ("unclassified", True),
}
async with async_session() as s:
moved = (await s.execute(
select(CodeShapeEvent.path).where(
CodeShapeEvent.project_id == pid, CodeShapeEvent.event == "moved",
)
)).scalars().all()
assert sorted(moved) == ["src/old/app.py", "src/old/sub/util.py"]
@pytest.mark.integration
async def test_after_the_sync_got_there_first_the_judgment_replaces_the_fresh_row(seeded):
pid = seeded["pid"]
await sync_repo_shapes(pid, REPO, NEW_TREE, seen_marker="c2")
result = await move_shapes(seeded["owner"], pid, "src/old", "src/new")
assert result == {"moved": 2, "replaced": 2, "kept": []}
assert await _ledger(pid) == {
("src/new/app.py", "make_app"): ("unclassified", True),
("src/new/sub/util.py", "helper"): ("exempt", True),
("src/older/keep.py", "untouched"): ("unclassified", True),
}
@pytest.mark.integration
async def test_a_judgment_already_at_the_new_path_stands(seeded):
pid = seeded["pid"]
await sync_repo_shapes(pid, REPO, OLD_TREE + NEW_TREE, seen_marker="c2")
await classify_shapes(seeded["owner"], pid, [
{"path": "src/new/sub/util.py", "symbol": "helper", "status": "exempt",
"reason": "judged again after the move"},
])
result = await move_shapes(seeded["owner"], pid, "src/old", "src/new")
assert result["moved"] == 1 and result["replaced"] == 1
assert result["kept"] == [
{"path": "src/old/sub/util.py", "symbol": "helper", "kind": "sym"},
]
ledger = await _ledger(pid)
assert ledger[("src/new/sub/util.py", "helper")] == ("exempt", True)
assert ("src/old/sub/util.py", "helper") in ledger
@pytest.mark.integration
async def test_overlapping_empty_and_unwritable_moves_are_refused(seeded):
owner, other, pid = seeded["owner"], seeded["other"], seeded["pid"]
with pytest.raises(ValueError, match="overlap"):
await move_shapes(owner, pid, "src/old", "src/old/inner")
with pytest.raises(ValueError, match="no shapes under"):
await move_shapes(owner, pid, "src/gone", "src/new")
with pytest.raises(ValueError, match="no write access"):
await move_shapes(other, pid, "src/old", "src/new")
assert ("src/old/app.py", "make_app") in await _ledger(pid)
-8
View File
@@ -383,14 +383,6 @@ def test_bind_repo_tool_can_move_a_renamed_repo():
assert "moved_from" in tool.parameters.get("properties", {})
def test_move_shapes_is_mounted_as_a_write_tool():
"""A directory moved in the tree carries its judgments to the new path."""
from grimoire.mcp.server import _WRITE_TOOLS, build_mcp_server
tool = build_mcp_server()._tool_manager.get_tool("move_shapes")
assert {"from_path", "to_path"} <= set(tool.parameters.get("properties", {}))
assert "move_shapes" in _WRITE_TOOLS
def test_coverage_routes_are_registered():
from grimoire.app import create_app
+1 -1
View File
@@ -642,5 +642,5 @@ def test_history_and_divergence_columns_are_pinned():
fk = next(iter(ev.c.shape_id.foreign_keys))
assert fk.ondelete == "CASCADE" and fk.column.table.name == "code_shapes"
assert not ev.c.snippet_id.foreign_keys # history outlives the snippet
assert SHAPE_EVENTS == ("classified", "vanished", "reappeared", "drifted", "moved")
assert SHAPE_EVENTS == ("classified", "vanished", "reappeared", "drifted")
assert "code_shape_events" in Base.metadata.tables