Three commits. CI run 7069 green on all six jobs at 97867d4, including the integration lane that applies migration 0105 against a real database.
26a757e — a lesson is yours to keep current too (#4195)
The lesson kind shipped with every mechanism for growing and nothing telling a session to use them: learned_from is a list on purpose, the dedup gate hands back an existing id rather than minting a twin, and update_lesson already names re-keying a bad trigger as the edit that pays most.
The exclusivity claim was the bug. The skill said "a preference is the one record you keep current yourself" — an active claim that no other kind qualifies, not an omission elsewhere. It now reads "a preference is yours to keep current", beside a new paragraph on what growing a lesson means. get_lesson carries the same prompt at the moment it bites. Written as a practice, not a prohibition (rule 165).
The guidance-ownership registry gains the topic and re-points the preference topic's statement, since the phrase it pinned is the sentence this rewrites. No index marker — _INSTRUCTIONS stays at 1998/2000, and this reflex fires mid-work rather than at session start.
a4aae97 — a usage event records which project the reader was in (#4196, #3735)
RetrievalLog has always carried project_id, so "surfaced on project B" was answerable. note_usage_events had none, so "opened on project B" was not — and the two cannot be joined, because there is deliberately no session identity server-side.
That gap sat exactly on milestone 385's acceptance: a lesson's claim is that it reaches a session on a project it was not written on, and step 8 asks for retrieved-elsewhere and opened. Each half was answerable; the conjunction was not.
Which project, because the wrong reading makes the column useless: the project the reader was in, never the record's own. The surfacing half is free — every arm already holds the scope it just searched. The pull half needs the caller, so the five single-record getters take project_id: int = 0 and pass it through. process_skill_sync is exempt and named as such: it installs every Process the operator can reach, which is not a project-scoped question.
Guarded by source inspection the way #2245 and #2476 taught — a parameter never threaded through changes no return value and shows up only as a column that is mysteriously always null.
97867d4 — the reading project survives a backup (#4196)
Run 7068 caught a real bug before it shipped. note_usage_events excludes only id from backup, so telemetry is exported, and a new column the serialiser never learned about would have restored as null on every row — #3182's exact failure, which is how nine columns vanished from notes.
So the column is exported, and the importer re-maps it and degrades rather than skipping: unlike a shape event, a usage event without a project is still a real pull, and dropping it would deflate the pull-through the table exists to report.
BACKUP_VERSION deliberately unchanged — the suite states that column additions do not bump it, only new sections do.
Also adds an import-side guard. The column family checks the export side only, so a column can be exported faithfully and dropped coming back in. No general guard for that direction exists yet; this covers the column added here and names the gap.
Milestone 385 is 9/11. #3735 stays open: it is now instrumented, but needs sessions to accumulate under this instrumentation before it can be read. #4196 is a spike result with two of its four questions answered.
Three commits. CI run 7069 green on all six jobs at `97867d4`, including the integration lane that applies migration `0105` against a real database.
## `26a757e` — a lesson is yours to keep current too (#4195)
The lesson kind shipped with every mechanism for growing and nothing telling a session to use them: `learned_from` is a list on purpose, the dedup gate hands back an existing id rather than minting a twin, and `update_lesson` already names re-keying a bad trigger as the edit that pays most.
**The exclusivity claim was the bug.** The skill said *"a preference is the one record you keep current yourself"* — an active claim that no other kind qualifies, not an omission elsewhere. It now reads *"a preference is yours to keep current"*, beside a new paragraph on what growing a lesson means. `get_lesson` carries the same prompt at the moment it bites. Written as a practice, not a prohibition (rule 165).
The guidance-ownership registry gains the topic and re-points the preference topic's statement, since the phrase it pinned is the sentence this rewrites. No index marker — `_INSTRUCTIONS` stays at 1998/2000, and this reflex fires mid-work rather than at session start.
## `a4aae97` — a usage event records which project the reader was in (#4196, #3735)
`RetrievalLog` has always carried `project_id`, so *"surfaced on project B"* was answerable. `note_usage_events` had none, so *"opened on project B"* was not — and the two cannot be joined, because there is deliberately no session identity server-side.
That gap sat exactly on milestone 385's acceptance: a lesson's claim is that it reaches a session on a project it was **not** written on, and step 8 asks for retrieved-elsewhere **and** opened. Each half was answerable; the conjunction was not.
**Which project**, because the wrong reading makes the column useless: the project the *reader* was in, never the record's own. The surfacing half is free — every arm already holds the scope it just searched. The pull half needs the caller, so the five single-record getters take `project_id: int = 0` and pass it through. `process_skill_sync` is exempt and named as such: it installs every Process the operator can reach, which is not a project-scoped question.
Guarded by source inspection the way #2245 and #2476 taught — a parameter never threaded through changes no return value and shows up only as a column that is mysteriously always null.
## `97867d4` — the reading project survives a backup (#4196)
Run 7068 caught a real bug before it shipped. `note_usage_events` excludes only `id` from backup, so telemetry **is** exported, and a new column the serialiser never learned about would have restored as null on every row — #3182's exact failure, which is how nine columns vanished from `notes`.
So the column is exported, and the importer re-maps it and **degrades rather than skipping**: unlike a shape event, a usage event without a project is still a real pull, and dropping it would deflate the pull-through the table exists to report.
`BACKUP_VERSION` deliberately unchanged — the suite states that column additions do not bump it, only new sections do.
Also adds an import-side guard. The column family checks the **export** side only, so a column can be exported faithfully and dropped coming back in. No general guard for that direction exists yet; this covers the column added here and names the gap.
---
Milestone 385 is 9/11. #3735 stays open: it is now instrumented, but needs sessions to accumulate **under** this instrumentation before it can be read. #4196 is a spike result with two of its four questions answered.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
The lesson kind shipped with every mechanism for growing and nothing
telling a session to use them. `learned_from` is a list on purpose, the
dedup gate hands back an existing id rather than minting a twin, and
`update_lesson` already names re-keying a bad trigger as the edit that
pays most. None of that was reachable as a habit.
The exclusivity claim was the bug. The skill said "a preference is the
one record you keep current yourself", and by naming only preferences it
put lessons outside the habit. That sentence is now "a preference is
yours to keep current", which says the same thing about preferences
without saying anything false about lessons.
Beside it, a paragraph on what growing a lesson means: another incident
added to what taught it, a claim stated more exactly, or a trigger
re-keyed to the situation that really fired. Written as a practice
rather than a prohibition (rule 165) — the reader is named as the one
person placed to judge the trigger, because they are standing in the
situation it claims to name.
`get_lesson` carries the same prompt at the moment it bites: a session
reading a lesson inside the situation it names is the only reader who
can tell whether the trigger is keyed to what actually fired.
The guidance-ownership registry gains the topic and re-points the
preference topic's statement, since the phrase it pinned is the sentence
this change rewrites — the module asks for exactly that, in the same
commit. No index marker: the index names session-start reflexes and this
one fires mid-work, so `_INSTRUCTIONS` stays at 1998/2000.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
`RetrievalLog` has carried `project_id` since it existed, so "this record
was SURFACED on project B" was always answerable. `note_usage_events`
had none, so "this record was OPENED on project B" was not — and the two
cannot be joined to recover it, because there is deliberately no session
identity server-side. NoteUsageEvent's own docstring rules that out.
That gap sat exactly on the question milestone 385 exists to answer. A
lesson's whole claim is that it reaches a session on a project it was not
written on, and step 8's acceptance is "retrieved on a different project
AND opened". Each half was answerable; the conjunction was not.
WHICH project, because the name is ambiguous and the wrong reading makes
the column useless: it is the project the READER was in, never the one
the record belongs to. The record's own project is already on the note;
copying it here would answer a question nobody asked while looking like
it answered this one.
The surfacing half is free — every arm already holds the scope it just
searched, so auto_inject, lesson_slot, the write-path arms and
enter_project now record it. process_skill_sync does not and should not:
it installs every Process the operator can reach, which is not a
project-scoped question, so a project there would be a fiction.
The pull half needs the caller, since a getter knows only what it was
handed. The five single-record getters take `project_id: int = 0` and
pass it through, following the convention `search` and `create_*`
already set. Null stays an ordinary answer meaning "not reported" — a
pull with no project is still a pull and still counts toward dead
weight; it simply cannot speak to transfer. The four REST detail views
report none for now: a human opening a record in a browser is a
different event from an agent recalling one, and #2245 left that
asymmetry deliberately undecided.
Guarded the way #2245 and #2476 taught: by source inspection, because a
parameter that was never threaded through changes no return value and
shows up only as a column that is mysteriously always null. Three
guards — the signature, the pass-through, and the arms — plus the
can-fail test rule 167 asks for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Run 7068 failed two tests on a4aae97, and the second was a real bug the
guard caught before it shipped.
THE COLUMN GUARD (#3182) was right. `note_usage_events` excludes only
`id` from backup, so telemetry IS exported — and a new column that the
serialiser never learned about would have restored as null on every row.
That is the exact failure #3182 was built from, where nine columns
vanished from `notes` the same way: added to the model and the migration,
which fail loudly, and never to the serialiser, which fails silently.
So `project_id` is exported, not excluded, via the one `_usage_event_rows`
builder both exporters share.
The importer re-maps it through `project_id_map` and DEGRADES rather than
skipping. `code_shape_events` drops a row whose project will not map,
because a shape event without its project says nothing; a usage event is
not like that. The project is optional by design and null already means
"not reported", so an unmappable one restores as unreported. Dropping it
would lose a real pull and deflate the pull-through this table exists to
report.
BACKUP_VERSION deliberately unchanged: the suite states that column
additions do not bump it, only new sections do, and a third test asserts
the current number.
The second failure was mine and smaller — test_record_pulled_writes_a_
single_row compares exact dicts, so it now expects the null project. I
had claimed these assertions were key-based; they are not, and I had
read a summary line rather than the assertion.
Added an import-side guard while here. The column family checks the
EXPORT side only, so a column can be exported faithfully and dropped on
the way back in — a restore that reports success and has quietly lost a
dimension. No general guard for that direction exists yet; this covers
the column added here and names the gap for whoever builds the general
one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Three commits. CI run 7069 green on all six jobs at
97867d4, including the integration lane that applies migration0105against a real database.26a757e— a lesson is yours to keep current too (#4195)The lesson kind shipped with every mechanism for growing and nothing telling a session to use them:
learned_fromis a list on purpose, the dedup gate hands back an existing id rather than minting a twin, andupdate_lessonalready names re-keying a bad trigger as the edit that pays most.The exclusivity claim was the bug. The skill said "a preference is the one record you keep current yourself" — an active claim that no other kind qualifies, not an omission elsewhere. It now reads "a preference is yours to keep current", beside a new paragraph on what growing a lesson means.
get_lessoncarries the same prompt at the moment it bites. Written as a practice, not a prohibition (rule 165).The guidance-ownership registry gains the topic and re-points the preference topic's statement, since the phrase it pinned is the sentence this rewrites. No index marker —
_INSTRUCTIONSstays at 1998/2000, and this reflex fires mid-work rather than at session start.a4aae97— a usage event records which project the reader was in (#4196, #3735)RetrievalLoghas always carriedproject_id, so "surfaced on project B" was answerable.note_usage_eventshad none, so "opened on project B" was not — and the two cannot be joined, because there is deliberately no session identity server-side.That gap sat exactly on milestone 385's acceptance: a lesson's claim is that it reaches a session on a project it was not written on, and step 8 asks for retrieved-elsewhere and opened. Each half was answerable; the conjunction was not.
Which project, because the wrong reading makes the column useless: the project the reader was in, never the record's own. The surfacing half is free — every arm already holds the scope it just searched. The pull half needs the caller, so the five single-record getters take
project_id: int = 0and pass it through.process_skill_syncis exempt and named as such: it installs every Process the operator can reach, which is not a project-scoped question.Guarded by source inspection the way #2245 and #2476 taught — a parameter never threaded through changes no return value and shows up only as a column that is mysteriously always null.
97867d4— the reading project survives a backup (#4196)Run 7068 caught a real bug before it shipped.
note_usage_eventsexcludes onlyidfrom backup, so telemetry is exported, and a new column the serialiser never learned about would have restored as null on every row — #3182's exact failure, which is how nine columns vanished fromnotes.So the column is exported, and the importer re-maps it and degrades rather than skipping: unlike a shape event, a usage event without a project is still a real pull, and dropping it would deflate the pull-through the table exists to report.
BACKUP_VERSIONdeliberately unchanged — the suite states that column additions do not bump it, only new sections do.Also adds an import-side guard. The column family checks the export side only, so a column can be exported faithfully and dropped coming back in. No general guard for that direction exists yet; this covers the column added here and names the gap.
Milestone 385 is 9/11. #3735 stays open: it is now instrumented, but needs sessions to accumulate under this instrumentation before it can be read. #4196 is a spike result with two of its four questions answered.
🤖 Generated with Claude Code
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy