Skip to content

An owner's answer binds to the question as the owner saw it - #116

Merged
hhff merged 4 commits into
mainfrom
answer-binds
Nov 6, 2026
Merged

hhff merged 4 commits into
mainfrom
answer-binds

Conversation

@hhff

@hhff hhff commented Nov 5, 2026

Copy link
Copy Markdown
Member

What

  • The hole (on main): the supervisor writes its own log file, and the fold resolves a question by at. A hand-written, backdated second new line for an answered question's id became the question, so the owner's earlier "yes" approved whatever that line said. Editing the question's line in place in the live run/log did the same. This covers every answer-backed approval: merge --approved, decisions, goal changes, link bundles (Tickets in other repos: one fleet, one log and one graph across sibling repos #108's review found it there).
  • The fix:
    • question_hash is set on the owner's answer line: a sha256 (16 hex) of every field the question was asked with, excluding the supervisor's later status and reason and the owner's own fields. It's written centrally in appendLog, so answer, the secrets post and goal decide all carry it. In the schema it's owner-only and set-only.
    • questionView: an owner answer whose recorded hash doesn't match the question as it now folds is dropped. The question reads open with rewritten: true, so every consumer that needs status === 'answered' refuses, and the owner answers what it says now. The view also exposes hash.
    • log-check and validate fail on a rewritten question and on any second new line for a question id (questionRewrites()).
    • answer --seen <hash>: the dashboard sends the hash of the question it rendered, and an answer to a question that changed since is refused ("reload and read it again").
  • Tests: in "a chore PR that touches the kit needs an owner answer":
    • both rewrites (a backdated duplicate, and an edit in place) make merge refuse, reopen the question as rewritten, and fail log-check;
    • after restoring the log, the merge goes through again;
    • --seen refuses a wrong hash and accepts the right one.

Why

An answer must approve exactly what the owner saw. The log can't tell kit-written lines from hand-written ones, so the binding has to live in the owner's own line. This is the same class of hole as #87, #88 and #93.

Migration

  • Answers given before this release have no hash and count as before. Only answers given from now on are bound.
  • Existing logs: if a log already has two new lines for one question id, log-check now fails on it. Stacks and stacksbot have none; I checked both read-only.

Local: Node 24.14.1, kit test 43/43.

🤖 Generated with Claude Code

hhff and others added 2 commits November 5, 2026 14:27
The supervisor writes its own log file, so it could put a backdated second
`new` line for an answered question's id (the fold sorts by `at`, so that line
becomes the question) or edit the question's line in place, and the owner's
earlier answer then approved whatever the question now said.

The answer line now records question_hash: a hash of every field the question
was asked with. questionView drops an answer whose question no longer hashes
the same, so the question is open again and every approval that needs an
answer refuses; log-check and validate fail on it, and on any second question
line for an id. The dashboard sends the hash of the question it showed
(answer --seen), so an answer to a question changed since is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…once re-answered

From review of #116:
- an answer from before answers carried the hash had no binding, so a
  backdated second line still got through merge; with a second line for the
  question, nothing shows which one the owner saw, so it no longer counts
- the dashboard shows "This question changed after you answered it" on a
  rewritten question, and keeps the context's line breaks
- the log is append-only, so a leftover second line fails log-check and
  validate only while the question is rewritten; once the owner answers what
  it says now, it warns

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

hhff commented Nov 5, 2026

Copy link
Copy Markdown
Member Author

Review (independent adversarial pass): the backdated duplicate and an edit in place are closed for hashed answers, #108's link-bundle forgery now fails, and the hash covers everything the supervisor can't set. Fixed from that review in 4f77665:

  • Legacy answers: an answer with no hash is voided by a second line for its question.
  • Dashboard: it shows a rewritten question in red. Context keeps pre-wrap.
  • Leftover duplicate line: it fails log-check and validate only while the question is rewritten; once the owner re-answers, it warns.
    Not closed here, and pre-existing: a hand-written owner line. Nothing signs owner lines, so that remains the kit's trust boundary.
    Local Node 24.14.1: 43/43.

hhff and others added 2 commits November 5, 2026 17:08
Its probes are all quick; under load the kit's 20 s default cut them and the
finding the test looks for never appeared (Node 20, a 177 s run).

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

hhff commented Nov 6, 2026

Copy link
Copy Markdown
Member Author

Merging for v0.20.1 (upstream loop).

@hhff
hhff merged commit 289dd32 into main Nov 6, 2026
4 checks passed
hhff added a commit that referenced this pull request Nov 6, 2026
Conflicts resolved keeping main's rules: ownerAnswer (matched, #113),
question_hash (#116), owner_rules (#115), per-entry link binding and
links in home only (#108), one-pass linkify (#107).

Review fixes:
- merge: each changed owner settings class needs its own settling; an
  owner_only class only by the owner's own answer in it (never chat) or
  their standing rule; --approved takes several answers. A ladder-class
  answer (monitor-change) never covers an owner_only key or a kit file.
- merge: a decision is carried out once; settings acts name what settled
  them (answer, rule, decision or rung), and an act a merge logged is
  never a pending landing for a later merge.
- monitors: name set last from the key, a name field refused; credentials
  in a monitor url refused (validate, check-pr/merge); userinfo and query
  never shown (status, alerts, observations, findings, dashboard,
  settings); 2xx only is up; state seeded from the log only when
  state.json is missing; a dead poll's lock is stale at once; no SNI for
  IP hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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