Field feature · illuminate rung

PR for Intent

When the intent itself moves: who sees it, who signs it.

The cheapest way to satisfy a stubborn test is to soften the sentence it answers for. No check that reads only the code can catch that: the durable ID holds, the named test still passes, nothing looks broken. It is also exactly the shortcut an agent under pressure to go green will find. So the report watches the words themselves. A restatement shows up as a diff on a known row, and Intent Changed asks the one question that matters: did the code and tests move with it? Then two small gates decide who signs, one stock GitHub and one SpecAssay's: Product decides whether we want the new promise, the build side confirms the code still keeps it. Most restatements are healthy; requirements tighten, the field pushes back. The report treats them all the same way: visibly, and it never blocks. The signatures are the gates, and they are human. An agent can reword a spec; it cannot merge one.

PR from the Field: the discovery PR

The healthy case, and the everyday one. Mid-build, the team measured reconcile on real devices: changes land in well under two seconds, every run. The 5s budget in AC-SYNC-01 was out of date. So PR #5 updates the wording and the test together, in one diff:

examples/example-app/PRD.mdAC-SYNC-01 restated: within 5s → within 2sintent moved
tests/test_sync.pythe proof moves with it: assert elapsed < 2.0on the thread
1
🧵 Thread Report
🟢 Golden Thread intact

Intent Changed

AC-SYNC-01restated
was: … appears on a second device within 5s of reconnect.
now: … appears on a second device within 2s of reconnect.
Value 5s2s changed, but not found verbatim in the code or tests — re-confirm by reading.

Nothing stale to point at, and the report says so usefully. The old 5s is gone from the statement and gone from the test, because both moved in the same PR. The (updated here) mark confirms the proof came along. A reviewer reads the wording change and the code change as one story. That's what the durable ID buys: AC-SYNC-01 itself never changes, so its wording and its proof can move together without breaking the thread.

PR from Intent: when the wording moves alone

The case to watch. PR #4 makes the same one-line change, 5s → 2s, and nothing else. Maybe Product tightened the requirement. Maybe an agent "cleaned up" the spec and changed its meaning without anyone deciding that. Either way the Gate stays green: the ID didn't change, and the named test still passes. But that test was written for 5s.

2
🧵 Thread Report
🟢 Golden Thread intact

Intent Changed

AC-SYNC-01restated
was: … appears on a second device within 5s of reconnect.
now: … appears on a second device within 2s of reconnect.
re-confirm:
sync.py:51 test_sync.py:37 ⚠ still contains the old 5s

Here the report earns its keep. Same rewording, but the code and tests sat still, and the report can prove it: test_sync.py line 37 still asserts elapsed < 5.0. A passing test, a green thread, and it's testing the old requirement. The report points at the exact line and stops. It won't rewrite your test; it shows the reviewer where to look. Put the two reports side by side and the difference is one glance.

Who may move the wording?

Product and Dev share the repo, but the wording can still belong to Product. GitHub has a built-in feature for exactly this: a file called CODEOWNERS. Each line in it names a file and its owner; one line here reads PRD.md @product-team. From then on, any PR that changes the PRD automatically gets Product added as a reviewer, and with one repo setting, it can't merge until Product approves. The example app ships this file →

So when Dev's field discovery rewrites a requirement, the PR itself is the request to Product: new wording, updated test, report attached. Product reviews it on the diff. No meeting. And an agent that rewords a spec can't merge anything; a person has to sign first.

The reverse direction needs a second tool. When Product rewrites a requirement, none of Dev's files change, so CODEOWNERS never asks Dev anything. But the tests now need a check. That's the intent_ack checkbox: someone from the build side ticks that the code and tests still match the new wording, before merge. Two gates, one each:

Product's gateDev's gate
InstrumentCODEOWNERS reviewintent_ack tick
Fires whenthe wording is touchedan intent is restated
Question"Do we want this promise?""Does the code still keep it?"

Can't tick it honestly because the test really is out of date? Two normal fixes: add the test update to this same PR (the report then shows (updated here)), or file the follow-up task and the row shows tracked-debt until it's done. Nothing merges hidden.

How sharp the pointer gets

How hard the report leans depends on what it can prove. Three tiers, sharpest first:

1

Pinpointed: the old value still lives in a carrier

test_sync.py:37 ⚠ still contains the old 5s

The old value (a number, a quoted string, a constant) still sits in the code or a test, and the report points at the exact line. PR #4's case, and the sharpest hint it can give: here is the line that still says what you just changed.

2

Value gone: changed, but not found verbatim

Value 5s2s changed, but not found verbatim in the code or tests — re-confirm by reading.

The value changed, but the old one isn't in the code anymore; on a discovery PR that's because it was just updated (PR #5's case). Nothing to point at, so the report says so and lists what to re-read.

3

Prose: no literal value to pin down

Prose change — no literal value to pin down; re-confirm the code and its test by reading them against the new wording.

The wording changed in meaning, not in any searchable value: "ordered by relevance" becomes "ordered by recency." The report doesn't pretend otherwise: it lists the code and the test and asks a human to read them against the new sentence. The most common case.

Why re-confirm, and never block

Rewording an intent is normal work: requirements tighten, reality pushes back, product learns. Whether the existing code still satisfies a new sentence is a human judgement, so the report never blocks on it:

A durable ID means the wording can change without the thread snapping. Intent Changed keeps that honest, in both directions, and never silently.

And it stays a comment. SpecAssay posts beside your PR; it never edits the wording, the code, or the test. The diff is the record; the Thread Report is the reading glass.

Intent Changed is one section of the Thread Report. The reference doc carries the technical depth: how restatements are detected, the token heuristic behind the tiers, and the intent_ack config. The upward direction has its own essay: Bidirectional Traceability.