|
| 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