Milestone 415 "An existing plan is found before a new one is made — milestones without steps count as open work", plus the #4081 planning payload fix.
#4081 "start_planning reply was 92k chars": planning reads now list applicable rules by id and title instead of restating them.
#4076 "unplanned milestones":enter_project lists active milestones with no steps as unplanned_milestones.
#4077 "board collapse": the No Milestone group collapses, and every group's Done column starts folded. Checked by the operator.
#4078 "milestone search": milestones are searchable by meaning via search(content_type="milestone"). New milestone_embeddings table (migration 0102) with a startup backfill.
#4079 "the plan gate":start_planning and create_milestone return the active milestone that already has this title, or reads as the same plan, instead of creating a second one. force=true bypasses it. The new Settings field "Existing-plan match threshold" defaults to 0.90.
#4080 "the instructions": the writing-plans and using-scribe skills, tool descriptions and _INSTRUCTIONS tell sessions to find the existing plan first and file related work into it. Plugin version 2026.09.15.1744.
CI run 6912 on fb36599 passed: lint, typecheck, unit, integration, plugin hooks and image build.
Milestone 415 "An existing plan is found before a new one is made — milestones without steps count as open work", plus the #4081 planning payload fix.
- **#4081 "start_planning reply was 92k chars":** planning reads now list applicable rules by id and title instead of restating them.
- **#4076 "unplanned milestones":** `enter_project` lists active milestones with no steps as `unplanned_milestones`.
- **#4077 "board collapse":** the No Milestone group collapses, and every group's Done column starts folded. Checked by the operator.
- **#4078 "milestone search":** milestones are searchable by meaning via `search(content_type="milestone")`. New `milestone_embeddings` table (migration 0102) with a startup backfill.
- **#4079 "the plan gate":** `start_planning` and `create_milestone` return the active milestone that already has this title, or reads as the same plan, instead of creating a second one. `force=true` bypasses it. The new Settings field "Existing-plan match threshold" defaults to 0.90.
- **#4080 "the instructions":** the writing-plans and using-scribe skills, tool descriptions and `_INSTRUCTIONS` tell sessions to find the existing plan first and file related work into it. Plugin version 2026.09.15.1744.
CI run 6912 on fb36599 passed: lint, typecheck, unit, integration, plugin hooks and image build.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
start_planning on project 2 replied with 92,645 characters, 65k of them
applicable_rules. Milestone 414 made a project's listing every global rule
tagged to an area it works in (before, the rules of subscribed rulebooks, and
project 2 subscribed to none), and the non-brief rules_payload sent each as a
full rule_brief. get_milestone, get_project and get_task carried the same.
Every rules_payload form now lists: id, title, the topic a global rule sits in,
and `via` for a co_surfaces partner. get_rule reads one in full, and retrieval
delivers them in full when work matches — the reasoning #4045 applied to the
handshake. A test pins 81 full-length rules under 6k characters.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
A plan written as a milestone with a description and no steps was invisible to
the session handshake: it lists the 5 most recently touched milestones (#4045),
and touching is a step changing, so a step-less milestone can never qualify.
FabledLibrarian's roadmap (nine such milestones) sat unseen while later plans
were opened as new milestones beside the ones that already described them.
enter_project adds `unplanned_milestones`: active milestones with no steps, in
roadmap order, id/title/description, up to 10 with an omitted count, none
repeated from the recent list, and absent when there are none. The docstring
says what they are for: check them before starting a new milestone, and add
steps to a match with create_records(milestone_id=...).
Milestone 415 step 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Operator, 2026-09-15: the unmilestoned group should collapse, "especially the
done column as it currently consume a lot of vertical space for [work] that's
already done."
- The "No Milestone" group gets the milestones' chevron and collapse, keyed as
0 in the same Set (no milestone id is 0), and starts collapsed on first load
when everything in it is finished — the rule finished milestones already
follow.
- Each group's Done column header becomes a button that folds its cards, with
the count still showing and aria-expanded set. Folded by default: done work is
the part of a board nobody is reading.
- Not persisted, matching the milestone collapse beside it.
Milestone 415 step 2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
`search` covered notes, tasks and rules, and a milestone — the record a plan
lives in — could not be found. A project whose roadmap was written as
milestones had every later plan opened beside the one that already described
it, because nothing could have told the session it existed.
- milestone_embeddings (migration 0102): the third sibling of note_ and
rule_embeddings, for note 3163's reason — the search is milestone-specific.
The document is title — description, then description and the plan body,
so a roadmap milestone with no description is still found by its design.
- Written on create, on a title/description/body update, and for a plan made
through start_planning / create_records, fire-and-forget with the parent-row
claim (#3262); a startup backfill covers every existing milestone. Derived,
so it joins _NOT_INCLUDED beside the other embeddings.
- semantic_search_milestones: a project's milestones when the caller can read
it (access.can_read_project), otherwise the caller's own; optional status.
- search(content_type="milestone"): id, title, description, status, project
and progress. Its own shape, and not part of "all", whose results are
note-shaped. The docstring says what it is for: ask before start_planning.
- Integration test on real Postgres: found in its project and not another,
status narrows, an unreadable project returns nothing.
Milestone 415 step 3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Step 4 of milestone 415 "An existing plan is found before a new one is made".
A session that could not see an existing plan made a second one beside it.
start_planning and create_milestone now ask first: an ACTIVE milestone in the
project with the same title, or one that reads as the same plan (title, design
and steps against milestone embeddings), is returned with its progress and a
pointer to create_records(milestone_id=...). Nothing is created; force=true
bypasses.
- dedup.find_matching_plan / plan_gate / plan_match_response; access-checked
before either arm (rule 78), fail-open like the other gates.
- Done milestones never block; the semantic arm needs 200+ chars of candidate.
- kb_plan_match_threshold (default 0.90) is a setting, in the Settings view,
and pinned against the Python default by test_settings_defaults_agree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
test_nothing_else_builds_the_embedding_document_itself caught start_planning
building f"{title}\n{body}" inline for the plan gate's candidate text. That is
the embedded-document shape; plan_candidate_text now takes (title, body) pairs
and calls embedding_text, so the candidate moves with the corpus it is ranked
against (#2486).
Also carries the create_task / create_records milestone_id docstring lines from
step 5 (#4080), which share the file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
Step 5 of milestone 415 "An existing plan is found before a new one is made".
Sessions opened a second milestone beside the roadmap milestone that already
covered the work, and filed related tasks loose, because no surface told them
to look first.
- writing-plans: a section on finding the plan that exists (enter_project's
unplanned_milestones, search(content_type="milestone"), list_milestones);
when an active milestone covers the work, add steps to it; a second
milestone only for a separate arc; the gate's existing_milestone reply.
- using-scribe: "when you plan" gains the same check and milestone_id on
related tasks.
- _INSTRUCTIONS PLAN line points at the milestone search (1,689 of 2,000).
- test_guidance_ownership pins the topic on writing-plans.
- Plugin version minted: 2026.09.15.1744.
Co-Authored-By: Claude Opus 5 (1M context) <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.
Milestone 415 "An existing plan is found before a new one is made — milestones without steps count as open work", plus the #4081 planning payload fix.
enter_projectlists active milestones with no steps asunplanned_milestones.search(content_type="milestone"). Newmilestone_embeddingstable (migration 0102) with a startup backfill.start_planningandcreate_milestonereturn the active milestone that already has this title, or reads as the same plan, instead of creating a second one.force=truebypasses it. The new Settings field "Existing-plan match threshold" defaults to 0.90._INSTRUCTIONStell sessions to find the existing plan first and file related work into it. Plugin version 2026.09.15.1744.CI run 6912 on
fb36599passed: lint, typecheck, unit, integration, plugin hooks and image build.🤖 Generated with Claude Code
https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
test_nothing_else_builds_the_embedding_document_itself caught start_planning building f"{title}\n{body}" inline for the plan gate's candidate text. That is the embedded-document shape; plan_candidate_text now takes (title, body) pairs and calls embedding_text, so the candidate moves with the corpus it is ranked against (#2486). Also carries the create_task / create_records milestone_id docstring lines from step 5 (#4080), which share the file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy