Every canvas position write (Auto-arrange, Save positions, drags, freeze pins)
used to send one PATCH per technology, all at once. On a large tree that was
dozens of concurrent full document updates, which exhausted the database pool
and could crash the server.
- Add POST /api/tech-tree/positions: validates `{ positions: [{ id, x, y }] }`
(null x/y clears a pin, capped at 2000) and applies every position in a
single UPDATE on the tree position columns. Access mirrors the technologies
update rule (technologies:update or Intelligence membership).
- Route all canvas position writes through it; failures now surface in the
toolbar instead of being swallowed.
- Auto-arrange is optimistic: the canvas re-lays out on click and the pins
are restored if the save fails. A spinner status chip shows while a layout
save runs, and layout controls and dragging are locked until it finishes.
- Integration tests for validation and the bulk write; the e2e checks the busy
indicator and that no per-node PATCHes are sent.
Update AGENTS.md for the tech tree drag/snap/edge-routing work, the promotion
nominations and leadership transfers, the mission lifecycle and side-op
workflow, and the Arma icon tooling; adjust the v0.2.59 changelog entry.
- Regenerate payload-types and the drizzle schema for the new collections and
fields (promotion nominations, leadership transfers, the accessory asset
type, the Ornith model settings) and register the new migrations.
- Fix the reset-sequences SQL function to skip empty tables, and update the
prod sync script to match.
Adds an "accessory" asset type for flashlights and lasers, allowed on the same
equipment slots as optics, muzzles, and bipods, with a locker label and colour.
Point the AI compose plugin at Unsloth Studio serving Ornith 1.0 9B (replacing
Gemma 4 26B), and rename the model settings to ornith-9b-settings. Ornith is a
reasoning model, so requests disable reasoning (reasoning_effort "none") to stop
a small token budget being spent entirely on the reasoning block. Adds the
migration and documents UNSLOTH_URL / UNSLOTH_API_KEY in .env.example.
- Add an `icons:arma` tool (extractArmaIcons.ts) that pulls inventory icons
out of the game's PBOs and materializes the .paa files the catalog needs.
- Extend the catalog import library with the matching planning and upsert
support, and cover it with integration tests.
- The mission tick now runs two idempotent sweeps: auto-start flips Scheduled
missions whose start time has arrived to Active, and auto-complete sets
missions whose scheduled window (start plus duration) has fully passed to
Completed with a stamped completedAt.
- Side ops are created as quiet Ready drafts and committed with a "Mark as
Scheduled" control, which is what posts the Discord roll-call and hands the
op to the lifecycle above.
- Daily schedule reminders nudge the creator to set a draft live (web
notification plus Discord DM).
- Adds the mission lifecycle/commit event types and the matching notification
labels, and updates the bot docs.
- Promotion nominations: members can nominate a peer for promotion from their
profile, and a new /personnel/promotions page lets staff review and action
the nominations (with an optional note on each).
- Leadership transfers: a member's leadership can be handed over to another
member, surfaced on the transfers page and in the roster (which also gains a
staff remove-member dialog).
- Adds the PromotionNominations and LeadershipTransfers collections (registered
in the Payload config), their service libs, the migration, and integration
coverage.
Round out the canvas editing experience.
- Dragging one card of a multi-selection now moves and persists the whole
selection (React Flow hands the full dragged set to onNodeDragStop).
- A category band is draggable by its header, which moves every technology in
that band; the band body stays click-through.
- A "Snap: On/Off" toolbar toggle (default on, remembered per browser) snaps
dragged cards to a 46px grid that divides the column and row steps, so
snapped cards line up with the automatic layout.
- Connection lines are adjustable: select an edge to reveal "+" handles at each
segment's midpoint (click to add a bend), drag bends to reroute, and
double-click a bend to remove it. Routes persist per browser.
- Edges route around technologies they are not connected to, and corners are
rounded, so lines no longer cut through unrelated cards.
- Ctrl/Cmd-clicking to multi-select no longer pops the side panel over the
canvas.
Add usage analytics via @microsoft/clarity across the frontend app and the
login page. ClarityAnalytics loads the tracking script only in production
builds (never local dev), keyed by NEXT_PUBLIC_CLARITY_ID; unset disables it.
Mounts it in both the (frontend) root layout and the standalone login layout
so both surfaces are covered without touching the admin panel.
Move the record contract, planning, and upsert logic out of the CLI script
and into src/lib/arma-import/catalog.ts so it can be unit-tested and reused.
The CLI (importArmaLibrary.ts) becomes a thin wrapper. Records are upserted
into assets (keyed by className) and vehicles (by classname); thumbnails are
materialized from .paa inventory icons, and fuel config is resolved per
record type. Adds integration coverage for the catalog import path.
Route the game's `money.delta` events into the web banking system so a
player's personal bank account tracks their in-game wallet.
- New `processMoneyEvent` (src/lib/arma-bridge/money.ts) applies a parsed
money delta to the player's personal account: debits go to the unit
treasury (or leave if no treasury), refunds hit the treasury first and
fall back to a plain deposit, and rewards/grants deposit directly.
- Wire the handler into ArmaSyncEvents and add a `POST /api/arma/v1/balances`
route (get_balances) that returns personal balances for Steam IDs, omitting
unlinked IDs so the game keeps its own stored value.
- Events dedupe by serverId+eventId so a retried delivery never
double-charges, and the whole path never throws.
Add a "Tech Tree (admin)" section covering the tech-categories collection,
treePosition, the React Flow admin view, the propagating layout, the
stability contract (connect/save-positions/auto-arrange), edge visuals, and
the integration and e2e test commands. Note the tech tree e2e suite.
- `tech-tree-layout.int.spec.ts`: pure layout (wrapping, manual overrides,
category bands, forward-edge warnings, cycles, catalog-stability).
- `tech-tree-prereq-edits.int.spec.ts`: add/remove/switch prerequisites with
cycle rejection and the pin-on-connect stability contract.
- `tech-tree-stability.int.spec.ts`: freeze pins every relocated card on a
data change, plus edge visuals (per-path colors, per-source lanes).
- `tech-categories.int.spec.ts`: CRUD, RBAC, division gating, FK set-null,
position round-trip.
- `tech-tree.e2e.spec.ts`: nav entry, render, preview/panel edits, both
quick-create paths, drag persistence + Auto-arrange, save positions
surviving a reload, connect/disconnect, and multi-column wrapping.
Add a custom admin view over the technologies collection so staff can model
prerequisites as a left-to-right tree instead of a flat list.
- React Flow v12 canvas with category bands, OR-prerequisite edges, a
lite side-panel editor, and a quick-create dialog.
- Propagating layout: a technology with prerequisites lands one column right
of its parents at their average row; uncategorized roots fill a catalog
grid keyed by each tech's rank among all uncategorized technologies, so
promoting one into the tree leaves an empty slot instead of re-shuffling
the grid.
- Stability contract: connecting or removing edges never moves a card (the
connected node is pinned to its current spot), and a "Save positions"
control commits every on-screen node so the arrangement survives reload;
"Auto-arrange" clears manual positions and restores the ordered layout.
- Progression-path edge colors and per-source lanes keep overlapping edges
readable. Wire the view and its nav entry into payload.config.ts and add
dark React Flow / scrollbar styling.
Add the tech-categories collection that gives technologies a home off the
flat uncategorized pool, plus the schema support the admin editor needs.
- New `tech-categories` collection (unique name, timeline `position`,
optional hex `color`) with a `Technologies.category` FK (ON DELETE set
null) so deleting a category just unfiles its technologies.
- Add `Technologies.treePosition {x,y}` so the canvas can pin a node's
manual position; clearing it uses `{x:null,y:null}`.
- Register the two hand-written migrations (tech-categories + treePosition)
and regenerate payload-types, the drizzle schema, and the admin importMap.
- Register the `tech-categories:*` permission group and grant Intelligence
division members admin-panel access, mirroring technologies.
Covers the category registry sync, create and update gating per role (plain user, admin, developer, guest), the update-time escalation block, and read gating.
The suite could not pass on the test database: global setup now seeds the shared dev/Test123 login with a superuser Roles doc and the dashboard rank, and points its own Payload connection at the test database. Playwright runs one worker with generous test and expect timeouts because first navigation to a route triggers a cold webpack compile, and gains E2E_BROWSER_PATH to swap the browser binary when the installed Vivaldi crashes under automation. The landing page gains the missing h1 the guest specs assert, login flows wait for the redirect to land, strict-mode locator collisions get exact or level-scoped matches, the session banner test re-signs the login claims with a short expiry using the runtime secret, and the reservation dialog click forces the dispatch and verifies the dialog opened (the trigger goes aria-hidden behind the modal mid-click, which stranded the retry loop).
Pages declare brief tours in code, keyed to stable data-tour anchors. The layout resolves each visitor's access signals (intelligence and logistics qualifications, admin-panel access, feature flags) and step filtering hides what a user cannot reach, mirroring the sidebar. A tour auto-starts the first time a browser visits its page; dismissals are remembered per browser in localStorage. The header gains a replay control for on-demand runs on toured routes, the tour card gains Skip all tours on this device to opt a browser out of auto-starts, and the Account page holds the re-enable toggle. The card and spotlight get a distinct overlay treatment (glow, accent strip, outlined target).
The note and warning icons were pinned at a fixed top offset and drifted off the text line inside short callouts. They now center vertically in the callout box.
Seeded Documentation pages have no lastEditor; the index cards and the detail header now read 'System' instead of 'Unknown editor' for those. Other categories keep the old fallback.
New in-depth, non-developer guides for the core systems: logistics and storage rules, banking, market and negotiation, shipments, base management, and the map. Read stays open to any logged-in user; create and edit require admin or developer roles (enforced in the collection access functions and the wiki server actions, since the service layer runs with overrideAccess). Category escalation via update is blocked too. Ships the enum migration, regenerated schema/types, and the idempotent seed script with its markdown sources.
package.json now declares patchedDependencies, so bun install --frozen-lockfile requires patches/ to exist when the prod-deps and builder stages install. The v0.2.56 Coolify build failed with "Couldn't find patch file" because only package.json and bun.lock were COPY-ed. Verified by reproducing the failure without patches/ and a clean frozen-lockfile install (patch applied) with them.
Default compose model moves to unsloth/gemma-4-26B-A4B-it-qat-GGUF (migration rewrites plugin_ai_instructions columns + enum). Strip markdown code fences from plain-text fallbacks so Gemma's JSON-flavored answers still render as prose. Pin a bun patchedDependencies patch for @ai-stack/payloadcms (activeFieldPath context) which requires the patches/ directory alongside package.json and bun.lock. Note: the change_ai_model migration also carries the new map permission enum values from the map permission split.
Players meeting a role's minimum rank and MOS can queue for mission roles before op time. Slot holders are recomputed statelessly from signup order plus current RSVP (first N committed signups hold the role); attendance writes diff holders around the write so RSVP changes auto-promote, with notifications and events. Adds mission-role-signups collection, roleSignups/missionTime libs, sign-up panel, role slots + rich-text descriptions (with RoleDescription renderer), orphan-pruning hook, migrations, tests, and AGENTS.md docs.
Replace the maps:create/update/delete catch-all for map features: roads, zones, and nodes each take a per-collection author permission, structure placement takes maps:place (still co-gated with the logistics qualification), and GeoJSON import requires all three author permissions. The map view page keys edit UI per feature type; admin role seed grants the new values. Tests updated for the permission split.
Touch drags flip pages: the page follows the finger live, the axis locks
on first movement so vertical pans never flip, and edge drags get
rubber-band resistance. Swipes starting on interactive controls are
ignored. Swipe intent lives in bookSwipeDirection with unit tests.
Chapter numbers now follow the order beats were written: sortBeats
defaults to creation date ascending, treats occurredAt as display-only,
and lets an explicit Order value curate the sequence. The /story page
shows the newest chapter on top with correct numbering, and switches to
exact hand-sequenced book mode when any beat is ordered.
Fullscreen reader alongside Immersive view: paginated two-page spread
with no vertical scroll, chevron and arrow-key page turns, and a page
indicator. Chapters always render in reading order (Chapter 1 first).
Layout knobs (spine gap, single-column breakpoint, book max width) live
in lib/story/bookLayout with unit-tested paging math.
- Introduced voiceover relationship to StoryBeats with AI-generated flag.
- Added BeatVoiceover component for playing audio narrations with a minimal interface.
- Updated StoryPage, StoryViewer, and StoryTableOfContents to display and handle voiceovers.
- Created Voiceovers collection for uploading and managing audio narrations for beats.
- Included migration script to support voiceover functionality in the database.