Skip to content

Commit 58bb1f6

Browse files
authored
Fix Static Reset (#164)
1 parent 99d2939 commit 58bb1f6

16 files changed

Lines changed: 578 additions & 128 deletions

.github/workflows/markdown-link-validity.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,12 @@ jobs:
2929
uses: lycheeverse/lychee-action@v2
3030
with:
3131
# Include fragments ensures anchors are validated
32+
# Use conservative concurrency (4) to avoid GitHub rate limiting
3233
args: >-
3334
-c .lychee.toml
3435
--no-progress
3536
--include-fragments
3637
--verbose
37-
--max-concurrency 12
38-
--timeout 20
39-
--retry-wait-time 2
40-
--max-retries 2
4138
"./**/*.md"
4239
env:
4340
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.llm/context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ Before acting on PR review feedback:
250250

251251
- Frameworks: NUnit + Unity Test Framework. Use `[Test]`/`[UnityTest]` as needed.
252252
- Location: add files under [Tests/Runtime](../Tests/Runtime/) `<Area>/` named `*Tests.cs` with classes `*Tests`.
253-
- Keep tests independent: prefer a local `MessageBus` and explicit `MessageRegistrationToken` lifecycles.
253+
- Keep tests independent: prefer a local `MessageBus` and explicit `MessageRegistrationToken` lifecycles. When using `MessageHandler`, set `active = true`. When using `MessageRegistrationToken`, pass the local `MessageBus` to `Create()` and call `Enable()` to activate handlers.
254254
- Do not use underscores in test function names.
255255
- Prefer expressive assertions and failure messages so it is clear what exactly is failing when a test fails.
256256
- Do not use regions.

.llm/skills/documentation/link-quality-guidelines.md

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
title: "Link Quality and External URL Management"
33
id: "link-quality-guidelines"
44
category: "documentation"
5-
version: "1.2.0"
5+
version: "1.4.0"
66
created: "2026-01-22"
7-
updated: "2026-01-23"
7+
updated: "2026-02-09"
88

99
source:
1010
repository: "wallstop/DxMessaging"
@@ -227,6 +227,49 @@ Some domains change URL structures frequently. Extra verification is needed:
227227

228228
For detailed guidance on URL fragment validation (`#section-name` links), see [External URL Fragment Validation](external-url-fragment-validation.md).
229229

230+
### Handling Link Checker False Positives
231+
232+
Automated link checkers (like lychee) can fail on valid URLs when websites block automated requests. This does not mean the link is broken.
233+
234+
#### Common Blocking Status Codes
235+
236+
| Status Code | Meaning | Example Cause |
237+
| ----------- | ---------------------- | ------------------------------------------- |
238+
| 403 | Forbidden | User-agent blocking, geographic restriction |
239+
| 415 | Unsupported Media Type | Server rejects non-browser Accept headers |
240+
| 429 | Too Many Requests | Rate limiting |
241+
| 503 | Service Unavailable | Bot protection, Cloudflare challenge |
242+
243+
#### Investigating Link Checker Failures
244+
245+
When a link checker reports an error:
246+
247+
1. **Manually verify the link**: Open the URL in a browser to confirm it works
248+
1. **Check the status code**: Some codes (403, 415, 429) often indicate bot blocking, not broken links
249+
1. **Try a different user-agent**: The link may work with browser-like headers
250+
1. **Check if the site has bot protection**: Cloudflare, Akamai, or custom protection may block automated clients
251+
252+
#### Adding Exclusions to `.lychee.toml`
253+
254+
When a link is valid but the site blocks automated checkers, add an exclusion pattern:
255+
256+
```toml
257+
exclude = [
258+
# NPM package page serves a Cloudflare JS challenge to non-browser clients
259+
"^https://www\\.npmjs\\.com/package/com\\.wallstop-studios\\.dxmessaging$",
260+
# Game Programming Patterns site returns 415 to automated clients despite valid content
261+
"^https://gameprogrammingpatterns\\.com/"
262+
]
263+
```
264+
265+
#### Exclusion Pattern Guidelines
266+
267+
- **Use regex anchors**: Start patterns with `^` to match from the beginning of the URL
268+
- **Escape special characters**: Dots in domain names need `\\.` escaping
269+
- **Document the reason**: Add a comment explaining why the exclusion exists
270+
- **Be specific**: Prefer specific URL patterns over broad domain exclusions when possible
271+
- **Verify first**: Always manually verify the link is actually valid before adding an exclusion
272+
230273
### GitHub Actions Version Consistency
231274

232275
Workflow files should use consistent action versions across all workflows. For detailed guidance including version update processes and common actions to monitor, see [GitHub Actions Version Consistency](github-actions-version-consistency.md).
@@ -259,9 +302,10 @@ Before committing documentation or skill files:
259302

260303
## Changelog
261304

262-
| Version | Date | Changes |
263-
| ------- | ---------- | --------------------------------------------------------------------------- |
264-
| 1.3.0 | 2026-01-27 | Split fragment validation and GitHub Actions to separate skills |
265-
| 1.2.0 | 2026-01-23 | Added external URL fragment validation section based on CI failure analysis |
266-
| 1.1.0 | 2026-01-22 | Added documentation linting scripts section with testing guidance |
267-
| 1.0.0 | 2026-01-22 | Initial version covering link quality fundamentals |
305+
| Version | Date | Changes |
306+
| ------- | ---------- | ------------------------------------------------------------------------------ |
307+
| 1.4.0 | 2026-02-09 | Added guidance for handling link checker false positives and lychee exclusions |
308+
| 1.3.0 | 2026-01-27 | Split fragment validation and GitHub Actions to separate skills |
309+
| 1.2.0 | 2026-01-23 | Added external URL fragment validation section based on CI failure analysis |
310+
| 1.1.0 | 2026-01-22 | Added documentation linting scripts section with testing guidance |
311+
| 1.0.0 | 2026-01-22 | Initial version covering link quality fundamentals |

.llm/skills/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Skills Index
22

3-
> **Auto-generated** on 2026-02-01. Do not edit manually.
3+
> **Auto-generated** on 2026-02-09. Do not edit manually.
44
> Run `node scripts/generate-skills-index.js` to regenerate.
55
66
---
@@ -42,7 +42,7 @@
4242
| [Documentation Updates and Maintenance](./documentation/documentation-updates.md) | 📝 149 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, code-comments, xml-docs |
4343
| [External URL Fragment Validation](./documentation/external-url-fragment-validation.md) | 📝 182 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, links, urls |
4444
| [GitHub Actions Version Consistency](./documentation/github-actions-version-consistency.md) | ✅ 204 | 🟢 Basic | ✅ Stable | ○○○○○ | github-actions, ci-cd, version-management |
45-
| [Link Quality and External URL Management](./documentation/link-quality-guidelines.md) |268 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, links, urls |
45+
| [Link Quality and External URL Management](./documentation/link-quality-guidelines.md) |312 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, links, urls |
4646
| [Markdown Compatibility Guidelines](./documentation/markdown-compatibility.md) | ⚠️ 477 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, markdown, compatibility |
4747
| [Mermaid Diagram Theming](./documentation/mermaid-theming.md) | ✅ 327 | 🟡 Intermediate | ✅ Stable | ○○○○○ | documentation, mermaid, theming |
4848
| [MkDocs Navigation Management](./documentation/mkdocs-navigation.md) | ✅ 291 | 🟢 Basic | ✅ Stable | ○○○○○ | documentation, mkdocs, navigation |
@@ -149,7 +149,7 @@
149149
| [Test Invalid Skill](./testing/test-invalid-skill.md) | 📝 27 | 🔴 Expert | ✅ Stable | ●●●○○ | |
150150
| [Test Organization and Assertions](./testing/test-coverage-organization-assertions.md) | 📝 172 | 🟢 Basic | ✅ Stable | ○○○○○ | testing, assertions, naming |
151151
| [Test Production Code Directly](./testing/test-production-code.md) | ✅ 349 | 🟡 Intermediate | ✅ Stable | ○○○○○ | testing, anti-patterns, code-quality |
152-
| [Unity Test Considerations and Anti-Patterns](./testing/test-coverage-unity-anti-patterns.md) |233 | 🟢 Basic | ✅ Stable | ○○○○○ | testing, unity, anti-patterns |
152+
| [Unity Test Considerations and Anti-Patterns](./testing/test-coverage-unity-anti-patterns.md) |260 | 🟢 Basic | ✅ Stable | ○○○○○ | testing, unity, anti-patterns |
153153

154154
---
155155

.llm/skills/testing/test-coverage-unity-anti-patterns.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,33 @@ Assert.That(component == null, Is.True, "Component should be null");
146146
Assert.That(gameObject != null, Is.True, "GameObject should exist");
147147
```
148148

149+
### MessageRegistrationToken Lifecycle
150+
151+
When testing with `MessageRegistrationToken`, always:
152+
153+
1. Set `MessageHandler.active = true` when creating the handler
154+
1. Pass the local `MessageBus` to avoid using the global bus
155+
1. Call `Enable()` after creating the token to activate registrations
156+
157+
```csharp
158+
// GOOD: Complete setup with active handler, local bus, and Enable()
159+
MessageBus messageBus = new MessageBus();
160+
MessageHandler handler = new MessageHandler(instanceId, messageBus) { active = true };
161+
MessageRegistrationToken token = MessageRegistrationToken.Create(handler, messageBus);
162+
token.Enable();
163+
164+
// BAD: Missing active = true - handler ignores all messages
165+
MessageHandler handler = new MessageHandler(instanceId, messageBus);
166+
// handler.active is false by default!
167+
168+
// BAD: Missing Enable() - handlers are staged but not activated
169+
MessageRegistrationToken token = MessageRegistrationToken.Create(handler, messageBus);
170+
// Handlers are registered but inactive!
171+
172+
// BAD: Missing messageBus - uses global state, causes test pollution
173+
MessageRegistrationToken token = MessageRegistrationToken.Create(handler);
174+
```
175+
149176
## Anti-Patterns to Avoid
150177

151178
### Don't Use Underscores in Test Names

.lychee.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ retry_wait_time = 2 # seconds between retries
1010
max_redirects = 10
1111
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36"
1212

13-
# Treat successes and rate-limiting as acceptable in CI
14-
# Accept all 2xx as valid plus 429 (rate limited)
15-
accept = ["200..=299", 429]
13+
# Treat successes, rate-limiting, and transient server errors as acceptable in CI
14+
# Accept all 2xx as valid plus 429 (rate limited) and 502 (bad gateway - transient)
15+
accept = ["200..=299", 429, 502]
1616

1717
# Only check web links
1818
scheme = ["https", "http"]
@@ -26,5 +26,7 @@ exclude = [
2626
# NPM package page serves a Cloudflare JS challenge to non-browser clients; registry endpoint remains covered.
2727
"^https://www\\.npmjs\\.com/package/com\\.wallstop-studios\\.dxmessaging$",
2828
# GitHub Pages site - not deployed yet, will be available after first successful deploy
29-
"^https://wallstop\\.github\\.io/DxMessaging"
29+
"^https://wallstop\\.github\\.io/DxMessaging",
30+
# Game Programming Patterns site returns 415 (Unsupported Media Type) to automated clients despite valid content
31+
"^https://gameprogrammingpatterns\\.com/"
3032
]

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Fixed a bug where no messages would get received by any listeners due to specifics in Unity play mode timings
13+
1014
## [2.1.8]
1115

1216
### Fixed

Runtime/Core/DxMessagingStaticState.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
namespace DxMessaging.Core
22
{
33
using System;
4-
using Helper;
54
using MessageBus;
65

76
/// <summary>
87
/// Centralised utility for resetting DxMessaging static state when Domain Reload is disabled.
98
/// </summary>
9+
/// <remarks>
10+
/// <para>
11+
/// This class is designed for Unity's Enter Play Mode Settings with Domain Reload disabled.
12+
/// When Domain Reload is disabled, static fields persist between play mode sessions, which
13+
/// can cause issues if stale state is not cleared.
14+
/// </para>
15+
/// <para>
16+
/// <strong>Important:</strong> Message type sequential IDs (managed by MessageHelperIndexer)
17+
/// are intentionally NOT reset. Once a message type is assigned an ID, it retains that ID
18+
/// for the lifetime of the application domain. This prevents ID collisions that would occur
19+
/// if a new message type were assigned an ID that was previously used by a different type.
20+
/// Resetting IDs could cause messages to be routed to the wrong handlers.
21+
/// </para>
22+
/// </remarks>
1023
public static class DxMessagingStaticState
1124
{
1225
private static readonly object ResetLock = new object();
@@ -20,6 +33,9 @@ static DxMessagingStaticState()
2033
/// <summary>
2134
/// Resets all static variables in DxMessaging to their default values.
2235
/// </summary>
36+
/// <remarks>
37+
/// Message type IDs are NOT reset by this method. See the class remarks for details.
38+
/// </remarks>
2339
public static void Reset()
2440
{
2541
lock (ResetLock)
@@ -31,13 +47,10 @@ public static void Reset()
3147
IMessageBus.GlobalMessageBufferSize = Baseline.GlobalMessageBufferSize;
3248
IMessageBus.GlobalSequentialIndex = Baseline.GlobalSequentialIndex;
3349

34-
MessageHelperIndexer.RestoreState(Baseline.HelperState);
35-
3650
MessageRegistrationHandle.SetIdSeed(Baseline.MessageRegistrationHandleSeed);
3751
MessageRegistrationBuilder.SetSyntheticOwnerCounter(Baseline.SyntheticOwnerCounter);
3852

3953
MessageHandler.ResetStatics();
40-
IMessageBus.GlobalSequentialIndex = Baseline.GlobalSequentialIndex;
4154
}
4255
}
4356

@@ -50,8 +63,6 @@ private static BaselineState CaptureBaseline()
5063
int globalSequentialIndex = IMessageBus.GlobalSequentialIndex;
5164
long messageRegistrationHandleSeed = MessageRegistrationHandle.GetCurrentIdSeed();
5265
int syntheticOwnerCounter = MessageRegistrationBuilder.GetSyntheticOwnerCounter();
53-
MessageHelperIndexer.MessageHelperIndexerState helperState =
54-
MessageHelperIndexer.CaptureState();
5566

5667
return new BaselineState(
5768
messagingDebugEnabled,
@@ -60,8 +71,7 @@ private static BaselineState CaptureBaseline()
6071
globalMessageBufferSize,
6172
globalSequentialIndex,
6273
messageRegistrationHandleSeed,
63-
syntheticOwnerCounter,
64-
helperState
74+
syntheticOwnerCounter
6575
);
6676
}
6777

@@ -74,8 +84,7 @@ internal BaselineState(
7484
int globalMessageBufferSize,
7585
int globalSequentialIndex,
7686
long messageRegistrationHandleSeed,
77-
int syntheticOwnerCounter,
78-
MessageHelperIndexer.MessageHelperIndexerState helperState
87+
int syntheticOwnerCounter
7988
)
8089
{
8190
MessagingDebugEnabled = messagingDebugEnabled;
@@ -85,7 +94,6 @@ MessageHelperIndexer.MessageHelperIndexerState helperState
8594
GlobalSequentialIndex = globalSequentialIndex;
8695
MessageRegistrationHandleSeed = messageRegistrationHandleSeed;
8796
SyntheticOwnerCounter = syntheticOwnerCounter;
88-
HelperState = helperState;
8997
}
9098

9199
internal bool MessagingDebugEnabled { get; }
@@ -101,8 +109,6 @@ MessageHelperIndexer.MessageHelperIndexerState helperState
101109
internal long MessageRegistrationHandleSeed { get; }
102110

103111
internal int SyntheticOwnerCounter { get; }
104-
105-
internal MessageHelperIndexer.MessageHelperIndexerState HelperState { get; }
106112
}
107113
}
108114
}

0 commit comments

Comments
 (0)