Skip to content

feat(plans): plan editor hardening deferred from #631 (atomic Save, editor API, managed dates) #1057

Open

Description

@cristim

Follow-up to #631. On 2026-10-17 the owner cut #631 to a UI-first ship. The laddering plan UI ships on the existing plan create/update path, extended to store the ramp settings and step dates. The hardening work below is deferred here. Every branch was kept locally. Owner decision 2026-11-04 (all options A): publish in dependency order, one PR per item through the full review gate (M3, then the API, then the editor UI Save, then the harness), and continue only while the previous item merges. Push the parked branches as backups, and open PRs linked to their issues where possible, after a per-branch ancestry check.

Deferred work

1. Atomic plan-editor Save command (M3). One transaction that merges the target, cancels and replaces roots, updates the plan and its next date, and writes an immutable command receipt. It also covers retries for serialization failures and deadlocks, replay by command id, a bounded wait when busy, and pool-size handling.

  • Branch feat/631-m3-on-guards holds about 31 local commits, rebased on main.
  • Its migration is the receipts table, plan_schedule_command_receipts. Main is at migration 000153, so it is renumbered to 000154 or higher at publish time and re-checked against main again before merge.
  • Parked locally: a two-client lock-coverage race proof (T6a). It has 18 passing subtests. Its flakiness, lint and full-suite runs were not done.
  • Not built: T6b (final clock, winner conflicts, pool max 2).
  • Includes the cumulative-percent store mode (T7).

2. Plan-editor HTTP API (preview and Save, plus the read-only GET).

  • Built: request decoding and bounds, the typed target, the field and intent policy (403 before 409, sorted), step-date rules, changed-field validators, scheduling validators, and cumulative-percent targets with a parity test against the store merge.
  • Not built: C1d, C2b-e, C3a-c, C4a-e, C5a-b.
  • Branches: feat/631-api-on-m3 (current, stacked on M3), plus feat/631-api-lane and feat/631-api-c1e (older copies).

