diff --git a/.claude/hooks/check-squash-subject.sh b/.claude/hooks/check-squash-subject.sh index 92ada41b6..841eb536a 100755 --- a/.claude/hooks/check-squash-subject.sh +++ b/.claude/hooks/check-squash-subject.sh @@ -28,6 +28,15 @@ # is worse than no guard, because it looks present. Note the program cannot simply read stdin # instead: stdin is where the heredoc delivers the program. # +# THE BODY IS GUARDED TOO, for squash merges. A squash body override (--body/-b/--body-file/-F) +# must carry a `Co-authored-by:` trailer - any merge performed through this harness is +# agent-assisted by definition, and the sign-off is the convention master's squash commits carry - +# and must NOT carry a `Claude-Session:` line, which belongs on ordinary commits only +# (docs/merge-checklist.md owns both rules). A --body-file is read when it is a readable file; +# when it is not (process substitution not yet materialised, typo), the hook fails open because +# `gh` itself will then fail loudly on the missing file - a guard needs no teeth where the tool +# already bites. +# # FAILS OPEN otherwise, deliberately: unparseable JSON, unbalanced quotes, no python3. A hook that # blocks on its own bug jams the tool call shut, and docs/merge-checklist.md still carries the rule # for anyone merging by hand. @@ -81,28 +90,90 @@ for start, end in zip(starts, starts[1:] + [len(cmd)]): break i += 1 - if subject is None: - continue # no override - GitHub appends the number itself - if re.search(r"\(#\d+\)\s*$", subject): - continue # ends with the number, which is the whole rule + def deny(reason): + print(json.dumps({ + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": reason + " See docs/merge-checklist.md.", + } + })) pr = next((t for t in tokens[tokens.index("merge") + 1:] if t.isdigit()), None) \ if "merge" in tokens else None hint = f"(#{pr})" if pr else "(#)" - print(json.dumps({ - "hookSpecificOutput": { - "hookEventName": "PreToolUse", - "permissionDecision": "deny", - "permissionDecisionReason": ( - f"This --subject does not end with {hint}. Overriding the subject suppresses the " - "PR number GitHub would otherwise append, so the commit lands out of step with " - "every neighbour on master - and that is not fixable afterwards without rewriting " - f"a pushed commit (astubbs#206). End the subject with ' {hint}', or drop the flag " - "and let the PR title be used. --body/--body-file do not affect the subject. " - "See docs/merge-checklist.md." - ), - } - })) - break + if subject is not None and not re.search(r"\(#\d+\)\s*$", subject): + deny( + f"This --subject does not end with {hint}. Overriding the subject suppresses the " + "PR number GitHub would otherwise append, so the commit lands out of step with " + "every neighbour on master - and that is not fixable afterwards without rewriting " + f"a pushed commit (astubbs#206). End the subject with ' {hint}', or drop the flag " + "and let the PR title be used. --body/--body-file do not affect the subject." + ) + break + + # Squash-body sign-off: same token walk as the subject, for the body flags. + if "--squash" not in tokens and "-s" not in tokens: + continue + body = None + i = 0 + while i < len(tokens): + t = tokens[i] + if t in ("--body", "-b") and i + 1 < len(tokens): + body, i = tokens[i + 1], i + 2 + continue + if t in ("--body-file", "-F") and i + 1 < len(tokens): + path = tokens[i + 1] + i += 2 + try: + with open(path, encoding="utf-8") as fh: + body = fh.read() + except OSError: + body = None # gh will fail loudly on it; nothing to guard + continue + for flag in ("--body", "--body-file"): + if t.startswith(flag + "="): + val = t[len(flag) + 1:] + if flag == "--body": + body = val + else: + try: + with open(val, encoding="utf-8") as fh: + body = fh.read() + except OSError: + body = None + break + else: + for flag in ("-b", "-F"): + if t.startswith(flag) and len(t) > 2: + val = t[2:] + if flag == "-b": + body = val + else: + try: + with open(val, encoding="utf-8") as fh: + body = fh.read() + except OSError: + body = None + break + i += 1 + + if body is None: + continue # no override, or unreadable file gh will reject itself + if not re.search(r"^co-authored-by:", body, re.I | re.M): + deny( + "This squash --body carries no Co-authored-by trailer. A merge performed through " + "this harness is agent-assisted, and master's squash commits carry exactly one " + "sign-off line: 'Co-authored-by: Claude (1M context) '. " + "Add it as the body's final line." + ) + break + if re.search(r"^claude-session:", body, re.I | re.M): + deny( + "This squash --body carries a Claude-Session: line. Squash-merge commits on master " + "carry only the Co-authored-by trailer; the session link belongs on ordinary " + "branch commits, not the permanent squash record. Remove the Claude-Session line." + ) + break PY diff --git a/bin/test-check-agent-hooks.sh b/bin/test-check-agent-hooks.sh index 31454b535..5564a8580 100755 --- a/bin/test-check-agent-hooks.sh +++ b/bin/test-check-agent-hooks.sh @@ -88,9 +88,28 @@ expect DENY "-t= equals form" 'gh pr merge 2999 --squash -t expect DENY "-tVALUE attached short form" 'gh pr merge 2999 --squash -tfoo' expect ALLOW "-tVALUE attached, with (#N)" 'gh pr merge 2999 --squash -t"foo (#2999)"' expect DENY "(#N) present but not at the END" 'gh pr merge 2999 --squash --subject "port (#2999) to master"' + +# Squash-body sign-off guard: a squash body override must carry the Co-authored-by trailer and +# must not carry a Claude-Session line (docs/merge-checklist.md owns both). Non-squash merges and +# merges without a body override are out of scope; an unreadable --body-file fails open because +# gh itself then fails loudly. +body_ok=$(mktemp); printf '%s\n%s\n' "Real explanation." "Co-authored-by: Claude Opus 5 (1M context) " > "$body_ok" +body_bare=$(mktemp); printf '%s\n' "Real explanation, no trailer." > "$body_bare" +body_sess=$(mktemp); printf '%s\n%s\n%s\n' "Real explanation." "Co-authored-by: Claude Opus 5 (1M context) " "Claude-Session: https://claude.ai/code/session_x" > "$body_sess" +expect DENY "squash --body without co-authored trailer" 'gh pr merge 2999 --squash --body "no trailer here"' +expect ALLOW "squash --body with co-authored trailer" 'gh pr merge 2999 --squash --body "why. +Co-authored-by: Claude Opus 5 (1M context) "' +expect DENY "trailer mid-line is not a trailer" 'gh pr merge 2999 --squash --body "why. Co-authored-by: Claude Opus 5 (1M context) "' +expect DENY "squash body with Claude-Session line" "gh pr merge 2999 --squash --body-file $body_sess" +expect ALLOW "squash --body-file with trailer" "gh pr merge 2999 --squash --body-file $body_ok" +expect DENY "squash --body-file without trailer" "gh pr merge 2999 --squash --body-file $body_bare" +expect ALLOW "squash --body-file unreadable fails open" 'gh pr merge 2999 --squash --body-file /nonexistent/nope.txt' +expect ALLOW "non-squash merge body needs no trailer" 'gh pr merge 2999 --merge --body "no trailer needed"' +rm -f "$body_ok" "$body_bare" "$body_sess" expect ALLOW "--subject ending with (#N)" 'gh pr merge 2999 --squash --subject "foo (#2999)"' expect ALLOW "-t ending with (#N)" 'gh pr merge 2999 --squash -t "foo (#2999)"' -expect ALLOW "--subject mentioned inside --body" 'gh pr merge 2999 --squash --body "why --subject matters"' +expect ALLOW "--subject mentioned inside --body" 'gh pr merge 2999 --squash --body "why --subject matters +Co-authored-by: Claude Opus 5 (1M context) "' expect ALLOW "escaped apostrophe, number present" 'gh pr merge 2999 --squash --subject '"'"'don'"'"'\'"'"''"'"'t drop it (#2999)'"'"'' expect ALLOW "--body-file does not touch subject" 'gh pr merge 2999 --squash --body-file b.txt' expect ALLOW "no subject override at all" 'gh pr merge 2999 --squash' diff --git a/docs/ideation/2026-08-17-actor-collection-revival-ideation.html b/docs/ideation/2026-08-17-actor-collection-revival-ideation.html new file mode 100644 index 000000000..71b1e3b0f --- /dev/null +++ b/docs/ideation/2026-08-17-actor-collection-revival-ideation.html @@ -0,0 +1,383 @@ + + + + + +Ideation: Reviving the Micro Actor Branch Collection + + + +
+ +
+
Ideation · repo-grounded
+

Reviving the Micro Actor Branch Collection

+
+ Date: 2026-08-17 + Topic: actor-collection-revival + Focus: "look at the upstream map and lets talk about reviving my micro actor collection of branches" +
+
+ +
+
~55
raw ideas
+
6
survivors
+
5
frames run
+
1
basis refuted
+
5/5
axes covered
+
+ +
+

Grounding Context

+

Codebase context. The fork carries five abandoned 2022 branches implementing a "Micro Actor framework for structured, ordered, simple IPC" (the author's own javadoc): improvements/lambda-actor-bus, commit-command-actor, poller-bus-actor, async-process-send-results-using-actor, actor-scheduled — plus three dependent branches (transactions-dont-block, scheduled-commit, remove-commit-queue). Tips date Jul–Dec 2022; master has since been fully package-renamed (io.confluent.* → bz.stub.*). The framework proper is 537 lines in 4 files in its own package, coupled to PC by a single 16-line marker interface. The God class (AbstractParallelEoSStreamProcessor) is 1534 lines and has moved by one line since the refactor index was written; master core carries 10 synchronized, 11 AtomicBoolean, 0 ReentrantLock.

+

Recorded verdicts under test. (1) docs/refactoring.md:151: the actor work is "Only meaningful as part of the confluentinc#200 rework." (2) upstream-pr-analysis.adoc Group C: "keep ideas, not code — branches are far too stale to apply directly." Both were written before the rename tooling, chaos harness maturity, and the map itself existed — and the fork's own tables already contradict them once (async-produce is ranked Group B, "throughput-critical").

+

Tracking gaps found during grounding. The actor family has no upstream-map.yaml entry, no fork issue, and no fork PR; the one adjacent map entry (sweep-2023-async-produce) records a wrong branch name and a stale SHA. Live adjacent work: bug-857 (poll/control synchronized(commitCommand) deadlock) is still open in draft PR astubbs#29 with recorded chaos replay seeds; the blocking futureSend.get(sendTimeout) that confluentinc#356 was written to remove is verified still in master source today.

+
+ +
+

Topic Axes

+
    +
  • A1 Framework — the csid.actors package as a standalone, testable, shippable artifact
  • +
  • A2 Call-site conversion — which coordination point converts to actors, in what order, how far
  • +
  • A3 Salvage mechanics — moving 2022 code across 3.5 years of drift plus a full package rename
  • +
  • A4 Proof — demonstrating a conversion fixed something real and broke nothing
  • +
  • A5 Framing — tracking, verdict revision, sequencing against astubbs#142/confluentinc#200 and the God-class work
  • +
+
+ +
+

Ranked Ideas

+ + + +
+
1

The one-day falsification pipeline

+
A1 Framework + A3 Salvage + A4 ProofConfidence 85%Complexity Low–Medium
+ +
Description + Run bin/rename-packages.sh over just the four framework files from lambda-actor-bus (Actor, ActorImpl, FunctionWithException, Interruptible — the "datum shift"), compile them standalone against current master in bz.stub.*, run their 2022 unit tests, then add a javadoc-derived conformance suite — starting with the explicitly documented FILO ordering among tellImmediately messages — and a PIT mutation score using the repo's existing mutation lane. No call site converts; nothing else on the branch comes along.
+ +
+ + + + 4 files, 537 lines + io.confluent.csid.actors + + + rename script + datum shift → bz.stub.* + + + compile standalone + germination test + + + 2022 tests + + FILO conformance + + PIT mutation score + existing lane + +
Both standing verdicts become measurements: the residue after the datum shift is the honest staleness number, and a green suite falsifies "far too stale to apply directly" for the framework proper.
+
+ +
Basis + direct: The framework's "only dependency on PC code is import io.confluent.parallelconsumer.internal.MultithreadingAPI — a 16-line marker interface" (verified against the branch tree; interface signature Actor<T> extends Interruptible, MultithreadingAPI independently confirmed by the verifier). bin/rename-packages.sh exists with its "BRINGING AN OPEN BRANCH ACROSS" section, which docs/inflight/branch-package-rename-sweep.md:8 calls "authoritative" — the salvage procedure is scripted and was rehearsed at 38-branch scale. The FILO invariant is quoted from the Actor javadoc: "a subsequent immediate message will be in front of previous ones." None of the branch's new interfaces exist on master to conflict with (verified by tree listing).
+ +
Rationale + Both recorded verdicts were formed by reading diffs of 69–72-file branches; neither has ever been tested against the one part of the collection that is structurally separable. Staleness is a property of the coupling, not the calendar — and here the coupling is 16 lines. A day of work converts "keep ideas, not code" from an opinion into a number, before any architectural decision is asked of anyone. Base-unification note: the verifier diffed the two 2022 actor bases directly and found lambda-actor-bus's interface is a strict superset of poller-bus-actor's — so the "needs unifying of the two actor classes" TODO from commit d391398f1 most likely collapses to "pick lambda-actor-bus."
+ +
Downsides + A green compile-and-test proves the framework is portable, not that it is wanted — value still depends on a call site adopting it (ideas 2, 3, 5). Landing unused code on master invites "dead code" objections; mitigate by pairing it with the register entry (idea 4) that names its intended consumers.
+
+ +
+
2

Un-bundle the family: async-produce jumps the queue

+
A2 Conversion + A5 FramingConfidence 80%Complexity Medium
+ +
Description + Stop treating the collection as one project. It coordinates three unrelated things — the commit path, the poller thread, and async produce — and only the poller one is genuinely subordinate to the shared-nothing (confluentinc#200) rework. Split docs/refactoring.md:151's blanket verdict into gated vs ungated claims, and promote async-process-send-results-using-actor (upstream draft confluentinc#356) to its own track: it is the one branch the fork's own analysis ranks Group B "throughput-critical," its target defect is verified still in master today, and fork issue astubbs#230 already exists to hang it on.
+ +
+ + + + "the actor collection" + one verdict, five branches + + + + + async produce (confluentinc#356, issue astubbs#230) + futureSend.get() still in master + + commit path (confluentinc#524 lineage) + bug-857 site, PR astubbs#29 still open + + poller thread (poller-bus-actor) + the one genuinely confluentinc#200-shaped piece + + UNGATED — Group B + "throughput-critical" + + GATE = the experiment (idea 3) + not confluentinc#200 + + GATED on confluentinc#200 / astubbs#142 + verdict stays, but only here + + + + +
Three projects wearing one name. The blanket "confluentinc#200 gate" verdict only fits one of them.
+
+ +
Basis + direct: Verified by the fresh-context verifier on master today: ParallelEoSStreamProcessor.java:122 reads "// wait for all acks to complete, see PR confluentinc#356 for a fully async version which doesn't need to block here", with futureSend.get(options.getSendTimeout()…) at line 130. upstream-pr-analysis.adoc:196 ranks it "B3 confluentinc#356 … throughput-critical" while the Group C header ("keep ideas, not code") sits at line 204–206 — the two verdicts already disagree about the same branch. The map's sweep-2023-async-produce entry adds the design constraints: "Needs work-unit limbo state + back pressure so producer timeouts do not feed themselves."
+ +
Rationale + A verdict that bundles a 10-file branch and a 72-file branch, a Group-B item and Group-C items, under one gate is the reason nothing has moved in 3.5 years. Un-bundling is the actual unblocking move — cheaper and higher-leverage than any single code change, and it aligns the fork's two editorial records, which currently contradict each other.
+ +
Downsides + The async-produce conversion itself is real work with real hazards (interacts with the produce lock in PERIODIC_TRANSACTIONAL_PRODUCER; changes when a commit cycle may safely begin). Promoting it means committing to design the limbo/back-pressure story, not just cherry-picking a 2022 diff.
+
+ +
+
3

The controlled experiment on bug-857: mailbox vs lock, same seeds

+
A2 Conversion + A4 ProofConfidence 65%Complexity High
+ +
Description + The synchronized(commitCommand) poll-vs-control deadlock is still open (draft PR astubbs#29 mitigates with ReentrantLock.tryLock(), chaos pass rate ~20%→~80%, fourth sighting 2026-08-12), and the map records the open question verbatim: "User still weighing a larger poll+control single-thread refactor to kill the whole bug class." commit-command-actor is the second design for that exact defect. Convert the commit path to a mailbox in a worktree, replay the two recorded chaos seeds (4734674029169027864 CLASS2_STALL, 7731567379755737438 ZOMBIE_MEMBER), and compare against astubbs#29's lock arm in the same pass-rate currency. Acceptance rule inverted: a conversion that moves no number is rejected as unproven, not accepted as safe. Decision rider: no dual-path/feature-flag — a flagged concurrency transport doubles the chaos state space for users who don't exist. Escalation option: a seeded-interleaving deterministic scheduler over the mailboxes (FoundationDB/TigerBeetle style — process() drains on the calling thread, so a mailbox-based controller is drivable single-threaded), which upgrades the comparison from statistical to replayable; its deterministic frontier honestly stops at ConsumerManager.
+ +
+ + + + synchronized(commitCommand) + poll vs control deadlock — OPEN + + + + Arm A: ReentrantLock (PR astubbs#29) + measured: ~20% → ~80% + + Arm B: mailbox (commit-command-actor) + never measured — 3.5 years of opinion + + + + same replay seeds, + same ProgressProbe verdict + +
The map's own open question — lock around the bug class, or kill it — gets an answer in the currency the repo already uses.
+
+ +
Basis + direct: Both replay seeds and the exact mvnw chaos invocations verified present at docs/inflight/bug-857-family.md:33,110; the "fourth sighting, 2026-08-12" ZOMBIE_MEMBER entry verified at line 94. PR astubbs#29's pass-rate framing and the "user still weighing" line are in upstream-map.yaml's bug-857-stall-after-rebalance entry. external: FoundationDB simulation-first testing / TigerBeetle VOPR for the deterministic escalation. One unverified assumption flagged by the verifier: that ConsumerOffsetCommitter is convertible without touching the God class — check before scoping.
+ +
Rationale + This is the one call site where reviving the actor work is not speculative architecture: it is a second candidate fix for a still-reproducing defect, with a recorded control arm and a recorded instrument. It directly answers the largest open architectural question in the fork's own tracking, and 80% is not a fix — it is a smaller haystack.
+ +
Downsides + Largest effort on this list; touches the God class's hottest coordination. If astubbs#29 lands first and holds at ~100%, the experiment's payoff drops to "confirms the cheaper fix sufficed" — worth knowing, but run the sequencing deliberately. Chaos pass rates are statistical; without the deterministic escalation, a small delta may not settle the argument.
+
+ +
+
4

Accession & graded register: make the family visible to the fork's own machinery

+
A5 FramingConfidence 90%Complexity Low
+ +
Description + One administrative pass, museum-accession style: (a) add a dedicated upstream-map.yaml entry naming all five actor branches plus the three dependents, with per-branch readiness grades instead of one binary verdict; (b) open one fork issue to hang decisions on; (c) annotated tags on the five tips so branch cleanup can never lose them; (d) fix the live data-integrity bug — sweep-2023-async-produce records branch async-process-send-results (actual: …-using-actor) and SHA a89f0bce2 (actual tip: 00f350166); (e) add poller-bus-actor to refactoring.md's Actor/IPC list, where it is currently missing despite carrying the second actor base; (f) write the short ideas-doc that finally executes "keep ideas, not code" — which has been recorded for years while the ideas lived exclusively in unmerged branch tips; (g) skim upstream/refactor/actor-base-fixes's ~121-line review diff for still-applicable feedback.
+ +
Basis + direct: "upstream-map.yaml has NO dedicated entry for the actor family… PRs confluentinc#325 and confluentinc#524 appear exactly once in the whole file: inside the bulk prs list of sweep-2023-admin-closure" — and the map's own schema warns a mis-listed ref "would be hidden from every future audit." The wrong branch name/SHA and the missing poller-bus-actor cross-reference were both independently re-verified by the verifier. The map is the declared SOURCE OF TRUTH; the family is currently marked accounted-for while being invisible.
+ +
Rationale + Cheapest item on the list and a prerequisite for every other one: decisions made in this conversation are worthless if the next audit, sweep, or session cannot find where they landed. Grading per-branch (the family is already graded inconsistently — B vs C) is what makes the un-bundling in idea 2 durable rather than a one-off argument.
+ +
Downsides + Pure process value — moves no code, fixes no defect. The readiness-grade scheme adds a small convention that must be kept honest or it becomes noise.
+
+ +
+
5

Skeleton-first strangler: land the seams, defer the transport

+
A2 ConversionConfidence 60%Complexity High
+ +
Description + The 2022 branches' hardest payload is arguably not the mailbox — it is the six interfaces (BrokerPollerAPI, ControllerInternalAPI, WorkManagerIPCAPI, Supervisable, MultithreadingAPI, ThreadSafe) that partition the God class's surface by who may call what from which thread. Land those first as a pure refactor with today's synchronous implementations behind them; the actor swap then becomes a reversible, per-seam decision behind a stable boundary. Pair with the event-loop reframing: processBlocking(Duration) is a libuv-style loop and Interruptible is its async-wakeup handle; unified with Kafka's Consumer.wakeup(), this is the concrete Java-8-safe mechanism behind "eliminate the separate poller thread" — the framing under which the confluentinc#857 deadlock class disappears rather than gets locked around. Track progress by a CI-checkable number, not merged branches: God class currently 1534 lines with 7 synchronized; the file has moved one line in 3.5 years, which is the clearest evidence that branch-shaped goals don't move it. Alternative seam: actorise ProcessingShard (the domain's natural single-writer boundary, Seastar-style) instead of the controller.
+ +
+ + + + callers + poll thread · control · workers + + + 6 seam interfaces + BrokerPollerAPI · ControllerInternalAPI + WorkManagerIPCAPI · Supervisable + MultithreadingAPI · ThreadSafe + who may call what, + from which thread — land this first + + + + today: synchronous impl (unchanged) + pure refactor, no semantics at risk + + later, per seam: mailbox / event loop + reversible, or never — no big bang + +
The strangler seam: interface segregation lands as a reviewable refactor; the transport underneath becomes a swap, not a bet.
+
+ +
Basis + direct: All six interfaces verified as named on the branch and all verified absent from master (no conflicts). The Interruptible wakeup-handle reading is quoted from the source itself ("push an effectively NO-OP message, which would wake it up if it were blocked… without needed to Thread#interrupt it"), which also carries the author's note "todo removable after actors fully merged?" — verified verbatim. refactoring.md:120-133: eliminating the poller thread "would also kill the confluentinc#857 deadlock class." external: strangler-fig migration; libuv/Win32 message pump; Seastar/Redis single-writer ownership for the shard-seam variant. Delivers the C1/confluentinc#488 God-class decomposition as a side effect.
+ +
Rationale + This is the only shape under which the "MASSIVE, UNDECIDED" thread-model rework becomes incremental: each seam converts (or doesn't) on its own evidence, and the delete-only number keeps the goal falsifiable and mechanism-neutral. It also reframes the review question from "adopt an actor framework" to "adopt one event loop," which is decidable on merit.
+ +
Downsides + Interface extraction across a 1534-line class with 7 synchronized blocks is delicate even as a "pure" refactor — thread-ownership boundaries are exactly where hidden assumptions live. Risk of landing seams that the eventual design doesn't want (the 2022 partitioning may not survive contact with the shared-nothing decision).
+
+ +
+
6

Concurrency mass budget: an ArchUnit ratchet every conversion must spend down

+
A4 ProofConfidence 65%Complexity Low
+ +
Description + Aerospace-style mass budget for concurrency primitives. Encode the current census as a per-package ArchUnit rule that fails when counts rise: synchronized 10 (7 in the God class), AtomicBoolean 11, BlockingQueue 4, volatile 4. Every actor conversion is then graded on primitives removed, and no conversion can quietly add a lock to make itself work. The 11 pre-existing SpotBugs AT_* thread-visibility findings become budget line items the rework is obliged to absorb — resolving the standing "fix piecemeal or wait for the rework?" stalemate recorded in refactoring.md. Needs a reviewed exemption list, since one mailbox legitimately replacing seven synchronized blocks is a win a naive count misreads.
+ +
Basis + direct: Census measured on master during grounding (counts above; verifier treated exact numbers as trusted-but-unverified). ArchUnit lane verified live: TestConventionsArchTest exists in core, and commit af88b6d13 is a real, recent ArchUnit-rule commit. refactoring.md:167-184 records the SpotBugs findings and the exact stalemate this resolves ("Fixing piecemeal now may conflict"). external: mass-properties control (NASA-HDBK-1002 lineage); performance budgets in web engineering.
+ +
Rationale + Gives the whole revival a definition of done a reviewer can check in CI, in whatever order ideas 2, 3, and 5 proceed. It is the cheapest item that would have prevented the last three years of the piecemeal-vs-rework question staying undecided.
+ +
Downsides + Counting primitives is a proxy metric; gaming it (e.g. swapping synchronized for equally shared mutable state that the rule doesn't count) is possible. The exemption list needs genuine review discipline or the ratchet decays into ceremony.
+
+ +
+ +
+

Rejection Summary

+

~55 raw ideas from five frames were merged into 8 clusters + 14 singles + 4 syntheses; a fresh-context verifier spot-checked load-bearing quotes against the repo and branches. Cuts:

+ + + + + + + + + + + +
#IdeaReason rejected
1"Critical edition" reconciliation of the two actor basesBasis refuted by verification: the cited method distribution ("askImmediately/isEmpty only on the older base; process/processBlocking only on the newer") is factually backwards — the verifier diffed both bases and all four methods exist on both, and lambda-actor-bus's interface is a strict superset. The real unification need survives, folded into idea 1 as "pick lambda-actor-bus."
2Merge actor-scheduled first (smallest branch)Overstated evidence: "removes 152 lines from the God class" is diffstat churn (58+/94−); net reduction is ~36 lines — a 4× overstatement. Also carries build landmines (TG 0.1.1-SNAPSHOT dep, OSSIndex exclusion).
3Design the package for deletion on Java 21 (virtual threads)Premise conflict: the documented baseline plan targets Java 11 for 0.7, not 21; virtual threads require 21. The disposable-transport instinct survives inside idea 5's seam framing.
4Shard-per-core: actorise ProcessingShard standaloneFolded into idea 5 as the alternative seam — as a standalone project it contradicts the branches' own design without a defect driving it.
5Mailbox as pure observability shim for the confluentinc#200 decisionDuplicates cheaper routes to the same data (idea 3's experiment and idea 5's seams both produce the traffic profile).
6Rebuild the 2022 toolchain; capture golden tracesToo expensive relative to value — idea 1's falsification pipeline gets most of the same evidence for a fraction of the cost.
7No-dual-path decision; ideas-doc; cross-reference fix; actor-base-fixes miningNot rejected — folded as riders into ideas 3 and 4 respectively (each too small to stand alone against the meeting-test).
+

Axis coverage: all five axes have survivors; no deliberate gaps.

+
+ +
+

Sources & References

+ +
+ +
Composed 2026-08-17T21:21 by ce-ideate from the session focus "reviving my micro actor collection of branches"
+ +
+ + diff --git a/docs/inflight/next-actor-revival.md b/docs/inflight/next-actor-revival.md new file mode 100644 index 000000000..8485b72e9 --- /dev/null +++ b/docs/inflight/next-actor-revival.md @@ -0,0 +1,34 @@ +# Actor-collection revival: ranked candidate directions + +The 2022 micro-actor branch family (manifest entry `sweep-2023-actor-ipc` in +[`upstream-map.yaml`](../../src/docs/development/upstream-map.yaml); editorial owner +`docs/refactoring.md` "Actor / IPC message bus" section) went through a grounded ideation pass on +2026-08-17. The full ranked set - six survivors with verified bases, confidence/complexity, and the +rejection record - is the deliverable: + +**[`docs/ideation/2026-08-17-actor-collection-revival-ideation.html`](../ideation/2026-08-17-actor-collection-revival-ideation.html)** +(self-contained HTML; open in a browser) + +Survivors, one line each (the doc carries the substance - read it before picking): + +1. One-day falsification pipeline - rename-script the 4 framework files, compile standalone, run + 2022 tests + a FILO conformance suite; turns the two standing verdicts ("only meaningful as part + of confluentinc#200", "far too stale to apply directly") into measurements +2. Un-bundle the family - async-produce (confluentinc#356, manifest `sweep-2023-async-produce`) is + Group B "throughput-critical", not architecture; split `docs/refactoring.md`'s blanket gate +3. Controlled experiment on the confluentinc#857 commit path - mailbox arm vs astubbs#29's lock + arm, judged by the recorded chaos replay seeds (`docs/inflight/bug-857-family.md`) +4. Accession & graded register - DONE 2026-08-17 for the manifest half (astubbs#305); per-branch + readiness grades still open +5. Skeleton-first strangler - land the six thread-ownership interfaces as a pure refactor; the + mailbox becomes a per-seam swap +6. Concurrency mass budget - ArchUnit ratchet on primitive counts; conversions graded on locks + removed + +Key facts the ideation verified (so nobody re-derives them): the framework proper is 537 lines in +4 files coupled to PC by one 16-line marker interface; the two 2022 actor bases were never unified +but lambda-actor-bus's interface is a strict superset of poller-bus-actor's; the blocking +`futureSend.get` that confluentinc#356 targets is still in master source. + +Open decision: which survivor (or sequence) to take first. The doc's top pick is 1 (cheapest, +falsifies both verdicts before any architectural bet); the real fork in the road is 3 vs 5. diff --git a/docs/merge-checklist.md b/docs/merge-checklist.md index 6adcc3e22..934f8ff1d 100644 --- a/docs/merge-checklist.md +++ b/docs/merge-checklist.md @@ -47,6 +47,12 @@ force-push to master to correct. Simplest is to omit the flag entirely and let t `.claude/hooks/check-squash-subject.sh` refuses an override without the suffix for Claude Code; the rule binds anyone merging by hand. +**Sign off the squash body, and nothing else.** An agent-assisted squash body ends with exactly one +trailer - `Co-authored-by: Claude (1M context) ` - and carries no +`Claude-Session:` line: the session link belongs on ordinary branch commits, not the permanent +squash record master keeps. The same hook enforces both halves for Claude Code when the body is +overridden (`--body`/`--body-file`); the rule binds anyone merging by hand. + ### Rebase-merge as-is Only when the existing commits are already clean and atomic.