A new elaborator session draws its lens and authors its instruction text
once, and the checkpoint row carries both from then on. A resume reads the
row's session instead of demanding the fresh derivation equal it.
Session:
- `ElaboratorSession` gains `lens: Lens` and `instructions: String`
(`deny_unknown_fields` kept).
- `select_answer` derives the command id from world, jurisdiction and
answer digest, draws `lens::draw(snapshot.lens_weights, world, id)`, and
authors `lens.instructions()`. It returns the id with the session, and
`step` uses that id instead of deriving it a second time.
- `session_command_id` takes the three identity fields instead of a session,
because the lens is drawn from the id and so the id exists before the
session does. The derivation bytes are unchanged: the Cut 0 id
`56dbcdb2-9337-ac07-f322-37376b58cbcb` is pinned in
`a_session_identity_is_derived_from_its_answer`.
- `ELABORATION_INSTRUCTIONS` moves into `lens.rs` as a private constant.
`elaboration_request` takes the text, and `step`, `persist_repair` and
`integrity_is_valid` pass `session.instructions`. Integrity binds the
stored invocation to the stored text, not to code (L1-Q7 (a)).
Adoption rule (L1-Q9, boundary half): the whole-session supersession
equality in `step` is deleted. A found row is this answer's row when world,
jurisdiction, answer and answer digest match (`same_answer`). Its session,
including ancestry, lens and text, replaces the fresh one. A boundary session
is no longer `Superseded` forever after an unrelated commit.
`valid_elaboration_progression` is unchanged and still compares whole
sessions row to row; the doc comments on both rules say why they differ.
Store: `controller_work.v15` -> `v16`; v15 rows are refused at open, not
migrated. The world ontology doc names v16.
Tests (controllers): `a_boundary_session_resumes_after_an_unrelated_commit`
(at 1a402e1 it reported Superseded),
`a_resumed_session_keeps_its_recorded_lens_when_the_weights_change`,
`a_checkpoint_that_changes_the_lens_or_its_text_is_an_illegal_transition`,
`a_row_whose_text_and_invocation_disagree_is_refused_at_open`,
`a_controller_work_row_from_v15_is_refused_at_open`,
`soul_the_same_patch_admits_the_same_under_every_lens` (pinned to the
Cut 0 mismatch JSON), and `the_catalog_takes_no_lens`. The single-dead-end
fixture now delegates to `dead_end_mailbox(weights, dead_ends)`, which builds
the first dead end with the same handles and labels as before.
Builds, but with two "this lint expectation is unfulfilled" warnings from
the `expect(dead_code)` markers on `Lens::instructions` and `lens::draw`. The
next commit removes those markers (c1.s1.f3).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>