feat(goals): judge a goal on whether the number moved, not on whether work happened (v0.438.0) - #813
Merged
Merged
Conversation
… work happened (v0.438.0) The Goals plane measured activity. `stuck` asks whether a goal has had events lately, which is the wrong question: a goal can be busy and failing, or quiet and succeeding. On the live fleet 5.6% of tasks link to a goal and goals stall at ~2 events each — an outcome layer nobody uses, because nothing in it reads a real number. Goals gain an optional METRIC (name, unit, target, baseline, direction, expected interval) and an append-only `goal_readings` table. `GoalStore.metricStatus` turns readings into a verdict: measuring / flat / regressing / achieved / unmeasured / new. The verdict refuses confidence on thin evidence. flat/regressing need >=3 readings spanning the metric's own interval, and movement below 5% of the distance to target — so one wobble, or three readings taken the same afternoon, reads as `new`, not failure. `direction` is first-class because incidents / p95 / churn goals go DOWN and a reviewer assuming up-is-better reads them backwards. `unmeasured` is distinct from `flat` on purpose: "nobody is measuring this" and "this isn't working" call for opposite responses. The review (src/edge/goal-review.ts) runs hourly off the scheduler tick and SPAWNS NOTHING — the judgement is arithmetic, and a review costing a session per tick would bill a tenant for being told nothing changed. One standing card per goal, to its owner, only when the verdict CHANGES (guard = the `goal.reviewed` audit event, so a restart never re-alarms). The response stays human: the existing Plan button, and `metricBrief` now puts the verdict into the strategist's prompt in words, so a flat goal is told to try a DIFFERENT approach instead of filing more of the same. `goal_measure` lets an agent record a reading — the one goal write agents get besides proposing. They report the number; the metric, its target and the goal's status stay human-owned, and every reading records who took it so a self-reported number is visibly self-reported. Pinned by scripts/goal-metric-review-test.cjs (32 assertions, wired into test:governance). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014hX85iZ5BhHypPNrob8vnH
vikasprogrammer
force-pushed
the
feat/goal-metrics
branch
from
September 10, 2026 12:36
de7b22b to
2127d1a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The Goals plane measures activity.
stuckGoalsasks whether a goal has had events lately — which is the wrong question, because a goal can be busy and failing, or quiet and succeeding.The live fleet shows what that produces:
Goals get created, receive about two events, and go quiet. It's an outcome layer nobody uses, because nothing in it reads a real number.
What
A metric on the goal. Optional
metric(name, unit, target, baseline,direction, expected interval) plus an append-onlygoal_readingstable.GoalStore.metricStatusturns readings into a verdict:measuring·flat·regressing·achieved·unmeasured·new.A deterministic review (
src/edge/goal-review.ts), hourly off the scheduler tick. One standing card per goal, to its owner, raised only when the verdict changes.The strategist is told the verdict, in words (
metricBrief, in both the plan run and the goal room). A flat goal is instructed to say what isn't working and plan a different approach; an unmeasured goal is instructed to establish the measurement first.goal_measure— an agent records a reading, so a run whose job is measuring closes the loop.Console — the goal drawer shows the metric, verdict, movement, a sparkline, and one line to record the next reading.
The three judgement calls
It refuses to be confident on thin evidence.
flat/regressingneed ≥3 readings spanning the metric's own interval, and movement below 5% of the distance to target. One wobble, or three readings taken the same afternoon, reads asnew. A review that cries wolf gets ignored, and then the metric was worse than useless.It spawns nothing. The judgement is arithmetic. A review that cost a session every tick would bill a tenant for the privilege of being told nothing changed. The response — re-plan, retire, adjust the target — is the human's, through the button that already exists.
Agents report the number; they don't move the goalposts.
goal_measureis the only goal write agents get besides proposing. Metric, target and status stay human-owned, and every reading records who took it, so a number reported by the same agent that did the work is visibly self-reported.Also:
directionis first-class because incidents, p95 and churn goals go down, and a reviewer that assumes up-is-better reads every one of those backwards. Andunmeasuredis deliberately separate fromflat— "nobody is measuring this" and "this isn't working" call for opposite responses.Test
scripts/goal-metric-review-test.cjs— 32 assertions, wired intotest:governance. Most of them are about the review not lying: one reading isn't flat, three same-day readings aren't flat, a falling down-metric is progress, a stale goal isunmeasuredrather than failing, and three consecutive reviews leave one card.Full
npm run test:governancegreen (exit 0); typecheck and both bundles build. A goal with no metric behaves exactly as it did before — the review skips it and the strategist prompt says nothing.Not in scope
Reading the number automatically from a source of truth (Ahrefs, GA4, an incident tracker). Readings arrive from a human or from an agent that measured — wiring a specific data source is a connector job, per goal, and belongs after we've seen which metrics people actually set.
🤖 Generated with Claude Code
https://claude.ai/code/session_014hX85iZ5BhHypPNrob8vnH