Simplify postponement proposal placement
Design a replacement for the proposal priority and synchronized ordering work that fits existing Rails, acts_as_list, Stimulus, and Turbo patterns. Intended outcome: - A newly activated postponement proposal is placed before ordinary waiting proposals while ongoing and biased proposals retain precedence. - Existing secretary manual ordering remains available without being redesigned. - Secretary, politician, and public views converge through ordinary Turbo broadcasts. Constraints and non-goals: - Reuse existing components, controllers, broadcasts, and proposal positioning behavior. - Keep authorization resource-specific. - Do not add an operation ledger, durable outbox, PostgreSQL listener, offline move queue, contextual merge algorithm, polling, or bespoke retry framework. - A failed or stale move may refresh the proposal list; preserving an in-flight drag through every race is not required. - Keep attachment pagination/refactors, poll submission changes, and unrelated UI fixes outside this feature. Acceptance criteria: - A concise architecture covers postponement placement, existing manual ordering, Turbo updates, failure behavior, and focused tests. - Concurrency behavior and explicit failure handling are defined. - Scope can be implemented in small reviewable changes and replaces the oversized PR rather than layering on it. Revised design decision: - Add no configuration columns, settings UI, meeting snapshot, revision counter, operation record, or delivery infrastructure. - Express one hard-coded initial-placement rule for special proposal types entering the waiting queue: close-meeting first, biased second, postponement third, and ordinary proposals at the bottom. Do not make this configurable. - Use the existing proposal position and acts_as_list behavior under a case row lock. Manual drag ordering remains an explicit override and is not continuously re-sorted by type. - Reuse existing proposal-list partials and direct Turbo broadcasts so connected secretary, politician, and public views render the persisted order. Private politician content remains session-rendered. - On ordinary request or broadcast failure, reload from the database; do not merge, queue, retry, or persist browser operations. - Attachment, poll, speaker, settings, and delivery-infrastructure changes are excluded. - Align proposal list persistence with existing case and speaker patterns by scoping acts_as_list to case and status. Ongoing proposals are rendered separately and do not participate in queue dragging; postponement placement and manual ordering operate only on the not_started queue. Add one explicit display-order scope for consumers that combine statuses, and migrate the unique index/positions accordingly. Verified implementation: - Implemented on branch `fix/prioritize-postponement-proposals` in an isolated worktree. - Migration up/down, focused model/controller/view/visibility suites, RuboCop for changed Ruby files, JavaScript syntax/build, Zeitwerk, and diff checks pass. - The full RSpec suite was intentionally stopped at user request; no full-suite result is claimed. - Pull request: #4236 https://git.kaukus.no/Kaukus/Kaukus/pulls/4236 Implementation acceptance criteria: - Existing proposal positions are safely migrated into contiguous case/status lists with matching database uniqueness. - Close-meeting, biased and postponement proposals entering not_started use one shared initial-placement rule, including biased proposals created after a case has started. - Secretary dragging affects only not_started proposals and uses proposal-specific authorization plus a case lock. - Secretary, politician and public views converge using ordinary Turbo updates without exposing private content. - Focused model, migration, request/controller and JavaScript tests pass; changed Ruby files pass RuboCop. Current CI investigation and fixes: - Fixes and the resolved master merge are committed as a7596d0cb (fix: Correct proposal placement and secure queue refreshes) on local fix/proposal-ci-regressions in /home/mathias/Projects/worktrees/moeteadm/proposal-ci-regressions. The commit was pushed without rewriting history to the existing fix/prioritize-postponement-proposals source branch for PR #4236, rather than creating a duplicate PR. Master 94aa12cd3 is included, and Gitea reports no conflicts. The original local feature branch and main master worktree were left untouched. - Queue placement now runs after encrypted-field persistence and reorders a separate record so list saves preserve the original save's change tracking. Secretary-frame access uses proposal-scoped read_secretary permission, preserving operator access without granting ordinary readers access. Poll controls share a case-level broadcaster independent of public visibility; frame reloads wait for transaction commit. Public visibility assertions parse rendered HTML and use an apostrophe-containing fixture. - Red-green validation reproduced 19 failures before the fixes. After the fixes and local master merge, 390 focused model, ability, controller, visibility, component and view examples pass with seeds 52483 and 30645. Migration rollback/re-application, JavaScript/CSS builds, asset precompilation, Zeitwerk and diff checks pass. RuboCop passes all 20 changed Ruby files excluding generated db/schema.rb; schema syntax passes (explicit linting of the generated schema only found existing generated-code style offenses). - The complete local eight-worker suite finished with 6899 examples, 3 failures and 4 pending. All failures are in party-chat pagination/catch-up system specs, not proposal specs. A separate four-example master chat baseline has two pagination failures too; no clean full-suite or remote-CI pass is claimed. Chat source was not changed. - An attempted fetch timed out; no authentication changes or retry were attempted. A later read-only branch API check confirms remote master is still 94aa12cd3, matching the locally merged target. - Inspected CI runs 3893 (fourth attempt), 3942 and 3947. The same four close-meeting poll-button failures recur; run 3947 has 6757 examples and exactly these four failures. - Reproduced poll_buttons_functionality_spec.rb with seed 52483: PR head 627c14791 has 37 examples / 4 failures; current master 94aa12cd3 has 37 / 0. - Root cause: Proposal#place_in_initial_queue excludes self but uses the next lower-priority record's existing position as insert_at's destination. When changing the first ordinary proposal to close_meeting, this incorrectly moves it from position 1 to 2. Calculate a one-based index in the self-excluded queue instead. - Confirmed two more high-severity regressions with isolated specs: the new secretary frame action permits an ordinary councillor with case-read permission to retrieve another author's unpublished proposal body (HTTP 200 despite no proposal-read permission); and changing a confidential proposal's type plus title silently retains old ciphertext because after_update placement recursively saves before Vault's after_save persistence. - Confirmed a further regression: manually moving proposals in an ongoing confidential case sends order markers but no secretary poll-button refresh; unanimous forms retain the previous head's proposal ID. The equivalent diagnostic passes on master. - One variable failure in run 3942 is genuinely pre-existing: public_case_content_visibility_spec.rb compares raw response HTML with Faker names. Noel O'Connell is rendered as Noel O'Connell. Forcing that name reproduces both relevant examples on PR and master (asset URL generation stubbed for request-only diagnostics). - The pre-commit reload race during Case#start! is fixed by binding Case#broadcast_proposal_order to the current transaction's after_commit callback. Commit, rollback and nested-rollback regression tests pass. No browser-level concurrent transaction stress test was performed. - Resolved the sole master merge conflict in db/schema.rb by retaining version 2026_10_21_120000 and the PR's case/status/position proposal index. No unmerged paths remain, and unrelated PostgreSQL schema-dump formatting was removed. - Repros, logs and patch backups are under /tmp/kaukus-pr4236-investigation, with a separate master baseline worktree and dedicated test databases. Implementation is in the persistent worktree noted above. The worktree is clean, and commit a7596d0cb is published to PR #4236: https://git.kaukus.no/Kaukus/Kaukus/pulls/4236. Its Summary/Testing description now covers the fixes and accurately records the full-suite chat failures and remaining browser/concurrency limits. Local implementation checks are complete; the new remote CI result remains pending/unverified. Latest update: - Unified special-proposal placement commit `9ae367ce4` was pushed to pull request #4236. - Review identified two follow-ups before readiness: guard public-view proposal broadcasts with the existing confidential-content policy, and replace duplicate reload-frame markup with a shared ViewComponent. - Both review findings were resolved and pushed in commit `fc3258539`; focused regression coverage, RuboCop and Zeitwerk pass. - A subsequent review found one valid stale loading path after create-from-notes; it now reuses display ordering and preserves the politician proposal Turbo Frame in commit `c72ec9a92`. - Browser verification exposed self-referencing reload-frame sources on the proposal pages, which could replace the entire proposal area with an empty frame after a move. Commit `c082f5ecd` replaces them with dedicated authenticated frame endpoints that return the matching frame IDs. - The remaining blank-page failure had two causes: SortableJS counted hidden sibling markup and submitted an out-of-range position, while manual moves unnecessarily replaced a fetching Turbo Frame. Commit `d7d69b0b7` restricts position calculation to proposal cards and broadcasts synchronous order-only markers. Browser retesting confirms moves complete without page blanking or reload.
Checklist
19 of 19 complete- Define minimal user behavior and explicit non-goals
- Design model, server, Turbo, and Stimulus flow using existing patterns
- Define normal concurrency, failure handling, authorization, and test boundaries
- Propose an implementation and PR split that excludes unrelated scope
- Revise design around the single postponement-placement rule and remove configuration scope
- Migrate proposal positions and uniqueness to case/status ordering
- Implement postponement placement and status-scoped display ordering
- Restrict manual ordering to the waiting queue with resource authorization and locking
- Synchronize secretary, politician, and public proposal order through existing Turbo patterns
- Run focused tests, migration validation, JavaScript tests, and RuboCop
- Unify close-meeting, biased, and postponement initial placement without affecting later manual order
- Resolve review findings for confidential broadcasts and shared reload-frame rendering
- Use shared display-ordered politician proposal loading after create-from-notes and preserve its Turbo Frame
- Replace self-referencing proposal reload sources with dedicated secretary and politician frame endpoints
- Use synchronous order-only Turbo markers for manual proposal moves to avoid frame fetch aborts
- Diagnose repeated PR CI failures against master and identify conflicting paths with reproducible evidence
- Resolve master schema conflict, stabilize escaped-name assertions, and validate fixes
- Fix queue placement and encrypted-field persistence with regression coverage
- Secure secretary proposal frame access and preserve committed poll/frame updates
- Area
- Kaukus
- Kind
- Feature
- Priority
- Medium
- Task ID
tsk_fae867abh7- Updated