todo2code

Ticket 040: Detect unsafe workspace state before governed edits

Goal and scope

Prevent governed work from starting in a stale, dirty or incorrectly scoped workspace. Add one dependency-free runtime service that observes an existing local Git worktree, resolves the active ticket through the existing governance checker and produces a deterministic, read-only t2c.workspace-preflight/v1 report.

The motivating incident occurred immediately after a clean-main audit: a parallel generator modified TODO.md, project/, project2.sh and several untracked analysis files while another session was preparing a ticket. The preflight must expose that state before an agent assumes it owns a clean workspace. It must preserve all user data and must not attempt an automatic repair.

Architecture before implementation

The runtime service accepts:

It returns a bounded report containing:

The service may run only local read-only Git commands and the repository’s existing .governance/governance_check.py --format json. It fails closed when the baseline, checker, JSON report or ticket binding is unavailable.

Diagnostic contract

The first version is limited to:

Safe actions are non-executable enum values such as PRESERVE_CHANGES, USE_ISOLATED_WORKTREE, FAST_FORWARD_AFTER_PRESERVE and RESOLVE_TICKET_SCOPE. No shell command, stash mutation or automatic checkout is returned or executed.

Delivery boundary

Acceptance criteria

Participants

Approval boundary

The user approved this exact contract and replacement base db368c020876ccac537538c9e8cac03c9ae2f02f by replying tak after plan PR #47 merged. The ticket entered IN_PROGRESS / EDIT and the approval was recorded before either implementation file was created. It has since moved to DONE without widening that approval. Chat approval authorized the bounded interactive edit; merge authority came from protected exact-head Koru, Validator App and required CI evidence.

Validation result

Live read-only audit

The built service inspected its own in-progress isolated worktree against refs/remotes/origin/main. It correctly returned BLOCKED because two implementation files were uncommitted, retained governance passed, resolved exactly ticket-040, reported one local plan/approval commit and emitted fingerprint dade38bc751e87da9edcb044bc8f0c0d97f33b5c02c27edf0cafdf937b351d62. This is the intended pre-edit safety behavior; no Git state changed.

Protected completion evidence