Files
FlowScope/docs/superpowers/plans/2026-05-20-p1-production-hardening-plan.md
2026-05-20 15:28:27 +08:00

30 KiB
Raw Permalink Blame History

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

  1. 资源生产化第一步:引入泛型 IResourceBackend,将 Addressables 适配从 FlowScope.Runtime 拆到 FlowScope.Addressables,并移除 Addressables 反射调用。
  2. 配置扩展点:引入 IConfigSourceIConfigParser,让 JSON provider 从“硬编码 JSON 文本映射”变成“source + parser”的组合。
  3. 存档迁移扩展点:引入 ISaveMigration 与带版本包裹的 JSON serializer支持单 key 数据从旧版本迁移到当前版本。
  4. UI 生产化第一步:引入轻量 IUIScreenNavigator 与 panel 预加载服务,仍保持 UIManager 的层内 LIFO 规则。
  5. MainMenuP0 升级为 P1 验收样例:验证 UI 打开、Gold 点击递增、保存恢复、资源/订阅释放。
  6. 使用规范文档Bootstrap、Feature 生命周期、资源释放、UI 路径、配置 DTO、存档迁移、资源后端接入。

本轮 P1 不包含

  • Luban 完整接入。
  • YooAsset / AssetBundle 后端实现。
  • 云存档、加密、压缩、冲突解决。
  • 完整 UI 路由系统、URL 式导航、跨 Feature 页面恢复。
  • AudioMixer、3D 音频、动态音乐。
  • Unity Package 分发结构、Samples~、编辑器导入向导。
  • Source Generator 深度优化。

推荐工作流

  1. 从当前 main 创建 p1-contracts worktree/branch。
  2. Task 0 完成并合并后,再并行执行 Task A、Task B、Task C。
  3. Task D 依赖 A/B/C 合并后执行。
  4. Task E 做最终集成、文档同步和验收。
  5. 所有 plan、汇报、commit message 使用中文。

推荐 worktrees

Worktree Purpose
p1-contracts P1 小接口、asmdef 边界、文档边界
p1-resource-backend IResourceBackend 与 Addressables 独立适配
p1-config-save-extensions IConfigSourceIConfigParserISaveMigration
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 IResourceBackend contract

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.

  • ResourceService must call IResourceBackend.LoadAsync<T> directly. Do not use reflection to bridge Type to generic calls.

  • Step 3: Move AddressablesResourceService compatibility 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 ISaveService signature.

  • 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.ConfigMapping without 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 GameObject handle and store by panel type.

  • Repeated preload for same panel is no-op.

  • Release<TPanel> disposes that handle.

  • Dispose releases 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> calls UIManager.OpenAsync.

  • It pushes a close action that calls UIManager.Close<TPanel>().

  • CanGoBack is true when close stack count is greater than 1.

  • GoBack closes only top screen if CanGoBack is true.

  • Clear closes 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 GameBootstrap registration to use P1 extension points

Use:

  • IConfigSource / JsonConfigParser for config.
  • JsonSaveSerializer(currentVersion: 1, migrations: registry) for save.
  • ResourceService with sample backend or Addressables backend depending on Task A outcome.
  • UIPreloadService and UIScreenNavigator if 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 --check has 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 实现。