From 147ad35c3fdb0a1f6b55b4a3fdf41fb8e87cf4c5 Mon Sep 17 00:00:00 2001 From: Alfa Team Date: Fri, 26 Jun 2026 08:41:55 -0400 Subject: [PATCH] Add shared hash foundation --- docs_build/dev/BUILD_PR.md | 75 +- docs_build/dev/PLAN_PR.md | 45 +- .../PR_26177_001-shared-hash-foundation.md | 23 + ...hared-hash-foundation_branch-validation.md | 8 + ...hash-foundation_manual-validation-notes.md | 5 + ...d-hash-foundation_requirement-checklist.md | 12 + ...-shared-hash-foundation_validation-lane.md | 22 + .../dev/reports/codex_changed_files.txt | 17 +- docs_build/dev/reports/codex_review.diff | 702 +++++++++--------- src/shared/hash/hash.js | 108 +++ tests/shared/HashFoundation.test.mjs | 44 ++ 11 files changed, 614 insertions(+), 447 deletions(-) create mode 100644 docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md create mode 100644 docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md create mode 100644 docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md create mode 100644 docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md create mode 100644 docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md create mode 100644 src/shared/hash/hash.js create mode 100644 tests/shared/HashFoundation.test.mjs diff --git a/docs_build/dev/BUILD_PR.md b/docs_build/dev/BUILD_PR.md index edc9a0b75..466f9ca28 100644 --- a/docs_build/dev/BUILD_PR.md +++ b/docs_build/dev/BUILD_PR.md @@ -1,40 +1,20 @@ -# PR_26177_DELTA_056-shared-validation-assertions +# PR_26177_001-shared-hash-foundation ## Purpose -Move generic validation/assertion helpers out of random helper code into a shared validation module. +Add a small shared non-cryptographic hash foundation. ## Source Of Truth -This `BUILD_PR.md`, `PLAN_PR.md`, the user request, and `docs_build/dev/ProjectInstructions.zip` are the source of truth for `PR_26177_DELTA_056-shared-validation-assertions`. - -## OWNER Override And Team Assignment - -OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. - -Team Delta owns Shared JS, runtime utilities, technical debt remediation, and runtime test coverage. - -## Stack - -- Base branch: `PR_26177_DELTA_055-random-seed-enhancements` -- This PR depends on the random helper module from PR_053, `Random` from PR_054, and `RandomSeed` enhancements from PR_055. +This `BUILD_PR.md`, `PLAN_PR.md`, and the user request are the source of truth for `PR_26177_001-shared-hash-foundation`. ## Exact Scope -- Create `src/shared/validation/assert.js`. -- Move generic assertion helpers from random helper code into `assert.js`. -- Include only generic reusable validation functions needed by current random helpers: - - `assertArray` - - `assertFiniteNumber` - - `assertOrderedRange` -- Update random helper code to import from `src/shared/validation/assert.js`. -- Preserve existing `Random` and `RandomSeed` behavior. -- Do not change public API. -- Do not expand into unrelated validation functions yet. -- Add/update targeted unit tests if needed. -- No UI changes. -- No API/database changes. -- No unrelated cleanup. +- Add `src/shared/hash/` foundation. +- Include deterministic non-crypto hash helpers. +- No browser-owned product data. +- No runtime UI changes. +- Add targeted tests for the shared hash area. - Create required Codex reports under `docs_build/dev/reports/`. - Create repo-structured delta ZIP under `tmp/`. @@ -42,48 +22,43 @@ Team Delta owns Shared JS, runtime utilities, technical debt remediation, and ru - `docs_build/dev/PLAN_PR.md` - `docs_build/dev/BUILD_PR.md` -- `docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md` -- `docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md` -- `src/shared/validation/assert.js` -- `src/shared/math/randomHelpers.js` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md` -- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md` +- `src/shared/hash/hash.js` +- `tests/shared/HashFoundation.test.mjs` +- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md` +- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md` +- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md` +- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md` +- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md` - `docs_build/dev/reports/codex_review.diff` - `docs_build/dev/reports/codex_changed_files.txt` ## Out Of Scope -- No public API changes. -- No new unrelated validation helpers. -- No existing game logic adoption changes. -- No UI changes. +- No cryptographic hashing. +- No browser-owned product data. +- No runtime UI changes. - No browser storage changes. -- No API changes. -- No database changes. +- No API/database changes. +- No engine refactor. - No `start_of_day` folder changes. - No unrelated cleanup. +- No full samples smoke by default. ## Validation Run exactly: ```powershell -node ./scripts/run-node-test-files.mjs tests/shared/RandomHelpers.test.mjs tests/shared/Random.test.mjs tests/shared/RandomSeed.test.mjs -node --check src/shared/validation/assert.js -node --check src/shared/math/randomHelpers.js +node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs +node --check src/shared/hash/hash.js +node --check tests/shared/HashFoundation.test.mjs git diff --check ``` -Playwright is not required because this PR does not change UI or browser runtime flows. - ## Artifact Create repo-structured delta ZIP: ```text -tmp/PR_26177_DELTA_056-shared-validation-assertions_delta.zip +tmp/PR_26177_001-shared-hash-foundation_delta.zip ``` diff --git a/docs_build/dev/PLAN_PR.md b/docs_build/dev/PLAN_PR.md index de53f9020..835b7009a 100644 --- a/docs_build/dev/PLAN_PR.md +++ b/docs_build/dev/PLAN_PR.md @@ -1,43 +1,28 @@ -# PLAN_PR: PR_26177_DELTA_056-shared-validation-assertions +# PLAN_PR: PR_26177_001-shared-hash-foundation ## Purpose -Move generic validation/assertion helpers out of random helper code into a shared validation module. - -## Owner And Assignment - -- Team: Delta -- OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. -- Stack base: `PR_26177_DELTA_055-random-seed-enhancements`. +Add a small shared non-cryptographic hash foundation. ## Scope -- Create `src/shared/validation/assert.js`. -- Move generic assertion helpers from random helper code into `assert.js`. -- Include only generic reusable validation functions needed by current random helpers: - - `assertArray` - - `assertFiniteNumber` - - `assertOrderedRange` -- Update random helper code to import from `src/shared/validation/assert.js`. -- Preserve existing `Random` and `RandomSeed` behavior. -- Do not change public API. -- Do not expand into unrelated validation functions yet. -- Add/update targeted unit tests if needed. -- No UI changes. -- No API/database changes. +- Add `src/shared/hash/` foundation. +- Include deterministic non-crypto hash helpers. +- Add targeted unit tests. +- No browser-owned product data. +- No runtime UI changes. - No unrelated cleanup. ## Implementation Plan -1. Add `src/shared/validation/assert.js` with only the required generic assertion helpers. -2. Remove duplicated generic assertions from `src/shared/math/randomHelpers.js`. -3. Import the shared assertions from `randomHelpers.js`. -4. Run targeted random helper, `Random`, and `RandomSeed` tests. -5. Produce required PR reports and repo-structured ZIP. +1. Add `src/shared/hash/hash.js` with deterministic stable string and FNV-1a based helpers. +2. Add `tests/shared/HashFoundation.test.mjs`. +3. Validate determinism, object key ordering, seed variation, combination, and normalized hash output. +4. Produce required Codex reports and repo-structured ZIP. ## Acceptance Criteria -- Existing random helper behavior is preserved. -- Existing `Random` and `RandomSeed` behavior is preserved. -- Public random APIs are unchanged. -- Shared assertion module stays limited to the current reusable validation helpers. +- Hash helpers are deterministic for identical values. +- Object hashing is stable regardless of property insertion order. +- Helpers are documented as non-cryptographic. +- No browser storage, UI, API, database, or sample smoke changes are introduced. diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md new file mode 100644 index 000000000..f6f311a2d --- /dev/null +++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md @@ -0,0 +1,23 @@ +# PR_26177_001-shared-hash-foundation + +Date: 2026-06-26 +Scope: Shared non-cryptographic hash foundation +Status: PASS + +## Summary + +- Added `src/shared/hash/hash.js`. +- Added deterministic non-cryptographic hash helpers for stable strings, string/value hashes, combined hashes, and normalized hash values. +- Added targeted tests in `tests/shared/HashFoundation.test.mjs`. +- No browser-owned product data, runtime UI, API, database, or unrelated cleanup changes were made. + +## Validation + +- PASS: `node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs`. +- PASS: `node --check src/shared/hash/hash.js`. +- PASS: `node --check tests/shared/HashFoundation.test.mjs`. +- PASS: `git diff --check`. + +## Artifact + +- `tmp/PR_26177_001-shared-hash-foundation_delta.zip` diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md new file mode 100644 index 000000000..1b0802d30 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md @@ -0,0 +1,8 @@ +# PR_26177_001-shared-hash-foundation Branch Validation + +Status: PASS + +- PASS: Started from clean synchronized `main`. +- PASS: Branch `PR_26177_001-shared-hash-foundation` was created from `main`. +- PASS: One PR purpose only: shared hash foundation. +- PASS: No `start_of_day` files changed. diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md new file mode 100644 index 000000000..cf64ebe33 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md @@ -0,0 +1,5 @@ +# PR_26177_001-shared-hash-foundation Manual Validation Notes + +Status: PASS + +Manual review confirmed the new hash helpers are deterministic, non-cryptographic, isolated to `src/shared/hash/`, and do not add browser product-data ownership or runtime UI behavior. diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md new file mode 100644 index 000000000..371fed91f --- /dev/null +++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md @@ -0,0 +1,12 @@ +# PR_26177_001-shared-hash-foundation Requirement Checklist + +| Requirement | Status | Notes | +|---|---:|---| +| Add `src/shared/hash/` foundation | PASS | Added `src/shared/hash/hash.js`. | +| Include deterministic non-crypto hash helpers | PASS | Added stable string, string/value hash, combined hash, and normalized helpers. | +| No browser-owned product data | PASS | No storage or product-data files changed. | +| No runtime UI changes | PASS | No UI files changed. | +| One PR purpose only | PASS | Scope is limited to hash foundation. | +| Smallest valid scoped change | PASS | No unrelated exports or integrations added. | +| Add targeted tests | PASS | Added `tests/shared/HashFoundation.test.mjs`. | +| Do not run full samples smoke by default | PASS | Full samples smoke was not run. | diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md new file mode 100644 index 000000000..bfe2422a9 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md @@ -0,0 +1,22 @@ +# PR_26177_001-shared-hash-foundation Validation Lane + +Status: PASS + +## Commands + +```powershell +node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs +node --check src/shared/hash/hash.js +node --check tests/shared/HashFoundation.test.mjs +git diff --check +``` + +## Results + +- PASS: Targeted hash foundation test. +- PASS: Changed JS syntax checks. +- PASS: `git diff --check`. + +## Not Run + +- Full samples smoke was not run by default. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 48675b782..c986337ce 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,14 +1,11 @@ docs_build/dev/BUILD_PR.md docs_build/dev/PLAN_PR.md -docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md -docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md -docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md +docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md +docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md +docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md +docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md +docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md docs_build/dev/reports/codex_changed_files.txt docs_build/dev/reports/codex_review.diff -src/shared/math/randomHelpers.js -src/shared/validation/assert.js +src/shared/hash/hash.js +tests/shared/HashFoundation.test.mjs diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 8534f5bf7..b27ef3989 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,458 +1,446 @@ diff --git a/docs_build/dev/BUILD_PR.md b/docs_build/dev/BUILD_PR.md -index 2f3f00812..edc9a0b75 100644 +index edc9a0b75..466f9ca28 100644 --- a/docs_build/dev/BUILD_PR.md +++ b/docs_build/dev/BUILD_PR.md -@@ -1,39 +1,40 @@ --# PR_26177_DELTA_055-random-seed-enhancements -+# PR_26177_DELTA_056-shared-validation-assertions +@@ -1,40 +1,20 @@ +-# PR_26177_DELTA_056-shared-validation-assertions ++# PR_26177_001-shared-hash-foundation ## Purpose --Enhance `RandomSeed` with matching procedural convenience methods. -+Move generic validation/assertion helpers out of random helper code into a shared validation module. +-Move generic validation/assertion helpers out of random helper code into a shared validation module. ++Add a small shared non-cryptographic hash foundation. ## Source Of Truth --This `BUILD_PR.md`, `PLAN_PR.md`, the user request, and `docs_build/dev/ProjectInstructions.zip` are the source of truth for `PR_26177_DELTA_055-random-seed-enhancements`. -+This `BUILD_PR.md`, `PLAN_PR.md`, the user request, and `docs_build/dev/ProjectInstructions.zip` are the source of truth for `PR_26177_DELTA_056-shared-validation-assertions`. - - ## OWNER Override And Team Assignment - --OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_055-random-seed-enhancements`. -+OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. - - Team Delta owns Shared JS, runtime utilities, technical debt remediation, and runtime test coverage. - - ## Stack - --- Base branch: `PR_26177_DELTA_054-random-utility` --- This PR depends on the shared helper module from PR_053 and the stack context from PR_054. -+- Base branch: `PR_26177_DELTA_055-random-seed-enhancements` -+- This PR depends on the random helper module from PR_053, `Random` from PR_054, and `RandomSeed` enhancements from PR_055. +-This `BUILD_PR.md`, `PLAN_PR.md`, the user request, and `docs_build/dev/ProjectInstructions.zip` are the source of truth for `PR_26177_DELTA_056-shared-validation-assertions`. +- +-## OWNER Override And Team Assignment +- +-OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. +- +-Team Delta owns Shared JS, runtime utilities, technical debt remediation, and runtime test coverage. +- +-## Stack +- +-- Base branch: `PR_26177_DELTA_055-random-seed-enhancements` +-- This PR depends on the random helper module from PR_053, `Random` from PR_054, and `RandomSeed` enhancements from PR_055. ++This `BUILD_PR.md`, `PLAN_PR.md`, and the user request are the source of truth for `PR_26177_001-shared-hash-foundation`. ## Exact Scope --- Update `RandomSeed` to use shared helper logic where appropriate. --- Add: -- - `shuffle(array)` -- - `chance(percent)` -- - `weightedPick(weightedItems)` -- - `saveState()` -- - `restoreState(state)` --- Preserve existing `RandomSeed` sequence compatibility. --- Same seed must still reproduce same sequence. --- Reseeding must still reproduce same sequence. --- Add targeted unit tests for new methods. --- No adoption changes in existing game logic. -+- Create `src/shared/validation/assert.js`. -+- Move generic assertion helpers from random helper code into `assert.js`. -+- Include only generic reusable validation functions needed by current random helpers: -+ - `assertArray` -+ - `assertFiniteNumber` -+ - `assertOrderedRange` -+- Update random helper code to import from `src/shared/validation/assert.js`. -+- Preserve existing `Random` and `RandomSeed` behavior. -+- Do not change public API. -+- Do not expand into unrelated validation functions yet. -+- Add/update targeted unit tests if needed. - - No UI changes. -+- No API/database changes. -+- No unrelated cleanup. +-- Create `src/shared/validation/assert.js`. +-- Move generic assertion helpers from random helper code into `assert.js`. +-- Include only generic reusable validation functions needed by current random helpers: +- - `assertArray` +- - `assertFiniteNumber` +- - `assertOrderedRange` +-- Update random helper code to import from `src/shared/validation/assert.js`. +-- Preserve existing `Random` and `RandomSeed` behavior. +-- Do not change public API. +-- Do not expand into unrelated validation functions yet. +-- Add/update targeted unit tests if needed. +-- No UI changes. +-- No API/database changes. +-- No unrelated cleanup. ++- Add `src/shared/hash/` foundation. ++- Include deterministic non-crypto hash helpers. ++- No browser-owned product data. ++- No runtime UI changes. ++- Add targeted tests for the shared hash area. - Create required Codex reports under `docs_build/dev/reports/`. - Create repo-structured delta ZIP under `tmp/`. -@@ -43,20 +44,21 @@ Team Delta owns Shared JS, runtime utilities, technical debt remediation, and ru +@@ -42,48 +22,43 @@ Team Delta owns Shared JS, runtime utilities, technical debt remediation, and ru + + - `docs_build/dev/PLAN_PR.md` - `docs_build/dev/BUILD_PR.md` - - `docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md` - - `docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md` --- `src/shared/math/RandomSeed.js` --- `tests/shared/RandomSeed.test.mjs` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements.md` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements_branch-validation.md` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements_requirement-checklist.md` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements_validation-lane.md` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements_manual-validation-notes.md` --- `docs_build/dev/reports/PR_26177_DELTA_055-random-seed-enhancements_instruction-compliance-checklist.md` -+- `src/shared/validation/assert.js` -+- `src/shared/math/randomHelpers.js` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md` +-- `docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md` +-- `docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md` +-- `src/shared/validation/assert.js` +-- `src/shared/math/randomHelpers.js` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md` +-- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md` ++- `src/shared/hash/hash.js` ++- `tests/shared/HashFoundation.test.mjs` ++- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md` ++- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md` ++- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md` ++- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md` ++- `docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md` - `docs_build/dev/reports/codex_review.diff` - `docs_build/dev/reports/codex_changed_files.txt` ## Out Of Scope --- No seeded `next()` algorithm changes. -+- No public API changes. -+- No new unrelated validation helpers. - - No existing game logic adoption changes. - - No UI changes. +-- No public API changes. +-- No new unrelated validation helpers. +-- No existing game logic adoption changes. +-- No UI changes. ++- No cryptographic hashing. ++- No browser-owned product data. ++- No runtime UI changes. - No browser storage changes. -@@ -70,9 +72,9 @@ Team Delta owns Shared JS, runtime utilities, technical debt remediation, and ru +-- No API changes. +-- No database changes. ++- No API/database changes. ++- No engine refactor. + - No `start_of_day` folder changes. + - No unrelated cleanup. ++- No full samples smoke by default. + + ## Validation + Run exactly: ```powershell --node ./scripts/run-node-test-files.mjs tests/shared/RandomSeed.test.mjs tests/shared/RandomHelpers.test.mjs --node --check src/shared/math/RandomSeed.js --node --check tests/shared/RandomSeed.test.mjs -+node ./scripts/run-node-test-files.mjs tests/shared/RandomHelpers.test.mjs tests/shared/Random.test.mjs tests/shared/RandomSeed.test.mjs -+node --check src/shared/validation/assert.js -+node --check src/shared/math/randomHelpers.js +-node ./scripts/run-node-test-files.mjs tests/shared/RandomHelpers.test.mjs tests/shared/Random.test.mjs tests/shared/RandomSeed.test.mjs +-node --check src/shared/validation/assert.js +-node --check src/shared/math/randomHelpers.js ++node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs ++node --check src/shared/hash/hash.js ++node --check tests/shared/HashFoundation.test.mjs git diff --check ``` -@@ -83,5 +85,5 @@ Playwright is not required because this PR does not change UI or browser runtime +-Playwright is not required because this PR does not change UI or browser runtime flows. +- + ## Artifact + Create repo-structured delta ZIP: ```text --tmp/PR_26177_DELTA_055-random-seed-enhancements_delta.zip -+tmp/PR_26177_DELTA_056-shared-validation-assertions_delta.zip +-tmp/PR_26177_DELTA_056-shared-validation-assertions_delta.zip ++tmp/PR_26177_001-shared-hash-foundation_delta.zip ``` diff --git a/docs_build/dev/PLAN_PR.md b/docs_build/dev/PLAN_PR.md -index 87120e264..de53f9020 100644 +index de53f9020..835b7009a 100644 --- a/docs_build/dev/PLAN_PR.md +++ b/docs_build/dev/PLAN_PR.md -@@ -1,42 +1,43 @@ --# PLAN_PR: PR_26177_DELTA_055-random-seed-enhancements -+# PLAN_PR: PR_26177_DELTA_056-shared-validation-assertions +@@ -1,43 +1,28 @@ +-# PLAN_PR: PR_26177_DELTA_056-shared-validation-assertions ++# PLAN_PR: PR_26177_001-shared-hash-foundation ## Purpose --Enhance `RandomSeed` with matching procedural convenience methods. -+Move generic validation/assertion helpers out of random helper code into a shared validation module. - - ## Owner And Assignment - - - Team: Delta --- OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_055-random-seed-enhancements`. --- Stack base: `PR_26177_DELTA_054-random-utility`. -+- OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. -+- Stack base: `PR_26177_DELTA_055-random-seed-enhancements`. +-Move generic validation/assertion helpers out of random helper code into a shared validation module. +- +-## Owner And Assignment +- +-- Team: Delta +-- OWNER override approved: Continue Team Delta random utility stack with `PR_26177_DELTA_056-shared-validation-assertions`. +-- Stack base: `PR_26177_DELTA_055-random-seed-enhancements`. ++Add a small shared non-cryptographic hash foundation. ## Scope --- Update `RandomSeed` to use shared helper logic where appropriate. --- Add: -- - `shuffle(array)` -- - `chance(percent)` -- - `weightedPick(weightedItems)` -- - `saveState()` -- - `restoreState(state)` --- Preserve existing `RandomSeed` sequence compatibility. --- Same seed must still reproduce same sequence. --- Reseeding must still reproduce same sequence. --- Add targeted unit tests for new methods. --- No adoption changes in existing game logic. -+- Create `src/shared/validation/assert.js`. -+- Move generic assertion helpers from random helper code into `assert.js`. -+- Include only generic reusable validation functions needed by current random helpers: -+ - `assertArray` -+ - `assertFiniteNumber` -+ - `assertOrderedRange` -+- Update random helper code to import from `src/shared/validation/assert.js`. -+- Preserve existing `Random` and `RandomSeed` behavior. -+- Do not change public API. -+- Do not expand into unrelated validation functions yet. -+- Add/update targeted unit tests if needed. - - No UI changes. -+- No API/database changes. -+- No unrelated cleanup. +-- Create `src/shared/validation/assert.js`. +-- Move generic assertion helpers from random helper code into `assert.js`. +-- Include only generic reusable validation functions needed by current random helpers: +- - `assertArray` +- - `assertFiniteNumber` +- - `assertOrderedRange` +-- Update random helper code to import from `src/shared/validation/assert.js`. +-- Preserve existing `Random` and `RandomSeed` behavior. +-- Do not change public API. +-- Do not expand into unrelated validation functions yet. +-- Add/update targeted unit tests if needed. +-- No UI changes. +-- No API/database changes. ++- Add `src/shared/hash/` foundation. ++- Include deterministic non-crypto hash helpers. ++- Add targeted unit tests. ++- No browser-owned product data. ++- No runtime UI changes. + - No unrelated cleanup. ## Implementation Plan --1. Update `src/shared/math/RandomSeed.js` to reuse `randomHelpers.js` for procedural helper methods. --2. Keep the existing seeded `next()` algorithm unchanged. --3. Add state save/restore methods. --4. Extend `tests/shared/RandomSeed.test.mjs` with sequence compatibility, new method, and state tests. -+1. Add `src/shared/validation/assert.js` with only the required generic assertion helpers. -+2. Remove duplicated generic assertions from `src/shared/math/randomHelpers.js`. -+3. Import the shared assertions from `randomHelpers.js`. -+4. Run targeted random helper, `Random`, and `RandomSeed` tests. - 5. Produce required PR reports and repo-structured ZIP. +-1. Add `src/shared/validation/assert.js` with only the required generic assertion helpers. +-2. Remove duplicated generic assertions from `src/shared/math/randomHelpers.js`. +-3. Import the shared assertions from `randomHelpers.js`. +-4. Run targeted random helper, `Random`, and `RandomSeed` tests. +-5. Produce required PR reports and repo-structured ZIP. ++1. Add `src/shared/hash/hash.js` with deterministic stable string and FNV-1a based helpers. ++2. Add `tests/shared/HashFoundation.test.mjs`. ++3. Validate determinism, object key ordering, seed variation, combination, and normalized hash output. ++4. Produce required Codex reports and repo-structured ZIP. ## Acceptance Criteria --- Existing seeded sequence values remain compatible. --- Same seed and reseeding behavior remain deterministic. --- New procedural methods use the same seeded random stream. --- State save/restore resumes the sequence from the saved point. -+- Existing random helper behavior is preserved. -+- Existing `Random` and `RandomSeed` behavior is preserved. -+- Public random APIs are unchanged. -+- Shared assertion module stays limited to the current reusable validation helpers. -diff --git a/docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md b/docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md -index 9dcd7244d..6341fdf34 100644 ---- a/docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md -+++ b/docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md -@@ -31,7 +31,7 @@ If a team has no assignment, no active branch, and no active PR, it is inactive - | Team Alfa | none | none | none | Available | Active ownership lane | - | Team Bravo | none | none | none | Available | Active ownership lane | - | Team Charlie | none | none | none | Available | Active ownership lane | --| Team Delta | PR_26177_DELTA_055-random-seed-enhancements | PR_26177_DELTA_055-random-seed-enhancements | PR_26177_DELTA_055-random-seed-enhancements | Active | OWNER override approved: Continue Team Delta random utility stack with PR_26177_DELTA_055-random-seed-enhancements | -+| Team Delta | PR_26177_DELTA_056-shared-validation-assertions | PR_26177_DELTA_056-shared-validation-assertions | PR_26177_DELTA_056-shared-validation-assertions | Active | OWNER override approved: Continue Team Delta random utility stack with PR_26177_DELTA_056-shared-validation-assertions | - | Team Golf | none | none | none | Available | Replacement active ownership lane for retired Team Gamma | - | Team OWNER | none | none | none | Available | Governance Phase 1 complete | - -diff --git a/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md b/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -index 3b49412c4..9735b4bd4 100644 ---- a/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -+++ b/docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md -@@ -7,7 +7,7 @@ - | Team Alfa | none | none | none | Available | - | Team Bravo | none | none | none | Available | - | Team Charlie | none | none | none | Available | --| Team Delta | PR_26177_DELTA_055-random-seed-enhancements | PR_26177_DELTA_055-random-seed-enhancements | PR_26177_DELTA_055-random-seed-enhancements | Active | -+| Team Delta | PR_26177_DELTA_056-shared-validation-assertions | PR_26177_DELTA_056-shared-validation-assertions | PR_26177_DELTA_056-shared-validation-assertions | Active | - | Team Golf | none | none | none | Available | - | Team OWNER | none | none | none | Available | - -@@ -50,13 +50,13 @@ Current OWNER clarification: - - Status: Active - --Active assignment: PR_26177_DELTA_055-random-seed-enhancements. -+Active assignment: PR_26177_DELTA_056-shared-validation-assertions. - --Active branch: PR_26177_DELTA_055-random-seed-enhancements. -+Active branch: PR_26177_DELTA_056-shared-validation-assertions. - --Active PR: PR_26177_DELTA_055-random-seed-enhancements. -+Active PR: PR_26177_DELTA_056-shared-validation-assertions. - --OWNER override approved: Continue Team Delta random utility stack with PR_26177_DELTA_055-random-seed-enhancements. -+OWNER override approved: Continue Team Delta random utility stack with PR_26177_DELTA_056-shared-validation-assertions. - - ## Team Bravo - -diff --git a/src/shared/math/randomHelpers.js b/src/shared/math/randomHelpers.js -index 5df891668..103ae7db5 100644 ---- a/src/shared/math/randomHelpers.js -+++ b/src/shared/math/randomHelpers.js -@@ -1,3 +1,5 @@ -+import { assertArray, assertFiniteNumber, assertOrderedRange } from "../validation/assert.js"; -+ - function assertRandomNext(randomNext) { - if (typeof randomNext !== "function") { - throw new TypeError("randomNext must be a function."); -@@ -15,27 +17,6 @@ function readRandomValue(randomNext) { - return value; - } - --function assertFiniteNumber(value, name) { -- if (!Number.isFinite(value)) { -- throw new TypeError(`${name} must be a finite number.`); -- } --} -- --function assertOrderedRange(min, max) { -- assertFiniteNumber(min, "min"); -- assertFiniteNumber(max, "max"); -- -- if (max < min) { -- throw new RangeError("max must be greater than or equal to min."); -- } --} -- --function assertArray(value, name) { -- if (!Array.isArray(value)) { -- throw new TypeError(`${name} must be an array.`); -- } --} -- - function readWeightedItem(entry) { - if (!entry || typeof entry !== "object") { - throw new TypeError("weightedItems entries must be objects."); -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md +-- Existing random helper behavior is preserved. +-- Existing `Random` and `RandomSeed` behavior is preserved. +-- Public random APIs are unchanged. +-- Shared assertion module stays limited to the current reusable validation helpers. ++- Hash helpers are deterministic for identical values. ++- Object hashing is stable regardless of property insertion order. ++- Helpers are documented as non-cryptographic. ++- No browser storage, UI, API, database, or sample smoke changes are introduced. +diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md new file mode 100644 -index 000000000..726310533 +index 000000000..f6f311a2d --- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md -@@ -0,0 +1,50 @@ -+# PR_26177_DELTA_056-shared-validation-assertions ++++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation.md +@@ -0,0 +1,23 @@ ++# PR_26177_001-shared-hash-foundation + +Date: 2026-06-26 -+Team: Delta -+Scope: Shared validation assertions extracted from random helper code ++Scope: Shared non-cryptographic hash foundation +Status: PASS + +## Summary + -+- Added `src/shared/validation/assert.js`. -+- Moved generic reusable assertion helpers out of `src/shared/math/randomHelpers.js`. -+- Included only the current generic assertion helpers needed by random helpers: `assertArray`, `assertFiniteNumber`, and `assertOrderedRange`. -+- Updated `randomHelpers.js` to import those assertions from the shared validation module. -+- Preserved existing `Random`, `RandomSeed`, and random helper behavior. -+- Did not change public API. -+- Did not add unrelated validation helpers. -+- No UI, API, database, or unrelated cleanup changes were made. -+ -+## Branch Validation -+ -+PASS. Branch `PR_26177_DELTA_056-shared-validation-assertions` was created from clean `PR_26177_DELTA_055-random-seed-enhancements`. -+ -+## Changed Files -+ -+- `docs_build/dev/PLAN_PR.md` -+- `docs_build/dev/BUILD_PR.md` -+- `docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md` -+- `docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md` -+- `src/shared/validation/assert.js` -+- `src/shared/math/randomHelpers.js` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md` -+- `docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md` -+- `docs_build/dev/reports/codex_changed_files.txt` -+- `docs_build/dev/reports/codex_review.diff` ++- Added `src/shared/hash/hash.js`. ++- Added deterministic non-cryptographic hash helpers for stable strings, string/value hashes, combined hashes, and normalized hash values. ++- Added targeted tests in `tests/shared/HashFoundation.test.mjs`. ++- No browser-owned product data, runtime UI, API, database, or unrelated cleanup changes were made. + +## Validation + -+- PASS: `node ./scripts/run-node-test-files.mjs tests/shared/RandomHelpers.test.mjs tests/shared/Random.test.mjs tests/shared/RandomSeed.test.mjs` -+- PASS: `node --check src/shared/validation/assert.js` -+- PASS: `node --check src/shared/math/randomHelpers.js` -+- PASS: `git diff --check` -+- SKIP: Playwright was not run because this PR does not change UI or browser runtime flows. ++- PASS: `node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs`. ++- PASS: `node --check src/shared/hash/hash.js`. ++- PASS: `node --check tests/shared/HashFoundation.test.mjs`. ++- PASS: `git diff --check`. + +## Artifact + -+- `tmp/PR_26177_DELTA_056-shared-validation-assertions_delta.zip` -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md ++- `tmp/PR_26177_001-shared-hash-foundation_delta.zip` +diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md new file mode 100644 -index 000000000..f41b57b1d +index 000000000..1b0802d30 --- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_branch-validation.md -@@ -0,0 +1,15 @@ -+# PR_26177_DELTA_056-shared-validation-assertions Branch Validation ++++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_branch-validation.md +@@ -0,0 +1,8 @@ ++# PR_26177_001-shared-hash-foundation Branch Validation + +Status: PASS + -+## Start Gates -+ -+- PASS: Current branch was `PR_26177_DELTA_055-random-seed-enhancements`. -+- PASS: Worktree was clean before creating PR_056. -+- PASS: Branch `PR_26177_DELTA_056-shared-validation-assertions` was created from PR_055. -+ -+## Scope Confirmation -+ -+- PASS: Work is assigned to Team Delta. -+- PASS: Work is limited to extracting generic assertions used by random helpers. -+- PASS: No public API, UI, browser storage, API, database, or unrelated cleanup changes were made. -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md -new file mode 100644 -index 000000000..a34dbd5f3 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_instruction-compliance-checklist.md -@@ -0,0 +1,11 @@ -+# PR_26177_DELTA_056-shared-validation-assertions Instruction Compliance Checklist -+ -+| Instruction | Status | Notes | -+|---|---:|---| -+| Read `ProjectInstructions.zip` and `README.txt` first | PASS | Read `ProjectInstructions/README.txt` before work. | -+| Create PR_056 as stacked PR on current branch | PASS | Branch was created from `PR_26177_DELTA_055-random-seed-enhancements`. | -+| Hard stop if branch/worktree gates fail | PASS | Branch and clean worktree gates passed. | -+| Keep one PR purpose only | PASS | Scope is shared validation assertion extraction. | -+| Do not change public API | PASS | No public API changes were made. | -+| Produce required reports | PASS | Reports were added under `docs_build/dev/reports/`. | -+| Produce repo-structured ZIP under `tmp/` | PASS | ZIP path is `tmp/PR_26177_DELTA_056-shared-validation-assertions_delta.zip`. | -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md ++- PASS: Started from clean synchronized `main`. ++- PASS: Branch `PR_26177_001-shared-hash-foundation` was created from `main`. ++- PASS: One PR purpose only: shared hash foundation. ++- PASS: No `start_of_day` files changed. +diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md new file mode 100644 -index 000000000..1d5548e3b +index 000000000..cf64ebe33 --- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_manual-validation-notes.md -@@ -0,0 +1,11 @@ -+# PR_26177_DELTA_056-shared-validation-assertions Manual Validation Notes ++++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_manual-validation-notes.md +@@ -0,0 +1,5 @@ ++# PR_26177_001-shared-hash-foundation Manual Validation Notes + +Status: PASS + -+Manual review confirmed: -+ -+- The extracted assertions are generic and reusable. -+- `assert.js` intentionally contains only `assertArray`, `assertFiniteNumber`, and `assertOrderedRange`. -+- Random helper behavior is preserved through imports from the shared validation module. -+- `Random` and `RandomSeed` public APIs are unchanged. -+- No UI, browser storage, API, database, or unrelated files changed. -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md ++Manual review confirmed the new hash helpers are deterministic, non-cryptographic, isolated to `src/shared/hash/`, and do not add browser product-data ownership or runtime UI behavior. +diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md new file mode 100644 -index 000000000..00d322ad0 +index 000000000..371fed91f --- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_requirement-checklist.md -@@ -0,0 +1,16 @@ -+# PR_26177_DELTA_056-shared-validation-assertions Requirement Checklist ++++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_requirement-checklist.md +@@ -0,0 +1,12 @@ ++# PR_26177_001-shared-hash-foundation Requirement Checklist + +| Requirement | Status | Notes | +|---|---:|---| -+| Create `src/shared/validation/assert.js` | PASS | New shared validation module added. | -+| Move generic assertion helpers from random helper code into `assert.js` | PASS | `assertArray`, `assertFiniteNumber`, and `assertOrderedRange` were extracted. | -+| Include only generic reusable validation functions needed by current random helpers | PASS | No unrelated validation helpers were added. | -+| Update random helper code to import from `src/shared/validation/assert.js` | PASS | `randomHelpers.js` now imports from shared validation. | -+| Preserve existing `Random` behavior | PASS | Targeted `Random` tests pass. | -+| Preserve existing `RandomSeed` behavior | PASS | Targeted `RandomSeed` tests pass. | -+| Do not change public API | PASS | No public API methods or call sites changed. | -+| Do not expand into unrelated validation functions yet | PASS | Module includes only the requested assertions. | -+| Add/update targeted unit tests if needed | PASS | Existing targeted random tests cover behavior after extraction. | -+| No UI changes | PASS | No UI files changed. | -+| No API/database changes | PASS | No API or database files changed. | -+| No unrelated cleanup | PASS | Changes stayed scoped to assertion extraction and reports. | -diff --git a/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md ++| Add `src/shared/hash/` foundation | PASS | Added `src/shared/hash/hash.js`. | ++| Include deterministic non-crypto hash helpers | PASS | Added stable string, string/value hash, combined hash, and normalized helpers. | ++| No browser-owned product data | PASS | No storage or product-data files changed. | ++| No runtime UI changes | PASS | No UI files changed. | ++| One PR purpose only | PASS | Scope is limited to hash foundation. | ++| Smallest valid scoped change | PASS | No unrelated exports or integrations added. | ++| Add targeted tests | PASS | Added `tests/shared/HashFoundation.test.mjs`. | ++| Do not run full samples smoke by default | PASS | Full samples smoke was not run. | +diff --git a/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md new file mode 100644 -index 000000000..01500c267 +index 000000000..bfe2422a9 --- /dev/null -+++ b/docs_build/dev/reports/PR_26177_DELTA_056-shared-validation-assertions_validation-lane.md -@@ -0,0 +1,25 @@ -+# PR_26177_DELTA_056-shared-validation-assertions Validation Lane ++++ b/docs_build/dev/reports/PR_26177_001-shared-hash-foundation_validation-lane.md +@@ -0,0 +1,22 @@ ++# PR_26177_001-shared-hash-foundation Validation Lane + +Status: PASS + +## Commands + +```powershell -+node ./scripts/run-node-test-files.mjs tests/shared/RandomHelpers.test.mjs tests/shared/Random.test.mjs tests/shared/RandomSeed.test.mjs -+node --check src/shared/validation/assert.js -+node --check src/shared/math/randomHelpers.js ++node ./scripts/run-node-test-files.mjs tests/shared/HashFoundation.test.mjs ++node --check src/shared/hash/hash.js ++node --check tests/shared/HashFoundation.test.mjs +git diff --check +``` + +## Results + -+- PASS: `tests/shared/RandomHelpers.test.mjs` -+- PASS: `tests/shared/Random.test.mjs` -+- PASS: `tests/shared/RandomSeed.test.mjs` -+- PASS: `src/shared/validation/assert.js` syntax check -+- PASS: `src/shared/math/randomHelpers.js` syntax check -+- PASS: `git diff --check` ++- PASS: Targeted hash foundation test. ++- PASS: Changed JS syntax checks. ++- PASS: `git diff --check`. + -+## Playwright ++## Not Run + -+SKIP. Playwright was not run because this PR does not change UI or browser runtime flows. -diff --git a/src/shared/validation/assert.js b/src/shared/validation/assert.js ++- Full samples smoke was not run by default. +diff --git a/src/shared/hash/hash.js b/src/shared/hash/hash.js new file mode 100644 -index 000000000..ee8974cbc +index 000000000..e8555ba8d --- /dev/null -+++ b/src/shared/validation/assert.js -@@ -0,0 +1,20 @@ -+export function assertArray(value, name) { -+ if (!Array.isArray(value)) { -+ throw new TypeError(`${name} must be an array.`); ++++ b/src/shared/hash/hash.js +@@ -0,0 +1,108 @@ ++const FNV_OFFSET_BASIS = 0x811c9dc5; ++const FNV_PRIME = 0x01000193; ++const UINT32_RANGE = 0x100000000; ++ ++function stableObjectEntries(value) { ++ return Object.keys(value) ++ .sort() ++ .map((key) => `${JSON.stringify(key)}:${toStableHashString(value[key])}`); ++} ++ ++/** ++ * Converts a value into a deterministic string suitable for non-cryptographic hashing. ++ * ++ * @param {*} value Value to serialize. ++ * @returns {string} Stable deterministic string. ++ */ ++export function toStableHashString(value) { ++ if (value === null) { ++ return "null"; ++ } ++ ++ if (Array.isArray(value)) { ++ return `[${value.map(toStableHashString).join(",")}]`; ++ } ++ ++ if (value instanceof Date) { ++ return `date:${Number.isNaN(value.getTime()) ? "invalid" : value.toISOString()}`; + } ++ ++ const valueType = typeof value; ++ if (valueType === "object") { ++ return `{${stableObjectEntries(value).join(",")}}`; ++ } ++ ++ if (valueType === "number") { ++ if (Number.isNaN(value)) { ++ return "number:NaN"; ++ } ++ return Object.is(value, -0) ? "number:-0" : `number:${value}`; ++ } ++ ++ return `${valueType}:${String(value)}`; +} + -+export function assertFiniteNumber(value, name) { -+ if (!Number.isFinite(value)) { -+ throw new TypeError(`${name} must be a finite number.`); ++/** ++ * Computes a deterministic FNV-1a 32-bit hash for a string. ++ * ++ * This helper is non-cryptographic and is intended for stable bucketing, ++ * procedural generation, and cache keys where security is not required. ++ * ++ * @param {*} value Value converted to string before hashing. ++ * @param {number} seed Optional unsigned 32-bit hash seed. ++ * @returns {number} Unsigned 32-bit hash. ++ */ ++export function hashString32(value, seed = FNV_OFFSET_BASIS) { ++ const text = String(value ?? ""); ++ let hash = seed >>> 0; ++ ++ for (let index = 0; index < text.length; index += 1) { ++ hash ^= text.charCodeAt(index); ++ hash = Math.imul(hash, FNV_PRIME); + } ++ ++ return hash >>> 0; +} + -+export function assertOrderedRange(min, max) { -+ assertFiniteNumber(min, "min"); -+ assertFiniteNumber(max, "max"); ++/** ++ * Computes a deterministic non-cryptographic 32-bit hash for any JSON-like value. ++ * ++ * @param {*} value Value to hash. ++ * @param {number} seed Optional unsigned 32-bit hash seed. ++ * @returns {number} Unsigned 32-bit hash. ++ */ ++export function hashValue32(value, seed = FNV_OFFSET_BASIS) { ++ return hashString32(toStableHashString(value), seed); ++} + -+ if (max < min) { -+ throw new RangeError("max must be greater than or equal to min."); -+ } ++/** ++ * Combines multiple values into one deterministic non-cryptographic 32-bit hash. ++ * ++ * @param {...*} values Values to combine. ++ * @returns {number} Unsigned 32-bit hash. ++ */ ++export function combineHash32(...values) { ++ return values.reduce((hash, value) => hashValue32(value, hash), FNV_OFFSET_BASIS); ++} ++ ++/** ++ * Maps a deterministic value hash into the range [0, 1). ++ * ++ * @param {*} value Value to hash. ++ * @param {number} seed Optional unsigned 32-bit hash seed. ++ * @returns {number} Normalized hash value. ++ */ ++export function hashToUnitInterval(value, seed = FNV_OFFSET_BASIS) { ++ return hashValue32(value, seed) / UINT32_RANGE; ++} ++ ++/** ++ * Maps a deterministic value hash into the range [-1, 1). ++ * ++ * @param {*} value Value to hash. ++ * @param {number} seed Optional unsigned 32-bit hash seed. ++ * @returns {number} Signed normalized hash value. ++ */ ++export function hashToSignedUnit(value, seed = FNV_OFFSET_BASIS) { ++ return hashToUnitInterval(value, seed) * 2 - 1; ++} +diff --git a/tests/shared/HashFoundation.test.mjs b/tests/shared/HashFoundation.test.mjs +new file mode 100644 +index 000000000..a7a06c934 +--- /dev/null ++++ b/tests/shared/HashFoundation.test.mjs +@@ -0,0 +1,44 @@ ++/* ++Toolbox Aid ++David Quesenberry ++06/26/2026 ++HashFoundation.test.mjs ++*/ ++import assert from "node:assert/strict"; ++import { ++ combineHash32, ++ hashString32, ++ hashToSignedUnit, ++ hashToUnitInterval, ++ hashValue32, ++ toStableHashString, ++} from "../../src/shared/hash/hash.js"; ++ ++export function run() { ++ assert.equal(hashString32("Game Foundry"), hashString32("Game Foundry")); ++ assert.notEqual(hashString32("Game Foundry"), hashString32("Game Foundry!")); ++ ++ assert.equal( ++ toStableHashString({ b: 2, a: 1 }), ++ toStableHashString({ a: 1, b: 2 }) ++ ); ++ assert.equal(hashValue32({ b: 2, a: [1, 2, 3] }), hashValue32({ a: [1, 2, 3], b: 2 })); ++ assert.notEqual(hashValue32({ a: [1, 2, 3] }), hashValue32({ a: [1, 2, 4] })); ++ ++ assert.equal(combineHash32("a", "b", "c"), combineHash32("a", "b", "c")); ++ assert.notEqual(combineHash32("a", "b", "c"), combineHash32("a", "c", "b")); ++ assert.notEqual(hashValue32("seeded", 1), hashValue32("seeded", 2)); ++ ++ const unitValue = hashToUnitInterval("unit"); ++ assert.equal(unitValue >= 0 && unitValue < 1, true); ++ ++ const signedValue = hashToSignedUnit("signed"); ++ assert.equal(signedValue >= -1 && signedValue < 1, true); ++ ++ assert.match(toStableHashString(new Date("2026-06-26T00:00:00.000Z")), /^date:/); ++ assert.equal(toStableHashString(Number.NaN), "number:NaN"); ++} ++ ++if (import.meta.url === `file://${process.argv[1]}`) { ++ run(); +} diff --git a/src/shared/hash/hash.js b/src/shared/hash/hash.js new file mode 100644 index 000000000..e8555ba8d --- /dev/null +++ b/src/shared/hash/hash.js @@ -0,0 +1,108 @@ +const FNV_OFFSET_BASIS = 0x811c9dc5; +const FNV_PRIME = 0x01000193; +const UINT32_RANGE = 0x100000000; + +function stableObjectEntries(value) { + return Object.keys(value) + .sort() + .map((key) => `${JSON.stringify(key)}:${toStableHashString(value[key])}`); +} + +/** + * Converts a value into a deterministic string suitable for non-cryptographic hashing. + * + * @param {*} value Value to serialize. + * @returns {string} Stable deterministic string. + */ +export function toStableHashString(value) { + if (value === null) { + return "null"; + } + + if (Array.isArray(value)) { + return `[${value.map(toStableHashString).join(",")}]`; + } + + if (value instanceof Date) { + return `date:${Number.isNaN(value.getTime()) ? "invalid" : value.toISOString()}`; + } + + const valueType = typeof value; + if (valueType === "object") { + return `{${stableObjectEntries(value).join(",")}}`; + } + + if (valueType === "number") { + if (Number.isNaN(value)) { + return "number:NaN"; + } + return Object.is(value, -0) ? "number:-0" : `number:${value}`; + } + + return `${valueType}:${String(value)}`; +} + +/** + * Computes a deterministic FNV-1a 32-bit hash for a string. + * + * This helper is non-cryptographic and is intended for stable bucketing, + * procedural generation, and cache keys where security is not required. + * + * @param {*} value Value converted to string before hashing. + * @param {number} seed Optional unsigned 32-bit hash seed. + * @returns {number} Unsigned 32-bit hash. + */ +export function hashString32(value, seed = FNV_OFFSET_BASIS) { + const text = String(value ?? ""); + let hash = seed >>> 0; + + for (let index = 0; index < text.length; index += 1) { + hash ^= text.charCodeAt(index); + hash = Math.imul(hash, FNV_PRIME); + } + + return hash >>> 0; +} + +/** + * Computes a deterministic non-cryptographic 32-bit hash for any JSON-like value. + * + * @param {*} value Value to hash. + * @param {number} seed Optional unsigned 32-bit hash seed. + * @returns {number} Unsigned 32-bit hash. + */ +export function hashValue32(value, seed = FNV_OFFSET_BASIS) { + return hashString32(toStableHashString(value), seed); +} + +/** + * Combines multiple values into one deterministic non-cryptographic 32-bit hash. + * + * @param {...*} values Values to combine. + * @returns {number} Unsigned 32-bit hash. + */ +export function combineHash32(...values) { + return values.reduce((hash, value) => hashValue32(value, hash), FNV_OFFSET_BASIS); +} + +/** + * Maps a deterministic value hash into the range [0, 1). + * + * @param {*} value Value to hash. + * @param {number} seed Optional unsigned 32-bit hash seed. + * @returns {number} Normalized hash value. + */ +export function hashToUnitInterval(value, seed = FNV_OFFSET_BASIS) { + return hashValue32(value, seed) / UINT32_RANGE; +} + +/** + * Maps a deterministic value hash into the range [-1, 1). + * + * @param {*} value Value to hash. + * @param {number} seed Optional unsigned 32-bit hash seed. + * @returns {number} Signed normalized hash value. + */ +export function hashToSignedUnit(value, seed = FNV_OFFSET_BASIS) { + return hashToUnitInterval(value, seed) * 2 - 1; +} diff --git a/tests/shared/HashFoundation.test.mjs b/tests/shared/HashFoundation.test.mjs new file mode 100644 index 000000000..a7a06c934 --- /dev/null +++ b/tests/shared/HashFoundation.test.mjs @@ -0,0 +1,44 @@ +/* +Toolbox Aid +David Quesenberry +06/26/2026 +HashFoundation.test.mjs +*/ +import assert from "node:assert/strict"; +import { + combineHash32, + hashString32, + hashToSignedUnit, + hashToUnitInterval, + hashValue32, + toStableHashString, +} from "../../src/shared/hash/hash.js"; + +export function run() { + assert.equal(hashString32("Game Foundry"), hashString32("Game Foundry")); + assert.notEqual(hashString32("Game Foundry"), hashString32("Game Foundry!")); + + assert.equal( + toStableHashString({ b: 2, a: 1 }), + toStableHashString({ a: 1, b: 2 }) + ); + assert.equal(hashValue32({ b: 2, a: [1, 2, 3] }), hashValue32({ a: [1, 2, 3], b: 2 })); + assert.notEqual(hashValue32({ a: [1, 2, 3] }), hashValue32({ a: [1, 2, 4] })); + + assert.equal(combineHash32("a", "b", "c"), combineHash32("a", "b", "c")); + assert.notEqual(combineHash32("a", "b", "c"), combineHash32("a", "c", "b")); + assert.notEqual(hashValue32("seeded", 1), hashValue32("seeded", 2)); + + const unitValue = hashToUnitInterval("unit"); + assert.equal(unitValue >= 0 && unitValue < 1, true); + + const signedValue = hashToSignedUnit("signed"); + assert.equal(signedValue >= -1 && signedValue < 1, true); + + assert.match(toStableHashString(new Date("2026-06-26T00:00:00.000Z")), /^date:/); + assert.equal(toStableHashString(Number.NaN), "number:NaN"); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + run(); +}