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
8 changes: 4 additions & 4 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Scribe is a **public, reusable library**. It is open-sourced independently. It m
## The Compass — Where to Find What You Need

| Document | What it covers |
|---|---|
| --- | --- |
| [README](../README.md) | Overview, quick start, component table, doc index |
| [Writing Generators](../docs/writing-generators.md) | Transform -> Register -> Render pattern, Quill usage guide |
| [Project Setup](../docs/project-setup.md) | .csproj configuration, packaging, Stubs, LocalDev multi-repo workflow with automatic local NuGet resolution |
Expand All @@ -48,7 +48,7 @@ Scribe is a **public, reusable library**. It is open-sourced independently. It m
## Terminology

| Term | What it is |
|---|---|
| --- | --- |
| **Quill** | The fluent source builder — handles indentation, blank-line separation, using directives, namespaces, and XML documentation. |
| **Template** | Structured output template for generated code. |
| **Naming** | Naming convention helpers for generated identifiers. |
Expand Down Expand Up @@ -115,7 +115,7 @@ This repository uses [Conventional Commits](https://www.conventionalcommits.org/
**Format:** `<type>(<scope>): <subject>`

| Type | When to use |
|---|---|
| --- | --- |
| `feat` | A new feature or public surface addition |
| `fix` | A bug fix |
| `docs` | Documentation-only changes |
Expand Down Expand Up @@ -155,7 +155,7 @@ git config core.hooksPath .githooks
## CI/CD

| Workflow | Trigger | What it does |
|---|---|---|
| --- | --- | --- |
| **Build & Test** (`build-test.yml`) | Push to `master`, PRs to `master` | Restore → Build (Release) → Test → Upload packages as artefact |
| **Release** (`release.yml`) | Manual dispatch from `master` | Build → Test → Push to NuGet → Tag commit → Create GitHub Release with notes derived from conventional commits |

Expand Down
8 changes: 4 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Scribe is a **public, reusable library**. It is open-sourced independently. It m
## The Compass — Where to Find What You Need

| Document | What it covers |
|---|---|
| --- | --- |
| [README](../README.md) | Overview, quick start, component table, doc index |
| [Writing Generators](../docs/writing-generators.md) | Transform -> Register -> Render pattern, Quill usage guide |
| [Project Setup](../docs/project-setup.md) | .csproj configuration, packaging, Stubs, LocalDev multi-repo workflow with automatic local NuGet resolution |
Expand All @@ -48,7 +48,7 @@ Scribe is a **public, reusable library**. It is open-sourced independently. It m
## Terminology

| Term | What it is |
|---|---|
| --- | --- |
| **Quill** | The fluent source builder — handles indentation, blank-line separation, using directives, namespaces, and XML documentation. |
| **Template** | Structured output template for generated code. |
| **Naming** | Naming convention helpers for generated identifiers. |
Expand Down Expand Up @@ -115,7 +115,7 @@ This repository uses [Conventional Commits](https://www.conventionalcommits.org/
**Format:** `<type>(<scope>): <subject>`

| Type | When to use |
|---|---|
| --- | --- |
| `feat` | A new feature or public surface addition |
| `fix` | A bug fix |
| `docs` | Documentation-only changes |
Expand Down Expand Up @@ -155,7 +155,7 @@ git config core.hooksPath .githooks
## CI/CD

| Workflow | Trigger | What it does |
|---|---|---|
| --- | --- | --- |
| **Build & Test** (`build-test.yml`) | Push to `master`, PRs to `master` | Restore → Build (Release) → Test → Upload packages as artefact |
| **Release** (`release.yml`) | Manual dispatch from `master` | Build → Test → Push to NuGet → Tag commit → Create GitHub Release with notes derived from conventional commits |

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Scribe LocalDev sentinel
.localscribe

# Build output
bin/
obj/
Expand Down
51 changes: 24 additions & 27 deletions Scribe/build/Scribe.LocalDev.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<!--
Scribe Local Development — Property Definitions (Early Phase)

Automatically activates when the build configuration ends with "(Local Scribe)".
Normalizes the configuration back to Debug/Release, sets up local NuGet package
resolution, and imports version override files from the shared artifacts directory.
Automatically activates when a sentinel file '.localscribe' exists in $(ScribeRoot).
Sets up local NuGet package resolution and imports version override files from
the shared artifacts directory.

── Setup ──────────────────────────────────────────────────────────────

Expand All @@ -14,15 +14,24 @@
2. Import this file in your Directory.Build.props:
<Import Project="path/to/Scribe.LocalDev.props" Condition="Exists(...)" />

The "(Local Scribe)" build configurations are injected automatically.
── Activation ─────────────────────────────────────────────────────────

── Provides ───────────────���───────────────────────────────────────────
Create a '.localscribe' sentinel file in $(ScribeRoot) to activate:
echo. > $(ScribeRoot)/.localscribe

$(IsLocalScribe) 'true' when the active config contains "(Local Scribe)".
Delete it to deactivate. Add '.localscribe' to .gitignore.

Alternatively:
• CLI property: dotnet build -p:IsLocalScribe=true
• Directory.Build.props: set IsLocalScribe=true for persistent activation

── Provides ─────────────────────────────────────────────────────────

$(IsLocalScribe) 'true' when active.
$(ScribeArtifactsDir) $(ScribeRoot)\.artifacts\
$(ScribePackagesDir) $(ScribeArtifactsDir)packages\

── Override Files ───────────────��─────────────────────────────────────
── Override Files ───────────────────────────────────────────────────

When IsLocalScribe is active, all *.Directory.Packages.targets files in
$(ScribeArtifactsDir) are imported. These are auto-generated by the
Expand All @@ -40,29 +49,17 @@
<_ScribeLocalDevImported>true</_ScribeLocalDevImported>
</PropertyGroup>

<!-- ── Inject Configurations ───────────────────────────────────────── -->
<!-- Append "(Local Scribe)" configurations so they appear in VS/Rider dropdowns.
Projects that define their own <Configurations> should use $(Configurations) to inherit.
Initialize with Debug;Release if empty (SDK normally does this, but we run first). -->
<PropertyGroup Condition="'$(_ScribeLocalDevSkip)' != 'true'">
<Configurations Condition="'$(Configurations)' == ''">Debug;Release</Configurations>
<Configurations>$(Configurations);Debug (Local Scribe);Release (Local Scribe)</Configurations>
</PropertyGroup>

<!-- ── Configuration Detection ──────────────────────────────────────── -->
<!-- Activated by:
1. Build configuration ending in "(Local Scribe)" — e.g. "Debug (Local Scribe)"
2. MSBuild property: dotnet build -p:IsLocalScribe=true
3. Setting IsLocalScribe=true in Directory.Build.props or Directory.Solution.props -->
<PropertyGroup Condition="'$(_ScribeLocalDevSkip)' != 'true' and $(Configuration.EndsWith('(Local Scribe)'))">
<!-- ── Sentinel File Detection ─────────────────────────────────────── -->
<!-- A '.localscribe' file in $(ScribeRoot) activates Local Scribe mode.
Works in both VS and CLI builds without build configuration hacks. -->
<PropertyGroup Condition="'$(_ScribeLocalDevSkip)' != 'true'
and '$(IsLocalScribe)' != 'true'
and '$(ScribeRoot)' != ''
and Exists('$(ScribeRoot)\.localscribe')">
<IsLocalScribe>true</IsLocalScribe>
<!-- Normalize back to Debug/Release so the SDK applies correct compiler settings
and output paths are shared with the normal configuration. -->
<Configuration Condition="$(Configuration.StartsWith('Debug'))">Debug</Configuration>
<Configuration Condition="$(Configuration.StartsWith('Release'))">Release</Configuration>
</PropertyGroup>

<!-- ── Path Setup ──────────────-───────────────────────────────────── -->
<!-- ── Path Setup ─────────────────────────────────────────────────── -->
<PropertyGroup Condition="'$(_ScribeLocalDevSkip)' != 'true' and '$(IsLocalScribe)' == 'true' and '$(ScribeRoot)' != ''">
<ScribeArtifactsDir>$(ScribeRoot)\.artifacts\</ScribeArtifactsDir>
<ScribePackagesDir>$(ScribeArtifactsDir)packages\</ScribePackagesDir>
Expand Down
18 changes: 15 additions & 3 deletions Scribe/build/Scribe.LocalDev.targets
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
Output: $(ScribeArtifactsDir)<Name>.Directory.Packages.targets
-->

<!-- ── Import Guard ──────────────────────────────────────────────────── -->
<!-- Prevent double-import when both a NuGet package and a direct file import
bring in this file (mirrors the guard in Scribe.LocalDev.props). -->
<PropertyGroup Condition="'$(_ScribeLocalDevTargetsImported)' == 'true'">
<_ScribeLocalDevTargetsSkip>true</_ScribeLocalDevTargetsSkip>
</PropertyGroup>
<PropertyGroup Condition="'$(_ScribeLocalDevTargetsImported)' != 'true'">
<_ScribeLocalDevTargetsImported>true</_ScribeLocalDevTargetsImported>
</PropertyGroup>
Comment on lines +29 to +34

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import guard sets a global property _ScribeLocalDevTargetsSkip to true on a second import. Because _ScribeLocalDevTargetsSkip is also used in the Condition for _ScribeLocalDevOverride, a later second import can unintentionally disable the target from running (even if it was defined by the first import). Consider switching to a <Choose>/<When> pattern that avoids evaluating the rest of the file on subsequent imports (without mutating a skip flag that affects target execution-time conditions).

Copilot uses AI. Check for mistakes.

<!-- ── Import Version Override Files ──────────────────────────────────── -->
<!--
Each producer solution (Scribe, Hermetic, etc.) generates an override file:
Expand All @@ -36,12 +46,13 @@
-->
<Import
Project="$(ScribeArtifactsDir)*.Directory.Packages.targets"
Condition="'$(IsLocalScribe)' == 'true'
Condition="'$(_ScribeLocalDevTargetsSkip)' != 'true'
and '$(IsLocalScribe)' == 'true'
and '$(ScribeArtifactsDir)' != ''
and Exists('$(ScribeArtifactsDir)')"
/>

<PropertyGroup Condition="'$(IsLocalScribe)' == 'true'">
<PropertyGroup Condition="'$(_ScribeLocalDevTargetsSkip)' != 'true' and '$(IsLocalScribe)' == 'true'">
<ScribeLocalDevOverrideName
Condition="'$(ScribeLocalDevOverrideName)' == ''"
>$(ScribeLocalDevTriggerProject)</ScribeLocalDevOverrideName
Expand Down Expand Up @@ -71,7 +82,8 @@
<Target
Name="_ScribeLocalDevOverride"
AfterTargets="Pack"
Condition="'$(IsLocalScribe)' == 'true'
Condition="'$(_ScribeLocalDevTargetsSkip)' != 'true'
and '$(IsLocalScribe)' == 'true'
and '$(MSBuildProjectName)' == '$(ScribeLocalDevTriggerProject)'
and '$(ScribeLocalDevPackageNames)' != ''"
>
Expand Down
15 changes: 9 additions & 6 deletions docs/architecture-infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,22 @@ Prevents double-import when both a NuGet package and a direct file import bring

All subsequent property groups and imports are conditioned on `'$(_ScribeLocalDevSkip)' != 'true'`.

### Configuration Injection
### Sentinel File Detection

Appends `Debug (Local Scribe)` and `Release (Local Scribe)` to the `$(Configurations)` property so they appear in IDE dropdowns:
Activates Local Scribe mode when a `.localscribe` file exists in `$(ScribeRoot)` (the shared workspace root). This works in both Visual Studio and CLI builds without build configuration hacks.

```xml
<Configurations>$(Configurations);Debug (Local Scribe);Release (Local Scribe)</Configurations>
<PropertyGroup Condition="'$(_ScribeLocalDevSkip)' != 'true'
and '$(IsLocalScribe)' != 'true'
and '$(ScribeRoot)' != ''
and Exists('$(ScribeRoot)\.localscribe')">
<IsLocalScribe>true</IsLocalScribe>
</PropertyGroup>
```

### Configuration Detection

Three activation methods:

1. **Configuration suffix:** `$(Configuration)` ends with `(Local Scribe)` — detected and normalized back to `Debug` or `Release`.
1. **Sentinel file (recommended):** Create `.localscribe` in `$(ScribeRoot)`. Delete it to deactivate. Add `.localscribe` to `.gitignore`.
2. **Explicit property:** `-p:IsLocalScribe=true` on the command line.
3. **Props file:** `<IsLocalScribe>true</IsLocalScribe>` in Directory.Build.props or Directory.Solution.props.

Expand Down
2 changes: 1 addition & 1 deletion docs/project-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ For direct import from a sibling checkout:

Three ways to activate LocalDev:

1. **Build configuration:** Select `Debug (Local Scribe)` or `Release (Local Scribe)` in your IDE — configurations are injected automatically.
1. **Sentinel file (recommended):** Create a `.localscribe` file in `$(ScribeRoot)` (the shared workspace root). Delete it to deactivate. Add `.localscribe` to `.gitignore`.
2. **MSBuild property:** `dotnet build -p:IsLocalScribe=true`
3. **Props file:** Set `<IsLocalScribe>true</IsLocalScribe>` in `Directory.Build.props` or `Directory.Solution.props`.

Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.2",
"version": "0.3",
"nugetPackageVersion": {
"semVer": 2
},
Expand Down
Loading