zfc-leveling-roadmap
ZFC Level-Up architecture alignment — file boundaries, API contracts, pre-flight checks, and milestone status. Read this BEFORE any level-up/rewards/XP code change.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 6
- Forks
- 0
01 前言 Front Matter
- name
- zfc-leveling-roadmap
- scope
- your-project.com/mvp_site
- description
- ZFC Level-Up architecture alignment — file boundaries, API contracts, pre-flight checks, and milestone status. Read this BEFORE any level-up/rewards/XP code change.
03 SKILL.md 正文
ZFC Leveling Roadmap Alignment
Design Principle
This skill is the level-up/rewards specialization of the general ZFC contract.
Before applying these file boundaries, read
the zero-framework-cognition skill for the repository-wide
rules: models make semantic decisions, new regex/keyword/heuristic semantic
routing is banned, and model contracts should request minimal semantic fields
instead of redundant aliases.
Before accepting a backend correction guard, also apply
the root-cause-first skill: prove the selected model path
received the right prompt/schema, fix missing or ambiguous prompt/schema
instructions first, and treat backend correction as a last resort.
"Model Computes, Backend Formats" — the LLM decides level-up;
rewards_engine.pyformats it;world_logic.pywraps the formatted result with modal semantics.
For WorldArchitect-specific backend ownership, this summary defers to the
repository overlay .claude/skills/worldai-zfc/SKILL.md, the portable
the zero-framework-cognition skill, and
the backend-justified skill. Do not infer permission to alter
model-owned domain state from schema validity, determinism, or this file alone.
"LLM Owns Domain Decisions; Backend Preserves and Executes" — the model owns gameplay interpretation, progression, rewards, and choices. The server owns authorization, storage, transport, concurrency, and modal/session control. A backend transformation of model-owned fields is an exception only when the active repository policy explicitly authorizes that named exception and its required evidence is recorded; preserving a valid player turn is the UX default.
Correction Exception Boundary: This guide does not authorize backend correction of gameplay meaning. Follow the local ZFC/backend-justification contract for each proposed transformation. A registered adjustment is necessary only where that active contract requires it; registration or determinism alone does not authorize a domain transformation. Name the exact field, function, policy basis, necessity, and required evidence. Do not add defaults, clamps, suppressions, or normalization to model-owned domain fields based only on contradiction, UX, or determinism.
Opaque Choice Boundary: Planning choice IDs are opaque handles, not semantic signals.
level_up_now,finish_level_up_return_to_game, andcustom_actionare migration/compatibility handles only. Level-up choice meaning must come from explicit model/schema fields on the resolved choice object, not from ID text, prefixes, labels, or backend allowlists.
File Responsibility Table (Mandatory)
Before modifying ANY file for level-up/rewards/XP work, verify against this table. If your change puts logic in a "MUST NOT DO" column → STOP and redesign.
| File | OWNS | MUST NOT DO |
|---|---|---|
rewards_engine.py |
Format and project explicit model signals; keep transport/payload atomicity handling limited to preserving complete supplied representations and server-owned transaction/control invariants. Any transformation of model-owned gameplay fields requires its own named, policy-authorized exception and evidence. | Model calls, Firestore writes, text interpretation, primary XP/level calculation, threshold-derived level-up decisions, or unregistered correction/normalization/suppression of gameplay meaning |
world_logic.py |
Modal locks and consumption of model-authored finish choices; wrap payloads only where its contract permits | Inject or synthesize choices; recompute model-owned progression; call canonicalize_level_up_signal() directly |
game_state.py |
State storage, deterministic mechanical primitives (xp_needed_for_level, level_from_xp, coerce_int) |
Decide level-up from state, replace model choices, own level-up decisions, contain level-up dataclasses or resolvers |
agents.py |
Route to correct agent using rewards_engine.is_level_up_active() |
Inline flag interpretation, XP extraction, stale-flag recovery logic |
llm_parser.py |
Parse model output, call the pipeline once via canonicalize_rewards(), persist/deliver |
Duplicate rewards formatting, inline false-positive mutations, call canonicalization separately |
narrative_response_schema.py |
Preserve model output losslessly and validate structural/schema requirements without deciding gameplay meaning | Class feature synthesis, state mutation, building UI payloads, or rejecting a turn because model-authored progression fields appear contradictory |
llm_service.py |
LLM request/response pipeline, retry on SchemaRejectionError |
Rewards formatting, level-up flag interpretation, modal state mutation |
structured_fields_utils.py |
Extract non-empty structured fields | Semantic validation, alias normalization, UI formatting |
prompts/level_up_instruction.md |
Class-AGNOSTIC contracts only — generic enumeration ("enumerate ALL subclass options as separate clickable choices"), generic announce-all-gains rules. The model owns the D&D 5e SRD. | Hardcode subclass names (oaths/circles/patrons/archetypes/domains), feature names (Divine Health, Channel Divinity, Second Wind…), spell names, or per-class numeric gains as named rules/HARD INVARIANTs. Class names allowed ONLY inside e.g./for example illustrations. |
Authorized Public API Surface
These are the only rewards_engine functions that external files may call:
| Function | Callers | Purpose |
|---|---|---|
canonicalize_rewards(structured_fields, game_state, original_state) |
llm_parser.py (1 call), world_logic.py (polling paths) |
Single convergence point for all rewards/level-up UI |
is_level_up_active(game_state) |
agents.py, world_logic.py |
Read explicit progression signals and server-owned modal control; any domain transformation follows the Correction Exception Boundary |
project_level_up_ui(game_state, original_state) |
world_logic.py (via _project_level_up_ui_locally()) |
Polling/projection path (transitional — delete when callers migrate) |
sync_level_up_signals(game_state, original_state) |
world_logic.py (via ensure_level_up_rewards_pending() shim) |
Idempotent rewards_pending sync |
format_model_level_up_signal(signal) |
llm_parser.py (fallback path) |
Explicit signal formatter (should become private _format_...) |
All other functions are private implementation details. Do not call _canonicalize_core(), _enforce_atomicity(), _apply_contradiction_guard(), resolve_level_up_signal(), or ensure_rewards_box() from outside rewards_engine.py.
API Contract Freeze
canonicalize_rewards()returns(rewards_box, planning_block)— a 2-tuple. Do NOT change this return signature. Useout_metadict parameter to pass additional data out (e.g., canonical signal, false-positive flags).canonicalize_level_up_signal()is called insiderewards_engine.pyonly. External files consume precomputed results.is_level_up_active()returnsbool. It reads explicit progression signals and server-owned modal control. This API shape does not authorize contradiction suppression or XP-threshold decisions; any transformation of a model-owned field requires the named active-policy exception and evidence in the Correction Exception Boundary.ensure_rewards_box()returnstuple[dict | None, dict | None](rewards_box, signal_metadata). Internal only.
Common Boundary Violations
These are the most frequently observed agent drift patterns. If you catch yourself doing any of these, STOP:
| If you're about to... | WRONG file | RIGHT approach |
|---|---|---|
Call canonicalize_level_up_signal() |
world_logic.py, llm_parser.py |
Only inside rewards_engine.py (via _canonicalize_core) |
Build a level_up_signal dict |
world_logic.py |
Build in rewards_engine.py, pass via return or out_meta |
| Compute XP progress, thresholds, target level, or level-up availability as the primary path | any backend file | Ask the LLM for explicit calculated fields; preserve them unless the exact transformation satisfies the Correction Exception Boundary |
Check level_up_pending / level_up_in_progress flags directly |
world_logic.py, agents.py |
Call rewards_engine.is_level_up_active() |
Call resolve_level_progression() |
anywhere | Deleted. Use rewards_engine.is_level_up_active() or rewards_engine.project_level_up_ui() |
Use build_level_up_rewards_box() |
anywhere | Deleted. Use rewards_engine.ensure_rewards_box() (internal) or project_level_up_ui() |
Change canonicalize_rewards() return type |
anywhere | Don't. Use out_meta dict instead |
| Add signal normalization aliases | structured_fields_utils.py |
First establish the exact active-policy exception and evidence under the Correction Exception Boundary; this table alone grants no normalization authority |
| Classify or scrub choices by ID text/prefix, label, enum name, or CSS class | any backend file | Resolve the submitted handle against persisted planning_block.choices, then read explicit structured fields; keep level_up_now, finish_level_up_return_to_game, and custom_action exact checks migration-only |
| Add a new feature to a level-up PR | the same PR | Open a separate PR — scope creep is the #1 cause of spin |
Access rewards_engine._is_state_flag_true() from outside |
agents.py, world_logic.py |
Use the public helpers or import from game_state.py |
Pre-Flight Checklist
Run this before writing any code for a PR that touches level-up, rewards, XP, or canonicalization logic.
1. File boundary check
For each file you plan to modify, check the table above:
- [ ] Is the change in the "OWNS" column? → Proceed
- [ ] Is it in the "MUST NOT DO" column? → STOP. Redesign.
2. API contract check
- [ ] Am I changing the return signature of
canonicalize_rewards()? → Don't. Useout_meta. - [ ] Am I calling
rewards_engine.*fromworld_logic.pyoutside the authorized API surface table? → Check boundary table. - [ ] Am I directly reading
level_up_pending/level_up_in_progressflags instead of callingis_level_up_active()? → Use the centralized function. - [ ] Am I adding or expanding a choice-ID allowlist, prefix check, or text/label check for level-up behavior? → STOP. First prove the selected choice is resolved from persisted structured state and that the model/schema exposes the needed semantic field. Exact legacy handles (
level_up_now,finish_level_up_return_to_game,custom_action) require a migration note and must not become the pattern for new behavior.
3. Deleted function check
- [ ] Am I calling any of these deleted functions? → They no longer exist:
game_state.resolve_level_progression_state()game_state.resolve_level_progression()game_state.ensure_level_up_rewards_pending()game_state.LevelProgressionStategame_state.PendingRewardsStateworld_logic.build_level_up_rewards_box()agents._is_stale_level_up_pending()
4. Scope check
- [ ] Does a task spec exist in
~/roadmap/zfc-pr-task-specs-2026-04-22.md? - [ ] If yes, is every file I'm touching in the "Likely files" list?
- [ ] Am I adding unrelated features to this PR? → Open a separate PR.
5. PR hygiene check
- [ ] Does an open PR already cover this scope? → Fix that PR instead of creating a new one (see
${CLAUDE_HOME:-$HOME/.claude}/skills/pr-quantity-control/SKILL.md).
Decision Flowchart
Is my change about level-up/XP/rewards logic?
├── YES → Model-owned gameplay meaning belongs in the prompt/schema
│ ├── Server execution or transport? Follow the ownership table and active contract
│ ├── Does world_logic.py need the result?
│ │ └── Pass via return value or out_meta dict
│ ├── Does agents.py need to know if level-up is active?
│ │ └── Call rewards_engine.is_level_up_active() — never inline flag checks
│ └── Does llm_parser.py need the result?
│ └── Call canonicalize_rewards() once; don't call internal functions directly
├── Is my change about model output?
│ └── Diagnose the prompt/schema at the root cause first
│ └── Preserve valid player turns; no contradiction-repair retry is authorized here
└── NO → Normal file protocol applies
Historical Milestone Snapshot
The statuses and implementation notes below are a snapshot from source commit 04afb7f1b93af731240f1298a1234237e4e608da, dated 2026-05-13. They do not establish current project status or grant permission to add a transformation; verify current source and owner instructions before acting. In particular, the “fail-closed” semantic SSE wording describes that historical implementation report and does not authorize rejecting player turns.
| Milestone | Status | Description |
|---|---|---|
| M0 | ✅ Complete | Delete legacy paths, net LOC ≤ 0 |
| M1 | ✅ Complete | Model compliance probe — prompt contract + streaming evidence |
| M1b | ✅ Complete | Repeated-run reliability harness (Wilson score intervals) |
| M2 | ? Near Complete | Legacy resolver deletion done; formatter narrowing partially done |
| M3 | ? Partially Started | Snapshot reported CI grep gates active via design-doc-gate.yml (8 gates reported enforced) |
M2 Work Reported Complete at Snapshot (via product-repository PR #6641)
- ✅
game_state.py— all legacy resolvers and dataclasses deleted (422 LOC) - ✅
world_logic.py—build_level_up_rewards_box()deleted, allresolve_level_progression()calls removed - ✅
agents.py—_is_stale_level_up_pending()deleted, all routing usesis_level_up_active() - ✅
rewards_engine.sync_level_up_signals()replaces 200-lineensure_level_up_rewards_pending() - At the snapshot,
_enforce_atomicity()was reported rewritten for semantic SSE validation (fail-closed); this is a historical implementation note, not current permission to reject turns. - ✅ Canonical XP field names enforced:
current_turn_exp,total_exp_for_next_level - ✅
ensure_rewards_box()returnstuple[dict|None, dict|None], fieldresolved_target_level→new_level
M2 Remaining Work as Reported at Snapshot
The snapshot listed these follow-ups; verify current callers and ownership before using them as a plan:
format_model_level_up_signal()→ make private (_format_model_level_up_signal)project_level_up_ui()→ delete when no remaining callers (reported then as used by_project_level_up_ui_locally())
M3 Snapshot — CI Enforcement Gates (Reported Active)
At the snapshot, design-doc-gate.yml was reported to enforce 8 grep-based boundary checks. Verify the workflow and its current scope before relying on this historical status.
| Gate | Rule | Status at snapshot |
|---|---|---|
world_logic 0 rewards_engine public API imports |
No direct from rewards_engine import in world_logic |
Reported enforced at snapshot |
world_logic 0 resolve_level_up_signal calls |
No direct resolver calls | Reported enforced at snapshot |
constants get_xp_for_level |
No duplicate XP helpers in constants | Reported enforced at snapshot |
constants get_level_from_xp |
No duplicate level helpers in constants | Reported enforced at snapshot |
_is_state_flag_true 2 files |
Helper exists in exactly 2 files | Reported enforced at snapshot |
world_logic ≤1 project_level_up_ui |
At most 1 non-wrapper call | Reported enforced at snapshot |
non-rewards 0 direct level-up pair builders |
No external pair builders | Reported enforced at snapshot |
llm_parser canonicalize_rewards=1 |
Exactly 1 canonicalize call | Reported enforced at snapshot |
M3 Remaining Work as Reported at Snapshot
The snapshot listed these planned checks; verify current code and workflow ownership before implementing them:
- Centralization ratchet: no new
definworld_logic.pywhose name exists inrewards_engine.py - Architecture test:
level_up_signalformatting stays inrewards_engine.py
Field Ownership Quick Reference
Every shared dict field must have exactly ONE authoritative writer.
| Dict / Field | Writer | Semantic |
|---|---|---|
level_up_signal.current_level |
LLM | Int: level before applying the level-up |
level_up_signal.target_level |
LLM | Int: target level now available; target_level > current_level is the model-owned level-up signal |
rewards_pending.level_up_available |
Backend-derived compatibility/display field from accepted level_up_signal |
Bool: deterministic UI alias; do not ask the model to emit it |
rewards_pending.xp_gained |
LLM via state_updates; any transitional transformation requires the named active-policy exception and evidence | Int: model-calculated XP delta for rewards box |
rewards_pending.new_level |
Backend-derived compatibility/display field from level_up_signal.target_level |
Int: deterministic UI alias; do not ask the model to emit it |
player_character_data.experience.current |
Active repository field-specific contract | Int: cumulative accepted XP; writer and projection are owned by the current prompt/schema and ZFC contract |
custom_campaign_state.level_up_in_progress |
Server-owned modal/session control state | Bool: level-up modal active; model must not write this on story/rewards turns |
custom_campaign_state.level_up_pending |
Backend-derived compatibility/control field from accepted level-up signal or modal state | Bool: modal routing compatibility; model must not write this on story/rewards turns |
custom_campaign_state.level_up_complete |
Server-owned level-up flow state | Bool: level-up flow finished (suppresses is_level_up_active()) |
custom_campaign_state.level_up_cancelled |
Server-owned level-up flow state | Bool: level-up deferred (suppresses is_level_up_active()) |
For the full field registry, see the field-ownership-contracts skill.
Key Architectural Invariants (Post-M2)
-
Model-owned progression; server-owned modal control:
is_level_up_active()may read explicit model-authored progression fields and server-owned modal/session fields, but it must not adjudicate progression from XP thresholds or other game mechanics. Suppressinglevel_up_pending=Truebecause a model-owned signal is “mechanically impossible” is a gameplay decision and is not authorized here. A correction to a model-owned field requires an exact active-policy exception, named field/function, concrete necessity, and that policy's evidence. Server-owned completion/cancellation state may control modal activity without rewriting the model's progression decision. -
Streaming preservation: Preserve supplied stream fields and their representation aliases. Missing one field is not a reason to drop the other or reject the turn. Stream/payload atomicity enforcement may protect complete transport representations or server-owned transaction invariants; it must not validate gameplay semantics or discard a valid player turn over metadata/representation mismatch. Reject only when committing would corrupt server-owned storage.
-
Completion/cancellation precedence:
level_up_complete=Trueorlevel_up_cancelled=Truealways returnsFalsefromis_level_up_active(), regardless of other flags. -
Progression limits: Follow the active campaign progression contract; do not add a hardcoded cap or threshold-based fallback to this helper.
-
Single delegation:
world_logic._project_level_up_ui_locally()is a 1-line call torewards_engine.project_level_up_ui(). No independent computation.
Canonical References
For any WorldArchitect backend or progression change, read the local overlay .claude/skills/worldai-zfc/SKILL.md together with the zero-framework-cognition skill, the backend-justified skill, and the root-cause-first skill. Prompt/schema correction comes before backend protection. Preserve model-owned gameplay meaning and unknown fields; server-owned storage, authorization, transport, concurrency, and modal/session control remain server responsibilities. Any domain-field transformation must identify its exact active-policy exception, field and function, concrete necessity, and required evidence; where that policy requires registration, register it. This guide does not authorize finish-choice injection, paired payload dropping, gameplay defaults, clamps, semantic suppression, or rejection of a valid turn.
Related Skills
- the
field-ownership-contractsskill — full field writer/reader registry - the
evidence-standardsskill — evidence requirements for production PRs - the
pr-green-definitionskill — canonical/green(CI green + no merge conflicts) definition; quality gates live in the draft phase
04 同仓库更多 Skills
instinct
Use when the user invokes /instinct or /in, or asks to "message Instinct", "ask Instinct", "tell Instinct", "check Instinct", or read replies from the configured peer assistant. iMessage first, WhatsApp Web as fallback.
hr
Compatibility alias for history-resume; produce the same fresh-state, provenance-aware resume briefing.
history-resume
Resume briefing for the current repository branch or worktree using fresh state and admitted history sources.
grok
Alias for the canonical grokbot browser conversation workflow; /grok talks to the Grok bot the same way /dot talks to Dot.
codexa-bench
Compare `codexa` and normal `codex` on a fair split of comparable work by default; use `--strict` for duplicate runs of the exact same task.
frontend-design
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.