30 KiB
FlowScope Game Core P1 Production Hardening Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 在 P0 全绿和人工验收完成后,交付第一批 P1 生产化能力:资源后端适配层、配置/存档扩展点、轻量 UI 导航与预加载、MainMenu 样例验收升级,以及框架使用规范。
Architecture: P1 不重写 P0 核心,不引入全局 EventBus、大型 DI、完整包生态或编辑器工具链。所有新增能力优先通过小接口和适配层扩展,保持 FlowScope.Runtime 主接口稳定;涉及 Addressables 的强依赖从核心 Runtime 中拆出为独立适配程序集。每个任务独立提交,最终由集成任务跑全量 EditMode、PlayMode 和人工样例验收。
Tech Stack: Unity 2022.3.62f2c1, C#, Unity Test Framework, R3, Addressables, Task/CancellationToken, asmdef, JSON 本地配置/存档。
P1 范围确认
本轮 P1 包含
- 资源生产化第一步:引入泛型
IResourceBackend,将 Addressables 适配从FlowScope.Runtime拆到FlowScope.Addressables,并移除 Addressables 反射调用。 - 配置扩展点:引入
IConfigSource与IConfigParser,让 JSON provider 从“硬编码 JSON 文本映射”变成“source + parser”的组合。 - 存档迁移扩展点:引入
ISaveMigration与带版本包裹的 JSON serializer,支持单 key 数据从旧版本迁移到当前版本。 - UI 生产化第一步:引入轻量
IUIScreenNavigator与 panel 预加载服务,仍保持UIManager的层内 LIFO 规则。 - MainMenuP0 升级为 P1 验收样例:验证 UI 打开、Gold 点击递增、保存恢复、资源/订阅释放。
- 使用规范文档:Bootstrap、Feature 生命周期、资源释放、UI 路径、配置 DTO、存档迁移、资源后端接入。
本轮 P1 不包含
- Luban 完整接入。
- YooAsset / AssetBundle 后端实现。
- 云存档、加密、压缩、冲突解决。
- 完整 UI 路由系统、URL 式导航、跨 Feature 页面恢复。
- AudioMixer、3D 音频、动态音乐。
- Unity Package 分发结构、Samples~、编辑器导入向导。
- Source Generator 深度优化。
推荐工作流
- 从当前
main创建p1-contractsworktree/branch。 - Task 0 完成并合并后,再并行执行 Task A、Task B、Task C。
- Task D 依赖 A/B/C 合并后执行。
- Task E 做最终集成、文档同步和验收。
- 所有 plan、汇报、commit message 使用中文。
推荐 worktrees:
| Worktree | Purpose |
|---|---|
p1-contracts |
P1 小接口、asmdef 边界、文档边界 |
p1-resource-backend |
IResourceBackend 与 Addressables 独立适配 |
p1-config-save-extensions |
IConfigSource、IConfigParser、ISaveMigration |
p1-ui-navigation-preload |
IUIScreenNavigator 与 Panel 预加载 |
p1-sample-docs |
MainMenuP0 验收升级和使用规范 |
p1-integration |
合并、修缝、全量验证 |
Task 0: P1 Contracts Worktree
Worktree: p1-contracts
Goal: 先稳定 P1 的接口边界,让后续并行任务不互相改核心契约。
Owned files:
- Create:
My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs - Create:
My project/Assets/FlowScope/Runtime/Config/IConfigSource.cs - Create:
My project/Assets/FlowScope/Runtime/Config/IConfigParser.cs - Create:
My project/Assets/FlowScope/Runtime/Save/ISaveMigration.cs - Create:
My project/Assets/FlowScope/Runtime/UI/IUIScreenNavigator.cs - Create:
My project/Assets/FlowScope/Runtime/UI/IUIPreloadService.cs - Modify:
docs/requirements/p0-requirements-set.md - Create:
docs/requirements/p1-production-hardening.md
Do not modify:
-
My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs -
My project/Assets/FlowScope/Runtime/Config/JsonConfigProvider.cs -
My project/Assets/FlowScope/Runtime/Save/SaveService.cs -
My project/Assets/FlowScope/Runtime/UI/UIManager.cs -
Any sample scene or prefab files.
-
Step 1: Create
IResourceBackendcontract
Create My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs:
using System.Threading;
using System.Threading.Tasks;
namespace FlowScope.Resources
{
public interface IResourceBackend
{
string Name { get; }
Task<T> LoadAsync<T>(
string key,
CancellationToken cancellationToken)
where T : class;
void Release<T>(string key, T asset)
where T : class;
}
}
- Step 2: Create config extension contracts
Create My project/Assets/FlowScope/Runtime/Config/IConfigSource.cs:
using System.Threading;
using System.Threading.Tasks;
namespace FlowScope.Config
{
public interface IConfigSource
{
string Name { get; }
Task<string> LoadTextAsync(
string fileName,
CancellationToken cancellationToken);
}
}
Create My project/Assets/FlowScope/Runtime/Config/IConfigParser.cs:
using System;
using System.Collections.Generic;
namespace FlowScope.Config
{
public interface IConfigParser
{
string Format { get; }
IReadOnlyList<T> ParseRows<T>(
string fileName,
string text)
where T : class, IConfigRow;
}
}
- Step 3: Create save migration contract
Create My project/Assets/FlowScope/Runtime/Save/ISaveMigration.cs:
namespace FlowScope.Save
{
public interface ISaveMigration
{
string Key { get; }
int FromVersion { get; }
int ToVersion { get; }
string Migrate(string json);
}
}
- Step 4: Create UI navigation and preload contracts
Create My project/Assets/FlowScope/Runtime/UI/IUIScreenNavigator.cs:
using System.Threading;
using System.Threading.Tasks;
namespace FlowScope.UI
{
public interface IUIScreenNavigator
{
Task<TPanel> PushAsync<TPanel, TViewModel>(
TViewModel viewModel,
CancellationToken cancellationToken)
where TPanel : UIPanelBase<TViewModel>;
bool CanGoBack { get; }
void GoBack();
void Clear();
}
}
Create My project/Assets/FlowScope/Runtime/UI/IUIPreloadService.cs:
using System.Threading;
using System.Threading.Tasks;
namespace FlowScope.UI
{
public interface IUIPreloadService
{
Task PreloadAsync<TPanel>(CancellationToken cancellationToken);
bool IsPreloaded<TPanel>();
void Release<TPanel>();
void ReleaseAll();
}
}
- Step 5: Write P1 requirement summary
Create docs/requirements/p1-production-hardening.md with these sections:
# FlowScope Game Core P1 生产化需求
## 目标
P1 在 P0 核心接口稳定后推进,目标是补齐第一批生产化扩展点,而不是扩张成完整框架生态。
## 本轮范围
- `IResourceBackend` 与 Addressables 独立适配。
- `IConfigSource` / `IConfigParser` 配置扩展点。
- `ISaveMigration` 存档迁移扩展点。
- `IUIScreenNavigator` 与 Panel 预加载。
- MainMenuP0 样例验收升级。
- 使用规范文档。
## 不进入本轮
- Luban 完整接入。
- YooAsset / AssetBundle 后端实现。
- 云存档。
- AudioMixer / 3D 音频。
- Package 分发。
- Step 6: Compile contracts
Run:
dotnet build "My project\FlowScope.Runtime.csproj" --no-restore
Expected: 0 个错误. Existing Unity package reference warnings are acceptable.
- Step 7: Commit contracts
git add "My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs" `
"My project/Assets/FlowScope/Runtime/Config/IConfigSource.cs" `
"My project/Assets/FlowScope/Runtime/Config/IConfigParser.cs" `
"My project/Assets/FlowScope/Runtime/Save/ISaveMigration.cs" `
"My project/Assets/FlowScope/Runtime/UI/IUIScreenNavigator.cs" `
"My project/Assets/FlowScope/Runtime/UI/IUIPreloadService.cs" `
"docs/requirements/p1-production-hardening.md" `
"docs/requirements/p0-requirements-set.md"
git commit -m "定义 Game Core P1 生产化契约"
Task A: Resource Backend Split
Worktree: p1-resource-backend
Depends on: Task 0
Goal: 让核心资源服务只依赖 IResourceBackend,将 Addressables 具体依赖放进独立 asmdef,降低 Runtime 核心升级风险。
Owned files:
- Delete:
My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs - Create:
My project/Assets/FlowScope/Runtime/Resources/ResourceService.cs - Create:
My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef - Create:
My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs - Create:
My project/Assets/FlowScope/Addressables/AddressablesResourceService.cs - Create:
My project/Assets/FlowScope/Tests/EditMode/Resources/ResourceServiceBackendTests.cs - Create:
My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs - Modify:
My project/Assets/FlowScope/Tests/PlayMode/FlowScope.Tests.PlayMode.asmdef
Do not modify:
-
Config, Save, UI, Audio, Flow runtime files.
-
MainMenu sample files.
-
Step 1: Write backend service edit mode tests
Create My project/Assets/FlowScope/Tests/EditMode/Resources/ResourceServiceBackendTests.cs with tests for:
[Test]
public void LoadAsync_WhenSameKeyLoadsConcurrently_UsesOneBackendLoad()
[Test]
public void LoadAsync_WhenCanceled_DoesNotCreateHandle()
[Test]
public void DisposeLastHandle_ReleasesBackendAsset()
[Test]
public void CreateGroup_DisposesAllHandles()
Use a fake backend:
private sealed class FakeResourceBackend : IResourceBackend
{
public string Name => "Fake";
public int LoadCalls { get; private set; }
public int ReleaseCalls { get; private set; }
public TaskCompletionSource<object> Gate { get; } = new();
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
where T : class
{
LoadCalls++;
return (T)await Gate.Task;
}
public void Release<T>(string key, T asset)
where T : class
{
ReleaseCalls++;
}
}
- Step 2: Implement backend-based
ResourceService
Create ResourceService by moving current reference counting, shared load, group creation, and completed-load behavior from AddressablesResourceService into:
public sealed class ResourceService : IResourceService, ICompletedResourceService
{
public ResourceService(IResourceBackend backend)
{
_backend = backend ?? throw new ArgumentNullException(nameof(backend));
}
}
Keep P0 semantics:
-
Same key + type concurrent load shares backend load.
-
Each caller receives an independent handle.
-
Last handle disposal calls backend release.
-
Canceled waiter does not prevent other waiters from completing.
-
If all waiters cancel before backend completes, release completed asset when backend eventually resolves.
-
ResourceServicemust callIResourceBackend.LoadAsync<T>directly. Do not use reflection to bridgeTypeto generic calls. -
Step 3: Move
AddressablesResourceServicecompatibility wrapper to adapter assembly
Delete or empty the old Runtime implementation file:
My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs
Create a compatibility wrapper in the Addressables adapter assembly:
My project/Assets/FlowScope/Addressables/AddressablesResourceService.cs
Use namespace FlowScope.Resources to preserve existing call sites:
using System.Threading;
using System.Threading.Tasks;
namespace FlowScope.Resources
{
public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService
{
private readonly ResourceService _inner;
public AddressablesResourceService()
: this(new AddressablesResourceBackend())
{
}
public AddressablesResourceService(IResourceBackend backend)
{
_inner = new ResourceService(backend);
}
public Task<IResourceHandle<T>> LoadAsync<T>(string key, CancellationToken cancellationToken)
where T : class => _inner.LoadAsync<T>(key, cancellationToken);
public IResourceGroup CreateGroup() => _inner.CreateGroup();
public bool TryLoadCompleted<T>(string key, out IResourceHandle<T> handle)
where T : class => _inner.TryLoadCompleted(key, out handle);
}
}
Do not keep an Addressables reflection backend in FlowScope.Runtime.
- Step 4: Create Addressables asmdef
Create My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef:
{
"name": "FlowScope.Addressables",
"rootNamespace": "FlowScope.Addressables",
"references": [
"FlowScope.Runtime",
"Unity.Addressables"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
- Step 5: Implement typed Addressables backend
Create AddressablesResourceBackend using direct typed Addressables API references. This backend must not use reflection:
using System.Threading;
using System.Threading.Tasks;
using FlowScope.Resources;
using UnityEngine.AddressableAssets;
namespace FlowScope.Addressables
{
public sealed class AddressablesResourceBackend : IResourceBackend
{
public string Name => "Addressables";
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
where T : class
{
var handle = Addressables.LoadAssetAsync<T>(key);
await WaitWithCancellation(handle.Task, cancellationToken);
return handle.Result;
}
public void Release<T>(string key, T asset)
where T : class
{
Addressables.Release(asset);
}
}
}
Add WaitWithCancellation helper matching P0 cancellation semantics.
- Step 6: Run resource tests
Run:
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
Expected: both builds pass with 0 个错误.
- Step 7: Commit
git add "My project/Assets/FlowScope/Runtime/Resources" `
"My project/Assets/FlowScope/Addressables" `
"My project/Assets/FlowScope/Tests/EditMode/Resources" `
"My project/Assets/FlowScope/Tests/PlayMode/Resources" `
"My project/Assets/FlowScope/Tests/PlayMode/FlowScope.Tests.PlayMode.asmdef"
git commit -m "拆分 P1 资源后端适配层"
Task B: Config Source, Parser, and Save Migration
Worktree: p1-config-save-extensions
Depends on: Task 0
Goal: 给配置和存档补扩展点,但不接入 Luban、云存档或复杂对象图。
Owned files:
- Modify:
My project/Assets/FlowScope/Runtime/Config/JsonConfigProvider.cs - Create:
My project/Assets/FlowScope/Runtime/Config/JsonConfigParser.cs - Create:
My project/Assets/FlowScope/Runtime/Config/InMemoryConfigSource.cs - Create:
My project/Assets/FlowScope/Runtime/Config/FileConfigSource.cs - Modify:
My project/Assets/FlowScope/Runtime/Save/JsonSaveSerializer.cs - Create:
My project/Assets/FlowScope/Runtime/Save/SaveEnvelope.cs - Create:
My project/Assets/FlowScope/Runtime/Save/SaveMigrationRegistry.cs - Test:
My project/Assets/FlowScope/Tests/EditMode/Config/ConfigExtensionTests.cs - Test:
My project/Assets/FlowScope/Tests/EditMode/Save/SaveMigrationTests.cs
Do not modify:
-
Container, Resource, UI, Audio, Flow implementation files.
-
Existing public
ISaveServicesignature. -
Step 1: Write config extension tests
Create tests:
[Test]
public void LoadAllAsync_UsesConfiguredSourceAndParser()
[Test]
public void LoadAllAsync_WhenSourceFails_IncludesSourceAndFileName()
[Test]
public void LoadAllAsync_WhenParserFindsDuplicateId_IncludesFileName()
Expected behavior:
-
JsonConfigProvider.Mapping<T>("player.json", source, parser)loads from source by file name. -
Parser returns rows and provider indexes by
Id. -
Duplicate ids still throw.
-
Step 2: Implement
JsonConfigParser
Create parser that wraps existing ReactivePropertyJsonConverter.DeserializeArray<T>:
public sealed class JsonConfigParser : IConfigParser
{
public string Format => "json";
public IReadOnlyList<T> ParseRows<T>(string fileName, string text)
where T : class, IConfigRow
{
return ReactivePropertyJsonConverter.DeserializeArray<T>(text);
}
}
- Step 3: Implement config sources
Create InMemoryConfigSource for tests and simple samples:
public sealed class InMemoryConfigSource : IConfigSource
{
private readonly Dictionary<string, string> _files;
public string Name { get; }
public Task<string> LoadTextAsync(string fileName, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_files.TryGetValue(fileName, out var text))
{
throw new InvalidOperationException($"Config file is missing: {fileName}");
}
return Task.FromResult(text);
}
}
Create FileConfigSource using File.ReadAllTextAsync when available; otherwise use Task.Run(() => File.ReadAllText(path), cancellationToken).
- Step 4: Extend
JsonConfigProvider.ConfigMappingwithout breaking P0 API
Keep current API:
JsonConfigProvider.Mapping<T>(string fileName, Func<CancellationToken, Task<string>> loadTextAsync)
Add overload:
JsonConfigProvider.Mapping<T>(
string fileName,
IConfigSource source,
IConfigParser parser)
where T : class, IConfigRow
The old overload should internally wrap loadTextAsync as an IConfigSource and use JsonConfigParser.
- Step 5: Write save migration tests
Create tests:
[Test]
public void Deserialize_WhenEnvelopeVersionIsOld_AppliesMigrationChain()
[Test]
public void Deserialize_WhenMigrationIsMissing_ThrowsWithKeyAndVersion()
[Test]
public void Serialize_WritesEnvelopeWithCurrentVersion()
Use a fake data class:
private sealed class PlayerSaveV2
{
public R3.ReactiveProperty<int> Gold { get; set; } = new();
}
- Step 6: Implement save envelope and migration registry
Create SaveEnvelope:
internal sealed class SaveEnvelope
{
public int Version { get; set; }
public string Payload { get; set; }
}
Create SaveMigrationRegistry:
public sealed class SaveMigrationRegistry
{
public void Register(ISaveMigration migration);
public string Migrate(
string key,
int fromVersion,
int currentVersion,
string json);
}
Migration chain must apply fromVersion -> fromVersion + 1 until currentVersion. Missing step throws InvalidOperationException containing key and version.
- Step 7: Extend
JsonSaveSerializer
Add constructor:
public JsonSaveSerializer(
int currentVersion = 1,
SaveMigrationRegistry migrations = null)
Behavior:
-
Existing raw JSON saves remain readable as version
1. -
New saves are wrapped in envelope when
currentVersion > 1. -
Migration applies before
PopulateObject. -
Step 8: Run config/save tests
Run:
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
Expected: 0 个错误.
- Step 9: Commit
git add "My project/Assets/FlowScope/Runtime/Config" `
"My project/Assets/FlowScope/Runtime/Save" `
"My project/Assets/FlowScope/Tests/EditMode/Config" `
"My project/Assets/FlowScope/Tests/EditMode/Save"
git commit -m "扩展 P1 配置来源和存档迁移"
Task C: UI Navigation and Panel Preload
Worktree: p1-ui-navigation-preload
Depends on: Task 0
Goal: 在不破坏 P0 UIManager 层内 LIFO 的前提下,提供轻量页面返回策略和 Panel 预加载。
Owned files:
- Modify:
My project/Assets/FlowScope/Runtime/UI/UIManager.cs - Create:
My project/Assets/FlowScope/Runtime/UI/UIPreloadService.cs - Create:
My project/Assets/FlowScope/Runtime/UI/UIScreenNavigator.cs - Test:
My project/Assets/FlowScope/Tests/PlayMode/UI/UIPreloadServiceTests.cs - Test:
My project/Assets/FlowScope/Tests/PlayMode/UI/UIScreenNavigatorTests.cs - Modify:
My project/Assets/FlowScope/Tests/PlayMode/UI/UIManagerTests.cs
Do not modify:
-
Resource service internals.
-
MainMenu sample except in Task D.
-
UI panel base contract unless Task 0 contract is insufficient and integration owner approves.
-
Step 1: Write preload tests
Create tests:
[UnityTest]
public IEnumerator PreloadAsync_LoadsPanelResourceAndKeepsHandle()
[UnityTest]
public IEnumerator OpenAsync_WhenPanelIsPreloaded_ReusesPreloadedHandle()
[UnityTest]
public IEnumerator Release_DisposesPreloadedHandle()
Fake resource service must count load and dispose calls.
- Step 2: Implement
UIPreloadService
Behavior:
-
Resolve panel path using same rules as
UIManager. -
Load
GameObjecthandle and store by panel type. -
Repeated preload for same panel is no-op.
-
Release<TPanel>disposes that handle. -
Disposereleases all preloaded handles. -
Step 3: Add optional preload lookup to
UIManager
Add constructor overload:
public UIManager(IResourceService resources, IUIPreloadService preloadService = null)
Open behavior:
-
If preload service has a handle for the requested panel, create an additional handle only if the resource service contract requires independent handle ownership.
-
If preloaded handle can be safely reused, keep preload handle owned by preload service and let panel record use a lightweight retained handle.
-
Tests must prove closing a preloaded Destroy panel does not dispose the preload cache.
-
Step 4: Write navigator tests
Create tests:
[UnityTest]
public IEnumerator PushAsync_OpensPanelAndRecordsHistory()
[UnityTest]
public IEnumerator GoBack_ClosesTopPanel()
[UnityTest]
public IEnumerator Clear_ClosesAllNavigatorPanels()
[UnityTest]
public IEnumerator GoBack_WhenOnlyOnePanelOpen_DoesNothing()
- Step 5: Implement
UIScreenNavigator
Create UIScreenNavigator:
public sealed class UIScreenNavigator : IUIScreenNavigator
{
private readonly UIManager _uiManager;
private readonly Stack<Action> _closeStack = new();
}
Rules:
-
PushAsync<TPanel, TViewModel>callsUIManager.OpenAsync. -
It pushes a close action that calls
UIManager.Close<TPanel>(). -
CanGoBackis true when close stack count is greater than 1. -
GoBackcloses only top screen ifCanGoBackis true. -
Clearcloses all panels opened by navigator. -
It does not close panels opened outside navigator.
-
Step 6: Run UI play mode build
Run:
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
Expected: 0 个错误.
- Step 7: Commit
git add "My project/Assets/FlowScope/Runtime/UI" `
"My project/Assets/FlowScope/Tests/PlayMode/UI"
git commit -m "扩展 P1 UI 导航和预加载"
Task D: MainMenuP0 Sample and Usage Docs
Worktree: p1-sample-docs
Depends on: Task A, Task B, Task C
Goal: 让样例成为 P1 规则的可见验收样板,并补清楚框架使用约定。
Owned files:
- Modify:
My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs - Modify:
My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs - Modify:
My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuPanel.cs - Modify:
My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity - Modify:
My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab - Modify:
My project/Assets/FlowScope/Tests/PlayMode/Samples/MainMenuP0Tests.cs - Create:
docs/guides/flowscope-runtime-usage.md - Create:
docs/guides/main-menu-p1-sample.md
Do not modify:
-
Core Resource, Config, Save, UI implementations except through public APIs.
-
Test assembly asmdefs unless a new adapter assembly reference is required.
-
Step 1: Write upgraded sample play mode tests
Update MainMenuP0Tests.cs to include:
[UnityTest]
public IEnumerator Bootstrap_StartsAndOpensVisibleMainMenuPanel()
[UnityTest]
public IEnumerator ClickIncrement_UpdatesGoldTextImmediately()
[UnityTest]
public IEnumerator ShutdownAndRestart_RestoresSavedGold()
[UnityTest]
public IEnumerator Shutdown_ReleasesFeatureResourcesAndSubscriptions()
Tests may instantiate GameBootstrap in an inactive root and call StartupAsync manually to avoid Awake async void races.
- Step 2: Update
GameBootstrapregistration to use P1 extension points
Use:
IConfigSource/JsonConfigParserfor config.JsonSaveSerializer(currentVersion: 1, migrations: registry)for save.ResourceServicewith sample backend or Addressables backend depending on Task A outcome.UIPreloadServiceandUIScreenNavigatorif sample flow uses navigation.
Keep no-console-error behavior.
- Step 3: Make sample UI visibly meaningful
Ensure scene/prefab contains:
- A Canvas.
- A configured
MainMenuPanel. - A visible Gold text.
- A visible increment button.
- Serialized references wired in prefab or scene where appropriate.
Runtime fallback creation may remain, but manual sample validation should use real scene/prefab references.
- Step 4: Write runtime usage guide
Create docs/guides/flowscope-runtime-usage.md with sections:
# FlowScope Runtime 使用规范
## Bootstrap
## Container 注册规则
## Feature 生命周期
## 资源所有权
## UI 路径和层级
## 配置 DTO 约束
## 存档版本迁移
## Addressables 后端接入
## 测试建议
- Step 5: Write sample guide
Create docs/guides/main-menu-p1-sample.md with:
-
How to open the scene.
-
What the user should see.
-
How Gold increment works.
-
How save/restore is validated.
-
What Console output is acceptable.
-
Step 6: Run sample tests
Run:
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
Then in Unity Test Runner run:
- PlayMode:
FlowScope.Tests.PlayMode.Samples.MainMenuP0Tests - PlayMode full suite
Expected: all green, no unexpected Console Error.
- Step 7: Manual validation
Open:
My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity
Verify:
-
UI opens.
-
Gold increments on click.
-
UI updates immediately.
-
Exit saves data.
-
Restart restores data.
-
Closing Feature releases resources and subscriptions.
-
Step 8: Commit
git add "My project/Assets/FlowScope/Samples/MainMenuP0" `
"My project/Assets/FlowScope/Tests/PlayMode/Samples" `
"docs/guides/flowscope-runtime-usage.md" `
"docs/guides/main-menu-p1-sample.md"
git commit -m "升级 P1 主菜单样例和使用规范"
Task E: Final P1 Integration
Worktree: p1-integration
Depends on: Task A, Task B, Task C, Task D
Goal: 合并全部 P1 任务,修正模块边界缝隙,完成全量验证与最终文档。
Owned files:
- Modify only files needed to resolve integration seams.
- Modify:
docs/requirements/p1-production-hardening.md - Modify:
docs/reviews/flowscope-framework-review.md - Create:
docs/reviews/p1-completion-report.md
Do not modify:
-
Add new P1 feature scope beyond this plan.
-
Introduce P2 package/editor tooling.
-
Revert P0 behavior unless tests prove a bug and doc is updated.
-
Step 1: Merge P1 branches in order
Recommended order:
p1-contracts
p1-resource-backend
p1-config-save-extensions
p1-ui-navigation-preload
p1-sample-docs
- Step 2: Run compile validation
Run:
dotnet build "My project\FlowScope.Runtime.csproj" --no-restore
dotnet build "My project\FlowScope.Tests.EditMode.csproj" --no-restore
dotnet build "My project\FlowScope.Tests.PlayMode.csproj" --no-restore
Expected: all pass with 0 个错误.
- Step 3: Run Unity Test Runner
Run in Unity:
- EditMode full suite.
- PlayMode full suite.
Expected:
-
EditMode all green.
-
PlayMode all green.
-
No unexpected Error logs.
-
Step 4: Manual sample validation
Repeat Task D manual validation after all branches are merged.
- Step 5: Update review and completion docs
Create docs/reviews/p1-completion-report.md:
# FlowScope Game Core P1 完成报告
## 范围
## 已完成
## 测试结果
## 人工验收
## 未进入本轮
## P2 建议
Update docs/reviews/flowscope-framework-review.md risk table:
-
Mark Addressables reflection risk as resolved or reduced.
-
Mark Config/Save extension risk as reduced.
-
Keep package/editor tooling as P2.
-
Step 6: Final status check
Run:
git -c core.quotepath=false status --short
git diff --check
Expected:
-
Only intended files changed before final commit.
-
git diff --checkhas no whitespace errors. -
Step 7: Commit integration
git add "My project/Assets/FlowScope" docs
git commit -m "完成 Game Core P1 生产化集成"
验收标准总表
| Area | Acceptance |
|---|---|
| Resource | FlowScope.Runtime 不再包含 Addressables 反射 backend;核心资源服务通过 IResourceBackend 工作;Addressables 适配在独立 asmdef 中。 |
| Config | JsonConfigProvider 支持 source + parser;旧 mapping API 继续可用;重复 id、缺文件、格式错误仍有明确异常。 |
| Save | JsonSaveSerializer 支持版本 envelope 和 migration chain;旧 raw JSON 存档可读。 |
| UI | Navigator 可 push/back/clear;预加载可加载、复用、释放 panel 资源;P0 UIManager 行为不回退。 |
| Sample | MainMenuP0 场景可见、可点、可保存恢复;测试覆盖 startup/click/shutdown/restart。 |
| Docs | 有 P1 使用规范、样例说明、完成报告;旧评审风险状态同步更新。 |
| Tests | EditMode 全绿;PlayMode 全绿;人工样例验收通过;无未预期 Console Error。 |
自检清单
- 每个任务都有明确 Owned files。
- 每个任务都有 Do not modify 边界。
- 每个任务都有测试和提交步骤。
- P1 范围没有包含 P2/P3 能力。
- P0 现有公开接口不被无故破坏。
- 所有 commit message 使用中文。
- 执行时使用新对话或新 worktree,避免 P0 调试上下文污染 P1 实现。