Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .beads/issues.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
{"id":"ge-hch.5.15.16","title":"Implement: Embedding Service","description":"Create web/demo/js/embedding-service.js with transformers.js.\n\n## Acceptance Criteria\n- [ ] Loads Xenova/all-MiniLM-L6-v2 model via transformers.js\n- [ ] WebWorker wrapper for non-blocking inference\n- [ ] embed(text) returns embedding vector\n- [ ] similarity(vec1, vec2) returns cosine similarity\n- [ ] Lazy loading on first use\n- [ ] Graceful fallback if model fails\n\n## Related Feature\nge-hch.5.15.4 (Embedding Service)","status":"closed","priority":2,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:41.761163209-08:00","created_by":"rgardler","updated_at":"2026-01-17T20:15:52.375357302-08:00","closed_at":"2026-01-17T20:15:52.375357302-08:00","close_reason":"Completed via PR #170","external_ref":"gh-170","labels":["Status: PR Created"],"dependencies":[{"issue_id":"ge-hch.5.15.16","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:41.761957697-08:00","created_by":"rgardler"}],"comments":[{"id":212,"issue_id":"ge-hch.5.15.16","author":"rgardler","text":"Opened PR https://github.com/TheWizardsCode/GEngine/pull/170 for embedding service. Added web/demo/js/embedding-service.js: lazy loads Xenova/all-MiniLM-L6-v2 via transformers.js inside a Web Worker; provides embed(text)-\u003eembedding (null on failure) and similarity(vecA, vecB). Graceful fallback when workers/transformers unavailable. Tests run: npx jest tests/unit/director.test.js --runInBand (pass).","created_at":"2026-01-18T03:31:14Z"},{"id":213,"issue_id":"ge-hch.5.15.16","author":"rgardler","text":"PR #170 merged. Deleted branch feature/ge-hch.5.15.16-embedding locally and remotely. Closing bead as Completed via PR #170.","created_at":"2026-01-18T04:15:54Z"}]}
{"id":"ge-hch.5.15.17","title":"Tests: Embedding Service","description":"Unit tests for embedding service.\n\n## Acceptance Criteria\n- [ ] Test: similarity(happy, joyful) \u003e 0.7\n- [ ] Test: similarity(happy, database) \u003c 0.4\n- [ ] Test: embed(null) returns null gracefully\n- [ ] Performance: first embed \u003c 3s, subsequent \u003c 100ms\n\n## Related Feature\nge-hch.5.15.4 (Embedding Service)","status":"closed","priority":2,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:41.806727691-08:00","created_by":"rgardler","updated_at":"2026-01-17T20:41:48.959573685-08:00","closed_at":"2026-01-17T20:41:48.959573685-08:00","close_reason":"Completed","external_ref":"gh-171","labels":["Status: PR Created"],"dependencies":[{"issue_id":"ge-hch.5.15.17","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:41.807448395-08:00","created_by":"rgardler"}],"comments":[{"id":214,"issue_id":"ge-hch.5.15.17","author":"rgardler","text":"PR https://github.com/TheWizardsCode/GEngine/pull/171 opened. Added deterministic unit tests for similarity thresholds (happy/joyful \u003e 0.7, happy/database \u003c 0.4), embed(null) fallback, and mocked worker plumbing for embed(). Optional gated integration test (INTEGRATION_EMBEDDING=1) included for real model check. Tests run: npx jest tests/unit/embedding.service.similarity.test.js tests/unit/embedding.service.embed.test.js --runInBand; npx jest tests/unit/director.test.js --runInBand.","created_at":"2026-01-18T04:19:41Z"}]}
{"id":"ge-hch.5.15.18","title":"Implement: Player Preference Tracker","description":"Create web/demo/js/player-preference.js for tracking preferences.\n\n## Acceptance Criteria\n- [ ] Records { branchType, accepted, timestamp } events\n- [ ] Computes preference score per branch type (0.0-1.0)\n- [ ] Persists in localStorage key ge-hch.ai-preferences\n- [ ] Cold-start returns 0.5 for all types\n- [ ] getPreference(branchType) and recordOutcome(branchType, accepted) APIs\n\n## Related Feature\nge-hch.5.15.5 (Player Preference Tracker)","status":"closed","priority":2,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:51.748963075-08:00","created_by":"rgardler","updated_at":"2026-01-18T00:42:47.180670606-08:00","closed_at":"2026-01-18T00:42:47.180670606-08:00","close_reason":"Completed","external_ref":"https://github.com/TheWizardsCode/GEngine/pull/172","labels":["Status: PR Created"],"dependencies":[{"issue_id":"ge-hch.5.15.18","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:51.750476216-08:00","created_by":"rgardler"}]}
{"id":"ge-hch.5.15.19","title":"Tests: Player Preference Tracker","description":"Unit tests for player preference tracking.\n\n## Acceptance Criteria\n- [ ] Test: 3 accepts + 1 reject of dialogue yields preference \u003e 0.6\n- [ ] Test: 0 history yields preference = 0.5\n- [ ] Test: 100+ events still performant (\u003c10ms)\n- [ ] Test: localStorage persistence works\n\n## Related Feature\nge-hch.5.15.5 (Player Preference Tracker)","status":"open","priority":2,"issue_type":"task","assignee":"Probe","created_at":"2026-01-16T15:03:51.807524607-08:00","created_by":"rgardler","updated_at":"2026-01-16T15:03:51.807524607-08:00","dependencies":[{"issue_id":"ge-hch.5.15.19","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:51.808421437-08:00","created_by":"rgardler"}]}
{"id":"ge-hch.5.15.19","title":"Tests: Player Preference Tracker","description":"Unit tests for player preference tracking.\n\n## Acceptance Criteria\n- [ ] Test: 3 accepts + 1 reject of dialogue yields preference \u003e 0.6\n- [ ] Test: 0 history yields preference = 0.5\n- [ ] Test: 100+ events still performant (\u003c10ms)\n- [ ] Test: localStorage persistence works\n\n## Related Feature\nge-hch.5.15.5 (Player Preference Tracker)","status":"in_progress","priority":2,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:51.807524607-08:00","created_by":"rgardler","updated_at":"2026-01-18T00:51:57.268187359-08:00","dependencies":[{"issue_id":"ge-hch.5.15.19","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:51.808421437-08:00","created_by":"rgardler"}]}
{"id":"ge-hch.5.15.2","title":"Return-Path Feasibility Checker","description":"Validate that the AI's proposed return knot exists in the story to prevent dead-ends.\n\n## Player Experience Change\nPlayers will never be stranded in an AI branch with no way back. If the AI proposes a non-existent return path, the choice is silently rejected.\n\n## Acceptance Criteria\n- [ ] Returns `{ feasible: boolean, reason: string, confidence: number }`\n- [ ] `feasible=true` if `return_path` knot exists in story (confidence=0.9)\n- [ ] `feasible=false` if knot does not exist (confidence=0.0, reason='Return path knot does not exist')\n- [ ] Completes in \u003c50ms\n- [ ] Unit test: `return_path: 'campfire'` passes (knot exists in demo.ink)\n- [ ] Unit test: `return_path: 'nonexistent_knot_xyz'` fails\n- [ ] Integration test: Director rejects proposal with invalid return_path\n\n## Minimal Implementation\n- Create `checkReturnPath(returnPath, story)` function\n- Extract knot names from `story.mainContentContainer._namedContent`\n- Simple existence check\n\n## Dependencies\n- ge-hch.5.15.1 (Decision Flow Engine)\n\n## Deliverables\n- Return-path checker in director.js\n- Unit tests with valid/invalid return paths","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-01-16T15:01:40.467783504-08:00","created_by":"rgardler","updated_at":"2026-01-17T10:51:48.6478971-08:00","closed_at":"2026-01-17T10:51:48.6478971-08:00","close_reason":"Return-path checker implemented, tested and integrated into Director","dependencies":[{"issue_id":"ge-hch.5.15.2","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:01:40.469157452-08:00","created_by":"rgardler"},{"issue_id":"ge-hch.5.15.2","depends_on_id":"ge-hch.5.15.1","type":"blocks","created_at":"2026-01-16T15:04:32.206416228-08:00","created_by":"rgardler"}]}
{"id":"ge-hch.5.15.20","title":"Implement: Director Integration","description":"Modify inkrunner.js to use Director for AI choice injection.\n\n## Acceptance Criteria\n- [ ] generateAIChoice() calls director.evaluate() before injecting\n- [ ] AI choice injected only if decision === approve\n- [ ] Silent skip on reject (no error, no AI choice)\n- [ ] Loading indicator shows Evaluating AI choice during evaluation\n- [ ] Logs rejection reasons to console\n\n## Implementation Notes\n- Modify generateAIChoice() in web/demo/js/inkrunner.js\n- Import director.js module\n- Handle both sync and async evaluation\n\n## Related Feature\nge-hch.5.15.6 (Director Integration)","status":"closed","priority":1,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:59.856948737-08:00","created_by":"rgardler","updated_at":"2026-01-17T19:07:33.698779843-08:00","closed_at":"2026-01-17T19:07:33.698779843-08:00","close_reason":"Completed","external_ref":"https://github.com/TheWizardsCode/GEngine/pull/167","labels":["Status: PR Created"],"dependencies":[{"issue_id":"ge-hch.5.15.20","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:59.857773656-08:00","created_by":"rgardler"}],"comments":[{"id":207,"issue_id":"ge-hch.5.15.20","author":"rgardler","text":"Integrated Director evaluate into addAIChoice with sync/async support; added require fallback for Director in Node. Loading indicator now shows 'Evaluating AI choice...' during Director evaluation. Rejects are silent with console reason; approvals inject AI choice. Tests updated for sync evaluate; targeted jest run: npx jest tests/unit/inkrunner.test.js tests/unit/director.test.js --runInBand (pass).","created_at":"2026-01-18T03:03:41Z"}]}
{"id":"ge-hch.5.15.21","title":"Tests: Director Integration","description":"Integration tests for Director-governed injection.\n\n## Acceptance Criteria\n- [ ] Playthrough: complete demo.ink with mix of accepted/rejected\n- [ ] Playthrough: no runtime errors when all branches rejected\n- [ ] Test: mocked Director approve leads to AI choice shown\n- [ ] Test: mocked Director reject leads to no AI choice\n\n## Related Feature\nge-hch.5.15.6 (Director Integration)","status":"closed","priority":1,"issue_type":"task","assignee":"@Patch","created_at":"2026-01-16T15:03:59.901304347-08:00","created_by":"rgardler","updated_at":"2026-01-17T19:17:35.645282782-08:00","closed_at":"2026-01-17T19:17:35.645282782-08:00","close_reason":"Completed","external_ref":"https://github.com/TheWizardsCode/GEngine/pull/168","labels":["Status: PR Created"],"dependencies":[{"issue_id":"ge-hch.5.15.21","depends_on_id":"ge-hch.5.15","type":"parent-child","created_at":"2026-01-16T15:03:59.902099293-08:00","created_by":"rgardler"}]}
Expand Down
32 changes: 27 additions & 5 deletions tests/unit/player-preference.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,28 @@ function clearStorage() {
}
}

function corruptStorage() {
if (typeof localStorage !== 'undefined') {
localStorage.setItem('ge-hch.ai-preferences', '{ not valid json');
}
}

describe('PlayerPreference', () => {
beforeEach(() => {
clearStorage();
});

test('cold start returns default 0.5', () => {
expect(PlayerPreference.getPreference('combat')).toBe(0.5);
expect(PlayerPreference.getPreference('dialogue')).toBe(0.5);
});

test('3 accepts + 1 reject yields > 0.6 for dialogue', () => {
PlayerPreference.recordOutcome('dialogue', true);
PlayerPreference.recordOutcome('dialogue', true);
PlayerPreference.recordOutcome('dialogue', true);
const score = PlayerPreference.recordOutcome('dialogue', false);
expect(score).toBeGreaterThan(0.6); // 0.75 expected
expect(PlayerPreference.getPreference('dialogue')).toBeGreaterThan(0.6);
});

test('records outcomes and updates preference', () => {
Expand All @@ -34,10 +49,17 @@ describe('PlayerPreference', () => {
test('clamps to 0-1 and handles bad inputs', () => {
expect(PlayerPreference.getPreference('')).toBe(0.5);
expect(PlayerPreference.recordOutcome('', true)).toBe(0.5);
// Corrupt storage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('ge-hch.ai-preferences', '{ not valid json');
}
corruptStorage();
expect(PlayerPreference.getPreference('combat')).toBe(0.5);
});

test('handles 100+ events quickly', () => {
const start = performance.now();
for (let i = 0; i < 100; i++) {
PlayerPreference.recordOutcome('exploration', i % 2 === 0);
}
const elapsed = performance.now() - start;
expect(PlayerPreference.getPreference('exploration')).toBeGreaterThanOrEqual(0);
expect(elapsed).toBeLessThan(10);
});
});