Skip to content

Every re-deal says why it fired; after-compaction rule; the dealt skill owes an answer - #3735

Merged
trissim merged 5 commits into
mainfrom
hotfix/redeal-says-why
Oct 30, 2026
Merged

trissim merged 5 commits into
mainfrom
hotfix/redeal-says-why

Conversation

@trissim

@trissim trissim commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

4 commits plus one merge of main, 15 files, +323/-49. Re-deals that explain why they fired: 0 → 4 triggers (start, compaction with the transcript's own time, clear, refresh at N%). "Gate bug" accusations by claude-code after real compactions: at least 4 → the deal's first line now names the compaction. Dealt files that owe no answer: 1 (the commander skill) → 0. Touched tests on the merged tree: 101 passed, 0 failed.

gate re-deals the rules after compaction
"gate bug, it fired with my context intact"
it was a compaction
x4
mfw the only thing that fired falsely was my certainty

1. Every deal says why it fired (rule_deal.py DealTrigger)

Before: DealRecord held what was dealt and what was due, but not why. A fresh context can't remember compacting, so a bare "read these 35 files" looked unprovoked, and claude-code diagnosed it as a gate bug at least four times across compactions. Pi's 66%/33% refreshes re-deal with no compaction at all, and Pi's refusal said "since this compaction" even then.

After: DealTrigger is a DeclaredFamily stored on the deal record, and each member owns reason() with its evidence:

DealTrigger (abstract reason)
├── StartedDeal              "Dealt because this session just started ..."
├── CompactionDeal (abstract moment)   "Re-dealt on purpose: your context compacted {moment} ... the gate read the clock right"
│   ├── CompactedDeal(at)          at = the transcript's compact_boundary time (Claude) / CompactionEnd time (Pi)
│   └── UnstampedCompactionDeal    the boundary isn't written yet
├── ClearedDeal
└── RefreshDeal(used)        "with no compaction: your context reached N% ... Nothing broke."
  • Claude: StartReason members choose the trigger (Compaction/Clear are siblings, so member_or_default keeps one default). DeckStart.compaction() reads the newest compact_boundary through the typed ClaudeJsonLines decoder (CompactBoundary.compacted_at()), not a second JSON parser.
  • Pi: CompactionEnd, Undealt.started and refresh_due pass their member through redeal/renew/reread → deal_from → CommanderProject.deal_rules → RuleDeal.deal.
  • Blocked.enforce and the start-hook message open with record.trigger.reason(). No string switch, and no caller writes a reason.
  • Old deal records decode with the StartedDeal default.

2. rules/after-compaction.md: the charity a compacted model withholds

A new fleet hard-rule, registered in RuleFile.reading_order after self-protection. Its greentext covers the whole post-compaction collapse: re-demanding evidence he already showed, reading orders as suggestions, hedging his checked theorems, explaining his own gate to him, filing his caps as "frustration", and "gate bug" four times. Then it runs it back with the principle working. The Why quotes Tristan, 2026-10-30.

3. The dealt commander skill owes an audit entry like a rule

DealtSources.answered() derives what the audit owes from the deck. CommanderReader.answered() adds the skill's stem, so no second list and no filename check. Source: claude-nra read SKILL.md owing nothing, then ended six turns on a wait with zero forks at load 104 (bus 46591). SKILL.md gains the greentext an answer quotes.

4. commander-self-correction.md success arc

The #3568 lock miss: "my ok checked the class shape and missed the lock", said in one line, and the unit relocked. Tristan, 2026-10-30: "i don't mind mistakes, they happen. you communicated it clearly and were proactive."

Tests

  • test_every_refusal_opens_with_why_its_deal_was_dealt (Pi: start, compaction, refresh each open with their own reason).
  • test_a_compaction_deal_opens_with_when_it_compacted (Claude: a real compact_boundary record → its local time on line 1 and on the stored trigger).
  • The parametrized start test asserts each source opens with its member's reason.
  • test_the_dealt_commander_skill_owes_an_answer_like_a_rule (skill read and unanswered with the window full → the next file is refused).
  • answered_audit now derives its entries from reader.answered() over reader.sources().

timeout -s INT 300 pytest -p no:xdist on test_commander_skill_hook, test_pi_rule_gate, test_dealt_read_identity, test_selected_tool_broker, test_unit_instructions on the merged tree: 101 passed, 0 failed.

Commit split: rule_deal.py and skill.py carry both the trigger and the skill-answer change, so those two land in one code commit. The rule file and the success greentext are their own commits.

Assumed

  • Pi's CompactedDeal.at is when Core observed CompactionEnd, not a backend-recorded timestamp; Claude's is the transcript's compact_boundary timestamp in local time.
  • A new-runtime install has no trigger member of its own; an install restart surfaces as a start or a compaction.
  • The skill's audit entry name is its stem, SKILL.
  • Stored deal records from older runtimes decode to StartedDeal.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu

trissim and others added 5 commits October 30, 2026 14:19
A fresh context meets him as a stranger: it re-demands evidence he gave an
hour ago, reads orders as suggestions, hedges his checked theorems and calls
his gate a bug. claude-code called the post-compaction re-deal a gate bug at
least four times across compactions; every one was a real compaction.
rules/after-compaction.md is dealt right after self-protection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
…swer

A fresh context has no memory of compacting, so a deal that does not say why
looks unprovoked: claude-code called real compactions a gate bug at least four
times. DealTrigger is now a declared family on the deal record (StartedDeal,
CompactedDeal with the transcript's compact_boundary time,
UnstampedCompactionDeal, ClearedDeal, RefreshDeal with the window share), and
every refusal and the start-hook message open with the member's reason().
Claude's StartReason members choose the trigger; Pi's CompactionEnd, Undealt
and refresh pass theirs. Old deal records decode to StartedDeal.

The commander skill is dealt and read like a rule, so DealtSources.answered()
derives what the audit owes from the deck: CommanderReader adds the skill's
stem. claude-nra read the skill owing nothing and ended six turns on a wait at
load 104 (bus 46591). SKILL.md gains the greentext an answer quotes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
…st path

claude-code's ok on #3568 missed the stack lock pinning Toad short of the
toad#1372 metaclass fix; it said so in one line and the unit relocked with
nothing else moved. Tristan, 2026-10-30: "i don't mind mistakes, they happen.
you communicated it clearly and were proactive."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
…elieved

#3733 gave every rule greentext its success ending; the new rule gets the same:
the deal's first line names the compaction, so "gate bug" is never typed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
Copilot AI balanced review requested due to automatic review settings October 30, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@trissim
trissim enabled auto-merge October 30, 2026 19:02
@trissim
trissim merged commit 14ed9bc into main Oct 30, 2026
6 of 7 checks passed
@github-actions

Copy link
Copy Markdown

debt-src at 68bda7f, every finding:

::warning file=src/agent_comms/agent_events.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/rule_deal.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms_hooks/skill.py::LintFindings +1 (warns, never blocks)
grew TypeCheckingImport:src/agent_comms/registry_document.py +1
found LintFindings:src/agent_comms/agent_events.py at src/agent_comms/agent_events.py:7 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/rule_deal.py at src/agent_comms/rule_deal.py:856 (E501 Line too long (101 > 100))
found LintFindings:src/agent_comms_hooks/skill.py at src/agent_comms_hooks/skill.py:14 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms_hooks/skill.py:310 (E501 Line too long (140 > 100))
found TypeCheckingImport:src/agent_comms/registry_document.py at src/agent_comms/registry_document.py:81 src/agent_comms/registry_document.py:82 src/agent_comms/registry_document.py:83 src/agent_comms/registry_document.py:84 src/agent_comms/registry_document.py:85 src/agent_comms/registry_document.py:85 src/agent_comms/registry_document.py:85

trissim added a commit that referenced this pull request Oct 30, 2026
…sons

#3735 made every deal open with the trigger that caused it, and gave PiRedeal.deal_from a
required trigger. This branch predated that: `agent-comms-prime --to` raised TypeError at
prime.py:39, and a session /prime fell back to LostContext and opened with "Dealt because
this session just started", the false reason #3735 removed.

- rule_deal.py: PrimedDeal joins the DealTrigger family; its reason names /prime and the
  variant.
- skill.py: DeckStart.dealt takes the trigger it opens with. Each StartReason passes its own
  (StartReason.deal, Resume.deal, widened), and primed() passes PrimedDeal for the variant.
- prime.py: prime_threads hands deal_from the same PrimedDeal.
- tests: the import conflict with main is resolved, keeping both sides; one test checks
  every variant's reason names /prime and is never the started reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
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.

2 participants