Skip to content

No-op runs issue expiry is stamped at runtime but never enforced without an explicit expires: field #36857

@michen00

Description

@michen00

Summary

The "[aw] No-Op Runs" tracker issue is always stamped with a 30-day expiration marker at runtime, but the agentics-maintenance.yml workflow that enforces expiration is only generated when some workflow declares an explicit expires: field in its safe-outputs frontmatter. In a repository where no workflow sets expires: — while using noop.report-as-issue: true, which is the default — the marker is written but nothing ever reads it. The issue then carries a checked "expires" checkbox whose deadline silently passes, and it grows without the monthly rotation the expiry implies.

Observed behavior

In our repository, the no-op runs issue was created on 2026-03-27 with:

- [x] expires <!-- gh-aw-expires: 2026-04-26T05:32:51.126Z --> on Apr 26, 2026, 5:32 AM UTC

As of 2026-06-03 — five-plus weeks past the stamped deadline — the issue is still open, still accumulating comments (565 so far, ~272KB), and agentics-maintenance.yml does not exist in the repository (no workflow configures expires:).

Root cause

Two halves of the expiry feature live at different stages and don't see each other:

  1. Runtime (always stamps): actions/setup/js/handle_noop_message.cjs unconditionally applies generateFooterWithExpiration({ expiresHours: 24 * 30 }) when creating the no-op runs issue.

  2. Compile time (conditionally sweeps): pkg/workflow/maintenance_workflow.go gates generation of agentics-maintenance.yml on scanWorkflowsForExpires, which only inspects CreateDiscussions.Expires, CreateIssues.Expires, and CreatePullRequests.Expires. The runtime-stamped no-op expiry is invisible to this scan, so the !hasExpires path skips (and even deletes) the maintenance workflow.

The intended rotation — handler searches is:issue is:open for the tracker, sweeper closes it after 30 days, handler mints a fresh one on the next no-op — therefore never happens unless the repo coincidentally uses expires: somewhere else.

Suggested fix

Treat noop.report-as-issue: true (including by default) as an expiry source in scanWorkflowsForExpires, contributing the same 30-day value the runtime handler stamps. That keeps the two halves in agreement without new configuration surface. Alternatively, the handler could skip the expiration footer when no maintenance workflow will exist, but compile-time knowledge isn't available at runtime, so aligning the scan seems like the cleaner direction.

Workaround

Closing the tracker issue manually reproduces what the sweeper would have done; the handler creates a fresh tracker on the next no-op. (The issue body's "Do not close this issue manually" note discourages this, which is part of why the dead marker is confusing.)

Metadata

Metadata

Assignees

No one assigned

    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