3. Editor-managed Save (dates_managed) and its UI. These are the UI commits that save through PUT /api/plans/{id}/editor and POST .../editor/preview: the review panel and acknowledgements, command-id retry, and handling of an uncertain outcome. They are on branch feat/631-editor-ui-rebased. The publication plan for the scoped ship lists which of its commits are parked. The legacy-writer guards (#1010-#1014) are merged and become active only once this ships.

4. Retention ledger runtime, #905. #905 is merged (8c0123d, 2026-10-17; migration 000127 handles never-started ramps).

5. Canonical service slugs everywhere. The owner decided that legacy pre-#608 slugs are rewritten to canonical, and that the ledger treats both as one service. The plan has 6 tasks (A2, A1, B1, B2, C, D) and was reviewed to closure. A2 is parked locally on branch fix/631-canonical-slugs-baselines. Part of this has landed (#1067, #1123, #1124 and migration 000151). What remains is the residual after those, likely the ledger baseline part of A2, which is diffed against main before anything is published.

6. End-to-end harness (M6) and editor route exposure (M7). A planned browser → API → Postgres Playwright harness with an egress recorder. It is reviewed to closure. It depends on 1-3. Deferred until item 3 merges; M7 (route exposure) must not ship before the guards and Save are in main.

7. Smaller items recorded during the work (filed separately on 2026-11-04):

Refs #631

Activity

  1. cristim commented on Nov 4, 2026

    @cristim
    MemberAuthor

    Status 2026-11-04. The owner chose option A on every question. The body is refreshed: #905 is merged, #1052 is closed, the item 5 residual follows #1067, #1123, #1124 and migration 000151, and the receipts migration will be renumbered to 000154 or higher. The item 7 findings are filed as #1733 to #1737. The Postgres item needed no change, because production runs 16. Next: back up the parked branches after an ancestry check, then publish in dependency order, one PR at a time: M3 first, then retention-ledger part 2 and the canonical-slug residual.

  2. cristim commented on Nov 4, 2026

    @cristim
    MemberAuthor

    Q6 done (owner decision A): the 73 local #631 and plan-editor branches were classified against main after a per-branch ancestry check. 14 are already merged, 36 are contained in a newer branch, and 23 are unique. The 23 unique branches are pushed as backups under backup/631/*: no PRs, and no CI runs on those refs. Nothing was deleted. PRs follow in dependency order, one at a time, starting with M3 (item 1).

  3. cristim commented on Nov 4, 2026

    @cristim
    MemberAuthor

    Coordination note (2026-11-04): plans created from a multi-selection will hold several config groups (#1746). The ramp, dates_managed, notification days, accounts, root rows and the plan cap stay per plan. Each group is an entry of the plan's services with a stable group_id, bound recommendation identities and an optional per-group step cap, marked by allocation_mode=bound_groups and keyed provider/service#<group_id>. When this tracker's editor work (items 1 to 3) resumes, it must address a group as plan_id plus group_id, keep the marker and the #<group_id> keys, and apply the effective cap min(group, plan) through CheckStepCommitmentCap.

  4. cristim commented on Nov 9, 2026

    @cristim
    MemberAuthor

    Checked as a possible pick (claude-ui-fix-1, 2026-11-09); parking it with the reason rather than claiming it, since it does not fit a one-PR slice yet.

    State measured on current main:

    Proposal: keep this as the epic and cut it into reviewable slices before anyone picks the first: (a) the read-only editor projections and policies (the first ~9 feat(api) commits of the M3 branch, no migration), (b) coherent snapshot and Save command store layer with a fresh migration number and the group-aware merge, (c) the editor HTTP API, (d) the UI Save, (e) the harness. Each slice needs a plan review against #1746 before code. I did not touch the branches or the labels.

  5. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Claimed by claude-platform (coordinator priority list, 2026-11-13). Working this in a worktree; will post the landed commit or a blocker here.

  6. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Plan on main (claude-platform): ~/.claude/projects/cloud-commitments-platform/plans/1057-plan-editor-hardening.md. Premise: Save is two requests (plans.ts:1357-1359, updatePlan then setPlanAccounts), so it can partially apply; updatePlan itself is one row-locked transaction. Five commits: receipts migration and store, command replay with bounded lock wait, group-aware merge with effective cap min(group, plan), cancel and replace roots with dates_managed and the accounts write in one transaction, and the two-client race proof. The migration number is highest on main plus one at merge time (the held #669 branch already claims 000168). Owner items: O1 slice order (default: store first), O2 count managed plans in production before exposing the route (expected 0), O3 sign-off for any change that lets Save alter what a managed plan buys. Stores and command replay start now; the route waits for O2.

  7. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Status (claude-platform): commits 1 and 2 of the plan are approved in two passes (efa71f5 receipts migration and store; f364bb8 command replay with lock_timeout and bounded retry). Not pushed: the migration number 000168 is shared with the held #669 branch, and pushing a migration also applies it to dev at the next deploy; the owner decides the timing (see owner-decisions.md). Note for the route commit: replay does not compare the actor, so the same command id from another actor returns the stored outcome; decide that when the route lands. Left open.

  8. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Status (claude-platform): commits 1 to 3 of the plan are approved in review (efa71f5 receipts migration and store; f364bb8 command replay; c7efa92 group-aware merge, with the review fixes in 3b8336d6: duplicate group ids refused, a plan-cap raise on an uncapped auto-purchase plan refused). Not pushed, for the migration-number and dev-deploy reasons in owner-decisions.md. Commits 4 (cancel and replace roots) and 5 (race proof) remain.

  9. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Landed on main: C1-C3 pushed fast-forward as 4b1a054 (plan schedule command receipts, migration 000168), 2d9ce69 (ApplyPlanScheduleCommand store skeleton), b1049e2 (group-aware target merge with effective cap), 9cf5fb8 (refuse duplicate GroupID and uncapped plan cap edits).

    Gates on the rebased tip (main was at 8450ee7): go build ./..., go vet on internal/config and internal/purchase, go test -count=1 on config, purchase and mocks, integration tests for receipts, ApplyPlanScheduleCommand, group target and the 000168 migration against Docker, golangci-lint v2.10.1 0 issues.

    Still open: C4 (cancel and replace roots, dates_managed, accounts write) and C5 (race proof). CI watch on 9cf5fb8 is running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions