Skip to content

Accept an earlier one-way-door yes; ask refuses re-asking a decided question - #213

Merged
hhff merged 6 commits into
mainfrom
one-way-door-no-reask
Nov 13, 2026
Merged

hhff merged 6 commits into
mainfrom
one-way-door-no-reask

Conversation

@hhff

@hhff hhff commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

What

1. An earlier one-way-door yes counts, whatever kind it was asked as. isDoorQuestion(v) is true for kind: one-way-door or class: one-way-door. The kit now uses it wherever it accepts an owner's one-way-door answer:

  • authorise()'s approve-plan door citation
  • decisionAuthorityGap and answerUseGap
  • act's ticket-level door check and its Goal-level door path
  • the remediation paths

What makes the answer count is unchanged: status: answered from questionView, which means the owner's own line in the owner's file, bound to the question's hash, and never a default. act still refuses a door the owner didn't answer, and the plan review still has to say - Goal-level door: <qid> covers this plan.

These checks are tighter than before (from review):

  • A door answer approves a plan only if the owner answered with its recommendation, when it has one (doorAgreed). This applies to both the cited path and the ticket-level path. Before, an owner's "no" to a door still let approve-plan pass.
  • A door question asked for one ticket never covers another ticket's plan, even when it also carries --goal.
  • Every one-way door is now asked with --recommendation. Without one, an answer would agree by default, so a bare "no" could still approve the plan.
  • Citing a goal-level door for approve-plan no longer uses it up for the one-way-door act itself. authorise (including its --approves branch) and answerUseGap skip those approve-plan calls.

2. The mismatch is stopped at the source. In ask, --class one-way-door with no --kind is recorded as kind: one-way-door. --class one-way-door --kind <anything else> is refused with "ask it --kind one-way-door, not --kind X". The no-default rule now applies to these questions as well: #210's refusal ("a one-way door has no --default") covers the normalised kind.

  • Why it normalises a missing kind but refuses an explicit one: the website supervisor most likely left --kind off, so that case now just works. An explicit other kind has rules of its own (a review's page, an owner-action's secrets) that a silent rewrite would drop. Refusing keeps every per-kind check as it is, and the normalised kind brings the no-default rule and the Inbox floor with it.

3. ask refuses a re-ask. This scope came from the owner: "ensure I am not asked the same question over and over again". ask dies only when all of these hold:

  • The new question is a decision. That means owner-decision, one-way-door, or an owner-action asking for the same secret to the same sink. Clarifications, goal definitions and proposals are only mentioned, because those questions are iterative.
  • The earlier answer is the owner's own. It is answered_by: owner, answered, and hash bound. A defaulted or open question never blocks.
  • The two questions match closely.
  • They are about the same work.
    • A shared ticket or blocked item counts. So do the same goal or feature, unless the two name different tickets, or neither naming any work.
    • Two questions pinned to different draft blobs or artifact hashes are different decisions.
  • The earlier answer could approve the new question.
    • It must be in the same class. An answer in another class can't be cited here, because the kit refuses it, so asking in the right class is not a re-ask.
    • A one-way door covers a door re-asked in any shape. This is the owner's case: a goal-level class-door answer, then --kind one-way-door --ticket <impl ticket>.
  • The earlier answer is not spent. A non-door answer that already approved its one call, or one recorded as a goal change, can't be cited again, so it is only mentioned.
  • The question isn't exempt. Trust, incident-fix, production-remediation, sensor-token questions and the kit's own wordings are exempt.

The refusal names the answer and how to cite it: --approved <qid>, and for a door, act --class approve-plan --approved <qid> plus the Goal-level door line. If the owner answered against the recommendation, the refusal says that answer is their decision.

--new-because "<what changed>" lets the question through. It is recorded on the line as new_because, a new optional schema field covered by the question hash. The dashboard card shows it above the question as "Asked again because: …", and so does questions. If nothing the owner answered is like the question, --new-because is left off with a warning, so a first question never reads "Asked again".

4. Docs. supervisor.md §6 now opens its re-ask rule with "Never ask the owner a decision they have already made: cite the answer. Re-asking is a failure, not caution." It covers class one-way-door, what ask refuses, and --new-because. constitution.md §4 says a question asked in class one-way-door is a one-way-door answer. When the similar earlier answer is a door, ask's warning points at citing it.

Why

On 2026-11-10 the owner answered "yes" to q-20261110-035713266-supervisor. It was a goal-level question with kind: owner-decision, class: one-way-door, about moving the proposal gate into a shared package. When ticket proposals-22 reached approve-plan, the kit only accepted kind: one-way-door, so the supervisor asked again (q-20261113-014021742-supervisor) under a different type. The owner: "this type of shit is really annoying and timeconsuming - you're re-asking me the same question with a different type."

Authority (what a hand-written line can't do)

All agents share one Unix user, and the supervisor writes its own log. Each of these still counts for nothing, and each has a test:

  • A supervisor line setting answer: the integrity check doesn't let the supervisor set answer, so the fold drops the line.
  • A supervisor status: answered: the fold reads answered only from an owner answer line.
  • An owner line in the supervisor's file: the writer must match the file.
  • A supervisor set adding class: one-way-door to an owner-answered question: the supervisor may not set class. The class is also hashed, so a duplicate line that rewrites it reopens the question.
  • A defaulted class-door question: act refuses it, and so does an act under a decision written by hand on it.
  • A door answered "no", or asked for another ticket: approve-plan refuses it.

Tested

  • test/lib/chain.mjs, new step "a one-way door asked in class one-way-door…":
    • A goal-level owner-decision/class: one-way-door yes is cited by decide → act approve-plan (with the review line) and accepted. Then act --class one-way-door --approved that same answer works once.
    • Refused: a defaulted door; a door the supervisor marked answered (three forgery shapes); a class set by the supervisor; a door answered against its recommendation; another ticket's door that names the goal; a hand-written decision on the default; a plan review without the door line.
    • A ticket-level class-door answer passes the no---approved path.
    • ask normalises a missing kind, refuses an explicit other kind, and refuses --default. It refuses the owner's exact re-ask shape (--kind one-way-door --ticket …) and the same --class re-ask, and --new-because records the reason.
  • test/search.test.mjs:
    • A near-identical question is refused. --new-because lets it through, and questions prints "Asked again because: …" before the question.
    • Asked: the same words in another class (with the warning); a question similar only to a defaulted or open one; a clarification asked again; the same words about another goal.
    • A blank reason is refused. A first question's --new-because is left off.
  • Existing tests:
    • Tests that ask the same words again on purpose after the owner's no now pass --new-because: chain sync, chain goal-change, and next-step's strategy test.
    • G1's dashboard golden gains "new_because": null.
  • Run locally (Node 24.14.1, private TMUX_TMPDIR): test/kit.test.mjs (82/82), plus search, approvals, components, merge-admin, plain-questions and next-step. CI runs the rest.

Not changed (noted in review)

  • checkLine still flags a default only on kind: one-way-door. Extending it to class: one-way-door could fail log-check on lines older kits wrote. A hand-written class-door line with a default gets no authority anyway, because act refuses it.

  • The remediation path's isDoorQuestion branch can't be reached through ask, since remediation: blocks are only allowed under class production-remediation. It is there for consistency.

  • The word match is whole-word with no stemming, which leans toward no false positives. A heavily reworded re-ask, such as "moving" plus a ticket id, is only warned about, not refused. A classless answer re-asked in an owner-only class is asked again (with a warning), because the kit can't cite the classless answer there.

  • An earlier owner-action without secrets never blocks a later question.

Migration

  • No action needed. Existing class-door answers start counting as door answers, and new_because is optional.
  • Mixed kit versions. new_because is part of the question hash. A kit older than this one drops the unknown field, so it would read an answered re-asked question as rewritten. Upgrade every box on the project together.
  • After upgrading, a supervisor that re-asks an answered decision gets a refusal naming the answer to cite.

This branch includes #210 (supervisor-bolder-plainer, merged), and #210's ask checks and §6 text are kept.

🤖 Generated with Claude Code

hhff and others added 6 commits November 13, 2026 11:35
A question asked in class one-way-door is a one-way door whatever its kind:
approve-plan's door citation, the Goal-level door line, the ticket-level
door check, the decision-authority check and the remediation paths now
accept it, still only on the owner's own answer, hash bound. ask records
--class one-way-door as --kind one-way-door, refuses another kind, and
refuses --default on a door.

ask refuses a question close to one the owner answered about the same
work, pointing at the answer to cite; --new-because "<what changed>" lets
it through and the owner reads it first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/devteam/dashboard/index.html
#	docs/devteam/scripts/devteam.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A goal-level door is cited only as recommended, never from a ticket-level
  question for another ticket; citing it for plans doesn't use it up for the
  one-way-door act itself.
- ask refuses only decisions (owner-decision, one-way door, a secret already
  asked for) in the same class, or a door re-asked in any shape; worded alike
  word for word (versions and names count whole); not after the answer was
  spent (goal changes included), not for another pinned draft, not when only
  a blocked ticket differs. --new-because is left off a first question.
- Merged #210's own door default refusal in place of ours.

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

- A one-way door is asked with --recommendation, so its answer approves a
  plan only as recommended.
- The --approves once-used scan skips approve-plan door citations too.
- An earlier owner-action without secrets never blocks a question.
- --new-because is kept only on a question that re-asks a decision.

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

hhff commented Nov 13, 2026

Copy link
Copy Markdown
Member Author

Owner request: "ensure I am not asked the same question over and over again". Reviewed at 3f049f2, after the author's adversarial Fable pass. A door answer counts only from the owner's own hash-bound line, agreeing with its recommendation, for the same ticket. Defaults never count. The re-ask refusal needs a close text match, the same work, an owner answer that could approve the new question, and allows --new-because. The ask-side mismatch is normalised or refused. CI 9/9.

@hhff
hhff merged commit 3a62ec4 into main Nov 13, 2026
9 checks passed
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