Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ jobs:
- name: Validate license headers
run: python3 scripts/license-header.py --check

- name: Validate runtime-generator boundaries
run: python3 scripts/validate-runtime-generator-boundaries.py

# 缓存MegaLinter
- name: Cache MegaLinter
uses: actions/cache@v5
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ jobs:

diff -u expected-packages.txt actual-packages.txt

- name: Validate runtime-generator package boundaries
run: python3 scripts/validate-runtime-generator-boundaries.py --package-dir ./packages

- name: Show packages
run: ls -la ./packages || true

Expand Down
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ All generated or modified code MUST include clear and meaningful comments where
- Private fields: `_camelCase`
- Keep abstractions projects free of implementation details and engine-specific dependencies.
- Preserve existing module boundaries. Do not introduce new cross-module dependencies without clear architectural need.
- Framework runtime, abstractions, and meta-package projects MUST NOT reference `*.SourceGenerators*` projects or packages,
and MUST NOT use source-generator attributes such as `GenerateEnumExtensions` or `ContextAware`. Those capabilities are
reserved for consumer projects, generator projects, examples explicitly meant to demonstrate generator usage, and related tests.

### Formatting

Expand Down
3 changes: 0 additions & 3 deletions GFramework.Game/Config/YamlConfigSchemaPropertyType.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

using GFramework.Core.SourceGenerators.Abstractions.Enums;

namespace GFramework.Game.Config;

