984: two adapter ids must not share one system, across adapter types Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxo4MXbp6VPoDkUrTBNXJ1
979: why links follow the merged platform decisions Decisions 25 and 27 of the platform are merged into decision 5, and 59 into 57. The links point at the entries which hold the text now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxo4MXbp6VPoDkUrTBNXJ1
Make the wiki say what the code does Fix counts, an anchor, an example which did not match its Java code, a stale sentence about Camunda 8.10, and the gap of the election cache which the start row already closes in most cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxo4MXbp6VPoDkUrTBNXJ1
Rewrite Home, the platform pages, BPMS migration and Workflow modules in plain English Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxo4MXbp6VPoDkUrTBNXJ1
898: waiting for a read model uses no attempts An entry whose BPMS does not report its workflow yet uses none of block-after-attempts any more. It is blocked once vanillabp.outbox.wait-for-visibility-at-most passed since it was written, about four hours by default. The outbox sections of both platform pages, the migration page and the metrics table say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGP1Z1mjfXKxCgHzecPvuX
897: an operation on a workflow reads the start row Correlating a message and aggregateChanged read the row written when the workflow started where the election cache has none, so they stay patient after a restart and on another node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGP1Z1mjfXKxCgHzecPvuX
The gruelbox store is named once, and it is named where it lives The store left adapter-platform-integration, so its documentation goes with it. What stays on the Spring Boot page is one section: what gruelbox was, where the artifact is now, and that an application which keeps it changes no configuration. The rows and paragraphs which described how that store differs from the ones VanillaBP writes itself are documented in its own repository. The migration page says the same thing from the version-1 side: entries move to VanillaBP's own table, and what is left in TXNO_OUTBOX is counted at every start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Fox39crp8dfWkZ9W84Ub
docs: the adapter order stands on the ladder of levels too vanillabp.prioritized-adapters knows the application, a workflow module and a workflow, and no task below them. The section which explains the levels never mentioned it, while four pages explained it themselves. The levels section now carries it, including the two ways it differs from every other setting: a list replaces the list above it, and a level which names nothing keeps out. The platform pages, the migration page and the version-1 page point there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Fox39crp8dfWkZ9W84Ub
docs: Spring Boot writes its outbox itself On a relational database both platforms now run the same store, so the Spring Boot page names the tables VanillaBP owns instead of gruelbox' one, and the table which says what each store does differently has the opted-in gruelbox in its last row. New on both platforms: the outbox dispatches several entries at once and the workflow aggregate decides which thread takes one. The Spring Boot page explains it, the Quarkus page and the observability page point at it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYPiyZ32jQQhqYKx44cw23
docs: when a cancelation arrives, and what the election carries Workflow tasks: the @TaskEvent paragraph said cancelation was a property of the BPMS and left it there. It now says when the event arrives on a remote BPMS, which is at the end of the workflow and at the next job of the same workflow, what that does not catch, and how to switch the check off. The schema paragraph names the second index the delivery table needs. BPMS migration: the election may carry the BPMS' own id of the workflow, what an adapter may do with it and what it may never do with it. Extensions: a section of its own about which BPMS holds a workflow, the two calls, and what an extension may do with the workflow id. Both platform pages: the delivery table gets the indexes its reads need, not only the one its retention deletes by. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYPiyZ32jQQhqYKx44cw23
docs: a rejected dispatch is planned again instead of waited for The gruelbox store used to wait out the window of a workflow its BPMS had not made searchable yet, on its dispatching thread. It cannot: gruelbox dispatches inside a transaction of its own, and a rejected attempt leaves that transaction unable to commit. The attempt now ends and the entry is planned again with the window the adapter named, so the page says where that window is written and what the store adds to it. The migration page promised one to three seconds for a Camunda 8 workflow which was not searchable yet. What decides is the adapter's window, so the page says that and where to lower it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HSy3LqLy3SDHdP4ojgZb2
docs(migration): the default store answers a dispatch later The page said the gruelbox-based outbox keeps the check about leftover adapter ids silent. It answers at the first dispatch of such an entry now, so the page says when the answer comes and why it is not the boot. The blocked entry which is in nobody's answer is named as well. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Re2yjHbaFRvnHyW61E3sao
docs: the Camunda 8 adapter asks for a cluster it can search Choosing-a-BPMS stops offering the degraded mode: the rows about workflow awareness, version tags, the viewer's fallback and two ids on one cluster say what the adapter needs instead of what happens without it, and the paragraph which called secondary storage the one decision to take is now the requirement plus the note that a SaaS cluster brings it. BPMS-migration keeps two guessing adapters, and the second one changes: not a Camunda 8 cluster without secondary storage any more, but a Camunda 8 adapter which booted degraded against one, which is the migration away from it and the one case where accepting the routing by list order is the answer. Workflow-tasks and the version-1 migration guide lose the old condition. Two conditions elsewhere were promises of the same degraded mode and read as a choice a reader still has. Renaming a BPMN process reported the check of the old versions "where the cluster has its query API", which is now every cluster the adapter serves. The version check said a BPMS which cannot answer is one whose query API is unavailable; what is left of that case is a BPMS counting no versions at all, or a cluster which no longer holds the version being asked about. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsxz15VNs7PoEA7VgBPXHL
docs: the cache's own numbers have a name which says which cache One table became two. The first three meters hold whichever cache you use, because VanillaBP counts them around it. The other five belong to the in-memory default and now carry its name, vanillabp.inmemory.election.cache.*, so that a shared cache does not report a size of NaN and an eviction counter which can never leave zero. With such a cache the second table is simply absent, and ended.marks of the first answers the question the size of the marks answered before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: the election cache for Hazelcast is shipped, the SPI stays the contract Both places which said the infrastructure is yours and VanillaBP ships nothing now name hazelcast-shared-election-cache, where the nodes of the application form the cluster themselves and find each other in Kubernetes through a headless service. Writing the bean on Redis or on a table of your own stays the case the SPI was written for, said explicitly in both, and the trap of a node which finds nobody is named where somebody choosing the shipped cache reads it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: one page for running more than one node What a team needs before it starts a second instance was spread over three pages, and the synthesis was left to the reader. It stands in one place now, and the three pages keep the long explanations and link to it. The correction worth reading twice: a shared election cache is not a requirement for several nodes. Routing never depends on the cache, an entry is a hint, so five nodes cost up to five questions to the BPMS instead of one and nothing else. Where it does bite is the record which says that VanillaBP itself started a workflow, because that is what buys the patience while an eventually consistent BPMS catches up. A node which lacks it does not wait a shorter time, it does not wait at all, and the call raises WorkflowNotFoundException. That needs a remote BPMS, an operation inside its visibility window and a node other than the one which started the workflow, all three at once - which is why the page says who has to care and who does not. Story 197. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjxJNwTRcKzTmXCwLU7Wi4
docs: every page opens with what to do and carries a table of contents A reader who has never used VanillaBP met the reasoning for a rule before the rule itself, and had no way of telling how far to read before writing a first workflow. The pages now start with the sentence somebody can act on and keep the background where it belongs, further down. Nothing was deleted; what explains a rule moved behind it. Every page gained a numbered table of contents in the shape Home and the Spring Boot page already used, so a reader sees the sections and what each of them answers before scrolling. Where a first heading only repeated the title GitHub shows above the page, it is gone and the page starts with its first sentence. Home is now ordered the way a newcomer needs it, and what VanillaBP is sized for became a page of its own: it answers a question somebody asks before they start, not one they trip over in the middle of the introduction. The architecture picture from the repository's diagrams folder is embedded there, linking to its Excalidraw source. The horizontal rules of the workflow-module pages are gone, the heading is the separator everywhere else. Hints are written as sentences rather than marked with a label, headings sit at one depth per page, and the paragraphs written by hand over the last months were brought to the tone of the rest without changing a single statement they make. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: what the delivery record answered, and the push it answers now The observability page gains the counter of the elections the record answered, together with the reason nothing counts the ones which walked: a store which cannot be queried by a task answers nothing, and nothing looks exactly like a task no record was ever written for. A push into the scope of a task is routed from the record as well now, which the task page and the migration page both said was not the case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjxJNwTRcKzTmXCwLU7Wi4
docs: completing a task reads the record instead of asking a BPMS The delivery record of a task names the BPMS which handed that task out, so completeTask and cancelTask are routed from it and a second call is the warned no-op without a round trip. Workflow-tasks gets the section which explains it and what happens where no record answers; BPMS-migration says that this is the one operation for which a shared election cache buys nothing, and the two platform pages name the column and the index the store needs for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjxJNwTRcKzTmXCwLU7Wi4
docs: what happens to a cached election when its workflow ends The election cache is the answer this page recommends to a cluster whose nodes each ask the BPMS the same question, so what it holds after a workflow ended belongs here. The end marks the record rather than deleting it, because operations still arrive after a workflow is over and the record is what keeps them a warned no-op, and a mark is kept for `ended-time-to-live` instead of the hour a running workflow gets. The section also names the price. A BPMS reports the end of a workflow only where somebody asked for it, so `release-on-workflow-end` attaches a listener to every deployed process of the module, and the same listener already serves a `@WorkflowEnded` method or the release of delivery records where one of those asked first. The two new meters go into the table above, and the last of them is where an operator sees whether the release works at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjxJNwTRcKzTmXCwLU7Wi4
docs: what a viewer sees right after the start Viewing-workflows gets the case which used to fail: a viewer opened by the application which just started the workflow. Camunda 8 answers from a read model an exporter feeds, so it honestly reports the workflow as unknown for a moment, and VanillaBP waits that moment out where it knows which BPMS holds the workflow. Without that knowledge a wrong id still fails at once, and where the window passes the error names the BPMS which was expected to answer. BPMS-migration says the same in one sentence next to the paragraph about the waiting which happens at the dispatch: a read has nothing to plan and nobody repeats it, so it waits itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: an adapter which can only guess is refused, and nothing waits in your transaction Three pages still described the state before stories 157, 159 and 163. BPMS-migration said what a guessing adapter costs in a migration and left the reader to avoid it. VanillaBP refuses such a setup at startup now, so the page says that, and shows 'election.guessing-adapters: ACCEPTED' for whoever wants the routing anyway. The same section still claimed the election waits inside the caller's transaction and told operators to size their connection pool for it; the waiting moved to the dispatch, so that advice is gone and what replaces it says where the waiting happens now. Workflow-aggregates said a pre-commit check is a thing remote BPMS ask for; it is a thing any adapter may ask for, and it never waits. Starting-workflows names why the SPI calls the value a start instant rather than a trigger time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: adapter-specific notes move to the adapter which owns them A platform page says what every BPMS has to deliver and where they differ; how one BPMS does it is that adapter's wiki. Several pages carried the second kind. Message correlation lost the Camunda 8 section about what a repetition costs. What stays is the rule underneath it: a BPMS may deduplicate on its own, in a window VanillaBP neither sets nor shortens, and whether that happens is on the adapter's wiki. The Camunda 8 wiki carries the detail now, including the log line naming both readings of a refusal. The model-side table keeps one clause per adapter instead of the recipe, the phase-one list keeps the comparison and leaves the reasoning to the Camunda 8 page, which gained the section explaining why the cluster is not asked, and the signal table says what a broadcast reaches rather than which client command sends it. Workflow tasks stops naming the Camunda 8 property behind an overdue task, the Camunda 7 wiring which completes a task too early, and the two version-tag attributes; in all three the message and the adapter's wiki say it. Workflow modules replaces the two bullets about what a BPMS says about a tenant with the rule and a pointer. BPMS migration replaces the Camunda-8 and Camunda-7 prerequisites of a side-by-side setup with what every BPMS is asked and links both. Observability drops two Camunda 8 examples which illustrated platform rules. Aggregate synchronisation keeps the comparison of what ignoring the recommendation costs but links the Camunda 7 page for the serialization format. The comparisons themselves stay, on purpose: which BPMS reports what, what identifies a delivery, what a model needs so it correlates. That is what a reader comes to the platform wiki for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: what VanillaBP is sized for, and the shared cache as the lever Home says it where a reader decides whether this is their framework: business processes whose steps are minutes and days apart, implemented in high quality at a price a normal project can afford, and deliberately not a stream of thousands of operations per second. That trade is behind several rules in this wiki, the most visible one being that VanillaBP asks the BPMS which engine holds a workflow instead of keeping a register of its own. BPMS-migration says what to do when that asking gets expensive, next to the bean which does it: shared, the node which started the workflow writes the association and every other node reads it, so the BPMS is asked once per workflow rather than once per workflow and node. The entries stay hints, so a stale one still costs a walk and never a wrong route. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: a rule says where its decision is written down Five decision logs hold 61 entries, the wikis linked none of them, and 22 of the 42 objections an outside review raised were already answered in one. A reader who wondered why a rule is what it is had no way there short of reading source repositories. So a rule carries a (why) link now, pointing at the numbered entry which decided it. Home says once what the links are and where the logs live, and the form is the same on every page of every wiki: an inline link at the end of the sentence stating the rule. Numbers are handed out once and never reused, so the links keep resolving. The rules the review named are linked first - the outbox on Camunda 7, probing per operation, the two retentions, sharing aggregate values, the Camunda 8 correlation key - and the remaining entries follow wherever a page states their rule. BPMS-adapters also says in one sentence what the module migration-adapter is, which it linked without explaining. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: which adapters can only guess, and where the caller waits Three things the pages promised or left out. "It never guesses another BPMS" was a promise two configurations cannot keep. Camunda 8 without secondary storage and the Process-Engine-API answer the election with an optimistic yes, which costs nothing while they are the only BPMS and decides everything in a migration: the walk stops at the first yes, so the setup routes by list order rather than by knowledge. BPMS-migration now names both configurations and what to do about them, and Workflow-tasks says what the sentence really promises, that an unreachable BPMS stops the operation instead of being routed around. The election waits inside the caller's transaction, which was written nowhere. An unreachable BPMS is asked twice more half a second apart, a hinted BPMS which does not report the workflow yet until its visibility window is used up, and on Camunda 8 with its defaults that is eleven seconds of an open transaction holding a connection and its row locks. The page says so under the cache which does the waiting, with the rule to size the connection pool by and with the property to lower where the trade is uncomfortable. The residual of a cluster without a shared election cache was in the contributor README only: an operation reaching a node which neither started the workflow nor received a delivery for it does not wait and fails while the BPMS catches up. It now sits next to the bean which removes the case. Workflow-tasks also names what identifies a delivery per adapter, including the one exception - a Camunda 7 engine on a datasource of its own, where the proof a redelivery usually carries is gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: the pages follow the decisions taken since 2026-08-11 Camunda 7 progresses a workflow after the caller's commit, through the outbox, since story 63. Three pages still described the model before it: correlating a message, broadcasting a signal and pushing a changed aggregate all said an embedded BPMS does the work inside the caller's transaction. They now say what every adapter does, with the reason in one sentence - a command which loses a concurrency conflict leaves the caller's transaction rollback-only, so it cannot be repeated there. The ordering paragraph of Workflow-aggregates loses its embedded exception for the same reason: an embedded BPMS is dispatched by the same outbox and promises no order either. A workflow module's configuration file carries defaults again (2026-08-21), so the Spring Boot and Quarkus pages stop promising it higher priority than the application's own files. Workflow-modules-in-Spring-Boot no longer says VanillaBP scans the classpath for workflow services: they are found among the bean definitions since 2026-08-26, which changes what a module nobody scanned looks like at startup. The migration guide's property table said retry-backoff is "not yet in version 2". It is back on the Camunda 8 adapter, PT10S by default and resolvable down to a single task, so the row now points there instead of asking readers to report it. Versions read 2.0 everywhere, without a patch digit and without -SNAPSHOT, and the pages which introduce the BOM say once what that means for a build. Two anchors were dead, workflow-visibility-timeout on the Camunda 8 Configuration page and a section about the aggregate's ID which does not exist on Workflow-aggregates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgyhjwTEh834mwbptZZxh
docs: VanillaBP notices the leftovers of a renamed adapter id Story 120 makes the rule of story 119 checkable: at startup VanillaBP asks its own stores which adapter ids they still hold waiting outbox entries or records of open tasks for, and names one which is not configured any more, with both readings and both remedies. BPMS-migration says what the message looks like, why it warns instead of failing the boot, and how 'vanillabp.retired-adapters' quits the leftovers of a finished migration. It also names the two limits: a store which cannot answer keeps the check silent (the gruelbox-based outbox is one, its entries are a serialized invocation), and a rename while nothing is left is undetectable and costs nothing. Both platform pages list the new property. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7HLKaJvVpdCiSYxhwHJTc
docs: an adapter id is a name to choose once and never rename Story 119 showed how easily an adapter id is taken for a label. It is not: VanillaBP persists it to know what belongs to which BPMS, so a rename is a new adapter next to workflows nothing serves any more. BPMS-migration gets the authoritative section: why a speaking id is worth choosing up front (which cluster, which database, SaaS next to on-premises, and a migration between two Camunda 8 configurations needs two names anyway), a caution that an id must never be renamed while workflows run, and a table of what a rename costs - the open START entries of the outbox, the delivery key of every processed delivery, the election cache and the process definition ids an application already holds. Both platform pages say in their convention list that "the type defaults to its ID" exists for applications upgrading from version 1 rather than as a recommendation, and point at that section. Migrating-from-version-1 says the same where an upgrader reads it: keep the id while one BPMS is all you run, and know that the second one cannot be had by renaming the first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7HLKaJvVpdCiSYxhwHJTc
docs: what a migration between two scopings of Camunda 8 requires "Leaving tenants behind" showed two camunda8 adapter ids on one rest-address and promised that both schemes coexist. They do, on a cluster with its query API: keys are unique per cluster rather than per scope, so telling the two ids apart is a query-API question and the boot ends without it (story 103). The section names that now and links the adapter's own page. Two links pointed at github.com/vanillabp/camunda{7,8}-adapter/wiki, which has no wiki and answers with a redirect. Both adapter wikis live in the community-hub repositories. Found by story 106. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7HLKaJvVpdCiSYxhwHJTc