Files
magnus919_agent-skills/neckbeard/references/progress-and-finality.md
T
Magnus HedemarkandGitHub 7964c060d3 fix(neckbeard): distinguish progress from repeated polling (#519)
Define semantic progress and stage finality for asynchronous work, add bounded-check guidance, and cover terminal advancement, productive complexity, and stuck-work regressions with deterministic transcript tests.

Signed-off-by: Magnus Hedemark <magnus919@pm.me>
2026-09-18 21:04:12 -04:00

118 lines
5.7 KiB
Markdown

# Progress and Finality
How to judge whether work is advancing, when to keep going, when a bounded
check of an asynchronous dependency is justified, and when to stop or
escalate. One rule governs every section: **decide on semantic state — never
on elapsed time, iteration counts, timestamps, request IDs, or how many times
the same state has been observed.**
## Semantic progress, not iteration count
Progress means the world changed in a way that moves an acceptance criterion
forward: a changed artifact, a confirmed or falsified hypothesis, a new stage,
a changed dependency state, or a new output cursor from an asynchronous
system. An advancing timestamp, a fresh request ID, a poll counter, or the
continued existence of a process is *volatile churn* — it is not progress and
must never be recorded as evidence of progress.
Judge churn by content, not by source. A heartbeat whose only change is its
timestamp is churn. But a heartbeat that carries an advancing payload — a new
phase or step, a processed-item count, a new output cursor — is reporting
semantic state: record that payload in the declared semantic state, and it
counts as progress. The timestamp itself stays out of the fingerprint either
way.
The failure mode this page prevents is not "too many turns." It is repeated
observation of an unchanged semantic state while the declared stage action
remains untaken.
## The dependency contract
When a stage hands off to an asynchronous system, record — in the change
contract and the evidence ledger — whether a dependency is required for the
next stage:
- **Accepted/finished output with no required dependency:** the next action is
to advance or deliver, or to declare the run terminal-complete. A status
poll before that action is never justified.
- **A pending or failed dependency explicitly required for the next stage:**
a bounded check is justified once (see below). Required-or-not is part of
the contract, not something to re-derive mid-run.
Treat `accepted`/`finished` as a stage output, not automatically as whole-run
completion. Terminality is declared only when the contract has no required
dependency left.
## Bounded checks
One infrastructure check per normalized state fingerprint. The fingerprint is
computed from: stage, deliverable state, dependency state, whether the
dependency is required, and the declared semantic state. Volatile fields —
timestamps, request IDs, raw poll counters — are excluded from the
fingerprint, so a timestamp-only heartbeat difference is unchanged state. An
advancing heartbeat payload (a new phase, step count, or output cursor)
belongs in the declared semantic state; when it advances, the fingerprint
changes and the new state receives its own check allowance.
- First observation of a fingerprint: one check is allowed.
- The fingerprint changes (pending → succeeded, or a new output cursor):
continue; the new fingerprint gets its own check allowance.
- The fingerprint is unchanged: do not re-check. Change the mechanism or
escalate. An identical re-poll is a finality failure even when the
timestamp and request ID differ.
## Complex work
There is no arbitrary turn cap. Continue while each turn produces new
evidence or a changed semantic state and at least one acceptance criterion
remains unmet. Multi-turn diagnosis, design iteration, and long test loops
are productive work when the semantic state keeps changing; a turn-count
ceiling must never stop them.
## Active-process ledger
The evidence ledger stays canonical. When work is asynchronous — or an
escalation is being considered — extend the run's record with an
active-process entry:
| Field | Content |
|---|---|
| `id` | Stable identifier for the asynchronous hand-off. |
| `status` | Current normalized state. |
| `output` | Latest meaningful output or artifact reference. |
| `normalized_state` | Fingerprint inputs: stage, deliverable state, dependency state, dependency-required flag, semantic state. |
| `dependency_required` | Whether the dependency gates the next stage. |
| `next_action` | The declared stage action: `work`, `check`, `advance`, `deliver`, `complete`, `mechanism_change`, or `escalate`. |
| `expected_transition` | What the next *semantic* change would look like. |
| `checks_for_state` | How many checks this fingerprint has consumed. |
| `escalation_trigger` | The condition under which the next unchanged observation escalates. |
## Examples
**Accepted but not blocked.** The deliverable is accepted; a publish job is
pending, but nothing requires it before delivery. Correct: deliver or
complete now. Wrong: check the publish job first.
**Genuinely pending dependency.** The delivery stage requires a published
release. One check is spent; the next observation shows the dependency
succeeded with a new output cursor. Correct: proceed to delivery. If the
observation had been unchanged, the correct move is a mechanism change or
escalation — not a second check.
**Productive multi-step work.** Three turns on an in-progress deliverable,
each with a new hypothesis, benchmark, or artifact, ending in verification
and delivery. Correct at any length: every turn changed the semantic state.
**Identical re-poll (failure).** Two observations with identical semantic
state but different timestamps and request IDs, with a check on each, while
the delivery action stays untaken. This fails finality: the second check was
an identical re-poll; the response to the unchanged result should have been a
mechanism change or escalation.
## Non-portable examples excluded
Keep this contract harness-neutral. Do not express the rules in terms of
specific tool names, scheduler or cron identifiers, process IDs, platform API
names, or any vendor's polling semantics. Describe state, dependencies,
actions, and terminality; each harness maps them to its own mechanisms.