/// <summary>
/// 表示当前运行时 schema 校验器支持的属性类型。
/// </summary>
[GenerateEnumExtensions]
internal enum YamlConfigSchemaPropertyType
{
/// <summary>
Expand Down
3 changes: 0 additions & 3 deletions GFramework.Game/Config/YamlConfigStringFormatKind.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0

using GFramework.Core.SourceGenerators.Abstractions.Enums;

namespace GFramework.Game.Config;

/// <summary>
/// 表示当前 Runtime / Generator / Tooling 共享支持的字符串 format 子集。
/// </summary>
[GenerateEnumExtensions]
internal enum YamlConfigStringFormatKind
{
/// <summary>
Expand Down
1 change: 0 additions & 1 deletion GFramework.Game/GFramework.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj" />
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
</ItemGroup>
Expand Down
7 changes: 7 additions & 0 deletions ai-plan/public/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ help the current worktree land on the right recovery documents without scanning
- Purpose: migrate release version calculation from fixed patch bumps to semantic-release while keeping the existing tag-driven NuGet publish flow.
- Tracking: `ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md`
- Trace: `ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md`
- `runtime-generator-boundary`
- Purpose: keep runtime and abstractions packages isolated from source-generator dependencies, packaging leaks, and attribute usage.
- Tracking: `ai-plan/public/runtime-generator-boundary/todos/runtime-generator-boundary-tracking.md`
- Trace: `ai-plan/public/runtime-generator-boundary/traces/runtime-generator-boundary-trace.md`

## Worktree To Active Topic Map

Expand All @@ -68,6 +72,9 @@ help the current worktree land on the right recovery documents without scanning
- Branch: `fix/release-notes-pr-links`
- Worktree hint: `GFramework`
- Priority 1: `semantic-release-versioning`
- Branch: `fix/runtime-generator-boundary`
- Worktree hint: `GFramework`
- Priority 1: `runtime-generator-boundary`
- Branch: `docs/sdk-update-documentation`
- Worktree hint: `GFramework-update-documentation`
- Priority 1: `documentation-full-coverage-governance`
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Runtime / Generator Boundary Tracking

## Goal

Keep runtime, abstractions, and meta-package modules free from source-generator project references, source-generator
attributes, and leaked NuGet dependencies.

## Current Recovery Point

- Recovery point: RGB-RP-001
- Phase: remove `GFramework.Game` generator coupling and add repository guardrails
- Focus:
- delete `GFramework.Game`'s dependency on `GFramework.Core.SourceGenerators.Abstractions`
- remove unused `[GenerateEnumExtensions]` usage from `GFramework.Game`
- add static and packed-package validation so runtime packages cannot regress

## Active Risks

- A runtime package can still compile locally if it references a non-packable generator helper project, so regressions are
easy to miss without an explicit guard.
- A leaked package dependency may only surface when a consumer restores from NuGet, not during normal repository builds.

## Completed In This Stage

- Confirmed `GFramework.Game` was the direct runtime offender and `GeWuYou.GFramework.Game` leaked
`GFramework.Core.SourceGenerators.Abstractions` into its nuspec dependency graph.
- Confirmed the two `[GenerateEnumExtensions]` usages inside `GFramework.Game` do not need generated output and can be
removed outright.
- Verified current PR review findings locally: the validator regex still missed standalone attributes, while the
`docs/zh-CN/contributing.md` generator-boundary text should be removed instead of repositioned because it is
maintainer-facing governance rather than reader-facing contribution guidance.
- Added a Python regression test for standalone, parameterized, fully qualified, and multi-attribute declarations so
future validator edits cannot silently reintroduce the false negative.
- Added comment-line filtering for the validator after the first regex fix started matching XML documentation examples
such as `/// [ContextAware]`, which would otherwise create false CI failures for reader-facing code comments.

## Validation Target

- `python3 scripts/validate-runtime-generator-boundaries.py`
- `python3 scripts/test_validate_runtime_generator_boundaries.py`
- `python3 scripts/license-header.py --check`
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
- `dotnet build GFramework.Godot/GFramework.Godot.csproj -c Release`
- `dotnet pack GFramework.sln -c Release -p:PackageVersion=<local>`

## Latest Validation Result

- `python3 scripts/test_validate_runtime_generator_boundaries.py` passed on 2026-05-05.
- `python3 scripts/validate-runtime-generator-boundaries.py` passed on 2026-05-05.
- `python3 scripts/license-header.py --check` passed on 2026-05-05.
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` passed on 2026-05-05 with 0 warnings and 0 errors.

## Next Recommended Resume Step

Run the boundary validator, the new Python regression tests, and the minimal Release build/pack validation; then push
the follow-up commit so the open PR review threads can be resolved against fresh CI.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Runtime / Generator Boundary Trace

## 2026-05-05

### RGB-RP-001 Runtime package boundary repair

- Trigger:
- external consumers restoring `GeWuYou.GFramework.Game` failed because NuGet looked for
`GFramework.Core.SourceGenerators.Abstractions`
- repository inspection showed `GFramework.Game` had a direct project reference to a non-packable generator
abstractions project and used `[GenerateEnumExtensions]`
- Decisions:
- treat the issue as a runtime/generator boundary violation, not as a missing publish target
- remove the runtime-side attribute usage instead of turning generator abstractions into public runtime packages
- add repository guardrails at both source-validation time and packed-package validation time
- Expected implementation:
- `GFramework.Game` removes the generator abstractions project reference
- `GFramework.Game` removes the two unused enum generator attributes
- CI and publish workflows run a dedicated boundary validator script
- PR review follow-up:
- verified CodeRabbit and Greptile findings against local source before acting on them
- accepted the validator regex finding because the original pattern missed standalone
`[GenerateEnumExtensions]` declarations in runtime code
- added comment-line filtering after the first regex repair surfaced false positives from XML documentation examples
such as `/// [ContextAware]`
- rejected the documentation reposition suggestion as stated and removed the
`代码生成器边界` block from `docs/zh-CN/contributing.md` because it documents internal governance rather than
reader-facing contributor guidance
- added a Python regression test covering standalone, parameterized, fully qualified, and multi-attribute matches
- Validation milestone:
- `python3 scripts/test_validate_runtime_generator_boundaries.py` passed
- `python3 scripts/validate-runtime-generator-boundaries.py` passed
- `python3 scripts/license-header.py --check` passed
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` passed with 0 warnings and 0 errors
- Immediate next step:
- push the PR follow-up commit and resolve the remaining review threads
63 changes: 63 additions & 0 deletions scripts/test_validate_runtime_generator_boundaries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0

"""Regression tests for runtime/source-generator boundary validation."""

from __future__ import annotations

import importlib.util
import sys
import unittest
from pathlib import Path


MODULE_PATH = Path(__file__).resolve().parent / "validate-runtime-generator-boundaries.py"
MODULE_SPEC = importlib.util.spec_from_file_location("validate_runtime_generator_boundaries", MODULE_PATH)
if MODULE_SPEC is None or MODULE_SPEC.loader is None:
raise RuntimeError(f"Unable to load module spec from {MODULE_PATH}")

validate_runtime_generator_boundaries = importlib.util.module_from_spec(MODULE_SPEC)
sys.modules[MODULE_SPEC.name] = validate_runtime_generator_boundaries
MODULE_SPEC.loader.exec_module(validate_runtime_generator_boundaries)


class ValidateRuntimeGeneratorBoundariesTests(unittest.TestCase):
"""Covers attribute matching edge cases that previously caused false negatives."""

def setUp(self) -> None:
self.patterns = validate_runtime_generator_boundaries.compile_attribute_patterns()

def test_matches_standalone_attribute(self) -> None:
pattern = self.patterns["GenerateEnumExtensions"]

self.assertIsNotNone(pattern.search("[GenerateEnumExtensions]"))

def test_matches_parameterized_attribute(self) -> None:
pattern = self.patterns["GenerateEnumExtensions"]

self.assertIsNotNone(pattern.search("[GenerateEnumExtensions(typeof(string))]"))

def test_matches_non_leading_attribute_in_attribute_list(self) -> None:
pattern = self.patterns["GenerateEnumExtensions"]

self.assertIsNotNone(pattern.search("[Serializable, GenerateEnumExtensions]"))

def test_matches_fully_qualified_attribute(self) -> None:
pattern = self.patterns["Priority"]

self.assertIsNotNone(
pattern.search("[global::GFramework.Core.SourceGenerators.Abstractions.Bases.PriorityAttribute(10)]")
)

def test_ignores_xml_doc_example_attribute(self) -> None:
text = "/// [ContextAware]\npublic interface IController;\n"
pattern = self.patterns["ContextAware"]
match = pattern.search(text)

self.assertIsNotNone(match)
self.assertTrue(validate_runtime_generator_boundaries.is_comment_attribute_match(text, match.start()))


if __name__ == "__main__":
unittest.main()
Loading
Loading