From 5ec5d7c791af13af5164aa04c4fce8e846ef29c5 Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:06:32 +0200 Subject: [PATCH 1/6] Propose CH-005: establish death-event evidence boundary --- .clue/id-ledger.yaml | 7 +++++- .../open-questions.md | 11 +++++++++ .../proposal.md | 23 +++++++++++++++++++ .../tasks.md | 3 +++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 changes/CH-005-death-event-evidence-boundary/open-questions.md create mode 100644 changes/CH-005-death-event-evidence-boundary/proposal.md create mode 100644 changes/CH-005-death-event-evidence-boundary/tasks.md diff --git a/.clue/id-ledger.yaml b/.clue/id-ledger.yaml index 9136cad..0ef3a4c 100644 --- a/.clue/id-ledger.yaml +++ b/.clue/id-ledger.yaml @@ -5,7 +5,7 @@ counters: ARCH: "3" C: "7" CAP: "8" - CH: "4" + CH: "5" CRIT: "8" DES: "8" EVT: "13" @@ -213,6 +213,11 @@ entries: state: live prefix: CH component: "4" + - id: CH-005 + kind: numeric + state: reserved + prefix: CH + component: "5" - id: CRIT-001 kind: numeric state: live diff --git a/changes/CH-005-death-event-evidence-boundary/open-questions.md b/changes/CH-005-death-event-evidence-boundary/open-questions.md new file mode 100644 index 0000000..17c874a --- /dev/null +++ b/changes/CH-005-death-event-evidence-boundary/open-questions.md @@ -0,0 +1,11 @@ +# Open questions + +## Q-001 — May bridge conformance evidence consume the unreleased Tank Royale death-event repair? + +`AN-009` recorded that the Tank Royale server emitted death events before its turn snapshot existed, so no bot received either its own death or another bot's death. The repair is now in Tank Royale `main` as `824028f9d` (`fix(server): deliver bot death events to the bots`), but it is not contained by a release tag. + +The bridge's `robocode-api` module declares Bot API `1.0.2`; its conformance tier launches a separate runner jar. `C-002` requires the Bot API and runner server to be protocol compatible, but the bridge does not yet mechanically verify that compatibility for locally built upstream artifacts. + +Should this change consume a locally built, pinned Tank Royale `main` server and Bot API pair to establish conformance evidence, wait for a compatible released pair, or defer these two criteria and take the next unblocked M-001 door instead? + +This blocks implementation. A human answer will be recorded as a decision before work resumes. diff --git a/changes/CH-005-death-event-evidence-boundary/proposal.md b/changes/CH-005-death-event-evidence-boundary/proposal.md new file mode 100644 index 0000000..62ff2b0 --- /dev/null +++ b/changes/CH-005-death-event-evidence-boundary/proposal.md @@ -0,0 +1,23 @@ +--- +id: CH-005 +type: change +status: proposed +links: [P-001, CAP-001, AN-009, C-002] +title: Establish the dependency boundary for death-event conformance evidence +--- + +# CH-005 — Establish the dependency boundary for death-event conformance evidence + +## What + +`AN-009` established that the released Tank Royale server failed to deliver death events to every bot, leaving both `EVT-004` (a robot's own death reaches `onDeath`) and `EVT-007` (a survivor receives another robot's death) correctly marked `@draft`. The upstream server repair is now present on Tank Royale `main`, but no released tag contains it. + +This change will establish whether the bridge may consume that unreleased upstream repair for conformance evidence, or must wait for a compatible released server and Bot API pair. Until that decision, it will not promote either criterion or alter the bridge's declared dependency version. + +## Why + +The bridge currently compiles against Bot API `1.0.2`, while its conformance tier launches a separate Tank Royale runner. `C-002` requires those two sides to stay protocol compatible, and the repository has no check that proves a locally built Bot API and runner pair meet that boundary. Treating an upstream-main build as accepted evidence without a decision would make `EVT-004` and `EVT-007` look reproducible when the project cannot yet guarantee that. + +## Route + +Full. The eventual decision determines whether two event-dispatch criteria may be accepted as proven and what upstream dependency boundary the bridge may rely on. diff --git a/changes/CH-005-death-event-evidence-boundary/tasks.md b/changes/CH-005-death-event-evidence-boundary/tasks.md new file mode 100644 index 0000000..118f0c1 --- /dev/null +++ b/changes/CH-005-death-event-evidence-boundary/tasks.md @@ -0,0 +1,3 @@ +- [ ] Resolve `Q-001` and record the resulting decision before selecting an implementation or evidence path. +- [ ] If the decision permits a reproducible compatible upstream pair, add conformance evidence for `EVT-004` and `EVT-007` and promote only the criteria the evidence proves. +- [ ] If the decision requires a published release, record the dependency gate in the permanent corpus and leave both criteria `@draft` until that release is available. From 717918218b42b0d66617fba7b8c9776cb0085a02 Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:10:45 +0200 Subject: [PATCH 2/6] Record local Tank Royale build policy --- .clue/id-ledger.yaml | 7 +++++- AGENTS.md | 2 ++ .../open-questions.md | 2 +- .../proposal.md | 4 ++-- .../tasks.md | 7 +++--- .../C-002-bot-api-protocol-compatibility.md | 4 +++- ...t-tank-royale-artifacts-for-conformance.md | 23 +++++++++++++++++++ docs/decisions/README.md | 1 + 8 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md diff --git a/.clue/id-ledger.yaml b/.clue/id-ledger.yaml index 0ef3a4c..8cfdcd9 100644 --- a/.clue/id-ledger.yaml +++ b/.clue/id-ledger.yaml @@ -13,7 +13,7 @@ counters: IDR: "3" OQ: "2" P: "1" - PDR: "1" + PDR: "2" REH: "1" ROUTE: "12" TASKS: "3" @@ -408,6 +408,11 @@ entries: state: live prefix: PDR component: "1" + - id: PDR-002 + kind: numeric + state: reserved + prefix: PDR + component: "2" - id: REH-001 kind: numeric state: live diff --git a/AGENTS.md b/AGENTS.md index 826a52c..a3f06ae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,8 @@ cd compat-test && python compat_test.py --trace # per-turn behavio Tier 2 **skips** rather than fails when the environment is absent, so a clean checkout still builds. It needs a classic Robocode installation, the classic source repository's compiled test robots, and the Tank Royale runner jar; point it with `-Probocode.home=` and `-Probocode.source=`. +**Tank Royale conformance artifacts are local builds.** When a conformance check needs an unreleased upstream repair, build the Bot API and runner from the same Tank Royale revision and use that pair. Do not wait for, request, or create a Tank Royale release merely to run bridge evidence; `PDR-002` records the policy and `C-002` still requires a compatible pair. + **The classic side needs its own JDK.** Classic installs a `SecurityManager`, which JDK 24 removed outright, so `-Djava.security.manager=allow` is a fatal VM error there and classic cannot start at all. The harness auto-detects a JDK 23 or older; override with `COMPAT_RC_JAVA`. **Evidence conventions.** A test carries its criterion, proof type, and direction in its own name — `testAPI001_UnitPositive_...`. Give each field of a wide positional constructor a distinct value in tests: two defects found so far were positional or dispatch faults that placeholder data would have passed. `PDR-001` explains why evidence is layered in three tiers and what each can prove. diff --git a/changes/CH-005-death-event-evidence-boundary/open-questions.md b/changes/CH-005-death-event-evidence-boundary/open-questions.md index 17c874a..3764ab2 100644 --- a/changes/CH-005-death-event-evidence-boundary/open-questions.md +++ b/changes/CH-005-death-event-evidence-boundary/open-questions.md @@ -8,4 +8,4 @@ The bridge's `robocode-api` module declares Bot API `1.0.2`; its conformance tie Should this change consume a locally built, pinned Tank Royale `main` server and Bot API pair to establish conformance evidence, wait for a compatible released pair, or defer these two criteria and take the next unblocked M-001 door instead? -This blocks implementation. A human answer will be recorded as a decision before work resumes. +Resolved by the human in `PDR-002`: build and use a locally matched Bot API and runner pair. A Tank Royale release is outside bridge evidence work. diff --git a/changes/CH-005-death-event-evidence-boundary/proposal.md b/changes/CH-005-death-event-evidence-boundary/proposal.md index 62ff2b0..c16accd 100644 --- a/changes/CH-005-death-event-evidence-boundary/proposal.md +++ b/changes/CH-005-death-event-evidence-boundary/proposal.md @@ -12,11 +12,11 @@ title: Establish the dependency boundary for death-event conformance evidence `AN-009` established that the released Tank Royale server failed to deliver death events to every bot, leaving both `EVT-004` (a robot's own death reaches `onDeath`) and `EVT-007` (a survivor receives another robot's death) correctly marked `@draft`. The upstream server repair is now present on Tank Royale `main`, but no released tag contains it. -This change will establish whether the bridge may consume that unreleased upstream repair for conformance evidence, or must wait for a compatible released server and Bot API pair. Until that decision, it will not promote either criterion or alter the bridge's declared dependency version. +This change records the approved local-build policy, then uses a locally built Bot API and runner from the same Tank Royale revision to add conformance evidence for `EVT-004` and `EVT-007`. It will promote only the criteria that evidence proves. ## Why -The bridge currently compiles against Bot API `1.0.2`, while its conformance tier launches a separate Tank Royale runner. `C-002` requires those two sides to stay protocol compatible, and the repository has no check that proves a locally built Bot API and runner pair meet that boundary. Treating an upstream-main build as accepted evidence without a decision would make `EVT-004` and `EVT-007` look reproducible when the project cannot yet guarantee that. +The bridge currently compiles against Bot API `1.0.2`, while its conformance tier launches a separate Tank Royale runner. `C-002` requires those two sides to stay protocol compatible, so the local-build setup must use a matched pair and confirm that bots act before treating the results as evidence. A Tank Royale release is not needed for that work. ## Route diff --git a/changes/CH-005-death-event-evidence-boundary/tasks.md b/changes/CH-005-death-event-evidence-boundary/tasks.md index 118f0c1..0c47a59 100644 --- a/changes/CH-005-death-event-evidence-boundary/tasks.md +++ b/changes/CH-005-death-event-evidence-boundary/tasks.md @@ -1,3 +1,4 @@ -- [ ] Resolve `Q-001` and record the resulting decision before selecting an implementation or evidence path. -- [ ] If the decision permits a reproducible compatible upstream pair, add conformance evidence for `EVT-004` and `EVT-007` and promote only the criteria the evidence proves. -- [ ] If the decision requires a published release, record the dependency gate in the permanent corpus and leave both criteria `@draft` until that release is available. +- [x] Resolve `Q-001` in `PDR-002`: use locally built Tank Royale artifacts from one upstream revision; releases are outside bridge evidence work. +- [ ] Build a matched local Bot API and runner pair from Tank Royale and confirm the pair runs an acting battle under `C-002`. +- [ ] Add conformance evidence for a robot's own death and another robot's death — serves `EVT-004`, `EVT-007`. +- [ ] Promote only the criteria the resulting evidence proves, then update `P-001` doors and digest the durable corpus. diff --git a/docs/constraints/C-002-bot-api-protocol-compatibility.md b/docs/constraints/C-002-bot-api-protocol-compatibility.md index a3e999b..93af2a5 100644 --- a/docs/constraints/C-002-bot-api-protocol-compatibility.md +++ b/docs/constraints/C-002-bot-api-protocol-compatibility.md @@ -4,7 +4,7 @@ type: constraint status: active links: [CAP-001, ARCH-001] title: The Bot API must be protocol-compatible with the server embedded in the runner -source: compat-test/README.md prerequisites, and the incident recorded in AN-001 +source: PDR-002, compat-test/README.md prerequisites, and the incident recorded in AN-001 enforcement: human provenance: inferred reversal-cost: high @@ -14,6 +14,8 @@ reversal-cost: high The bridge links one version of the Tank Royale Bot API; battles run against a server embedded in the Tank Royale runner jar. The two speak a versioned protocol, and a mismatched pairing is not a build error — it is a battle in which the bots never act. +Conformance may use unreleased Tank Royale repairs, but it builds the Bot API and runner locally from the same upstream revision rather than requiring a Tank Royale release. `PDR-002` records that boundary. + ## Why this is a constraint rather than a build detail The failure is silent and looks like a bridge defect. Under an incompatible pairing the robots sit idle and score zero, which reads exactly like a bridge that cannot drive its robots. diff --git a/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md b/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md new file mode 100644 index 0000000..75fcf0a --- /dev/null +++ b/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md @@ -0,0 +1,23 @@ +--- +id: PDR-002 +type: decision +status: verified +author: agent +accepted-by: [Flemming N. Larsen] +links: [PDR-001, C-002, CAP-001] +title: Conformance uses locally built Tank Royale artifacts rather than waiting for releases +--- + +# PDR-002 — Conformance uses locally built Tank Royale artifacts rather than waiting for releases + +## Decision + +When bridge conformance needs a Tank Royale repair that is not released, build the Tank Royale Bot API and runner locally from the same upstream revision and use that pair for the comparison. Do not wait for, request, or create a Tank Royale release solely to establish bridge evidence. + +## Context + +Tank Royale releases are a separate, consequential product operation. They are not a prerequisite for checking whether an upstream repair restores classic Robocode behaviour through this bridge. The bridge needs a compatible Bot API and runner server, not published coordinates. + +## Consequences + +The conformance setup identifies the Tank Royale source revision and builds both artifacts from it. `C-002` remains in force: a local pair must still be protocol-compatible and a battle must show that bots act before its results are trusted. A Tank Royale release may later consume the same repair, but that release is outside the bridge change. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 2de4dbe..1c0d789 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -26,4 +26,5 @@ A decision that changes a methodology contract inventories every live carrier th - [IDR-002 — getDataDirectory resolves through the same robot data lookup getDataFile uses](IDR-002-data-directory-resolves-through-robot-data.md) · `verified` — One question about where a robot's data lives should have one answer. Alignment, not confinement. - [PDR-001 — Evidence comes in three tiers, separated by cost and by what each can prove](PDR-001-three-tier-evidence-strategy.md) · `verified` — Unit, conformance, and sweep: what each tier can establish, and what is rejected because Tank Royale has no seed. - [IDR-003 — EVT-003's higher-priority claim is retired; interruptible re-entry evidence is scoped to what classic's own robot proves](IDR-003-evt-003-scoped-to-what-classic-actually-proves.md) · `inferred` — `EVT-003` retires. +- [PDR-002 — Conformance uses locally built Tank Royale artifacts rather than waiting for releases](PDR-002-locally-built-tank-royale-artifacts-for-conformance.md) · `verified` — When bridge conformance needs a Tank Royale repair that is not released, build the Tank Royale Bot API and runner locally from the same upstream revision and use that pair for the comparison. From 5725ab2b0565582fa573af14e75466fdceab939e Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:19:33 +0200 Subject: [PATCH 3/6] Prove EVT-004 with local Tank Royale build --- .../tasks.md | 6 +++-- compat-test/compat_test.py | 23 +++++++++++++------ .../RoundOutcomeEventsConformanceTest.java | 7 ------ .../CAP-001-event-dispatch-parity/README.md | 2 +- .../CAP-001-event-dispatch-parity/criteria.md | 9 ++++---- docs/plans/P-001-bridge-parity-campaign.md | 2 +- robocode-api/build.gradle.kts | 11 ++++----- robots-wrapper/build.gradle.kts | 6 ++--- 8 files changed, 34 insertions(+), 32 deletions(-) diff --git a/changes/CH-005-death-event-evidence-boundary/tasks.md b/changes/CH-005-death-event-evidence-boundary/tasks.md index 0c47a59..85c83e7 100644 --- a/changes/CH-005-death-event-evidence-boundary/tasks.md +++ b/changes/CH-005-death-event-evidence-boundary/tasks.md @@ -1,4 +1,6 @@ - [x] Resolve `Q-001` in `PDR-002`: use locally built Tank Royale artifacts from one upstream revision; releases are outside bridge evidence work. -- [ ] Build a matched local Bot API and runner pair from Tank Royale and confirm the pair runs an acting battle under `C-002`. -- [ ] Add conformance evidence for a robot's own death and another robot's death — serves `EVT-004`, `EVT-007`. +- [x] Build a matched local Bot API and runner pair from Tank Royale revision `4545a850726c3fb83a9accb2261f3bcdf46ebba5` and configure the bridge/harness to consume it. +- [x] Confirm the local pair runs an acting battle under `C-002` before treating it as conformance evidence. +- [x] Re-enable the ported BattleWin conformance evidence for a robot's own death — serves `EVT-004`. +- [ ] Add ported RobotDeathEvents conformance evidence for another robot's death — serves `EVT-007`. - [ ] Promote only the criteria the resulting evidence proves, then update `P-001` doors and digest the durable corpus. diff --git a/compat-test/compat_test.py b/compat-test/compat_test.py index b122d02..7667df0 100644 --- a/compat-test/compat_test.py +++ b/compat-test/compat_test.py @@ -43,29 +43,38 @@ # ---------------------------------------------------------------------------------- BASE_DIR = Path(__file__).resolve().parent +TANK_ROYALE_HOME = Path(os.environ.get("COMPAT_TANK_ROYALE_HOME", r"C:\Code\tank-royale")) + + +def local_bot_api_jar(): + """Returns the locally built Bot API jar, or the expected path when it has not been built.""" + libs = TANK_ROYALE_HOME / "bot-api" / "java" / "build" / "libs" + jars = [path for path in libs.glob("robocode-tankroyale-bot-api-*.jar") + if not path.name.endswith(("-javadoc.jar", "-sources.jar"))] + if jars: + return str(max(jars, key=lambda path: path.stat().st_mtime)) + return str(libs / "robocode-tankroyale-bot-api-local.jar") DEFAULTS = { "collection_dir": os.environ.get("COMPAT_COLLECTION_DIR", r"C:\Code\LiteRumble robots"), "robocode_home": os.environ.get("COMPAT_ROBOCODE_HOME", r"C:\robocode"), "runner_jar": os.environ.get( "COMPAT_RUNNER_JAR", - r"C:\Code\tank-royale\runner\examples\lib\robocode-tankroyale-runner.jar"), + str(TANK_ROYALE_HOME / "runner" / "examples" / "lib" / "robocode-tankroyale-runner.jar")), "bridge_api_jar": os.environ.get( "COMPAT_BRIDGE_API_JAR", r"C:\Code\robocode-api-bridge\robocode-api\build\libs\robocode-api-0.5.0.jar"), "wrapper_jar": os.environ.get( "COMPAT_WRAPPER_JAR", r"C:\Code\robocode-api-bridge\robots-wrapper\build\libs\robots-wrapper-0.3.1.jar"), - # NOTE: must be protocol/API compatible with what the bridge's robocode-api jar was - # compiled against AND with the server embedded in the runner jar. Publish it with - # `gradlew :bot-api:java:publishToMavenLocal` in the tank-royale repository. + # The bridge uses a locally built Bot API and runner from the same Tank Royale revision; + # override only with another matched local pair. Build the API with + # `gradlew :bot-api:java:publishToMavenLocal` and runner with `:runner:copyRunnerJar`. # (0.33.1 had an event-queue bug dropping deferred same-priority events, e.g. every # other scan event for bots that call blocking methods inside onScannedRobot.) "bot_api_jar": os.environ.get( "COMPAT_BOT_API_JAR", - os.path.expanduser(r"~\.m2\repository\dev\robocode\tankroyale" - r"\robocode-tankroyale-bot-api\1.0.2" - r"\robocode-tankroyale-bot-api-1.0.2.jar")), + local_bot_api_jar()), # Classic Robocode installs a SecurityManager to sandbox robots. JDK 24 removed # SecurityManager support outright, so -Djava.security.manager=allow is no longer a # deprecation warning but a fatal VM error, and the classic side cannot start at all. diff --git a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java index ac310d3..ad56c6a 100644 --- a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java +++ b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java @@ -1,6 +1,5 @@ package dev.robocode.tankroyale.bridge.conformance; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -33,12 +32,6 @@ class RoundOutcomeEventsConformanceTest extends ConformanceTestBase { private static final String ROBOT = "tested.robots.BattleWin"; @Test - @Disabled("Fails against the bridge: onDeath is never called, because no death event reaches " - + "any bot at all. AN-009 has the cause -- the Tank Royale server emits a death before " - + "the turn's bot snapshot exists, so it fans out over nobody -- and the repair is " - + "committed upstream and unreleased. Kept rather than deleted because it is the only " - + "thing that detects the defect, and disabled rather than left failing so the build " - + "stays honest. Re-enable when a Tank Royale release carries the repair.") @DisplayName("EVT-004: a robot's own death reaches onDeath on both engines") void testEVT004_IntegrationPositive_OwnDeathReachesTheDeathHandler() { assertOnBothEngines(ROBOT, (outcome, engine) -> diff --git a/docs/capabilities/CAP-001-event-dispatch-parity/README.md b/docs/capabilities/CAP-001-event-dispatch-parity/README.md index 4384292..c4e3bce 100644 --- a/docs/capabilities/CAP-001-event-dispatch-parity/README.md +++ b/docs/capabilities/CAP-001-event-dispatch-parity/README.md @@ -35,4 +35,4 @@ The physics the events describe. That a `ScannedRobotEvent` arrives at the right `draft`. The redesign that routed events through the Bot API's own event queue is implemented and believed correct, but it was verified by running battles and reading scores. Every criterion here is unproven in the sense that matters: nothing would tell us if it broke again. `M-001` is the plan door. -The conformance tier now reaches some of them. `EVT-011`, `EVT-012`, and `EVT-013` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003` retired rather than be credited with evidence no available robot can produce ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md)). The capability still holds at `draft` because most criteria remain unproven, and `EVT-004`/`EVT-007` specifically have an established cause and an unreleased repair — [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md): the Tank Royale server sends no death to any bot, so neither the dying robot nor the survivors are told. +The conformance tier now reaches some of them. `EVT-004`, `EVT-011`, `EVT-012`, and `EVT-013` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003` retired rather than be credited with evidence no available robot can produce ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md)). `EVT-004` is now proven with a locally built matched Tank Royale Bot API and runner pair under [`PDR-002`](../../decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md), which contains the server repair [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md) identified. The capability still holds at `draft` because most criteria remain unproven, including `EVT-007`, which is blocked by the same original defect until the ported RobotDeathEvents evidence is wired. diff --git a/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md b/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md index 8a570f8..75b577d 100644 --- a/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md +++ b/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md @@ -42,16 +42,15 @@ Feature: Event dispatch and timing parity # classic's own InteruptibleEvent deliberately uses the SAME priority. See IDR-003. # Superseded by EVT-013. - @EVT-004 @draft + @EVT-004 Scenario: A robot's own death reaches its death handler Test-type: Integration Given a robot that reports from onDeath When the robot is destroyed on each engine Then the report appears on both engines - # Draft because the behaviour is missing, not because nothing tests it: the test exists and - # is disabled. AN-009 establishes the cause -- the Tank Royale server emits a death before the - # turn's bot snapshot exists, so it reaches no bot at all -- and the repair is committed - # upstream and unreleased. Promote when a release carries it. Plan door: M-001. + # Proven by the ported BattleWin robot in RoundOutcomeEventsConformanceTest against a locally + # built matched Tank Royale Bot API and runner pair. AN-009 establishes the original server + # cause; PDR-002 records why bridge evidence uses this local upstream build. Plan door: M-001. @EVT-005 @draft Scenario: New-turn events arrive at the classic point in the turn diff --git a/docs/plans/P-001-bridge-parity-campaign.md b/docs/plans/P-001-bridge-parity-campaign.md index 0196f14..ad2b07f 100644 --- a/docs/plans/P-001-bridge-parity-campaign.md +++ b/docs/plans/P-001-bridge-parity-campaign.md @@ -43,7 +43,7 @@ They are bookkeeping rather than a second plan. A door closes when its criterion | M-101 | `EVT-001` | `EVT-001` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-102 | `EVT-002` | `EVT-002` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-103 | `EVT-003` | Dropped: `EVT-003` retired (`IDR-003`); see `M-141` for its successor `EVT-013`. | dropped | -| M-104 | `EVT-004` | `EVT-004` is active, with evidence attributable to it. Work lands under M-001. | todo | +| M-104 | `EVT-004` | `EVT-004` is active, with evidence attributable to it. Work lands under M-001. | done | | M-105 | `EVT-005` | `EVT-005` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-106 | `EVT-006` | `EVT-006` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-107 | `EVT-007` | `EVT-007` is active, with evidence attributable to it. Work lands under M-001. | todo | diff --git a/robocode-api/build.gradle.kts b/robocode-api/build.gradle.kts index cbb637d..a122dcb 100644 --- a/robocode-api/build.gradle.kts +++ b/robocode-api/build.gradle.kts @@ -8,16 +8,15 @@ group = "dev.robocode" version = "0.5.0" repositories { - // bot-api 1.0.2 is on Maven Central. mavenLocal stays first so an unreleased build can - // be tried against the bridge by publishing it with `gradlew :bot-api:java:publishToMavenLocal` - // in the tank-royale repository -- but C-002 applies: the version must stay protocol - // compatible with the server embedded in the runner jar, and nothing checks that. + // Conformance uses a locally built Bot API. Publish the Tank Royale checkout's matching + // API with `gradlew :bot-api:java:publishToMavenLocal` before building this module. C-002 + // still applies: the API must come from the same revision as the runner's embedded server. mavenLocal() mavenCentral() } dependencies { - implementation("dev.robocode.tankroyale:robocode-tankroyale-bot-api:1.0.2") + implementation("dev.robocode.tankroyale:robocode-tankroyale-bot-api:1.2.0") // Tier 1 of the evidence strategy (PDR-001): unit tests over the adapter's value // conversions. No engine, so this is the only tier that runs in CI. @@ -47,4 +46,4 @@ tasks { attributes["Package"] = project.group } } -} \ No newline at end of file +} diff --git a/robots-wrapper/build.gradle.kts b/robots-wrapper/build.gradle.kts index ffa1433..0abc846 100644 --- a/robots-wrapper/build.gradle.kts +++ b/robots-wrapper/build.gradle.kts @@ -8,8 +8,8 @@ group = "dev.robocode" version = "0.3.1" repositories { - mavenLocal() // bot-api 1.0.2 is not published on Maven Central (yet); publish it with - // `gradlew :bot-api:java:publishToMavenLocal` in the tank-royale repository + mavenLocal() // The bridge's local Tank Royale Bot API build is carried transitively from + // :robocode-api; publish it with `gradlew :bot-api:java:publishToMavenLocal`. mavenCentral() } @@ -52,4 +52,4 @@ tasks { build { dependsOn(fatJar) // Trigger fat jar creation during build } -} \ No newline at end of file +} From 2ae89f91329863684e31fd16c666916412603e05 Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:33:01 +0200 Subject: [PATCH 4/6] Repair CH-005 validation and CI dependency --- .clue/id-ledger.yaml | 13 +++++++++---- AGENTS.md | 2 +- .../open-questions.md | 8 ++++++++ .../proposal.md | 2 +- .../CH-005-death-event-evidence-boundary/tasks.md | 8 ++++++++ ...y-built-tank-royale-artifacts-for-conformance.md | 2 +- robocode-api/build.gradle.kts | 9 +++++---- 7 files changed, 33 insertions(+), 11 deletions(-) diff --git a/.clue/id-ledger.yaml b/.clue/id-ledger.yaml index 8cfdcd9..d7b26d6 100644 --- a/.clue/id-ledger.yaml +++ b/.clue/id-ledger.yaml @@ -16,7 +16,7 @@ counters: PDR: "2" REH: "1" ROUTE: "12" - TASKS: "3" + TASKS: "4" entries: - id: ADR-001 kind: numeric @@ -215,7 +215,7 @@ entries: component: "4" - id: CH-005 kind: numeric - state: reserved + state: live prefix: CH component: "5" - id: CRIT-001 @@ -395,7 +395,7 @@ entries: component: "1" - id: OQ-002 kind: numeric - state: reserved + state: live prefix: OQ component: "2" - id: P-001 @@ -410,7 +410,7 @@ entries: component: "1" - id: PDR-002 kind: numeric - state: reserved + state: live prefix: PDR component: "2" - id: REH-001 @@ -493,3 +493,8 @@ entries: state: live prefix: TASKS component: "3" + - id: TASKS-004 + kind: numeric + state: live + prefix: TASKS + component: "4" diff --git a/AGENTS.md b/AGENTS.md index a3f06ae..a7ff52b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,7 @@ cd compat-test && python compat_test.py --trace # per-turn behavio Tier 2 **skips** rather than fails when the environment is absent, so a clean checkout still builds. It needs a classic Robocode installation, the classic source repository's compiled test robots, and the Tank Royale runner jar; point it with `-Probocode.home=` and `-Probocode.source=`. -**Tank Royale conformance artifacts are local builds.** When a conformance check needs an unreleased upstream repair, build the Bot API and runner from the same Tank Royale revision and use that pair. Do not wait for, request, or create a Tank Royale release merely to run bridge evidence; `PDR-002` records the policy and `C-002` still requires a compatible pair. +**Tank Royale conformance artifacts are local builds.** When a conformance check needs an unreleased upstream repair, build the Bot API and runner from the same Tank Royale revision, then rebuild this bridge with `-PtankRoyaleBotApiVersion=` before running tier 2. Do not wait for, request, or create a Tank Royale release merely to run bridge evidence; `PDR-002` records the policy and `C-002` still requires a compatible pair. **The classic side needs its own JDK.** Classic installs a `SecurityManager`, which JDK 24 removed outright, so `-Djava.security.manager=allow` is a fatal VM error there and classic cannot start at all. The harness auto-detects a JDK 23 or older; override with `COMPAT_RC_JAVA`. diff --git a/changes/CH-005-death-event-evidence-boundary/open-questions.md b/changes/CH-005-death-event-evidence-boundary/open-questions.md index 3764ab2..b45f20d 100644 --- a/changes/CH-005-death-event-evidence-boundary/open-questions.md +++ b/changes/CH-005-death-event-evidence-boundary/open-questions.md @@ -1,3 +1,11 @@ +--- +id: OQ-002 +type: question +status: active +links: [CH-005, PDR-002] +title: Whether bridge evidence may use unreleased Tank Royale artifacts +--- + # Open questions ## Q-001 — May bridge conformance evidence consume the unreleased Tank Royale death-event repair? diff --git a/changes/CH-005-death-event-evidence-boundary/proposal.md b/changes/CH-005-death-event-evidence-boundary/proposal.md index c16accd..9dd93e4 100644 --- a/changes/CH-005-death-event-evidence-boundary/proposal.md +++ b/changes/CH-005-death-event-evidence-boundary/proposal.md @@ -1,7 +1,7 @@ --- id: CH-005 type: change -status: proposed +status: open links: [P-001, CAP-001, AN-009, C-002] title: Establish the dependency boundary for death-event conformance evidence --- diff --git a/changes/CH-005-death-event-evidence-boundary/tasks.md b/changes/CH-005-death-event-evidence-boundary/tasks.md index 85c83e7..64d2eb3 100644 --- a/changes/CH-005-death-event-evidence-boundary/tasks.md +++ b/changes/CH-005-death-event-evidence-boundary/tasks.md @@ -1,3 +1,11 @@ +--- +id: TASKS-004 +type: tasks +status: open +links: [CH-005] +title: Establish and verify local death-event conformance evidence +--- + - [x] Resolve `Q-001` in `PDR-002`: use locally built Tank Royale artifacts from one upstream revision; releases are outside bridge evidence work. - [x] Build a matched local Bot API and runner pair from Tank Royale revision `4545a850726c3fb83a9accb2261f3bcdf46ebba5` and configure the bridge/harness to consume it. - [x] Confirm the local pair runs an acting battle under `C-002` before treating it as conformance evidence. diff --git a/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md b/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md index 75fcf0a..3dcc593 100644 --- a/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md +++ b/docs/decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md @@ -20,4 +20,4 @@ Tank Royale releases are a separate, consequential product operation. They are n ## Consequences -The conformance setup identifies the Tank Royale source revision and builds both artifacts from it. `C-002` remains in force: a local pair must still be protocol-compatible and a battle must show that bots act before its results are trusted. A Tank Royale release may later consume the same repair, but that release is outside the bridge change. +The conformance setup identifies the Tank Royale source revision and builds both artifacts from it. The bridge is rebuilt with that local Bot API version before the comparison, while the CI-only unit tier retains its published dependency default. `C-002` remains in force: a local pair must still be protocol-compatible and a battle must show that bots act before its results are trusted. A Tank Royale release may later consume the same repair, but that release is outside the bridge change. diff --git a/robocode-api/build.gradle.kts b/robocode-api/build.gradle.kts index a122dcb..006a9e0 100644 --- a/robocode-api/build.gradle.kts +++ b/robocode-api/build.gradle.kts @@ -7,16 +7,17 @@ plugins { group = "dev.robocode" version = "0.5.0" +val tankRoyaleBotApiVersion = providers.gradleProperty("tankRoyaleBotApiVersion").orElse("1.0.2") + repositories { - // Conformance uses a locally built Bot API. Publish the Tank Royale checkout's matching - // API with `gradlew :bot-api:java:publishToMavenLocal` before building this module. C-002 - // still applies: the API must come from the same revision as the runner's embedded server. + // CI resolves the published default. A live conformance run overrides its version with a + // locally published Tank Royale Bot API from the same revision as the runner (C-002). mavenLocal() mavenCentral() } dependencies { - implementation("dev.robocode.tankroyale:robocode-tankroyale-bot-api:1.2.0") + implementation("dev.robocode.tankroyale:robocode-tankroyale-bot-api:${tankRoyaleBotApiVersion.get()}") // Tier 1 of the evidence strategy (PDR-001): unit tests over the adapter's value // conversions. No engine, so this is the only tier that runs in CI. From b43ead4602f6b6ab5b4d986a09c64764425ecc22 Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:45:08 +0200 Subject: [PATCH 5/6] Digest CH-005: prove survivor death delivery --- .clue/id-ledger.yaml | 16 +++++++-- .../open-questions.md | 19 ---------- .../proposal.md | 23 ------------ .../tasks.md | 14 -------- compat-test/compat_test.py | 32 ++++++++++++++++- .../conformance/probes/DeathEventProbe.java | 35 +++++++++++++++++++ .../conformance/ConformanceHarness.java | 11 +++++- .../conformance/ConformanceTestBase.java | 14 ++++++-- .../RobotDeathEventsConformanceTest.java | 26 ++++++++++++++ .../CAP-001-event-dispatch-parity/README.md | 2 +- .../CAP-001-event-dispatch-parity/criteria.md | 16 +++++++-- ...-scoped-to-observable-survivor-delivery.md | 23 ++++++++++++ docs/decisions/README.md | 1 + docs/plans/P-001-bridge-parity-campaign.md | 3 +- 14 files changed, 167 insertions(+), 68 deletions(-) delete mode 100644 changes/CH-005-death-event-evidence-boundary/open-questions.md delete mode 100644 changes/CH-005-death-event-evidence-boundary/proposal.md delete mode 100644 changes/CH-005-death-event-evidence-boundary/tasks.md create mode 100644 compat-test/conformance-robots/conformance/probes/DeathEventProbe.java create mode 100644 conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RobotDeathEventsConformanceTest.java create mode 100644 docs/decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md diff --git a/.clue/id-ledger.yaml b/.clue/id-ledger.yaml index d7b26d6..e0368a6 100644 --- a/.clue/id-ledger.yaml +++ b/.clue/id-ledger.yaml @@ -8,9 +8,9 @@ counters: CH: "5" CRIT: "8" DES: "8" - EVT: "13" + EVT: "14" G: "2" - IDR: "3" + IDR: "4" OQ: "2" P: "1" PDR: "2" @@ -330,7 +330,7 @@ entries: component: "6" - id: EVT-007 kind: numeric - state: live + state: retired prefix: EVT component: "7" - id: EVT-008 @@ -363,6 +363,11 @@ entries: state: live prefix: EVT component: "13" + - id: EVT-014 + kind: numeric + state: live + prefix: EVT + component: "14" - id: G-001 kind: numeric state: live @@ -388,6 +393,11 @@ entries: state: live prefix: IDR component: "3" + - id: IDR-004 + kind: numeric + state: live + prefix: IDR + component: "4" - id: OQ-001 kind: numeric state: live diff --git a/changes/CH-005-death-event-evidence-boundary/open-questions.md b/changes/CH-005-death-event-evidence-boundary/open-questions.md deleted file mode 100644 index b45f20d..0000000 --- a/changes/CH-005-death-event-evidence-boundary/open-questions.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -id: OQ-002 -type: question -status: active -links: [CH-005, PDR-002] -title: Whether bridge evidence may use unreleased Tank Royale artifacts ---- - -# Open questions - -## Q-001 — May bridge conformance evidence consume the unreleased Tank Royale death-event repair? - -`AN-009` recorded that the Tank Royale server emitted death events before its turn snapshot existed, so no bot received either its own death or another bot's death. The repair is now in Tank Royale `main` as `824028f9d` (`fix(server): deliver bot death events to the bots`), but it is not contained by a release tag. - -The bridge's `robocode-api` module declares Bot API `1.0.2`; its conformance tier launches a separate runner jar. `C-002` requires the Bot API and runner server to be protocol compatible, but the bridge does not yet mechanically verify that compatibility for locally built upstream artifacts. - -Should this change consume a locally built, pinned Tank Royale `main` server and Bot API pair to establish conformance evidence, wait for a compatible released pair, or defer these two criteria and take the next unblocked M-001 door instead? - -Resolved by the human in `PDR-002`: build and use a locally matched Bot API and runner pair. A Tank Royale release is outside bridge evidence work. diff --git a/changes/CH-005-death-event-evidence-boundary/proposal.md b/changes/CH-005-death-event-evidence-boundary/proposal.md deleted file mode 100644 index 9dd93e4..0000000 --- a/changes/CH-005-death-event-evidence-boundary/proposal.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -id: CH-005 -type: change -status: open -links: [P-001, CAP-001, AN-009, C-002] -title: Establish the dependency boundary for death-event conformance evidence ---- - -# CH-005 — Establish the dependency boundary for death-event conformance evidence - -## What - -`AN-009` established that the released Tank Royale server failed to deliver death events to every bot, leaving both `EVT-004` (a robot's own death reaches `onDeath`) and `EVT-007` (a survivor receives another robot's death) correctly marked `@draft`. The upstream server repair is now present on Tank Royale `main`, but no released tag contains it. - -This change records the approved local-build policy, then uses a locally built Bot API and runner from the same Tank Royale revision to add conformance evidence for `EVT-004` and `EVT-007`. It will promote only the criteria that evidence proves. - -## Why - -The bridge currently compiles against Bot API `1.0.2`, while its conformance tier launches a separate Tank Royale runner. `C-002` requires those two sides to stay protocol compatible, so the local-build setup must use a matched pair and confirm that bots act before treating the results as evidence. A Tank Royale release is not needed for that work. - -## Route - -Full. The eventual decision determines whether two event-dispatch criteria may be accepted as proven and what upstream dependency boundary the bridge may rely on. diff --git a/changes/CH-005-death-event-evidence-boundary/tasks.md b/changes/CH-005-death-event-evidence-boundary/tasks.md deleted file mode 100644 index 64d2eb3..0000000 --- a/changes/CH-005-death-event-evidence-boundary/tasks.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -id: TASKS-004 -type: tasks -status: open -links: [CH-005] -title: Establish and verify local death-event conformance evidence ---- - -- [x] Resolve `Q-001` in `PDR-002`: use locally built Tank Royale artifacts from one upstream revision; releases are outside bridge evidence work. -- [x] Build a matched local Bot API and runner pair from Tank Royale revision `4545a850726c3fb83a9accb2261f3bcdf46ebba5` and configure the bridge/harness to consume it. -- [x] Confirm the local pair runs an acting battle under `C-002` before treating it as conformance evidence. -- [x] Re-enable the ported BattleWin conformance evidence for a robot's own death — serves `EVT-004`. -- [ ] Add ported RobotDeathEvents conformance evidence for another robot's death — serves `EVT-007`. -- [ ] Promote only the criteria the resulting evidence proves, then update `P-001` doors and digest the durable corpus. diff --git a/compat-test/compat_test.py b/compat-test/compat_test.py index 7667df0..aba6acc 100644 --- a/compat-test/compat_test.py +++ b/compat-test/compat_test.py @@ -889,6 +889,8 @@ def parse_args(): conf.add_argument("--conformance", metavar="JAR", help="run one robot jar on one engine and print the result, " "including each participant's console output, as JSON") + conf.add_argument("--conformance-source", type=Path, + help="compile this bridge-owned robot source against classic before running it") conf.add_argument("--engine", choices=("rc", "tr"), default="rc", help="which engine --conformance drives") conf.add_argument("--robot-class", @@ -1053,6 +1055,24 @@ def compile_trace_robot(opts): return out_dir, None +def compile_conformance_robot(opts, source: Path): + """Compiles a bridge-owned conformance probe against the classic API.""" + out_dir = WORK_DIR / "conformance-probe-classes" + clean_dir(out_dir) + if not source.exists(): + return None, f"conformance probe source not found: {source}" + + classpath = str(Path(opts.robocode_home) / "libs" / "*") + cmd = [javac_beside(opts.rc_java_exe), "-cp", classpath, "-d", str(out_dir), str(source)] + try: + completed = subprocess.run(cmd, capture_output=True, text=True, timeout=180) + except (OSError, subprocess.SubprocessError) as e: + return None, f"could not compile conformance probe: {e}" + if completed.returncode != 0: + return None, f"conformance probe did not compile:\n{completed.stdout}\n{completed.stderr}" + return out_dir, None + + def javac_beside(java_exe): """The javac that ships next to a given java executable. @@ -1213,7 +1233,17 @@ def run_conformance(opts): if opts.participants is not None: setup["participants"] = opts.participants - if jar.is_dir(): + if opts.conformance_source: + class_dir, error = compile_conformance_robot(opts, opts.conformance_source) + if error: + print(json.dumps({"ok": False, "fatal": error})) + return 2 + packaged, error = package_test_robot_jar(class_dir, classname, WORK_DIR / "conformance") + if error: + print(json.dumps({"ok": False, "fatal": error})) + return 2 + jar, version = packaged, "1.0" + elif jar.is_dir(): # Package the one robot under test, for both engines. # # The bridge side needs a real robot jar because the wrapper finds robots by their diff --git a/compat-test/conformance-robots/conformance/probes/DeathEventProbe.java b/compat-test/conformance-robots/conformance/probes/DeathEventProbe.java new file mode 100644 index 0000000..ebfa1d3 --- /dev/null +++ b/compat-test/conformance-robots/conformance/probes/DeathEventProbe.java @@ -0,0 +1,35 @@ +package conformance.probes; + +import robocode.DeathEvent; +import robocode.Robot; +import robocode.RobotDeathEvent; +import robocode.ScannedRobotEvent; + +/** A two-instance probe that reports its own and another robot's death handlers. */ +public class DeathEventProbe extends Robot { + + @Override + public void run() { + while (true) { + ahead(100); + turnGunRight(360); + back(100); + turnGunRight(360); + } + } + + @Override + public void onScannedRobot(ScannedRobotEvent event) { + fire(2); + } + + @Override + public void onRobotDeath(RobotDeathEvent event) { + out.println("OtherDeath!"); + } + + @Override + public void onDeath(DeathEvent event) { + out.println("OwnDeath!"); + } +} diff --git a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceHarness.java b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceHarness.java index 7aa61f6..b1d5895 100644 --- a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceHarness.java +++ b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceHarness.java @@ -39,6 +39,11 @@ private ConformanceHarness(String python, Path robocodeHome, Path testRobotClass this.rounds = rounds; } + /** Repository root, for bridge-owned probe sources passed to the Python harness. */ + static Path repoRoot() { + return REPO_ROOT; + } + /** The number of rounds every battle this harness runs is configured for. */ int rounds() { return rounds; @@ -89,7 +94,7 @@ static String missingEnvironment() { * * @param robotClass fully qualified, e.g. {@code tested.robots.InteruptibleEvent} */ - BattleOutcome run(Engine engine, String robotClass) { + BattleOutcome run(Engine engine, String robotClass, Path source) { List command = new ArrayList<>(List.of( python, HARNESS.toString(), @@ -98,6 +103,10 @@ BattleOutcome run(Engine engine, String robotClass) { "--engine", engine.harnessName(), "--rounds", String.valueOf(rounds), "--robocode-home", robocodeHome.toString())); + if (source != null) { + command.add("--conformance-source"); + command.add(source.toString()); + } try { Process process = new ProcessBuilder(command) diff --git a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceTestBase.java b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceTestBase.java index ceab7e7..1ba1656 100644 --- a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceTestBase.java +++ b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceTestBase.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -43,8 +44,13 @@ interface Expectation { * Runs the robot on both engines and applies the same expectation to each. */ void assertOnBothEngines(String robotClass, Expectation expectation) { + assertOnBothEngines(robotClass, null, expectation); + } + + /** Runs a locally held probe source on both engines after compiling it against classic. */ + void assertOnBothEngines(String robotClass, Path source, Expectation expectation) { for (Engine engine : Engine.values()) { - BattleOutcome outcome = outcomeFor(engine, robotClass); + BattleOutcome outcome = outcomeFor(engine, robotClass, source); assertTrue(outcome.completed(), () -> "the battle did not complete on " + engine + " (" + outcome.summary() + ")"); expectation.check(outcome, engine); @@ -59,8 +65,12 @@ void assertOnBothEngines(String robotClass, Expectation expectation) { * reason that has nothing to do with what it claims to check. */ BattleOutcome outcomeFor(Engine engine, String robotClass) { + return outcomeFor(engine, robotClass, null); + } + + private BattleOutcome outcomeFor(Engine engine, String robotClass, Path source) { return ran.computeIfAbsent(engine.name() + " " + robotClass, - key -> harness.run(engine, robotClass)); + key -> harness.run(engine, robotClass, source)); } /** The number of rounds every battle in this run is configured for. */ diff --git a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RobotDeathEventsConformanceTest.java b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RobotDeathEventsConformanceTest.java new file mode 100644 index 0000000..ddddec1 --- /dev/null +++ b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RobotDeathEventsConformanceTest.java @@ -0,0 +1,26 @@ +package dev.robocode.tankroyale.bridge.conformance; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** Acceptance evidence for EVT-014 — a survivor receives another robot's death. */ +class RobotDeathEventsConformanceTest extends ConformanceTestBase { + + private static final String ROBOT = "conformance.probes.DeathEventProbe"; + private static final Path SOURCE = ConformanceHarness.repoRoot().resolve(Path.of("compat-test", + "conformance-robots", "conformance", "probes", "DeathEventProbe.java")); + private static final String OTHER_DEATH = "OtherDeath!"; + + @Test + @DisplayName("EVT-014: a surviving robot reports another robot's death on both engines") + void testEVT014_IntegrationPositive_SurvivorReceivesAnotherRobotsDeath() { + assertOnBothEngines(ROBOT, SOURCE, (outcome, engine) -> + assertTrue(outcome.anyConsoleContains(OTHER_DEATH), + () -> "no survivor reported another robot's death on " + engine + " (" + + outcome.summary() + ")")); + } +} diff --git a/docs/capabilities/CAP-001-event-dispatch-parity/README.md b/docs/capabilities/CAP-001-event-dispatch-parity/README.md index c4e3bce..6031d7e 100644 --- a/docs/capabilities/CAP-001-event-dispatch-parity/README.md +++ b/docs/capabilities/CAP-001-event-dispatch-parity/README.md @@ -35,4 +35,4 @@ The physics the events describe. That a `ScannedRobotEvent` arrives at the right `draft`. The redesign that routed events through the Bot API's own event queue is implemented and believed correct, but it was verified by running battles and reading scores. Every criterion here is unproven in the sense that matters: nothing would tell us if it broke again. `M-001` is the plan door. -The conformance tier now reaches some of them. `EVT-004`, `EVT-011`, `EVT-012`, and `EVT-013` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003` retired rather than be credited with evidence no available robot can produce ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md)). `EVT-004` is now proven with a locally built matched Tank Royale Bot API and runner pair under [`PDR-002`](../../decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md), which contains the server repair [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md) identified. The capability still holds at `draft` because most criteria remain unproven, including `EVT-007`, which is blocked by the same original defect until the ported RobotDeathEvents evidence is wired. +The conformance tier now reaches some of them. `EVT-004`, `EVT-011`, `EVT-012`, `EVT-013`, and `EVT-014` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003`/`EVT-007` retired rather than be credited with evidence they cannot honestly claim ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md), [`IDR-004`](../../decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md)). `EVT-004` and `EVT-014` are proven with a locally built matched Tank Royale Bot API and runner pair under [`PDR-002`](../../decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md), which contains the server repair [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md) identified. The capability still holds at `draft` because most criteria remain unproven. diff --git a/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md b/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md index 75b577d..269c8cc 100644 --- a/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md +++ b/docs/capabilities/CAP-001-event-dispatch-parity/criteria.md @@ -69,14 +69,24 @@ Feature: Event dispatch and timing parity Then the firing and the silence after removal match on both engines # Proven by the ported CustomEvents robot. Plan door: M-001. - @EVT-007 @draft + @EVT-007 @retired Scenario: The death of another robot reaches the survivors Test-type: Integration Given a robot that reports each robot death it observes When the same battle runs on both engines Then the same deaths are reported in the same order on both engines - # Proven by the ported RobotDeathEvents robot. Blocked by the same cause as EVT-004: no death - # event reaches any bot, so the survivors are not told either. See AN-009. Plan door: M-001. + # Retired: Tank Royale has no deterministic seed, so cross-engine death ordering cannot be + # compared honestly. Superseded by EVT-014; see IDR-004. + + @EVT-014 + Scenario: A survivor receives another robot's death event + Test-type: Integration + Given two robots that report from onRobotDeath + When one robot is destroyed on each engine + Then a surviving robot reports the other robot's death on both engines + # Proven by the bridge-owned DeathEventProbe in RobotDeathEventsConformanceTest. Successor + # to EVT-007; see IDR-004. (single-direction): a battle with a death necessarily has a + # survivor, so the missing marker is the behavior this criterion detects. @EVT-008 @draft Scenario: Skipped turns are reported to the robot diff --git a/docs/decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md b/docs/decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md new file mode 100644 index 0000000..8a5a25b --- /dev/null +++ b/docs/decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md @@ -0,0 +1,23 @@ +--- +id: IDR-004 +type: decision +status: inferred +author: agent +accepted-by: [] +links: [CAP-001, PDR-001, AN-009] +title: EVT-007's cross-engine death-order claim is retired; survivor delivery is measured directly +--- + +# IDR-004 — EVT-007's cross-engine death-order claim is retired; survivor delivery is measured directly + +## Decision + +Retire `EVT-007` and mint `EVT-014`: a surviving robot receives another robot's death event on each engine. The conformance probe reports its handler marker on both engines. + +## Context + +`EVT-007` required the same deaths in the same order across the engines. Tank Royale has no deterministic seed, so those are different battles; ordering cannot be compared honestly. `AN-009` established the observable defect: survivors received no death event at all. + +## Consequences + +The new criterion measures that missing behavior directly without claiming cross-engine sequence equality. The exact order of otherwise valid deaths remains outside this evidence boundary. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 1c0d789..93d3144 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -27,4 +27,5 @@ A decision that changes a methodology contract inventories every live carrier th - [PDR-001 — Evidence comes in three tiers, separated by cost and by what each can prove](PDR-001-three-tier-evidence-strategy.md) · `verified` — Unit, conformance, and sweep: what each tier can establish, and what is rejected because Tank Royale has no seed. - [IDR-003 — EVT-003's higher-priority claim is retired; interruptible re-entry evidence is scoped to what classic's own robot proves](IDR-003-evt-003-scoped-to-what-classic-actually-proves.md) · `inferred` — `EVT-003` retires. - [PDR-002 — Conformance uses locally built Tank Royale artifacts rather than waiting for releases](PDR-002-locally-built-tank-royale-artifacts-for-conformance.md) · `verified` — When bridge conformance needs a Tank Royale repair that is not released, build the Tank Royale Bot API and runner locally from the same upstream revision and use that pair for the comparison. +- [IDR-004 — EVT-007's cross-engine death-order claim is retired; survivor delivery is measured directly](IDR-004-evt-007-scoped-to-observable-survivor-delivery.md) · `inferred` — Retire `EVT-007` and mint `EVT-014`: a surviving robot receives another robot's death event on each engine. diff --git a/docs/plans/P-001-bridge-parity-campaign.md b/docs/plans/P-001-bridge-parity-campaign.md index ad2b07f..a33e04e 100644 --- a/docs/plans/P-001-bridge-parity-campaign.md +++ b/docs/plans/P-001-bridge-parity-campaign.md @@ -46,7 +46,7 @@ They are bookkeeping rather than a second plan. A door closes when its criterion | M-104 | `EVT-004` | `EVT-004` is active, with evidence attributable to it. Work lands under M-001. | done | | M-105 | `EVT-005` | `EVT-005` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-106 | `EVT-006` | `EVT-006` is active, with evidence attributable to it. Work lands under M-001. | todo | -| M-107 | `EVT-007` | `EVT-007` is active, with evidence attributable to it. Work lands under M-001. | todo | +| M-107 | `EVT-007` | Dropped: `EVT-007` retired (`IDR-004`); see `M-142` for its successor `EVT-014`. | dropped | | M-108 | `EVT-008` | `EVT-008` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-109 | `EVT-009` | `EVT-009` is active, with evidence attributable to it. Work lands under M-001. | todo | | M-110 | `EVT-010` | `EVT-010` is active, with evidence attributable to it. Work lands under M-001. | todo | @@ -81,6 +81,7 @@ They are bookkeeping rather than a second plan. A door closes when its criterion | M-139 | `EVT-011` | `EVT-011` is active, with evidence attributable to it. Work lands under M-001. | done | | M-140 | `EVT-012` | `EVT-012` is active, with evidence attributable to it. Work lands under M-001. | done | | M-141 | `EVT-013` | `EVT-013` is active, with evidence attributable to it. Successor to `EVT-003` (`M-103`). Work lands under M-001. | done | +| M-142 | `EVT-014` | `EVT-014` is active, with evidence attributable to it. Successor to `EVT-007` (`M-107`). Work lands under M-001. | done | ## Why this order From fd901fea7cc9534cb1e2f586510b38a5bbe8871f Mon Sep 17 00:00:00 2001 From: "Flemming N. Larsen" Date: Mon, 31 Aug 2026 22:47:37 +0200 Subject: [PATCH 6/6] Add negative evidence for EVT-004 --- .../conformance/RoundOutcomeEventsConformanceTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java index ad56c6a..c03d992 100644 --- a/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java +++ b/conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/RoundOutcomeEventsConformanceTest.java @@ -40,6 +40,15 @@ void testEVT004_IntegrationPositive_OwnDeathReachesTheDeathHandler() { + ", so onDeath was never called (" + outcome.summary() + ")")); } + @Test + @DisplayName("EVT-004 negative: each destruction reaches onDeath once, not repeatedly") + void testEVT004_IntegrationNegative_DeathHandlerDoesNotRepeatForOneDestruction() { + assertOnBothEngines(ROBOT, (outcome, engine) -> + assertTrue(outcome.countOf("Death!") == configuredRounds(), + () -> "onDeath was reported other than once per destroyed robot on " + engine + + " (" + outcome.summary() + ")")); + } + @Test @DisplayName("EVT-012: winning a round reaches onWin on both engines") void testEVT012_IntegrationPositive_WinningARoundReachesTheWinHandler() {