Skip to content

produces: bare/non-root-relative path silently fails presence gate → 3-attempt spin; lint should catch it #77

@clabonte

Description

@clabonte

Summary

A WU produces: entry that is a bare filename (or any path not resolvable from the repo root) silently fails the driver's presence gate and spins to a 3-attempt blocked state — even when the file is actually produced, just in a subdirectory. The failure is only discoverable at runtime (after 3 wasted opus sessions), not at lint time.

Observed on DRIVER_VERSION = "0.2.0".

Concrete incident

A close-adjacent WU declared:

produces:
  - GATE-02-REVIEW.md   # file actually lives at .specfuse/features/FEAT-XXXX-.../GATE-02-REVIEW.md

The WU did its work correctly (the file was written/updated in the feature dir, external side effects done), but the presence gate resolves produces relative to the repo root, where GATE-02-REVIEW.md does not exist. Result:

DELIVERABLE MISSING attempt 1/3 — declared deliverable absent: GATE-02-REVIEW.md
DELIVERABLE MISSING attempt 2/3 — declared deliverable absent: GATE-02-REVIEW.md
DELIVERABLE MISSING attempt 3/3 — declared deliverable absent: GATE-02-REVIEW.md
BLOCKED after 3 attempts — escalated (spinning_detected)

~3 opus sessions burned on an unsatisfiable check; the fix was a one-line path correction (.specfuse/features/FEAT-XXXX-.../GATE-02-REVIEW.md). Nothing the agent did would ever satisfy it — the path was wrong in the WU frontmatter, so every attempt fails identically.

Why it's worth a guard

  • The error message ("declared deliverable absent: GATE-02-REVIEW.md") looks like the agent failed to produce the file, sending the operator to debug the agent's work — when the real cause is a frontmatter path. (Same diagnosis-misdirection family as the files_changed_mismatch ambiguity.)
  • It's a pure authoring mistake that a static check can catch before dispatch, for free.

Suggested fix (1 is the high-value one)

  1. Lint-time guard (lint_plan.py): warn (or error) when a produces: path is not repo-root-resolvable in an obvious way — at minimum, flag a bare filename with no / (almost always wrong, since WU deliverables live under .specfuse/, modules/, environments/, etc.), or a path that doesn't match any plausible tracked location. Catch it at lint, not after a 3-attempt runtime spin.
  2. Runtime clarity: when the presence gate fails, make the message name the resolved absolute path it looked for (e.g. "looked for <repo_root>/GATE-02-REVIEW.mdproduces paths are repo-root-relative") so the misdirection is obvious. Optionally, if a bare filename matches exactly one file under the WU's feature dir, resolve it there (lenient) rather than failing.

Workaround (today)

Always write produces: as a repo-root-relative path (e.g. .specfuse/features/FEAT-XXXX-<slug>/GATE-02-REVIEW.md), never a bare filename. Related: a deletion-deliverable WU must omit produces entirely (a deleted path can never satisfy a presence gate).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions