Skip to content

feat: enforce status:in_progress when task has calendar date#19

Merged
bborbe merged 5 commits into
masterfrom
feature/enforce-status-date
Jun 14, 2026
Merged

feat: enforce status:in_progress when task has calendar date#19
bborbe merged 5 commits into
masterfrom
feature/enforce-status-date

Conversation

@bborbe

@bborbe bborbe commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

Enforce the calendar-as-commitment invariant: any task with a calendar date (planned_date, defer_date, or due_date) must have status: in_progress (or terminal). Closes silent-miss failure mode where status: next + future date hides tasks from Kanban (filter is status=in_progress&status=completed).

Spec: 017-enforce-status-in-progress-on-calendar-date

Three enforcement points

  1. Lint detect + fix (prompt 138) — pkg/ops/lint.go adds IssueTypeStatusDateMismatch; vault-cli task lint --fix promotes next/backlogin_progress; same detector serves task validate
  2. Defer auto-promote (prompt 139) — pkg/ops/defer.go::findAndDeferTask writes status: in_progress in the same atomic file write as defer_date for next/backlog tasks; idempotent on in_progress; terminal statuses (completed/aborted/hold) untouched
  3. Task-creator emission (prompt 140) — agents/task-creator.md Step 8 emits status: in_progress when any date field is being written, status: next otherwise; docs/task-writing.md Lifecycle gains a "Calendar-as-commitment rule" subsection; CHANGELOG.md ## Unreleased bullet

Out of scope (intentionally deferred)

  • task-auditor agent — semantic/judgment checks only; deterministic field-combo belongs in Go lint
  • task set CLI warn-and-offer-promote flag
  • One-time vault sweep — separate task once lint rule ships
  • Step 13 success-message echo in task-creator — separate follow-up

Test plan

  • make precommit green
  • vault-cli task lint on a status: next + defer_date fixture reports STATUS_DATE_MISMATCH
  • vault-cli task lint --fix promotes status, leaves date byte-identical
  • vault-cli task validate <fixture> surfaces the same issue (single source of truth)
  • vault-cli task defer on status: next task auto-promotes to in_progress
  • vault-cli task defer on status: in_progress task is idempotent (no re-write)
  • task-creator agent emits status: in_progress when given date field
  • Local `/coding:pr-review` loop converges
  • Bot reviewer approves

@bborbe bborbe marked this pull request as ready for review June 14, 2026 19:36

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my review of the changed files, here is my assessment:

Summary: The PR implements the calendar-as-commitment rule (spec 017) correctly across three enforcement points: lint detection/fix, defer-time auto-promote, and task-creator agent. The code is well-structured, follows existing patterns, and the test coverage is comprehensive.

Must Fix (Critical): None

Should Fix (Important): None — implementation is sound

Nice to Have (Optional): One minor edge case in lint.go


Detailed Findings

1. pkg/ops/lint.go:569-581detectStatusDateMismatch regex

Finding: The date value regex [^\s'"]+ doesn't match quoted values containing spaces, e.g., defer_date: "2026-12-01 12:00" (quoted string with a space).

dateRegex := regexp.MustCompile(
    `(?m)^(planned_date|defer_date|due_date):\s*['"]?([^\s'"]+)?['"]?\s*$`,
)

Impact: Low — ISO dates don't contain spaces; quoted datetime strings with spaces are uncommon in this vault's date format conventions.

Verdict: Accept as-is. The pattern matches the project's established date format (ISO 8601 without spaces in quoted values).


2. pkg/ops/lint.go:775ReplaceAllString usage in fixStatusDateMismatch

Finding: Uses ReplaceAllString rather than replacing only the first occurrence. If the frontmatter contained duplicate status lines (which would also be a DUPLICATE_KEY lint issue), all would be replaced.

newContent := statusRegex.ReplaceAllString(content, "status: in_progress")

Impact: Minimal — YAML frontmatter with duplicate keys is already flagged as DUPLICATE_KEY; the replace-all behavior is correct for the single-status case.

Verdict: Accept as-is.


3. pkg/ops/defer.go:147-152 — Auto-promote atomicity

Finding: The concern about atomicity is addressed correctly. The status promotion and defer-date write happen in-memory, then a single WriteTask call persists both atomically:

if status := task.Status(); status == domain.TaskStatusNext ||
    status == domain.TaskStatusBacklog {
    if err := task.SetStatus(domain.TaskStatusInProgress); err != nil {
        return nil, errors.Wrap(ctx, err, "set status to in_progress")
    }
}
// ... later ...
if err := d.taskStorage.WriteTask(ctx, task); err != nil {
    return nil, errors.Wrap(ctx, err, "write task")
}

If SetStatus fails, WriteTask is never called — no partial write occurs.

Verdict: Correctly implemented.


4. pkg/ops/lint_test.go — Test isolation

Finding: The new 330-line STATUS_DATE_MISMATCH suite properly isolates each test case. AfterEach cleans up vaultPath, and each subtest writes to a unique filename via writeSDM. No cross-test contamination.


5. pkg/ops/defer_test.go — Renamed test behavior

Finding: The test at line 70 ("promotes status from next (todo alias) to in_progress") correctly reflects the new behavior: status: todo (alias for next) is auto-promoted to in_progress on defer. The behavior change is intentional per spec 017.


6. agents/task-creator.md:114 — Conditional status emit

Finding: Step 8 correctly implements the spec rule: status: in_progress is emitted when any date field is being written, status: next otherwise. The conditional is clear and well-documented.


{
  "verdict": "approve",
  "summary": "The PR implements spec 017 (calendar-as-commitment rule) correctly across lint detection/fix, defer-time auto-promote, and task-creator agent. Code follows existing patterns in the codebase. The atomic write concern in defer.go is resolved — status change and file write happen in one WriteTask call. Test coverage is comprehensive for all three enforcement points.",
  "comments": [],
  "concerns_addressed": [
    "correctness: auto-promote block in findAndDeferTask — SetStatus error handling wraps correctly, WriteTask is called once atomically after all in-memory changes",
    "correctness: detectStatusDateMismatch regex — handles the project's date format conventions (unquoted ISO dates); edge case with quoted values containing spaces is low risk",
    "correctness: fixStatusDateMismatch ReplaceAllString — only matches status:next or status:backlog lines; duplicate status would already be DUPLICATE_KEY",
    "tests: STATUS_DATE_MISMATCH test suite — proper isolation via unique filenames per subtest, AfterEach cleanup",
    "tests: defer_test.go renamed test behavior — status:todo alias correctly auto-promotes to in_progress"
  ]
}

@bborbe bborbe merged commit 149e6d6 into master Jun 14, 2026
2 checks passed
@bborbe bborbe deleted the feature/enforce-status-date branch June 14, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant