Paper Scene Editor Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: UseGoal: Ship the first Paper scene-editor slice so builders can author canonical, catalog-valid props and NPCs in a build world, save rootsuperpowers:subagent-driven-development(recommended) orsuperpowers:executing-plansto implement this plan task by task. Usesuperpowers:test-driven-developmentfor every behavior change andsuperpowers:verification-before-completionbefore each merge or release. Steps use checkbox (- [ ]) syntax for tracking.
scene.json safely, and receive a dirty-session guard before /map push.
Architecture: First publish a non-empty resourcepacks 0.6.0 catalog. Then create the standalone plugin-scene-editor repository with a Bukkit-free common module and a Paper adapter. Keep documents immutable, persist through scene-format only, and integrate GroundsMaps through the editor’s small SceneEditStatus service API.
Tech Stack: Kotlin, Java 25, Gradle, Grounds conventions, scene-format 0.1.0, scene-testkit 0.1.0, resourcepacks-catalog 0.6.0, Paper 26.1.2, Adventure, MockBukkit, JUnit 5
Global constraints
- Use isolated worktrees for
resourcepacks,plugin-scene-editor,buildsystem, and later deployment changes. Preserve every unrelated dirty checkout. - Do not wait for
arc-linux, Kubernetes, service-maps Stage acceptance, or cluster recovery. Record those as external release/deployment gates. - Never create a second scene JSON model or catalog JSON codec. Use released public APIs from
scene-formatandGroundsAssetCatalog. - Write a failing focused test before each production change. Confirm that it fails for the expected reason before implementing.
- Keep all common state free of Bukkit types. Paper objects enter only through adapter interfaces.
- Do not claim a saved scene is published.
/scene savereports local file state only. - Do not merge a consumer pin until its exact producer artifact is published, except for locally verified branches explicitly held behind that gate.
- Use
GRADLE_USER_HOME=/tmp/<task-specific-name>when the default Gradle cache is unavailable. - Update the Confluence masterplan only after repository evidence exists; never mark deployment or Stage acceptance complete from local tests.
Task 1: Add declarative bootstrap assets to resourcepacks
Repository:/home/lukas/grounds/resourcepacks
Files:
- Modify:
resourcepacks-catalog/build.gradle.kts - Modify:
resourcepacks-catalog/src/main/kotlin/gg/grounds/resourcepacks/catalog/GroundsAssets.kt - Modify:
resourcepacks-catalog/src/test/kotlin/gg/grounds/resourcepacks/catalog/CatalogApiTest.kt - Modify:
resourcepacks-catalog/src/test/java/gg/grounds/resourcepacks/catalog/CatalogJavaApiTest.java - Modify:
resourcepacks-product/src/test/kotlin/gg/grounds/resourcepacks/product/CatalogParityValidatorTest.kt
-
grounds:editor/markeris aPROPwithLocalBounds(center=(0,0.5,0), size=(1,1,1)). -
grounds:editor/guideis anNPC_BODYwithLocalBounds(center=(0,0.9,0), size=(0.6,1.8,0.6)). - Both have no animations and empty editor metadata.
-
GroundsAssetCatalog.catalog.assetsis immutable and deterministically ordered. - Step 1: Create an isolated feature worktree
- Step 2: Write failing catalog API tests
- Step 3: Generate the catalog from one declarative source
GroundsAssets.kt and retain the public Java-facing GroundsAssets.all: Set<AssetKey> as its immutable key set. Keep the existing public owners, types, and Java ABI unchanged. The selected Gradle build version must reach the runtime catalog for both releases and exact edge builds.
- Step 4: Run the focused tests
- Step 5: Commit the catalog declaration
Task 2: Package matching bootstrap models
Repository: resourcepacks feature worktree from Task 1 Files:-
Create:
art/content/models/editor/marker.json -
Create:
art/content/models/npc_bodies/editor/guide.json -
Modify:
art/content/LICENSE -
Modify:
resourcepacks-product/src/main/kotlin/gg/grounds/resourcepacks/product/ContentContribution.kt -
Modify:
resourcepacks-product/src/main/kotlin/gg/grounds/resourcepacks/product/ProductGraph.kt -
Modify:
resourcepacks-product/src/test/kotlin/gg/grounds/resourcepacks/product/PackComposerTest.kt -
Modify:
resourcepacks-catalog/src/test/kotlin/gg/grounds/resourcepacks/catalog/PackArtworkLicenseContractTest.kt - Step 1: Write failing composition and license tests
- Step 2: Add deliberately authored minimal models
art/content/LICENSE while preserving the existing vendor/MCModels attribution text and tests. The marker is a one-block editor marker; the guide is a simple bootstrap NPC-body model. Do not add image files or vendor assets.
- Step 3: Capture and compose the files safely
ProductGraph to capture both sources with HeldSourceFile, bounded size, and pinned bytes. Feed byte-backed entries through ContentContribution; do not pass mutable paths into pack composition. Preserve no-follow behavior and exact release-input capture.
- Step 4: Verify model/catalog parity and the built pack
version.txt; Release Please changes it to 0.6.0 later:
- Step 5: Commit the pack content
Task 3: Merge and release resourcepacks 0.6.0
Repository: resourcepacks feature worktree- Step 1: Run full verification and request review
superpowers:requesting-code-review; resolve every blocking finding and rerun the commands.
- Step 2: Push, create the PR, and merge after green checks
arc-linux indefinitely. If required hosted checks cannot start, keep the verified branch and record the release gate rather than weakening branch protection.
Delivered by resourcepacks PR #26. CI run 33076082670 passed and the PR was squash-merged as bc69e647c6111edfa8e6efaf9c2f79715f6d51d6.
- Step 3: Complete Release Please for 0.6.0
version.txt, changelog, and release metadata. On that release branch, rerun clean check with the exact new version.txt value. Merge its release PR, verify tag v0.6.0, Maven package gg.grounds:resourcepacks-catalog:0.6.0, and immutable PackSet artifacts. Do not activate a cluster channel while the cluster is down.
Release PR #24 and its clean verification run 33077019256 were merged as a1ccb3d4981c268253744f88142d2c849082006c; tag v0.6.0 exists. Immutable release run 33077618746 built and locally verified all four artifacts, then failed at the catalog Maven upload with GitHub Packages 402 Payment Required. Keep this step open until Maven, CDN/release assets, and Stable advancement are complete.
- Step 4: Record exact producer evidence
Task 4: Create and scaffold plugin-scene-editor
Repository: new /home/lukas/grounds/plugin-scene-editor
Files:
-
Create:
settings.gradle.kts,build.gradle.kts,gradle.properties -
Create:
common/build.gradle.kts,paper/build.gradle.kts -
Create:
paper/src/main/resources/plugin.yml -
Create:
README.md,CHANGELOG.md,LICENSE -
Create:
release-please-config.json,.release-please-manifest.json -
Create:
.github/dependabot.yml -
Create:
.github/workflows/ci.yml,.github/workflows/release-please.yml,.github/workflows/release.yml - Create: Gradle wrapper files
-
Create:
common/src/main/kotlin/gg/grounds/scene/editor/SceneEditStatus.kt -
Create: packaging and Java-consumer tests under
common/src/testandpaper/src/test - Step 1: Create the repository locally and on GitHub
main, then create an isolated feat/scene-editor-mvp worktree. Do not reuse the dirty docs or plugin template checkouts.
- Step 2: Copy only conventions, not product code
plugin-grounds-platform and plugin-permissions as structure exemplars. Configure exact released inputs:
GroundsSceneEditor, main class gg.grounds.scene.editor.paper.GroundsSceneEditorPlugin, API version matching the current Paper convention, hard depend: [BuildSystem], root /scene, and the permissions from the approved design. Expand ${VERSION} explicitly with ProcessResources; paper-conventions does not do this itself.
- Step 3: Add the public service boundary first
boolean hasUnsavedChanges(UUID) without Kotlin-specific types.
- Step 4: Add packaging tests
SceneEditStatus, scene-format, and the pinned catalog exactly once, leaves SceneEditStatus unrelocated, expands ${VERSION}, and excludes test libraries. Configure Paper’s Maven publication to replace the disabled/empty standard JAR with shadowJar, following plugin-grounds-runtime/paper/build.gradle.kts; publish both common and the runnable Paper artifact. Use the default Grounds artifact naming unless deployment tooling proves a fixed versioned filename is required.
- Step 5: Verify and commit the scaffold
30ee898 passed a serial clean build and both Maven publications against an isolated Maven repository populated from the exact v0.6.0 tag. The public GitHub repository remains intentionally uncreated until repository visibility is explicitly approved; the remote package outage is not represented as a successful release.
Task 5: Implement catalog binding, scene creation, and typed mutations
Repository: plugin-scene-editor feature worktree Files:-
Create:
common/src/main/kotlin/gg/grounds/scene/editor/catalog/SceneCatalogBinding.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/catalog/CatalogStatus.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/mutation/SceneMutation.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/mutation/SceneMutationResult.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/mutation/SceneMutations.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/validation/SceneValidationState.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/validation/SaveEligibility.kt -
Create matching tests under
common/src/test/kotlin/... - Step 1: Write failing catalog and creation-default tests
(0,2.25,0) label offset, fixed look, null proximity, empty bindings, visible state, and automatic activation.
- Step 2: Implement catalog binding and scene creation
GroundsAssetCatalog.catalog and constructs immutable grounds:actions@1 with no actions. Tests inject catalogs. New documents pin these exact references.
- Step 3: Write failing mutation tests
ApplicationAction: unrelated prop/NPC mutations preserve it byte-semantically, the empty action catalog makes it read-only and catalog-unverified, and no common mutation can create, replace, or silently remove it without an exact catalog definition.
- Step 4: Implement typed immutable mutations
- Step 5: Verify and commit
d9c3770. TDD and review covered exact catalog pins/defaults, every first-slice prop/NPC mutation family, canonical transforms, exact-original rejection identity, unsupported composite rejection, non-finite input, pure intrinsic/catalog status separation, SaveEligibility, and read-only preservation of unverified application actions. Final forced :common:spotlessCheck :common:test rerun passed against the exact locally staged v0.6.0 catalog.
Task 6: Implement shared sessions, history, selections, and leases
Files:-
Create:
common/src/main/kotlin/gg/grounds/scene/editor/session/EditorSession.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/session/EditorSessionService.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/session/SessionState.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/history/SceneHistory.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/lease/ElementLease.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/lease/ElementLeaseRegistry.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/session/EditorSelection.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/SceneEditorEvents.kt - Create matching tests
- Step 1: Write failing state-machine tests
SceneEditStatus across absent/clean/dirty sessions.
- Step 2: Implement bounded history
- Step 3: Implement leases with an injected clock
- Step 4: Verify and commit
a40f2ff. TDD and two review rounds covered one shared session per
world, independent player selections, serialized mutations, FIFO post-commit events with
listener-failure isolation, canonical-byte dirty state, generation-bound save snapshots,
100-entry immutable history, atomic multi-step undo/redo, 120-second element leases,
renewal/expiry/override/release behavior, and controlled no-session/input edge cases. The final
forced :common:spotlessCheck :common:test --rerun-tasks run passed with all 11 Gradle tasks
executed, and the final read-only review reported no P1/P2 findings.
Task 7: Implement atomic world-root persistence and recovery
Files:-
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/SceneRepository.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/WorldSceneRepository.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/AtomicSceneFileStore.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/SceneFingerprint.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/SceneLoadResult.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/repository/SceneSaveResult.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/recovery/RecoveryService.kt - Create matching tests
- Step 1: Write failing load and path-security tests
scene.json; exact-byte fingerprinting; normalized world root; symlink escape rejection; size bounds; and preservation of invalid bytes.
- Step 2: Write failing atomic-save tests
ATOMIC_MOVE, per-world serialization, concurrent fingerprint rejection, stale async generation rejection, and retention of the old file when atomic replacement is unsupported.
- Step 3: Implement canonical persistence only
- Step 4: Implement explicit recovery
- Step 5: Verify and commit
7ce3a1e. TDD and three independent review rounds covered bounded
no-follow reads, exact-byte SHA-256 fingerprints with distinct absence, canonical SceneJson
encoding, eligibility-bound save reservations, coordinated conditional ATOMIC_MOVE, concurrent
disk conflicts, atomic-only failure retention, defensive byte boundaries, invalid-file backup and
create, diagnostic exports, clean/dirty reload rules, literal-confirm discard audit, JVM capability
forgery, fatal-error reservation cleanup, and best-effort temp cleanup. The final forced
:common:spotlessCheck :common:test --rerun-tasks run passed with all 11 Gradle tasks executed,
and the final read-only review reported no P1/P2 findings under the documented cooperative-writer
coordination contract.
Task 8: Compose the Paper plugin and command surface
Files:-
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/GroundsSceneEditorPlugin.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/PaperSceneEditorRuntime.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/PaperSessionResolver.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/command/SceneCommand.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/command/SceneTabCompleter.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/command/SceneCommandAuthorizer.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/AdventureSceneFeedback.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/PaperScheduler.kt - Create matching MockBukkit tests
- Step 1: Write failing lifecycle/service tests
SceneEditStatus registration, reverse-order close/unregister, and no mutable global singleton.
- Step 2: Implement lifecycle composition
JavaPlugin limited to dependency construction and registration. Run Bukkit work on the server thread and file work asynchronously with session-generation checks.
- Step 3: Write failing command/permission/completion tests
PaperSessionResolver must query the published BuildSystem API for the player’s current BuildWorld; ordinary loaded Bukkit worlds are rejected. MockBukkit tests cover a missing BuildWorld, a valid build world, world change, and BuildSystem service loss. The hard plugin dependency makes a completely absent BuildSystem an enable-time failure rather than an ambiguous editor mode.
- Step 4: Implement commands as adapters over common operations
/scene save says saved, never published.
- Step 5: Verify and commit
2cf36d6, with supporting Common hardening in e66a808, c80ebae,
880193a, and db4a59f. TDD covers transactional plugin lifecycle, BuildSystem service and exact
world resolution, scheduler shutdown races, lazy valid/absent/invalid scene bootstrap, atomic
first-command recovery, generation-bound reload/discard, all first-slice prop/NPC mutations,
owner-bound leases and administrative takeover, exact leaf permissions, kind- and
session-aware completion, pagination, finite-number rejection, preserved read-only application
actions, deterministic diagnostics, and local-only save wording. The final forced
:common:spotlessCheck :common:test :paper:spotlessCheck :paper:test --rerun-tasks verification
passed, and three independent review rounds ended with no P1/P2 findings.
Task 9: Add previews and lifecycle cleanup
Files:-
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/preview/PaperPreviewAdapter.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/preview/PreviewRegistry.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/PaperWorldLifecycleListener.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/PaperPlayerLifecycleListener.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/tool/RaySelection.kt - Create matching tests
- Step 1: Write failing pure selection and adapter tests
- Step 2: Implement server-thread preview reconciliation
- Step 3: Implement cleanup listeners
- Step 4: Verify and commit
cd3f1c1. Pure ray selection uses normalized world-unit distances and a
deterministic ID tie-breaker. Paper previews reconcile immutable generation snapshots into
non-persistent, plugin-tagged, viewer-only Display entities with generic prop/NPC bodies, labels,
selected outline/axes/ID, and a visible text fallback. Registry ownership is transactional,
viewer/world scoped, and retains failed removals for scoped retry. Quit, world-change, unload, and
disable cleanup release selections/leases, cancel preview work, remove entities, revoke active-save
capabilities before atomic replacement, log dirty sessions, and notify every online participant.
The final forced Common/Paper test and format run executed 25 of 26 tasks successfully; two
independent review rounds ended with no remaining P1/P2 findings.
Task 10: Add the editor tool
Files:-
Create:
common/src/main/kotlin/gg/grounds/scene/editor/tool/TransformComponent.kt -
Create:
common/src/main/kotlin/gg/grounds/scene/editor/tool/TransformMath.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/tool/EditorTool.kt -
Create:
paper/src/main/kotlin/gg/grounds/scene/editor/paper/tool/EditorToolListener.kt - Create matching tests
- Step 1: Write failing transform-math tests
- Step 2: Write failing listener tests
- Step 3: Implement the tool and action bar
PlayerItemHeldEvent only after a valid editor mutation is accepted.
- Step 4: Verify and commit
73e9a4b. The exact plugin-tagged Blaze Rod is available without inventing
a scene, while all input remains gated by BuildSystem world, permission, session, selection, and
owner lease. Main-hand ray selection uses rotated/scaled world hit bounds; right click cycles the
seven transform components; adjacent wheel events apply signed fine/normal/coarse command-equivalent
mutations and cancel only after acceptance. The action bar reports element, component/value, step,
dirty state, and lease time. Quit/world-change cleanup removes component state. The final forced
Common/Paper test and format run executed 25 of 26 tasks successfully, and two independent reviews
reported no remaining P1/P2 findings.
Task 11: Verify, merge, and release the editor API and Paper plugin
Repository: plugin-scene-editor feature worktree- Step 1: Add the canonical end-to-end fixture
scene-format 0.1.0, reload it into a fresh session, and assert byte-canonical equality. Package the Paper shadow JAR and rerun its contents contract.
- Step 2: Run clean verification and review
superpowers:requesting-code-review; resolve all blocking findings and rerun verification.
Local release preparation is complete through Step 2. Commit af5fa84 adds the canonical fixture
against the exact production bootstrap catalog; signed commit 22ceb60 normalizes the generated
Windows wrapper so git diff --check main...HEAD is clean. A split clean-then-versioned-check run
completed all Common/Paper tests, format gates, and the Paper Shadow JAR for 0.1.0. Both Maven
publications were installed locally and inspected: Common JAR/sources/Javadoc/POM and the
classifierless Paper Shadow JAR are present; plugin.yml reports 0.1.0; no Bukkit, Paper, or
BuildSystem API classes are bundled. The Paper JAR and local Paper Maven artifact share SHA-256
50be2507ddee8fa896abd1571c202cdc63e5aa3a8f905e2546da85576579ef17; the Common JAR SHA-256 is
b86dc7f3bf589ab3eab990038c660c9d7bea6ae86e7562060cd08ebc1493f069. Two independent whole-branch
reviews found no remaining P1/P2 after the wrapper normalization.
- Step 3: Push, create the PR, and merge with the explicitly authorized pipeline exception
d66bbb0f27007d41ac0802d1f95ae90fca33697b. The remote Java build was not used as
release evidence: the user explicitly authorized merging without waiting for the runner, while the
fresh local release check executed all 29 requested tasks successfully and git diff --check was
clean. Release Please bootstrap corrections were merged through
plugin-scene-editor#6 and
plugin-scene-editor#7.
- Step 4: Release the first editor version
common Maven coordinate, and the runnable Paper shadow artifact. Download both from the release/package service and rerun the Java API and JAR-contents smoke checks against the published files.
Release Please merged plugin-scene-editor#5
as bef5ee9eef23c0fda85bab86149e5cb1f06e2177 and created
v0.1.0 as grounds-bot.
Keep this step open: automatic publish run
33105049938 failed in
the build before publishing because it could not resolve the still-unpublished
gg.grounds:resourcepacks-catalog:0.6.0. No editor Maven coordinate or remote runnable Paper
artifact may be claimed until the upstream GitHub Packages 402 Payment Required gate is resolved
and Release Please’s tag-triggered publish workflow is rerun successfully.
- Step 5: Record exact consumer pins
Task 12: Guard dirty scenes before GroundsMaps push
Repository:/home/lukas/grounds/buildsystem using a new worktree based on the branch containing async map publication
Files:
-
Modify:
settings.gradle.kts -
Modify:
buildsystem-grounds/build.gradle.kts -
Modify:
buildsystem-grounds/src/main/java/gg/grounds/buildsystem/command/MapCommand.java -
Create:
buildsystem-grounds/src/main/java/gg/grounds/buildsystem/command/SceneEditorPushGuard.java -
Create:
buildsystem-grounds/src/test/java/gg/grounds/buildsystem/command/SceneEditorPushGuardTest.java -
Modify: archive contract test that owns
WorldArchive - Step 1: Add dependency and descriptor contract tests
dependencyResolutionManagement, using github.user/github.token Gradle properties with GITHUB_ACTOR/GITHUB_TOKEN CI fallback and never logging either value. Prove the exact released common POM/JAR resolves before changing production code.
Pin that released common API as compileOnly and testImplementation. Add GroundsSceneEditor to the generated GroundsMaps softDepend. Test that the shadow JAR does not bundle SceneEditStatus and that plugin.yml contains the soft dependency.
- Step 2: Write failing guard tests
- Step 3: Implement a linkage-safe optional adapter
SceneEditStatus access in SceneEditorPushGuard, and load/invoke that adapter only after PluginManager confirms GroundsSceneEditor is enabled. The no-editor test must prove MapCommand loads and pushes without NoClassDefFoundError when the compile-only API is absent. Query on the server thread.
- Step 4: Wire the guard at the start of push
You have unsaved scene edits. Run /scene save before /map push. for dirty state.
- Step 5: Retain the archive-root contract
scene.json remains a direct archive-root entry and its bytes are unchanged.
- Step 6: Verify and commit
Task 13: Run final cross-repository acceptance and prepare the buildserver
Repositories: plugin-scene-editor, resourcepacks, buildsystem, and the service-maps feature worktree when its derivation implementation exists Buildserver files after acceptance:-
Modify:
containers/buildserver/Dockerfile -
Modify:
containers/buildserver/README.md -
Create:
containers/scripts/test-buildserver-scene-editor-pin.sh -
Modify:
containers/.github/workflows/ci.yml -
Modify, only when deployment resumes:
deploy/environments/stage/components/buildserver/values.yaml - Step 1: Verify archive and published artifact contracts together
scene.json is byte-identical and still decodes with released scene-format 0.1.0.
- Step 2: Exercise downstream contracts locally
SceneDeriver, feed the bytes through it using the exact 0.6.0 asset catalog and empty grounds:actions@1, then assert representative derived output without contacting the cluster. If that implementation has not landed, record this as a non-blocking downstream check; released scene-format decoding and BuildSystem archive-root verification remain mandatory.
- Step 3: Run full clean builds
git diff --check, inspect artifact contents, and request final code review.
- Step 4: Merge and publish the remaining consumers in dependency order
- Confirm the already released resourcepacks 0.6.0 and editor artifacts.
- Merge and publish BuildSystem/GroundsMaps with the exact editor-common pin.
- In a fresh containers worktree, pin the new BuildSystem base image by immutable digest and add exact
SCENE_EDITOR_URL/SCENE_EDITOR_SHA256build arguments. DownloadGroundsSceneEditor.jarin the existing verified plugin stage and copy it to/app/plugins. - Add and wire
scripts/test-buildserver-scene-editor-pin.sh; assert the Dockerfile/README pins agree and the built image contains exactly one editor JAR alongside BuildSystem and GroundsMaps. - Merge containers and publish the derived
ghcr.io/groundsgg/buildserverimage by immutable digest. - Only after cluster work resumes, update
deploy/environments/stage/components/buildserver/values.yamlto that exact derived image tag/digest and retain its parser/Helm tests.
- Step 5: Update the masterplan with evidence
