Skip to content

Goal measures name their source: repo, prod or owner - #93

Merged
hhff merged 5 commits into
mainfrom
goal-measure-sources
Nov 4, 2026
Merged

hhff merged 5 commits into
mainfrom
goal-measure-sources

Conversation

@hhff

@hhff hhff commented Nov 4, 2026

Copy link
Copy Markdown
Member

What

  • schema/log.schema.json: goal.measure gains source (repo | prod | owner), check (repo and prod: a command whose last line of output is the number) and question (owner: the yes/no that says it's met). Reading observations may carry sha (the commit on main a repo check ran on) and approved (the goal-met question an owner reading rests on).
  • governance/constitution.md: goal-met joins owner_only, so it's never delegated, never defaulted, and never answered from chat.
  • scripts/devteam.mjs:
    • goal reading <id> --run runs a repo check on a throwaway checkout of main (recording main's sha), or a prod check from the main checkout (keeping its output). --value is refused for repo and prod goals.
    • goal reading <id> --approved <qid> records an owner goal's 1, only from a goal-met question about that goal that the owner answered with its recommendation. Each answer authorises one reading.
    • ask --class goal-met needs --goal (an owner-read goal) and a recommendation, and takes no --default.
    • goal check lists a missing source or check/question. Owner measures imply target 1, unit attested, direction up and a baseline of 0.
    • goal status <id> met reads a repo check again on main and needs an owner goal's yes. --force is the owner's override.
    • The fold: goalReadings counts only readings that match the goal's source, whoever wrote the line. A repo reading needs a sha on main. An owner reading needs the owner's own non-chat line, or an approved goal-met question answered in its recommendation, not in chat, before the reading was written, and used once.
  • process/planning.md §1, process/supervisor.md: the measure is what the fleet can move by merging PRs and read without anyone outside the dev cycle. The business result stays in the outcome and never gates met. Each of the three sources has an example. A measure that depends on another team's work or on production time passing becomes an owner question.
  • test/kit.test.mjs:
    • The existing goals get prod sources.
    • A new subtest covers the gaps, repo --run at main's sha, --value refused, and prod.
    • It covers owner attestation: the good path, the wrong class, unanswered, a non-recommendation answer, a chat answer, and a reused question.
    • It checks that hand-written supervisor readings are ignored, including one that cites a question before it was answered.

Why

A measure only had to be a number, so goals drifted toward business results the dev cycle can't produce, such as a monthly count of manual fixes in production, or a backlog that only empties when another team does its work. Such a goal can't be met by merging PRs, and its readings were whatever the supervisor typed: goalReadings accepted any reading line from the supervisor. Readings drive the current milestone and the forecast, so a typed number moved them. Now the number comes from a check the kit runs, from production output that's kept with the reading, or from the owner's own yes.

Known limit: a prod reading is still the supervisor's report of a run (the fold can't re-run a production query). The output is stored for the owner to compare, and met stays the owner's to set.

Migration

  • Goals without measure.source get a gap in goal check. For a draft, it blocks approval. For a running goal, it's listed under coverage, and the goal keeps its readings as before.
  • The supervisor files one goal-change question per goal with the new measure (--change '{"measure": {…, "source": …, "check"|"question": …}}') and applies the owner's answer with goal set <id> --approved <qid>.
  • An owner-read goal's yes is asked with ask --class goal-met --goal <id> --question "<measure.question>" --recommendation yes once its work is merged.

🤖 Generated with Claude Code

hhff and others added 5 commits November 4, 2026 14:37
A goal's measure had to be a number, and nothing asked whether the fleet could move it or read it.
So measures crept toward business results the dev cycle can't produce: a monthly count of manual
fixes in production, or a backlog another team works through. A goal could then never be met by
merging PRs, and its readings were whatever the supervisor typed.

Each measure now says how it is read. repo: measure.check runs on a throwaway checkout of main and
the reading records main's sha. prod: a read-only check, run from the main checkout, keeping its
output. owner: a yes/no measure.question (target 1, unit attested, from 0), asked as a goal-met
question (owner_only: never defaulted, never answered in chat), and recorded only from the owner's
yes, one reading per answer. The fold counts only readings that match the source, whoever wrote the
line: a repo reading needs a commit on main, and an owner reading needs the owner's own line or an
answered goal-met question it was written after. goal status met reads a repo check again on main
and needs an owner goal's yes (--force overrides). A draft can't be approved without a source; a
running goal without one keeps its old readings and shows the gap for a goal-change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
goal status <id> met re-runs a repo goal's check, from the owner's terminal or the dashboard. The
check is the supervisor's command (it drafted the goal), and the owner is whoever has no
DEVTEAM_ROLE, so the check ran as the owner: `devteam.mjs answer <qid> yes; echo 0` would have
written owner lines stamped with the owner's own channel, indistinguishable from the owner's. The
check now runs with DEVTEAM_ROLE=supervisor and no DEVTEAM_CHANNEL: owner commands refuse it,
and what it writes is the supervisor's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hhff

hhff commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Review (upstream loop), adversarial, focused on what a hand-written supervisor line can do:

  • Found and fixed (6ebe138, 8824b57): goal status <id> met re-runs a repo goal's measure.check in the caller's environment. The owner is whoever has no DEVTEAM_ROLE, so the supervisor-authored check ran as the owner, from the owner's terminal or the dashboard. A check like devteam.mjs answer <qid> yes; echo 0 would have written owner lines stamped with the owner's own channel. Checks now run with DEVTEAM_ROLE=supervisor and no DEVTEAM_CHANNEL, DEVTEAM_TICKET or DEVTEAM_FEATURE. Owner commands refuse them, and what they write is the supervisor's. The new subtest has a check try goal status <other> abandoned during the owner's met, and asserts it's refused. planning.md says so.
  • Verified holding:
    • goalReadings filters by source for every line, hand-written or not: repo needs a sha on main, prod needs source prod, owner needs the owner's line (not chat) or an approved goal-met qid that passes goalMetProblem. That qid has to be answered before the line, matching the recommendation, not via chat, and used once.
    • The supervisor can't swap measure.check on an approved goal: the fold ignores supervisor edits to non-draft goals without an answered goal-change.
    • goal-met is in owner_only, and ask refuses --default on it.
  • Known limits, stated in the docs: a prod reading is the supervisor's report of a run, since the kit can't re-run production. A repo reading's number isn't re-verified until met, which re-runs the check on main.
  • Merged main (A worktree runs the main checkout's kit; a move names missing pinned tools #92, Workers start without the supervisor's agent-session markers; act classes read as act #94, Fleet-park test: give the pane 15s to show the agent command #95), clean. CI 18/20/22/24 green at 5b05a8e. Local 24.14.1 and 20.19.6: 43/43. Local 18.20.4's one failure is the wait race that predates this PR, fixed separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant