Skip to content

A sprint on an Actionless Outcome can never settle, so the row never strikes off #711

Description

@capo-the-ai-bot

User Story

As a user who starts a Focus sprint on an Outcome that has no current Action, finishes it, and gives it a verdict, I want it struck off for the session like any other, so the session view does not keep demanding attention for something I have already handled.

Observed Behaviour

An Outcome on the day's Plan that is Actionless (no current Action) can be started from the Now screen — _StartButton offers a sprint on any Plan member regardless of Action state. Complete the sprint and give any non-done verdict ("More to do…", "Waiting For", "Someday") and the row never strikes off, and never will.

Marking the Outcome done still works (that is a different arm of the predicate).

Why it cannot settle, by construction

The Settled predicate introduced by #693 has two arms: Completion recorded, or an Action of the Outcome was completed within this FocusSession and the Outcome was re-clarified at or after that completion. The second arm is anchored on a completed Action, and for an Actionless Outcome there is none to anchor to:

  • TimeLogDao.openLog resolves action_id from the Outcome's current Action; with none, the log is opened with action_id = NULL.
  • ActionDao.applyCompleteCurrentAction no-ops when there is no current Action.
  • The settlement anchor subquery (MAX(a.done_at) over actions joined to time_logs on action_id) is therefore NULL, so arm (b) can never be satisfied.

This is documented in the DAO docstring, pinned by a test, and named in ADR-0048 — deliberately, so the behaviour is a decision rather than an accident. The reading "nothing was completed, so nothing settled" is defensible in the model. It is still a hole from the user's side: they did work, gave a verdict, and the UI disagrees.

The same applies to an off-Plan Outcome engaged with action_id IS NULL.

Scope

  • Decide whether an Actionless Outcome should be startable at all. The likely shape — and the one that fits the model rather than patching around it — is that starting a sprint should first give the Outcome an Action, using the title-as-action fallback the codebase already applies elsewhere (an empty next-action phrase means the Outcome's title, written only while the Outcome is Actionless — TodoDao.setCurrentActionTextIfActionless; issue Saving an empty next action during review should resolve the item as Next and advance, not stall on the item #691). Then arm (b) works with no change to the predicate.
  • The alternative — teaching the settlement anchor to accept a NULL-action TimeLog — is not preferred: it would make "an Action was completed" mean something it does not, and the Settled predicate is deliberately the session-scoped negation of Stale.
  • Whichever is chosen, _StartButton's affordance and the resulting state must agree.

Acceptance Criteria

  • Starting a sprint on an Actionless Plan member, completing it, and giving a non-done verdict strikes the row off for the session.
  • The Settled predicate is not weakened to achieve it — arm (b) still means an Action was genuinely completed in this session.
  • The same holds for an off-Plan Outcome engaged during the session.
  • The existing test pinning today's "Actionless never settles" behaviour is updated deliberately, not deleted.
  • ADR-0048's note recording this hole is updated to record how it was closed.

Related

Metadata

Metadata

Labels

bugSomething is broken or silently wrongengineeringready-for-planningIssue needs planning/triage

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions