From fd2096a90c3a20ed4b47420ca4902d12e8e2cc86 Mon Sep 17 00:00:00 2001 From: superwesleyhys-ux <251160695+superwesleyhys-ux@users.noreply.github.com> Date: Wed, 16 Sep 2026 20:16:41 +0800 Subject: [PATCH] docs(catalog): add IP-033 recorded rejection is not absent authority The standing-decision contract treats an explicit `reject` or `cancel` as a recorded receipt whose scope, owner and chronology survive exactly like an approval's, while activation stays bound to `decision_outcome === "approve"`. No catalog pattern named that boundary, so a surface could render "no active approval" as "no decision was made" and re-ask a scope the operator already refused, or over-read a retained, done, global todo as the approval it structurally resembles. Add the pattern under Human Decision with its trigger, three expected-behavior rules, visual model, bad smells and validation; list it in the Pattern-To-Canary matrix; and pin the entry in examples/interaction-pattern-catalog-smoke.py. The fixture slice that exercises the invariant lands with GH-C102 (#4541). Refs GH-C37 Signed-off-by: superwesleyhys-ux <251160695+superwesleyhys-ux@users.noreply.github.com> --- docs/concepts/interaction-pattern-catalog.md | 81 ++++++++++++++++++- examples/interaction-pattern-catalog-smoke.py | 3 + 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/concepts/interaction-pattern-catalog.md b/docs/concepts/interaction-pattern-catalog.md index 249dd44c29..abf2d0c651 100644 --- a/docs/concepts/interaction-pattern-catalog.md +++ b/docs/concepts/interaction-pattern-catalog.md @@ -87,7 +87,7 @@ Map P0/P1 catalog rows to canary archetypes before picking commands: | Family | P0/P1 Pattern Coverage | Default Canary Archetypes | Trigger Surfaces | Minimum Useful Fixture | Failure Meaning | | --- | --- | --- | --- | --- | --- | | Work Routing | IP-001, IP-002, IP-003, IP-007, IP-008, IP-021, IP-029 | Hot-path route canary; Planning governance canary when cadence or repair is involved | `quota should-run`, `interaction_contract`, `work_lane_contract`, scheduler hint, handoff todo state | one eligible delivery fixture, one blocked/fallback fixture, one quiet or monitor fixture | agent turn routing is unsafe: it may spend, wait, notify, or choose fallback incorrectly | -| Human Decision | IP-004, IP-014, IP-017, IP-027, IP-030 | Scoped decision canary; Product/readiness canary when first-screen human copy changes | user todos, decision scope, operator-gate/reward preview, deferred resume candidates | one concrete user ask, one scoped non-blocking gate, one preview-or-append dry run | humans may be asked the wrong question, or an agent may continue without the needed decision | +| Human Decision | IP-004, IP-014, IP-017, IP-027, IP-030, IP-033 | Scoped decision canary; Product/readiness canary when first-screen human copy changes | user todos, decision scope, operator-gate/reward preview, deferred resume candidates | one concrete user ask, one scoped non-blocking gate, one preview-or-append dry run | humans may be asked the wrong question, or an agent may continue without the needed decision | | State And Boundary | IP-005, IP-006, IP-011, IP-016, IP-019, IP-020, IP-022, IP-023, IP-025, IP-026, IP-028, IP-031, IP-032 | Projection and boundary canary; Hot-path route canary when the projection feeds quota/status | active state, todo metadata, task graph, authority source, claim lease, completed-work archive, connector runtime policy, public/private scan | fixture state plus structured projection check; boundary scan for touched public files | compact state and executable truth diverge, so dashboards and agents may trust stale or unsafe authority | | Evidence Lifecycle | IP-012, IP-015 | Evidence lifecycle canary; Product/readiness canary when evidence is rendered | external handle observation, benchmark lifecycle reducer, compact result projection | compact public-safe evidence fixture with raw-material exclusion assertions | progress evidence may be missing, double-counted, or represented with unsafe raw material | | Planning Governance | IP-010, IP-013, IP-018, IP-024 | Planning governance canary; Hot-path route canary when cadence changes affect execution | stalled run history, autonomous replan obligation, repair delta, cadence hint, plan-to-todo writeback | two-turn stalled fixture plus repair/writeback delta assertion | the agent may keep planning in prose while the machine-visible frontier stays unchanged | @@ -321,6 +321,7 @@ Human asks, approvals, interventions, and reward-derived lessons. | P0 | IP-014 | Decision Write Preview And Append | User/operator | explicit preview/apply decision | append only exact run-bound reward or gate decision event | | P1 | IP-017 | User Reward Lesson Promotion | User plus LoopX | acknowledge only when lesson changes route/priority/boundary | promote correction into durable lesson, todo, or projection before continuing | | P1 | IP-030 | Machine Configuration Preview And Revision-Guarded Apply | User plus agent | require explicit approval of the exact plan revision | preview the exact change, then apply, remove, or roll back only the matching revision | +| P1 | IP-033 | Recorded Rejection Is Not Absent Authority | User plus LoopX | no interruption; the refusal is already recorded | read the recorded outcome; do not re-ask a settled scope or infer approval from a missing rejection | | P2 | IP-009 | Active User Assistance | User simulator / operator | bounded intervention | inject audited user help without leaking reward/oracle signals | ### State And Boundary @@ -1294,6 +1295,84 @@ reward signals, oracle information, or unbounded human hints. - `examples/worker-bridge-install-contract-smoke.py` - benchmark active-user protocol docs. +#### IP-033 Recorded Rejection Is Not Absent Authority + +**Trigger** + +- a user gate carries a typed `decision_scope` together with an explicit + `decision_outcome` of `reject` or `cancel`; +- the gate is broad (`granularity` of `goal`, `project`, or `global`) and is + either `global_gate=true` or `blocks_agent`-scoped, so the same record would + qualify as standing authority if its outcome were `approve`; and +- a later turn, projection, archive pass, or operator surface has to answer + "has this scope already been decided?". + +**Expected behavior** + +A recorded rejection is a decision, not the absence of one. Three rules keep +the two apart. + +1. **Record it.** The todo stays a standing decision receipt + (`standing_decision_receipt_v0`) whose scope, owner, and chronology are + preserved exactly like an approval's. Archive retains it for the same reason + it retains an approval, and `retained_standing_decision_count` counts it. +2. **Do not activate it.** Activation is `decision_outcome === "approve"` and + nothing else. `reject` and `cancel` raise `inactive_count` and never + `active_count`. "No active approval" must not be rendered as "no decision was + made", and a mixed or undated chronology must resolve to a recorded conflict + rather than silently choosing approval. +3. **Read the outcome; do not infer it.** A consumer that needs to know whether + a scope was settled reads the recorded receipt. It may not treat a missing + rejection as an approval, and it may not treat a retained, done, global todo + as authority when the recorded outcome says otherwise. + +IP-014 owns how a decision is written and previewed, and IP-032 owns what +happens to a durable decision when its todo leaves the active window. Neither +owns the meaning of an explicitly refused decision, which is the gap this +pattern fills. + +**Visual Model** + +```mermaid +flowchart TD + A["user gate with typed decision_scope
and explicit decision_outcome"] --> B{"outcome"} + B -->|"approve"| C["standing receipt, active=true
active_count += 1"] + B -->|"reject / cancel"| D["standing receipt, active=false
inactive_count += 1"] + B -->|"missing or mixed chronology"| E["conflict, no silent approval"] + C --> F["later turns read settled approval"] + D --> G["later turns read settled refusal
do not re-ask as undecided"] + E --> H["standing_decision_order_unresolved"] +``` + +**Bad smell** + +An operator refuses a broad write scope. The surface only renders active +authority, so the refusal disappears from view and two turns later the agent +re-proposes the same write scope as though it had never been considered. The +operator experience is "I already said no" followed by "why is this being asked +again". + +The mirror-image smell is over-reading a retained record: because the todo is +done, global, and retained by archive, a consumer treats the refusal as the +approval it structurally resembles. A third smell is a fixture or projection +that only ever generates approvals, so no test can tell a refused scope from an +unasked one. + +**Validation** + +- `tests/control_plane_ts/production_scale_rejected_decision.test.ts` owns the + mutation and negative cases: a rejection is a receipt, only an explicit + approval activates a scope, and dropping the typed scope leaves no entry. + It arrives with the GH-C102 fixture slice (#4540). +- `tests/control_plane_ts/authority_store_conformance.ts` asserts + `inactive_count` and per-entry `active` on every provider conformance arm. +- `tests/fixtures/control_plane/coordination_production_scale_v0.json` and + `tests/control_plane_ts/production_scale_coordination_fixture.ts` carry the + shared rejection band. +- `loopx/control_plane/todos/standing_decision.ts` owns the predicate and the + projection. +- `examples/interaction-pattern-catalog-smoke.py` protects this entry. + ### State And Boundary #### IP-005 State Projection Gap diff --git a/examples/interaction-pattern-catalog-smoke.py b/examples/interaction-pattern-catalog-smoke.py index d267513bc3..eb6f9ed178 100644 --- a/examples/interaction-pattern-catalog-smoke.py +++ b/examples/interaction-pattern-catalog-smoke.py @@ -143,6 +143,9 @@ def main() -> int: "retained_standing_decision_count", "The role defaults to `agent`", "examples/control_plane/todo-archive-completed-smoke.py", + "IP-033 | Recorded Rejection Is Not Absent Authority", + "A recorded rejection is a decision, not the absence of one.", + "`inactive_count`", "## Catalog Maintenance And Validation Design", "Do not add\na new IP merely because a maintainer needs a validation technique", "Those are uses of the\ncatalog, not catalog patterns by themselves.",