Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/PROJECT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ Payments/monetization P0s are separately listed in `docs/MVP_STATUS_AND_DEV_PLAN

## Log

### 2026-10-15 — Safe crew inventory transfer and demo reload continuity (#208)

Reserved the existing economy-transfer action, TRAP give flow, their new regressions, and narrowly scoped demo-reseed inventory continuation before editing from protected main `9eef5aa`. This is independent of open ATTACK #204, Nightfall #205, DRIVE #202, identity #169, current-login API #206, and legacy session-owner #207 runtime files; their branches were not edited or merged.

The existing `transferItemToMember` used browser-incompatible CommonJS `require`, accepted malformed quantities, dropped metadata, and deducted all matching stash stacks. It now uses static ESM imports and existing role rules, validates positive whole requested quantities against current stock/active recipient, accepts an optional exact source-stack ID, preserves product/gear metadata with unique carried IDs, and returns explicit success/failure. TRAP confirms only successful gives, shows retryable inline denial, and guards one selected action against same-frame taps. Unclassified legacy weapons remain unsupported; no DRIVE classification or new authority was invented. Contacts' ignored return result remains a separately coordinated follow-up because its file overlaps deferred identity work.

Actual reload tracing additionally reproduced demo startup erasing transferred carried inventory. The minimal demo-only repair preserves it only for the prior demo player and matching demo-gang member, never another account/gang. Other seed reset behavior is unchanged.

Regression-first evidence covers base store/UI runtime failures; second-batch, missing-feedback and reseed failures before their fixes; and actual carried-record loss after page reload. Final focused/adjacent suite: 35 passed (29 new regressions plus six existing seeder tests). Full gates: 1,062 frontend passed / four existing intentional skips / 94 files; lint 0 errors and unchanged 200 baseline warnings; typecheck, production build, asset audit/packages and diff hygiene passed; isolated backend 107 passed. Independent read-only review found no material issues. A separately built local demo passed desktop-to-TRAP give, role-denial and actual reload at 390×844, 375×812 and 1440×900 with zero uncaught page exceptions.

Built-preview console evidence remains honest: unchanged `/favicon.svg`, `/assets/game_logo.png` and `/assets/generated/ui/icons/icon_news_v001.png` return 404 (5/4/4 occurrences including reload), while dev SPA fallback masks those HTTP failures. Reported under #190; no shell/index/sprite/manifest or asset edit occurred. This is not clean-console, authenticated saved-game, external-beta or launch proof. Evidence: `docs/evidence/2026-10-15-inventory-transfer/README.md`, raw red/green/full gates, browser JSON, screenshots, portable runner and source hashes.

The ten-PR audit also identified the already posted unresolved P1 in #206; integration hold recorded on #175. #175 remains open for separately approved isolated-staging two-synthetic-user Auth/RLS/denied-direct-write and canonical Flask consequence/reload/second-session proof. Migration 007 is already applied and must not be replayed. Older project-log current-state/roadmap blocks remain historical; this entry does not rewrite them or supersede the contributor guide. No remote SQL/migration, Auth configuration, production, deployment, scheduler, payment, release tag, protected branch or other model branch changed. Branch `fix/safe-crew-inventory-transfer` is prepared for integration-owner review, not merged or deployed.

### 2026-10-01 — Backend timestamps are timezone-aware UTC (#170)

- Replaced all 17 `datetime.utcnow()` call sites in the live backend runtime with one shared helper, `backend/python/utils/utc.py` (`utc_now`, `to_iso_utc`, `utc_now_iso`). Python 3.12 deprecates `utcnow()` and the required CI runs the full backend suite, so the debt was visible on every run: the audited baseline emitted 153 warnings, all of them this single warning. The suite now runs with 0 warnings.
Expand Down
75 changes: 75 additions & 0 deletions docs/evidence/2026-10-15-inventory-transfer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Safe crew inventory transfer — verification record

_Manus · 2026-10-15 · [Issue #208](https://github.com/BrandDead/slide/issues/208) · base `9eef5aac28a82275d7a2e1596e8658b167953ed7`_

---

## Player outcome and authority

A player opens **TRAP** from the existing command desktop, selects a product stack and an active eligible crew member, and presses **GIVE**. The existing economy store deducts only the requested units from that exact stack; the existing roster stores the complete carried record. Recorded product risk/quality and gear stats are retained. Rejected actions do not move inventory or report success, provide visible feedback, and remain retryable. Two same-frame taps on one selected give submit once; a later explicit selection can give again.

The store action uses ESM imports, positive safe-integer requested quantities, current stock/recipient checks, existing role rules, optional exact source-stack identity, and unique carried-item IDs. Legacy unclassified `weapon` items remain blocked; no ranged/melee classification or DRIVE loadout behavior was invented. Contacts remains compatible with the action's original three arguments, but its pre-existing unconditional success message is a separately coordinated follow-up.

Demo startup previously rebuilt starter crew without its persisted inventory. The narrowly scoped reseed repair carries inventory forward only when the pre-seed player is `demo-player` and the prior member belongs to the same demo gang. It does not import another account/gang's items. Other demo reset behavior is unchanged.

**No new store, combat/economy authority, API, schema, migration, production configuration, scheduler, payment flow, asset, or multiplayer behavior was added.** Local demo persistence is not authenticated saved-game proof for #175.

## Regression-first evidence

`store-ui-red.log` records failure against unchanged main: the live action's CommonJS `require` fails at runtime. Real browser reproduction at 390×844 and 1440×900 also recorded `require is not defined` with no inventory transfer in `browser-base.json`.

`selected-batch-red.log` shows an explicitly selected second same-item stack incorrectly consuming the first stack; `inline-feedback-red.log` shows no visible rejection alert; `demo-reseed-red.log` shows two real startup/persistence regressions failing; and `browser-reload-red.log` shows the transferred carried record becoming `undefined` after real reload. These were observed before their respective fixes.

`focused-green.log` then records **35 passing tests**: 29 new transfer/UI/reseed regressions and six existing adjacent Contacts/books/progression reseed tests. `independent-review.md` records a read-only second review with **no material findings**.

## Full local gates

| Gate | Verified result |
| --- | --- |
| Full frontend tests | 1,062 passed; 4 existing intentional skips; 94 files |
| Lint | 0 errors; 200 unchanged baseline warnings |
| Typecheck and production build | Passed |
| Asset audit | 112 assets; 0 errors/warnings; 7.70 MB / 20 MB budget |
| Package validation | 5 packages; 4 schemas; passed |
| Isolated backend pytest | 107 passed |
| Diff hygiene | Passed |

Commands and raw output are retained in `full-preflight.log` and `backend-green.log`. Only terminal trailing whitespace and extra EOF blank lines were normalized for diff hygiene; assertions, outcomes and errors are unchanged. The existing oversized-bundle warnings remain. Backend tests ran with remote Supabase/database variables blank; no remote production data was accessed or changed.

## Final built-game player path

The **separately built demo artifact**, not just the dev server, was checked at **390×844, 375×812, and 1440×900**. `browser-built.json` records each assertion and raw error list. The route was mature-content notice → command desktop → TRAP → product → Lil Dre → GIVE → product → Big Rome → rejected GIVE → actual page reload → desktop → TRAP.

All three scoped paths passed: stash 50→49, one retained carried unit with the same complete persisted record after reload, denied shooter product with no second success message, visible inline rejection, and reachable GIVE hit target. There were **zero uncaught page exceptions**. Same-frame repeat, exact batch selection, stale stock/member state, and legitimate repeat-selection behavior have real component/store regression coverage; browser proof is not claimed for every unit-test case.

![Phone denial leaves stock unchanged and provides visible rejection](built-small-phone-role-denied.png)
_Figure 1: Final 375×812 built-demo rejection state; GIVE/CANCEL remain reachable._

![Desktop reload retains both the remaining stash and Lil Dre's carried item](built-desktop-after-reload.png)
_Figure 2: Final 1440×900 built-demo after reload; stash remains x49 and Lil Dre carries one item._

### Existing resource failures are not hidden

This is **not clean-console or launch approval**. The built artifact exposed unchanged missing `/favicon.svg`, `/assets/game_logo.png`, and `/assets/generated/ui/icons/icon_news_v001.png`. Their files are absent from the protected base, and this repair does not alter their index/shell/sprite references. The raw evidence records 5, 4, and 4 resource-error occurrences across the three viewports including reload. The dev server's SPA fallback had hidden those HTTP 404s.

These paths were reported to the existing [visual workstream #190](https://github.com/BrandDead/slide/issues/190#issuecomment-6193823864). The runner only classifies those explicit proven-baseline 404 paths; any other HTTP error, non-resource console error, or page exception fails the scoped check. No replacement assets or hardcoded fallback URLs were added.

## Reproduction and handoff

From the repo on Node 24:

```bash
bash .agents/skills/slide-release-safety/scripts/preflight.sh src/stores/__tests__/inventoryTransfer.test.ts
cd backend/python
SUPABASE_URL= SUPABASE_KEY= SUPABASE_SERVICE_ROLE_KEY= SUPABASE_ANON_KEY= DATABASE_URL= ./venv/bin/python -m pytest tests -q
```

For the browser runner, build with `VITE_DEMO_MODE=1` into a disposable output folder, serve it on port 3001, and use an isolated Chromium profile. Copy `check-trap.mjs` to a disposable QA folder, install `puppeteer-core` there without changing repo dependencies, then run:

```bash
SLIDE_PREVIEW_URL=http://127.0.0.1:3001 \
SLIDE_EVIDENCE_DIR=/absolute/path/to/this/evidence/folder \
node /absolute/path/to/disposable-qa/check-trap.mjs built
```

The tested source/test SHA-256 values and base revision are recorded in `verification-summary.json`. Later integration must preserve every concurrent append-only project-log entry and rerun the combined candidate. Protected-main merge, production deployment, external beta, Auth/RLS and second-account proof remain separate gates. Do not replay migration 007.
3 changes: 3 additions & 0 deletions docs/evidence/2026-10-15-inventory-transfer/backend-green.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
........................................................................ [ 67%]
................................... [100%]
107 passed in 2.20s
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions docs/evidence/2026-10-15-inventory-transfer/browser-base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[
{
"name": "phone",
"viewport": {
"width": 390,
"height": 844
},
"mode": "base",
"before": 50,
"after": 50,
"giveControl": {
"x": 124,
"y": 800.5116577148438,
"width": 181.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [
"require is not defined",
"require is not defined"
],
"pass": true
},
{
"name": "desktop",
"viewport": {
"width": 1440,
"height": 900
},
"mode": "base",
"before": 50,
"after": 50,
"giveControl": {
"x": 244,
"y": 856.479248046875,
"width": 991.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [
"require is not defined",
"require is not defined"
],
"pass": true
}
]
169 changes: 169 additions & 0 deletions docs/evidence/2026-10-15-inventory-transfer/browser-built.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
[
{
"name": "phone",
"viewport": {
"width": 390,
"height": 844
},
"mode": "built",
"url": "http://127.0.0.1:3001",
"before": 50,
"after": 49,
"scope": "inventory player path; not a clean-console or launch approval",
"giveControl": {
"x": 124,
"y": 800.5116577148438,
"width": 181.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"consoleErrors": [
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)"
],
"failedResources": [
{
"url": "http://127.0.0.1:3001/favicon.svg",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
}
],
"checks": [
"real Trap give",
"stock deducted once",
"carried item persisted",
"role denial has no success",
"inline rejection visible",
"real reload retains exact carried record and stock"
],
"pass": true
},
{
"name": "small-phone",
"viewport": {
"width": 375,
"height": 812
},
"mode": "built",
"url": "http://127.0.0.1:3001",
"before": 50,
"after": 49,
"scope": "inventory player path; not a clean-console or launch approval",
"giveControl": {
"x": 124,
"y": 768.3202514648438,
"width": 166.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"consoleErrors": [
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)"
],
"failedResources": [
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
}
],
"checks": [
"real Trap give",
"stock deducted once",
"carried item persisted",
"role denial has no success",
"inline rejection visible",
"real reload retains exact carried record and stock"
],
"pass": true
},
{
"name": "desktop",
"viewport": {
"width": 1440,
"height": 900
},
"mode": "built",
"url": "http://127.0.0.1:3001",
"before": 50,
"after": 49,
"scope": "inventory player path; not a clean-console or launch approval",
"giveControl": {
"x": 244,
"y": 856.4043579101562,
"width": 991.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"consoleErrors": [
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)",
"Failed to load resource: the server responded with a status of 404 (File not found)"
],
"failedResources": [
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/game_logo.png",
"status": 404
},
{
"url": "http://127.0.0.1:3001/assets/generated/ui/icons/icon_news_v001.png",
"status": 404
}
],
"checks": [
"real Trap give",
"stock deducted once",
"carried item persisted",
"role denial has no success",
"inline rejection visible",
"real reload retains exact carried record and stock"
],
"pass": true
}
]
62 changes: 62 additions & 0 deletions docs/evidence/2026-10-15-inventory-transfer/browser-fixed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[
{
"name": "phone",
"viewport": {
"width": 390,
"height": 844
},
"mode": "fixed",
"before": 50,
"after": 49,
"giveControl": {
"x": 124,
"y": 800.5042724609375,
"width": 181.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"pass": true
},
{
"name": "small-phone",
"viewport": {
"width": 375,
"height": 812
},
"mode": "fixed",
"before": 50,
"after": 49,
"giveControl": {
"x": 124,
"y": 768.5186767578125,
"width": 166.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"pass": true
},
{
"name": "desktop",
"viewport": {
"width": 1440,
"height": 900
},
"mode": "fixed",
"before": 50,
"after": 49,
"giveControl": {
"x": 244,
"y": 856.3202514648438,
"width": 991.578125,
"height": 32,
"visible": true,
"hit": true
},
"pageErrors": [],
"pass": true
}
]
Loading
Loading