From c35d212394e7f26b0e7c7a3e13c530f902b5ca77 Mon Sep 17 00:00:00 2001 From: brenpike Date: Fri, 15 May 2026 10:03:04 -0600 Subject: [PATCH 1/5] docs: add domain context documentation (CONTEXT-MAP and per-project glossaries) --- CONTEXT-MAP.md | 13 ++ .../CONTEXT.md | 40 +++++ src/Chatter.Rest.Hal.Core/CONTEXT.md | 22 +++ src/Chatter.Rest.Hal/CONTEXT.md | 137 ++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 CONTEXT-MAP.md create mode 100644 src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md create mode 100644 src/Chatter.Rest.Hal.Core/CONTEXT.md create mode 100644 src/Chatter.Rest.Hal/CONTEXT.md diff --git a/CONTEXT-MAP.md b/CONTEXT-MAP.md new file mode 100644 index 0000000..6525648 --- /dev/null +++ b/CONTEXT-MAP.md @@ -0,0 +1,13 @@ +# Context Map + +## Contexts + +- [HAL Domain & Serialization](./src/Chatter.Rest.Hal/CONTEXT.md) — in-memory HAL document model, fluent builder API, and JSON serialization/deserialization +- [HAL Shared Kernel](./src/Chatter.Rest.Hal.Core/CONTEXT.md) — shared marker types consumed by both the domain library and the source generator +- [HAL Code Generation](./src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md) — Roslyn source generator that emits HAL-aware partial classes from annotated user types + +## Relationships + +- **Shared Kernel -> HAL Domain & Serialization**: Core defines **HalResponseAttribute**; the domain library declares the **Link Collection** and **Embedded Resource Collection** types that generated code references +- **Shared Kernel -> HAL Code Generation**: Code Generation reads **HalResponseAttribute** at compile time to discover annotated classes and emit partial-class source +- **HAL Domain & Serialization <-> HAL Code Generation**: No direct project reference; generated code depends on domain types (**Link Collection**, **Embedded Resource Collection**) at the consuming project's compile time, not at generator compile time diff --git a/src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md b/src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md new file mode 100644 index 0000000..942bbd5 --- /dev/null +++ b/src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md @@ -0,0 +1,40 @@ +# HAL Code Generation + +A Roslyn incremental source generator that discovers classes annotated with **HalResponseAttribute** and emits partial-class source containing HAL navigation properties (`Links`, `Embedded`). + +## Language + +**HalResponseGenerator**: +The Roslyn incremental source generator entry point that discovers **HalResponseAttribute**-annotated classes and feeds them to the **Emitter**. +_Avoid_: source generator, code generator (too generic), analyzer + +**Emitter**: +The component that produces the generated C# source text for each discovered class, adding `Links` and `Embedded` properties with the correct `[JsonPropertyName]` attributes. +_Avoid_: code writer, template, renderer + +**HalClassInfo**: +A value type capturing the name and namespace of a single annotated class, used as the pipeline's intermediate representation between discovery and emission. +_Avoid_: class metadata, class descriptor, target info + +**Generated Partial Class**: +The source file emitted per annotated class, adding `Links` (typed as **Link Collection**) and `Embedded` (typed as **Embedded Resource Collection**) properties so the user's DTO can participate in HAL serialization without inheriting from **Resource**. +_Avoid_: generated file, code-gen output, scaffolded class + +## Relationships + +- **HalResponseGenerator** discovers classes annotated with **HalResponseAttribute** (defined in HAL Shared Kernel) +- **HalResponseGenerator** collects each discovered class as a **HalClassInfo** and passes the set to the **Emitter** +- The **Emitter** produces one **Generated Partial Class** per **HalClassInfo** +- Each **Generated Partial Class** references **Link Collection** and **Embedded Resource Collection** (defined in HAL Domain & Serialization) via `using Chatter.Rest.Hal` + +## Example dialogue + +> **Dev:** "My `[HalResponse]` class compiles but the generated `Links` property doesn't appear." +> **Domain expert:** "The class must be declared `partial`. **HalResponseGenerator** emits a **Generated Partial Class** that adds the property -- if the class isn't partial, the compiler can't merge them." + +> **Dev:** "Can I customize what the **Emitter** generates for my class?" +> **Domain expert:** "No. The **Emitter** produces a fixed shape: a `Links` property of type **Link Collection** and an `Embedded` property of type **Embedded Resource Collection**. Customization happens at the domain level, not at generation time." + +## Flagged ambiguities + +- "generator" is used both for the Roslyn concept (IIncrementalGenerator) and for the project-specific **HalResponseGenerator** class -- resolved: use **HalResponseGenerator** when referring to this project's generator; use "Roslyn source generator" when referring to the platform concept. diff --git a/src/Chatter.Rest.Hal.Core/CONTEXT.md b/src/Chatter.Rest.Hal.Core/CONTEXT.md new file mode 100644 index 0000000..88d611d --- /dev/null +++ b/src/Chatter.Rest.Hal.Core/CONTEXT.md @@ -0,0 +1,22 @@ +# HAL Shared Kernel + +Shared marker types consumed by both the HAL domain library and the source generator. This context exists to break the circular dependency: the generator needs to know which classes to target, and the domain library defines the types that generated code references. + +## Language + +**HalResponseAttribute**: +A class-level attribute that marks a user-defined type for HAL source generation; the generator scans for this attribute at compile time to discover target classes. +_Avoid_: HalAttribute, HAL marker, response marker + +## Relationships + +- **HalResponseAttribute** is declared here and consumed by two downstream contexts: HAL Code Generation reads it to discover annotated classes; HAL Domain & Serialization ships the domain types (**Link Collection**, **Embedded Resource Collection**) that appear in the generated partial-class members + +## Example dialogue + +> **Dev:** "I decorated my DTO with `[HalResponse]` but nothing happened." +> **Domain expert:** "**HalResponseAttribute** only marks the class for the source generator. You also need the `Chatter.Rest.Hal.CodeGenerators` analyzer package referenced so the generator can find it and emit the partial class." + +## Flagged ambiguities + +- None at this time. diff --git a/src/Chatter.Rest.Hal/CONTEXT.md b/src/Chatter.Rest.Hal/CONTEXT.md new file mode 100644 index 0000000..05bbc60 --- /dev/null +++ b/src/Chatter.Rest.Hal/CONTEXT.md @@ -0,0 +1,137 @@ +# HAL Domain & Serialization + +The in-memory object model for HAL (Hypertext Application Language) documents, a fluent builder API for constructing them, and System.Text.Json converters for round-trip serialization. + +## Language + +### Document model + +**Resource**: +A HAL document or an individual embedded item; the root aggregate that holds **State**, a **Link Collection**, and an **Embedded Resource Collection**. +_Avoid_: document, response, payload, HAL object + +**State**: +The application-specific properties of a **Resource**, excluding reserved HAL properties (`_links`, `_embedded`). +_Avoid_: body, data, properties, content + +**Link**: +A named entry in a **Link Collection**, identified by a **Relation** and containing one or more **Link Objects**. +_Avoid_: hyperlink, URL entry, link relation entry + +**Relation**: +A string that identifies the semantics of a **Link** (e.g., `"self"`, `"next"`, `"acme:widgets"`). +_Avoid_: rel type, link name, link key + +**Link Object**: +A single hyperlink target within a **Link**, carrying an **Href** and optional properties (templated, type, deprecation, name, profile, title, hreflang). +_Avoid_: link entry, link item, href object + +**Href**: +The URI or URI Template value on a **Link Object**; the only required property. +_Avoid_: URL, address, endpoint + +**Embedded Resource**: +A named entry in an **Embedded Resource Collection**, identified by a name string and containing a **Resource Collection**. +_Avoid_: nested resource, sub-resource, child resource, embed + +**Link Collection**: +The ordered set of **Links** on a **Resource**, serialized as the `_links` JSON property. +_Avoid_: links map, link set + +**Link Object Collection**: +The ordered set of **Link Objects** within a single **Link**. +_Avoid_: link array, href list + +**Embedded Resource Collection**: +The ordered set of **Embedded Resources** on a **Resource**, serialized as the `_embedded` JSON property. +_Avoid_: embeds, embedded map + +**Resource Collection**: +The ordered set of **Resources** within a single **Embedded Resource**. +_Avoid_: resource array, resource list + +### Reserved relations + +**Self Link**: +A **Link** with **Relation** `"self"` that identifies the **Resource's** own URI. +_Avoid_: self-ref, canonical link, identity link + +**CURIE**: +A compact URI prefix defined under the reserved `"curies"` **Relation**, enabling short-form **Relations** (e.g., `"acme:widgets"`) that expand to full URIs via a URI Template. +_Avoid_: compact URI, namespace, prefix + +**CURIE Expansion**: +The process of replacing a prefixed **Relation** (e.g., `"acme:widgets"`) with its full URI by substituting the suffix into the CURIE's **Href** template at the `{rel}` token. +_Avoid_: prefix resolution, namespace expansion + +### Serialization + +**Force Array**: +A per-**Link** flag (`IsArray`) or a global option (`AlwaysUseArrayForLinks`) that forces a **Link** to serialize as a JSON array even when it contains a single **Link Object**. +_Avoid_: always-array, array mode + +**Force Write As Collection**: +A per-**Embedded Resource** flag (`ForceWriteAsCollection`) that forces its **Resource Collection** to serialize as a JSON array even when it contains a single **Resource**. +_Avoid_: always-array (embedded), collection mode + +**HAL JSON Options**: +Configuration object (`HalJsonOptions`) controlling HAL-specific serialization behavior, notably the global **Force Array** setting for links. +_Avoid_: serialization settings, converter config + +### Builder + +**Fluent Builder**: +The staged builder API (`ResourceBuilder`) for constructing a **Resource** graph through chained method calls, using stage interfaces to guide valid construction sequences. +_Avoid_: resource factory, DSL, construction API + +**Stage Interface**: +An interface (e.g., `IResourceCreationStage`, `ILinkCreationStage`) that constrains which builder methods are available at each step of **Fluent Builder** construction. +_Avoid_: step interface, phase interface, builder contract + +### Marker type + +**IHalPart**: +Marker interface implemented by every HAL domain type (**Resource**, **Link**, **Link Object**, and all collection types); used as the generic constraint for the builder hierarchy. +_Avoid_: IHalType, IHalEntity + +### External dependency + +**URI Template**: +An RFC 6570 template string (provided by the external `Chatter.Rest.UriTemplates` package) used by **Link Object** for template expansion and by **CURIEs** for relation expansion. +_Avoid_: URL template, route template + +## Relationships + +- A **Resource** contains exactly one **Link Collection** and one **Embedded Resource Collection** +- A **Resource** contains zero or one **State** objects +- A **Link Collection** contains zero or more **Links** +- A **Link** is identified by exactly one **Relation** and contains one **Link Object Collection** +- A **Link Object Collection** contains one or more **Link Objects** +- A **Link Object** carries exactly one **Href** and optional metadata properties +- An **Embedded Resource Collection** contains zero or more **Embedded Resources** +- An **Embedded Resource** is identified by a name and contains one **Resource Collection** +- A **Resource Collection** contains one or more **Resources** (recursive: each may have its own **Links** and **Embedded Resources**) +- A **CURIE** is a **Link** under the reserved `"curies"` **Relation** whose **Link Objects** define prefix-to-template mappings +- **CURIE Expansion** uses a **CURIE's** **Href** (a **URI Template**) to resolve prefixed **Relations** to full URIs +- A **Self Link** is a **Link** with **Relation** `"self"` +- **Force Array** applies to **Links**; **Force Write As Collection** applies to **Embedded Resources** — both override the default singular/plural JSON shape +- The **Fluent Builder** produces a **Resource** graph; **Stage Interfaces** control the construction sequence +- Every HAL domain type implements **IHalPart** +- **HalResponseAttribute** (defined in HAL Shared Kernel) is not consumed directly in this context; it is consumed by HAL Code Generation, whose generated code references types from this context + +## Example dialogue + +> **Dev:** "When I add a **Link** with **Relation** `"next"`, does it automatically get a **Link Object**?" +> **Domain expert:** "No -- a **Link** starts with an empty **Link Object Collection**. You add **Link Objects** to it, each with its own **Href**." + +> **Dev:** "If my **Link** only has one **Link Object**, will it serialize as a JSON object or a JSON array?" +> **Domain expert:** "By default it serializes as a single object. If you need a stable array shape, set **Force Array** on the **Link** -- or enable it globally via **HAL JSON Options**." + +> **Dev:** "How does **CURIE Expansion** work for `acme:widgets`?" +> **Domain expert:** "It finds the **CURIE** **Link Object** with name `acme` under the `curies` **Relation**, takes its **Href** template, and substitutes `widgets` for the `{rel}` token." + +## Flagged ambiguities + +- "link" was used to mean both the relation-level entry (**Link**) and the individual hyperlink target (**Link Object**) -- resolved: **Link** is the relation-keyed container; **Link Object** is the target with an **Href**. +- "embedded" was used to mean both the named entry (**Embedded Resource**) and the collection property on a **Resource** (**Embedded Resource Collection**) -- resolved: these are distinct types at different levels of the hierarchy. +- "force array" applies to two distinct mechanisms: **Force Array** (on **Links**, controlling `_links` shape) and **Force Write As Collection** (on **Embedded Resources**, controlling `_embedded` shape) -- resolved: use the specific term for each. From 6b3ca95f983df5090a87f95bd3773e1833f87b17 Mon Sep 17 00:00:00 2001 From: brenpike Date: Fri, 15 May 2026 10:42:26 -0600 Subject: [PATCH 2/5] docs: fix Core dependency description and collection cardinality in context glossaries --- src/Chatter.Rest.Hal.Core/CONTEXT.md | 4 ++-- src/Chatter.Rest.Hal/CONTEXT.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Chatter.Rest.Hal.Core/CONTEXT.md b/src/Chatter.Rest.Hal.Core/CONTEXT.md index 88d611d..f7b7730 100644 --- a/src/Chatter.Rest.Hal.Core/CONTEXT.md +++ b/src/Chatter.Rest.Hal.Core/CONTEXT.md @@ -1,6 +1,6 @@ # HAL Shared Kernel -Shared marker types consumed by both the HAL domain library and the source generator. This context exists to break the circular dependency: the generator needs to know which classes to target, and the domain library defines the types that generated code references. +Shared marker types referenced by consumer projects and test projects. The source generator discovers HalResponseAttribute by metadata name at compile time and does not require a direct project reference to Core. Generated code references the domain package (Chatter.Rest.Hal) at the consuming project's compile time. ## Language @@ -10,7 +10,7 @@ _Avoid_: HalAttribute, HAL marker, response marker ## Relationships -- **HalResponseAttribute** is declared here and consumed by two downstream contexts: HAL Code Generation reads it to discover annotated classes; HAL Domain & Serialization ships the domain types (**Link Collection**, **Embedded Resource Collection**) that appear in the generated partial-class members +- **HalResponseAttribute** is declared here and consumed by consumer projects and test projects; HAL Code Generation discovers it by metadata name at compile time to find annotated classes; generated code references HAL Domain & Serialization types (**Link Collection**, **Embedded Resource Collection**) at the consuming project's compile time ## Example dialogue diff --git a/src/Chatter.Rest.Hal/CONTEXT.md b/src/Chatter.Rest.Hal/CONTEXT.md index 05bbc60..908339e 100644 --- a/src/Chatter.Rest.Hal/CONTEXT.md +++ b/src/Chatter.Rest.Hal/CONTEXT.md @@ -106,11 +106,11 @@ _Avoid_: URL template, route template - A **Resource** contains zero or one **State** objects - A **Link Collection** contains zero or more **Links** - A **Link** is identified by exactly one **Relation** and contains one **Link Object Collection** -- A **Link Object Collection** contains one or more **Link Objects** +- A **Link Object Collection** contains zero or more **Link Objects** - A **Link Object** carries exactly one **Href** and optional metadata properties - An **Embedded Resource Collection** contains zero or more **Embedded Resources** - An **Embedded Resource** is identified by a name and contains one **Resource Collection** -- A **Resource Collection** contains one or more **Resources** (recursive: each may have its own **Links** and **Embedded Resources**) +- A **Resource Collection** contains zero or more **Resources** (recursive: each may have its own **Links** and **Embedded Resources**) - A **CURIE** is a **Link** under the reserved `"curies"` **Relation** whose **Link Objects** define prefix-to-template mappings - **CURIE Expansion** uses a **CURIE's** **Href** (a **URI Template**) to resolve prefixed **Relations** to full URIs - A **Self Link** is a **Link** with **Relation** `"self"` From 1aa0f725f678c9ef1f6bcf4602dbbdfd6e7a2927 Mon Sep 17 00:00:00 2001 From: brenpike Date: Fri, 15 May 2026 11:04:57 -0600 Subject: [PATCH 3/5] docs: fix Core dependency description in context map and Link cardinality in glossary --- CONTEXT-MAP.md | 2 +- src/Chatter.Rest.Hal/CONTEXT.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTEXT-MAP.md b/CONTEXT-MAP.md index 6525648..6680a9a 100644 --- a/CONTEXT-MAP.md +++ b/CONTEXT-MAP.md @@ -3,7 +3,7 @@ ## Contexts - [HAL Domain & Serialization](./src/Chatter.Rest.Hal/CONTEXT.md) — in-memory HAL document model, fluent builder API, and JSON serialization/deserialization -- [HAL Shared Kernel](./src/Chatter.Rest.Hal.Core/CONTEXT.md) — shared marker types consumed by both the domain library and the source generator +- [HAL Shared Kernel](./src/Chatter.Rest.Hal.Core/CONTEXT.md) — attribute package referenced by consumer and test projects; the source generator discovers HalResponseAttribute by metadata name, not via a direct Core reference - [HAL Code Generation](./src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md) — Roslyn source generator that emits HAL-aware partial classes from annotated user types ## Relationships diff --git a/src/Chatter.Rest.Hal/CONTEXT.md b/src/Chatter.Rest.Hal/CONTEXT.md index 908339e..f44dc49 100644 --- a/src/Chatter.Rest.Hal/CONTEXT.md +++ b/src/Chatter.Rest.Hal/CONTEXT.md @@ -15,7 +15,7 @@ The application-specific properties of a **Resource**, excluding reserved HAL pr _Avoid_: body, data, properties, content **Link**: -A named entry in a **Link Collection**, identified by a **Relation** and containing one or more **Link Objects**. +A named entry in a **Link Collection**, identified by a **Relation** and containing zero or more **Link Objects**. _Avoid_: hyperlink, URL entry, link relation entry **Relation**: From 6475fff714792132171d20b0c62ba66c25abd537 Mon Sep 17 00:00:00 2001 From: brenpike Date: Fri, 15 May 2026 12:16:09 -0600 Subject: [PATCH 4/5] docs: align Core description with CLAUDE.md package table in context map --- CONTEXT-MAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTEXT-MAP.md b/CONTEXT-MAP.md index 6680a9a..fcffc5f 100644 --- a/CONTEXT-MAP.md +++ b/CONTEXT-MAP.md @@ -3,7 +3,7 @@ ## Contexts - [HAL Domain & Serialization](./src/Chatter.Rest.Hal/CONTEXT.md) — in-memory HAL document model, fluent builder API, and JSON serialization/deserialization -- [HAL Shared Kernel](./src/Chatter.Rest.Hal.Core/CONTEXT.md) — attribute package referenced by consumer and test projects; the source generator discovers HalResponseAttribute by metadata name, not via a direct Core reference +- [HAL Shared Kernel](./src/Chatter.Rest.Hal.Core/CONTEXT.md) — shared types with no standalone package, referenced by consumer and test projects; the source generator discovers HalResponseAttribute by metadata name, not via a direct Core reference - [HAL Code Generation](./src/Chatter.Rest.Hal.CodeGenerators/CONTEXT.md) — Roslyn source generator that emits HAL-aware partial classes from annotated user types ## Relationships From 6f5ea50810d1bc466d09e16899b163d16ddfd136 Mon Sep 17 00:00:00 2001 From: brenpike Date: Fri, 15 May 2026 12:27:29 -0600 Subject: [PATCH 5/5] docs: clarify CURIE expansion uses string substitution, not external UriTemplates package --- src/Chatter.Rest.Hal/CONTEXT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Chatter.Rest.Hal/CONTEXT.md b/src/Chatter.Rest.Hal/CONTEXT.md index f44dc49..6a1988a 100644 --- a/src/Chatter.Rest.Hal/CONTEXT.md +++ b/src/Chatter.Rest.Hal/CONTEXT.md @@ -97,7 +97,7 @@ _Avoid_: IHalType, IHalEntity ### External dependency **URI Template**: -An RFC 6570 template string (provided by the external `Chatter.Rest.UriTemplates` package) used by **Link Object** for template expansion and by **CURIEs** for relation expansion. +An RFC 6570 template string used by **Link Object** for template expansion via the external `Chatter.Rest.UriTemplates` package. **CURIEs** also use a template-shaped **Href** (containing a `{rel}` token), but CURIE expansion performs plain `{rel}` string substitution — it does not use the external `Chatter.Rest.UriTemplates` package. _Avoid_: URL template, route template ## Relationships @@ -112,7 +112,7 @@ _Avoid_: URL template, route template - An **Embedded Resource** is identified by a name and contains one **Resource Collection** - A **Resource Collection** contains zero or more **Resources** (recursive: each may have its own **Links** and **Embedded Resources**) - A **CURIE** is a **Link** under the reserved `"curies"` **Relation** whose **Link Objects** define prefix-to-template mappings -- **CURIE Expansion** uses a **CURIE's** **Href** (a **URI Template**) to resolve prefixed **Relations** to full URIs +- **CURIE Expansion** uses a **CURIE's** **Href** (a template-shaped string with a `{rel}` token) to resolve prefixed **Relations** to full URIs via plain string substitution — the external `Chatter.Rest.UriTemplates` package is not involved - A **Self Link** is a **Link** with **Relation** `"self"` - **Force Array** applies to **Links**; **Force Write As Collection** applies to **Embedded Resources** — both override the default singular/plural JSON shape - The **Fluent Builder** produces a **Resource** graph; **Stage Interfaces** control the construction sequence