Skip to content

Commit a0eed5a

Browse files
authored
feat: add generator-count achievement track kind (#836)
* feat(achievements): add generator-count track kind (#821) * test(core,content-schema): cover achievement edge cases
1 parent 7456a8e commit a0eed5a

12 files changed

Lines changed: 803 additions & 25 deletions

File tree

docs/content-schema-reference.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Used in the `track` field of an Achievement definition to determine how progress
3131
| :--- | :--- | :--- |
3232
| `resource` | `resourceId`, `threshold`, `comparator` | Tracks the amount of a specific resource. |
3333
| `generator-level` | `generatorId`, `level` | Tracks the level of a specific generator. |
34+
| `generator-count` | `threshold`, `comparator` (optional: `generatorIds`) | Tracks the total number of owned generators, optionally filtered to a subset. |
3435
| `upgrade-owned` | `upgradeId` (optional: `purchases`) | Tracks if a specific upgrade has been purchased. |
3536
| `flag` | `flagId` | Tracks if a specific game flag is set. |
3637
| `script` | `scriptId` | specific script execution conditions (see script docs). |
@@ -43,15 +44,23 @@ Used in the `track` field of an Achievement definition to determine how progress
4344
{
4445
"kind": "resource",
4546
"resourceId": "gold",
46-
"threshold": 1000,
47+
"threshold": { "kind": "constant", "value": 1000 },
4748
"comparator": "gte"
4849
}
4950

5051
// Generator Level Track
5152
{
5253
"kind": "generator-level",
5354
"generatorId": "mine",
54-
"level": 10
55+
"level": { "kind": "constant", "value": 10 }
56+
}
57+
58+
// Generator Count Track
59+
{
60+
"kind": "generator-count",
61+
"threshold": { "kind": "constant", "value": 25 },
62+
"comparator": "gte",
63+
"generatorIds": ["cursor", "grandma"]
5564
}
5665
```
5766

docs/coverage/index.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ Run `pnpm coverage:md` from the repository root to regenerate this page after mo
1010
## Overall Coverage
1111
| Metric | Covered | Total | % |
1212
| --- | --- | --- | --- |
13-
| Statements | 40594 | 45024 | 90.16% |
14-
| Branches | 8384 | 9815 | 85.42% |
15-
| Functions | 2128 | 2281 | 93.29% |
16-
| Lines | 40594 | 45024 | 90.16% |
13+
| Statements | 40757 | 45161 | 90.25% |
14+
| Branches | 8415 | 9826 | 85.64% |
15+
| Functions | 2141 | 2294 | 93.33% |
16+
| Lines | 40757 | 45161 | 90.25% |
1717

1818
## Coverage by Package
1919
| Package | Statements | Branches | Functions | Lines |
2020
| --- | --- | --- | --- | --- |
21-
| @idle-engine/content-compiler | 1374 / 1525 (90.10%) | 237 / 300 (79.00%) | 88 / 92 (95.65%) | 1374 / 1525 (90.10%) |
21+
| @idle-engine/content-compiler | 1374 / 1525 (90.10%) | 239 / 302 (79.14%) | 88 / 92 (95.65%) | 1374 / 1525 (90.10%) |
2222
| @idle-engine/content-sample | 17 / 21 (80.95%) | 2 / 3 (66.67%) | 0 / 0 (0.00%) | 17 / 21 (80.95%) |
23-
| @idle-engine/content-schema | 8645 / 9715 (88.99%) | 1231 / 1446 (85.13%) | 307 / 337 (91.10%) | 8645 / 9715 (88.99%) |
23+
| @idle-engine/content-schema | 8707 / 9754 (89.27%) | 1253 / 1463 (85.65%) | 308 / 338 (91.12%) | 8707 / 9754 (89.27%) |
2424
| @idle-engine/content-validation-cli | 1608 / 2200 (73.09%) | 276 / 374 (73.80%) | 80 / 99 (80.81%) | 1608 / 2200 (73.09%) |
2525
| @idle-engine/controls | 228 / 230 (99.13%) | 67 / 69 (97.10%) | 14 / 14 (100.00%) | 228 / 230 (99.13%) |
26-
| @idle-engine/core | 24720 / 27308 (90.52%) | 5450 / 6463 (84.33%) | 1435 / 1534 (93.55%) | 24720 / 27308 (90.52%) |
26+
| @idle-engine/core | 24743 / 27322 (90.56%) | 5463 / 6476 (84.36%) | 1435 / 1534 (93.55%) | 24743 / 27322 (90.56%) |
2727
| @idle-engine/renderer-contract | 516 / 523 (98.66%) | 134 / 138 (97.10%) | 27 / 27 (100.00%) | 516 / 523 (98.66%) |
28-
| @idle-engine/renderer-debug | 794 / 796 (99.75%) | 263 / 290 (90.69%) | 38 / 38 (100.00%) | 794 / 796 (99.75%) |
29-
| @idle-engine/renderer-webgpu | 1829 / 1843 (99.24%) | 498 / 505 (98.61%) | 95 / 96 (98.96%) | 1829 / 1843 (99.24%) |
28+
| @idle-engine/renderer-debug | 870 / 876 (99.32%) | 255 / 266 (95.86%) | 49 / 49 (100.00%) | 870 / 876 (99.32%) |
29+
| @idle-engine/renderer-webgpu | 1831 / 1847 (99.13%) | 500 / 508 (98.43%) | 96 / 97 (98.97%) | 1831 / 1847 (99.13%) |
3030
| @idle-engine/shell-desktop | 863 / 863 (100.00%) | 226 / 227 (99.56%) | 44 / 44 (100.00%) | 863 / 863 (100.00%) |

docs/issue-821-design.md

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
---
2+
title: "content-schema: add generator-count achievement track kind (Issue 821)"
3+
sidebar_position: 99
4+
---
5+
6+
# content-schema: add generator-count achievement track kind (Issue 821)
7+
8+
## Document Control
9+
- **Title**: Add `generator-count` achievement track kind for aggregate generator ownership
10+
- **Authors**: Ralph (AI agent)
11+
- **Reviewers**: @hansjm10
12+
- **Status**: Draft
13+
- **Last Updated**: 2026-01-25
14+
- **Related Issues**: https://github.com/hansjm10/Idle-Game-Engine/issues/821
15+
- **Execution Mode**: AI-led
16+
17+
## 1. Summary
18+
Add a new achievement track kind, `generator-count`, that measures total generator ownership by summing generator “levels” (owned counts) across all generators (or an explicit subset). This makes “Own N total generators (of any type)” achievable purely in content definitions, without requiring the `custom-metric` workaround and a runtime `getCustomMetricValue` callback.
19+
20+
## 2. Context & Problem Statement
21+
- **Background**:
22+
- The achievement `track` discriminated union in `packages/content-schema/src/modules/achievements.ts` currently supports `resource`, `generator-level`, `upgrade-owned`, `flag`, `script`, and `custom-metric`.
23+
- `generator-level` tracks the owned count for a *single* generator id.
24+
- Runtime evaluation lives in `packages/core/src/progression/achievement-tracker.ts` via `getAchievementTrackValue(...)`, using a `ConditionContext` backed by the `ProgressionFacade` (`packages/core/src/progression/progression-facade.ts`).
25+
- **Problem**:
26+
- Content authors cannot define aggregate ownership achievements like “Own 10 generators total” across all generator types.
27+
- The current workaround (`custom-metric`) requires code integration (`getCustomMetricValue`) and therefore cannot be solved purely in content packs.
28+
- **Forces**:
29+
- Keep the achievement system deterministic and content-first (avoid requiring callbacks for common progression patterns).
30+
- Minimise surface-area changes to `ConditionContext` (used broadly for unlock/visibility/automation evaluation).
31+
- Avoid introducing per-step cost that scales poorly with large numbers of generators/achievements.
32+
33+
## 3. Goals & Non-Goals
34+
- **Goals**:
35+
1. Support aggregate generator ownership achievements in content via a first-class track kind.
36+
2. Allow “all generators” by default, with an optional filter to specific generator ids.
37+
3. Keep behaviour deterministic and consistent with existing achievement progress semantics.
38+
4. Update schema validation, runtime evaluation, tests, and schema reference docs.
39+
- **Non-Goals**:
40+
- Tracking other aggregates (e.g., total generator production rate) as part of this change.
41+
- Replacing or deprecating `custom-metric` (it remains the escape hatch for bespoke logic).
42+
- Redesigning achievement progress modes (oneShot/incremental/repeatable).
43+
44+
## 4. Stakeholders, Agents & Impacted Surfaces
45+
- **Primary Stakeholders**:
46+
- Content authors / pack maintainers
47+
- `@idle-engine/content-schema` maintainers
48+
- `@idle-engine/core` progression maintainers
49+
- **Agent Roles**:
50+
- **Schema Agent**: Add `generator-count` to the content schema and cross-reference validators.
51+
- **Runtime Agent**: Implement `generator-count` evaluation in the achievement tracker.
52+
- **Docs Agent**: Document the new track kind in `docs/content-schema-reference.md`.
53+
- **Test Agent**: Add/extend Vitest coverage for schema + runtime behaviour.
54+
- **Affected Packages/Services**:
55+
- `packages/content-schema` (track schema + pack validator + tests)
56+
- `packages/core` (achievement tracker + coordinator wiring + tests)
57+
- `docs/content-schema-reference.md` (documentation)
58+
- **Compatibility Considerations**:
59+
- This is an additive schema/runtime change; existing packs remain valid.
60+
- Packs that previously attempted to use a non-existent `generatorCount` kind will still fail until they migrate to `generator-count` (see Open Questions re: aliases).
61+
62+
## 5. Current State
63+
- Content schema defines achievement track kinds in `packages/content-schema/src/modules/achievements.ts` (see `trackSchema` around line 46).
64+
- Cross-reference validation checks track references in `packages/content-schema/src/pack/validators/achievements.ts` (e.g., verifying `generator-level.generatorId` exists).
65+
- Runtime measurement uses `AchievementTracker.getAchievementTrackValue(...)` in `packages/core/src/progression/achievement-tracker.ts` (see switch around line 457):
66+
- `generator-level` delegates to `conditionContext.getGeneratorLevel(generatorId)` which resolves to generator owned counts in `ProgressionFacade` (see `conditionContext` wiring around line 112 in `packages/core/src/progression/progression-facade.ts`).
67+
- No runtime or schema support exists for summing across multiple generators without `custom-metric`.
68+
69+
## 6. Proposed Solution
70+
### 6.1 Architecture Overview
71+
- Introduce a new achievement track kind: `generator-count`.
72+
- The runtime calculates the track’s measurement as:
73+
- `sum(getGeneratorLevel(id))` over `generatorIds` if specified, otherwise
74+
- `sum(getGeneratorLevel(id))` over all generator ids in the loaded content pack.
75+
- Completion uses a comparator against the computed achievement target:
76+
- Default comparator: `gte` (consistent with existing comparator defaults).
77+
78+
### 6.2 Detailed Design
79+
- **Runtime Changes**:
80+
- Extend `AchievementTracker` to support a `generator-count` case in `getAchievementTrackValue(...)`.
81+
- Provide `AchievementTracker` access to “all generator ids” (via `ProgressionFacade`, since it already receives `options.content.generators`).
82+
- Update track completion logic so `generator-count` can use its `comparator` (similar to `resource` tracks).
83+
- **Data & Schemas**:
84+
- Extend `trackSchema` in `packages/content-schema/src/modules/achievements.ts` with:
85+
- `kind: 'generator-count'`
86+
- `threshold: numericFormulaSchema`
87+
- `comparator: comparatorSchema` (defaulting to `gte`)
88+
- `generatorIds?: string[]` (content ids, intended to reference generators)
89+
- Extend the achievements pack cross-reference validator (`packages/content-schema/src/pack/validators/achievements.ts`) to:
90+
- validate all `generatorIds` (if present) exist in the generator index
91+
- validate `threshold` formula references (like `resource` / `custom-metric` do)
92+
- **APIs & Contracts**:
93+
- No public API changes are required if `generator-count` measurement is computed using existing `ConditionContext.getGeneratorLevel(...)` plus a generator id list passed into `AchievementTracker`.
94+
- Content shape change is additive: old packs remain valid; new packs can opt into `generator-count`.
95+
- **Tooling & Automation**:
96+
- Update `docs/content-schema-reference.md` to list `generator-count`, required fields, and examples.
97+
98+
Example achievement definition:
99+
```json
100+
{
101+
"id": "achievement.ten-generators",
102+
"name": { "default": "Generator Collector" },
103+
"description": { "default": "Own 10 generators of any type" },
104+
"category": "progression",
105+
"tier": "bronze",
106+
"track": {
107+
"kind": "generator-count",
108+
"threshold": { "kind": "constant", "value": 10 },
109+
"comparator": "gte"
110+
}
111+
}
112+
```
113+
114+
Example filtered definition (subset of generators):
115+
```json
116+
{
117+
"track": {
118+
"kind": "generator-count",
119+
"generatorIds": ["generator.cursor", "generator.grandma"],
120+
"threshold": { "kind": "constant", "value": 25 },
121+
"comparator": "gte"
122+
}
123+
}
124+
```
125+
126+
### 6.3 Operational Considerations
127+
- **Deployment**:
128+
- Standard workspace flow: `pnpm lint` and `pnpm test`.
129+
- If tests/coverage change, regenerate `docs/coverage/index.md` via `pnpm coverage:md` (per repo guidelines).
130+
- **Telemetry & Observability**:
131+
- Not applicable (no runtime telemetry added).
132+
- **Security & Compliance**:
133+
- Not applicable (no new external inputs or permissions).
134+
135+
## 7. Work Breakdown & Delivery Plan
136+
### 7.1 Issue Map
137+
| Issue Title | Scope Summary | Proposed Assignee/Agent | Dependencies | Acceptance Criteria |
138+
|-------------|---------------|-------------------------|--------------|---------------------|
139+
| `feat(content-schema): add generator-count achievement track` | Add `generator-count` to `trackSchema` + cross-reference validation + schema tests | Schema Agent | Design doc approved | Pack validates `generator-count`; bad generator ids rejected; tests added |
140+
| `feat(core): evaluate generator-count achievement tracks` | Sum generator owned counts for `generator-count` and respect comparator | Runtime Agent | Schema merged | Unit tests cover track value + completion semantics |
141+
| `docs: document generator-count achievement tracks` | Update schema reference doc and add examples | Docs Agent | Schema merged | `docs/content-schema-reference.md` includes new kind + example |
142+
143+
### 7.2 Milestones
144+
- **Phase 1**: Ship schema + runtime support + tests + docs for `generator-count`.
145+
- **Phase 2**: Optional follow-ups (alias support, sample pack additions, performance caching) if needed.
146+
147+
### 7.3 Coordination Notes
148+
- **Hand-off Package**:
149+
- Issue context: https://github.com/hansjm10/Idle-Game-Engine/issues/821
150+
- Relevant schema/runtime entry points:
151+
- `packages/content-schema/src/modules/achievements.ts`
152+
- `packages/content-schema/src/pack/validators/achievements.ts`
153+
- `packages/core/src/progression/achievement-tracker.ts`
154+
- `packages/core/src/progression/progression-facade.ts`
155+
- `docs/content-schema-reference.md`
156+
- **Communication Cadence**:
157+
- One implementation PR is sufficient; review checkpoint after tests + docs update.
158+
159+
## 8. Agent Guidance & Guardrails
160+
- **Context Packets**:
161+
- Load Issue 821 and inspect the track schema + runtime tracker implementation before coding.
162+
- Compare to existing track behaviour (`resource`, `generator-level`, `custom-metric`) for consistency.
163+
- **Prompting & Constraints**:
164+
- Follow workspace TypeScript conventions: ES modules, 2-space indent, type-only imports/exports.
165+
- Do not edit checked-in `dist/` outputs by hand.
166+
- **Safety Rails**:
167+
- Avoid adding noisy console output during Vitest runs (the LLM reporter expects clean JSON end-of-run output).
168+
- Keep achievement evaluation deterministic (no access to wall-clock time or randomness).
169+
- **Validation Hooks**:
170+
- `pnpm test --filter @idle-engine/content-schema`
171+
- `pnpm test --filter @idle-engine/core`
172+
- `pnpm lint`
173+
174+
## 9. Alternatives Considered
175+
1. **Continue using `custom-metric`**:
176+
- Pros: no schema/runtime changes.
177+
- Cons: requires runtime integration, preventing content-only packs from expressing a common achievement pattern.
178+
2. **Require explicit `generatorIds` (no “all generators” default)**:
179+
- Pros: avoids needing runtime access to generator lists.
180+
- Cons: burdens content authors and makes “all generators” difficult/verbose.
181+
3. **Extend `ConditionContext` with a “total generator count” API**:
182+
- Pros: avoids `AchievementTracker` needing generator ids.
183+
- Cons: broadens a shared interface and requires more widespread wiring/migration.
184+
185+
## 10. Testing & Validation Plan
186+
- **Unit / Integration**:
187+
- `packages/content-schema`: add schema tests for `generator-count` (parsing, default progress target derivation) and validator tests for `generatorIds` references.
188+
- `packages/core`: add unit tests validating measurement and completion with `generator-count` using a small content pack fixture.
189+
- **Performance**:
190+
- Validate that summing across generator ids is acceptable for typical pack sizes; consider caching if necessary.
191+
- **Tooling / A11y**:
192+
- Not applicable.
193+
194+
## 11. Risks & Mitigations
195+
1. **Performance overhead with many generators/achievements**:
196+
- Mitigation: keep `generatorIds` optional for narrowing; add caching later if benchmarks show need.
197+
2. **Ambiguity (“level” vs “count”)**:
198+
- Mitigation: document clearly that generator “level” equals owned count; `generator-count` sums owned counts.
199+
3. **Early adopters using `generatorCount` (camelCase) in content**:
200+
- Mitigation: document the correct kind (`generator-count`); optionally add an alias if that becomes a recurring pain point.
201+
202+
## 12. Rollout Plan
203+
- **Milestones**:
204+
1. Merge schema + validator changes.
205+
2. Merge core runtime support + tests.
206+
3. Update docs.
207+
- **Migration Strategy**:
208+
- Packs using the `custom-metric` workaround can migrate to `generator-count` to become content-only.
209+
- **Communication**:
210+
- Note in release/PR description: “New achievement track kind: `generator-count` (total generators owned).”
211+
212+
## 13. Open Questions
213+
1. Should we accept `generatorCount` as an alias for `generator-count` to reduce schema mismatch footguns?
214+
2. Should `generatorIds` (when present) be required to be non-empty and de-duplicated/normalized?
215+
3. Do we need step-level caching in `AchievementTracker` for `generator-count`, or is naive summation sufficient for expected pack sizes?
216+
217+
## 14. Follow-Up Work
218+
- Add a sample-pack achievement demonstrating `generator-count` once the kind is available (optional).
219+
- Consider additional aggregate track kinds if content author feedback suggests a pattern (e.g., “total upgrades purchased”).
220+
221+
## 15. References
222+
- Issue 821: https://github.com/hansjm10/Idle-Game-Engine/issues/821
223+
- Track schema: `packages/content-schema/src/modules/achievements.ts`
224+
- Pack achievement validator: `packages/content-schema/src/pack/validators/achievements.ts`
225+
- Achievement runtime evaluation: `packages/core/src/progression/achievement-tracker.ts`
226+
- Condition context wiring: `packages/core/src/progression/progression-facade.ts`
227+
- Schema reference docs: `docs/content-schema-reference.md`
228+
229+
## Appendix A — Glossary
230+
- **Generator level**: The owned count of a specific generator id (`ConditionContext.getGeneratorLevel(...)`).
231+
- **generator-count**: Proposed achievement track kind that sums generator levels across multiple generator ids.
232+
- **custom-metric**: Existing escape hatch track kind whose measurement is provided by a runtime callback.
233+
234+
## Appendix B — Change Log
235+
| Date | Author | Change Summary |
236+
|------------|--------|----------------|
237+
| 2026-01-25 | Ralph (AI agent) | Initial draft for Issue 821 |
238+

0 commit comments

Comments
 (0)