Repository navigation
Rebrand the Polly fork as Fences - #2
Merged
Merged
Conversation
Fences is a detached copy of App-vNext/Polly at 47e3b41 (Polly 8.7.0). This lands the working documents for turning it into an independent project. - fork-migration-plan.md: the authoritative plan. Decisions D1-D11 (all confirmed), phases 0-8, risks, and the decision log. - CLAUDE.md: an interim stub pointing a fresh agent session at the plan. Phase 5 replaces it with the full version. - assets/logo/: the accepted Fences mark and lockup, as SVG source. - images/: PNG exports at 64, 128 and 264, plus the 512 lockup. - .gitignore: PROMPT.md (local session state) and baseline/ (the pre-rename build and public-API baselines, which are a local oracle, not repo content). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
global.json pins 10.0.400, which is correct and stays: it is the current latest .NET 10 SDK. The problem is that an exact pin with no rollForward blocks any contributor on 10.0.1xx/2xx/3xx outright, which is how this machine failed before the SDK was installed side-by-side. rollForward: latestFeature accepts any 10.0.x installed at or above the pin. This matters more now that D-level decisions dropped update-dotnet-sdk.yml (Phase 6): nothing bumps the pin automatically any more, so the pin needs to be tolerant rather than exact. Refs P0.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Do this before the rename, so the record of what we forked and why is committed while the tree still visibly matches upstream. Fences derives from App-vNext/Polly at 47e3b41 (Polly 8.7.0). The code is BSD-3-Clause, which permits redistribution provided the copyright notice and disclaimer are retained, and requires that the upstream names not be used to endorse or promote a derived product. This commit is the machinery for both halves of that: credit what is theirs, and be unambiguous that Fences is not theirs. - LICENSE: our copyright line added alongside App vNext's, which stays verbatim. The terms are unchanged; BSD-3-Clause stays. - NOTICE.md: what Fences is, the exact upstream commit it derives from, the retained notice, and an explicit statement that we are not affiliated with, endorsed by, or supported by App vNext or the Polly maintainers. - ACKNOWLEDGEMENTS.md: App vNext, Michael Wolfenden, the maintainer team and the contributor community. Polly is actively maintained and remains the right choice for many users; Fences is a divergence, not a criticism. - CHANGELOG.md: upstream's 92 KB of history kept intact, headed by a note that 8.7.0 and below are Polly's releases, not ours, and that the App-vNext links in them are historical citations. Fences entries start at 9.0.0. - docs/adr/0001: adopt ADRs, matching Brighter's convention and format. - docs/adr/0002: the fork decision itself, carrying D1-D11 and the attribution guardrails G1-G6 into a durable home. The migration plan is retired once the migration completes; this is not. Polly-owned imagery is removed, per G4 - trade dress is separate from the copyright licence: - Polly-Logo.{jpg,png} and all of logos/ deleted. That includes aws.png, github.png and microsoft.png: nothing referenced them, they were assets for a sponsor section that does not survive, and they are third-party marks we have no reason to carry. - package-icon.png and docs/icon.png replaced with the Fences mark at 128x128. eng/Library.targets packs package-icon.png unconditionally, so this was a hard blocker for packing; a full pack has been run against the new icon. - package-readme.md and docs/index.md: the remote Polly logo images swapped for the Fences mark. Only the images - the prose in both files is still Polly's and is left for Phases 2-3. .github/FUNDING.yml is deleted rather than repointed. It routed sponsorship to a Polly maintainer, and neither Brighter nor Darker has one. Refs P1.1-P1.7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
The plan's §2.3 claimed no public type or member name contains "Polly", and called it "the single most important finding" - the fact D1 rests on. It is false. `Polly.PollyServiceCollectionExtensions` is public, with 8 public members. The verification behind the claim had two independent bugs: git grep -ohE "\b[A-Za-z_]*Polly[A-Za-z_]*\b" -- "src/*.cs" `\b` is a GNU extension this platform's POSIX ERE does not support, so the pattern matches nothing at all - `git grep -cE "\bPolly\b"` on a file with six hits exits 1. And `src/*.cs` matches only files directly in `src/`, of which there are none. Two reasons to return empty, neither of them the absence of matches. The corrected check runs against the P0.3 public-API baseline instead of the source, and is in the plan. The same false negative hid six embedded identifiers that Pass 1 must handle: PollyVersion (54 hits), PollyConfig, EnablePollyMetering, ListenPollyMetrics, and the two ServiceCollectionExtension names. Without them the dotted replacement produces Paramore.FencesVersion and the like. D12 records the resulting public API change: PollyServiceCollectionExtensions becomes ResilienceServiceCollectionExtensions, dropping the brand token rather than swapping it. The rest of the surface - ResiliencePipeline, ResilienceContext, ResiliencePipelineBuilder - is already brand-free; Polly* was the odd one out, and the fork is the moment to fix that rather than carry it forward as Fences*. Consumers never name the class, so migration cost is nil either way. eng/rename-to-fences.py implements Phase 2a and 2b. Three things in it are load-bearing: Pass 1 runs before the dotted replacement; both Pass 2 rules are case-sensitive, which is what keeps resilience.polly.* from becoming resilience.paramore.fences.* (D4); and http(s) URLs are masked throughout, because ~34 comments cite App-vNext/Polly issues that explain the code they sit beside, plus the Polly-Contrib jitter link. Markdown is out of scope for the script, recorded as 2b-bis. Renaming prose mechanically manufactures falsehoods rather than merely reading clumsily: it misquotes third-party article titles in docs/community/resources.md, invents a Fences-Contrib organisation, and produces "migrating from Fences v7" for a version that never existed. Documentation is Phase 7 (D8 already owns the pollydocs.org links) and is editorial work, not substitution. The corollary is that P2.3's `git grep -in polly` will still return every *.md after the rename commit - expected, not a miss. Refs P2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
One scripted, reproducible pass: eng/rename-to-fences.py, run once from a clean tree. Namespaces, assemblies, package IDs, project and folder names, and the solution file all move from Polly.* to Paramore.Fences.*. Paths move with git mv so rename detection survives; this commit's SHA goes into .git-blame-ignore-revs so blame still reaches the real author of each line. Validated three ways. The build is green - 0 errors, 0 warnings - with test counts identical to the pre-rename baseline on every target framework: Specs 2115, Core 777, Extensions 111, RateLimiting 34 plus 1 skipped, Testing 7. All five packages pack. Across the 1,725 lines of public API baseline the only diff is the one deliberate type rename below. Every remaining "polly" outside Markdown was triaged; the survivors are listed in the plan. Three things in the script are load-bearing: - Pass 1 rewrites identifiers with Polly glued inside them before the dotted replacement runs, or PollyStrongNamePublicKey becomes Paramore.FencesStrongNamePublicKey. The plan listed four such identifiers; it is actually ten - see the previous commit for why they were missed. - Both Pass 2 rules are case-sensitive. That is what keeps Polly -> Paramore.Fences away from lowercase resilience.polly.*, so polly -> fences can turn it into resilience.fences.* (D4) rather than resilience.paramore.fences.*. It also means uppercase POLLY_ never matched - the POLLY_UPDATER_BOT_* secrets in six workflows survive for Phase 6. - http(s) URLs are masked. Around twenty code comments cite App-vNext/Polly issues that explain the code they sit beside, and Polly.Contrib.WaitAndRetry is a third-party package; rewriting either would break real references. Polly.Contrib.* is masked for the same reason after the first run invented Paramore.Fences.Contrib.WaitAndRetry and failed restore. Markdown is out of scope. Renaming prose mechanically manufactures falsehoods - misquoted third-party article titles, a Fences-Contrib organisation that does not exist, "migrating from Fences v7" for a version that never shipped. Documentation is Phase 7 and is editorial work. `git grep -in polly` therefore still returns every *.md; that is expected. The one public API change (D12): PollyServiceCollectionExtensions becomes ResilienceServiceCollectionExtensions, dropping the brand token rather than swapping it, because the rest of the surface - ResiliencePipeline, ResilienceContext, ResiliencePipelineBuilder - carries no brand. Consumers never name the class, so migration is unaffected. Telemetry moves per D4: the meter and ActivitySource become Paramore.Fences, metrics become resilience.fences.*. Four things had to be pulled forward from Phase 3, because Phase 2 cannot be green without them: - Fences.snk is generated (2048-bit, D5's openssl recipe) and Polly.snk is deleted. Without a key file every signed assembly fails CS7027. FencesStrongNamePublicKey now holds our public key, not Polly's - otherwise InternalsVisibleTo would still be granting access to Polly-signed assemblies. test/Shared/StrongNameTests.cs hard-codes both the key and its token, so it changes here too, split across lines because 576 hex characters exceeds the 500-character limit that Polly's 1024-bit key fit under. - PackageValidationBaselineVersion pointed at 8.5.2, which restore tried to download as Paramore.Fences.Core 8.5.2. Commented out until 9.0.0 ships. - The samples referenced published Polly packages and there is no published Paramore.Fences.* to switch them to. All eight - including the F# and VB ones, where the plan counted seven - now use ProjectReference into src/. This is the preferred shape, not a stopgap. Microsoft.Extensions.Http.Resilience has a hard dependency on Polly, so its AddResilienceHandler hands the callback a Polly builder that no Fences strategy can attach to. Rather than delete that documentation, the snippets and the Chaos sample take an explicit Polly.Core reference and use Polly's API deliberately, with comments saying why. This is why Snippets.csproj loses its project-wide `<Using Include="Paramore.Fences" />`: both libraries export ResiliencePipelineBuilder, PredicateResult and DelayBackoffType, and a global using cannot be opted out of per file. 23 snippet files gain an explicit import instead, and Pattern_CircuitPerEndpoint moves to HttpClientIntegrations.cs - transparent to the docs, because mdsnippets resolves regions by name. That limitation bounds what the fork achieves and is recorded in ADR 0002: Fences removes Brighter's Polly dependency, but it cannot remove Polly from an application that calls AddStandardResilienceHandler. Refs P2.1, P2.2, P2.3, and P3.1/P3.2 in part. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
ad904b6 rewrote 885 files mechanically. Without this, blame on any renamed line stops at the rename instead of reaching the person who wrote it. Note that git does not read this file unless told to - contributors need `git config blame.ignoreRevsFile .git-blame-ignore-revs` locally. GitHub's blame UI honours it automatically. CONTRIBUTING.md should say so; that is Phase 4. Verified: blame through RetryHelper.cs still resolves to martintmk's 2023 commit, across both the rename and the src/Polly.Core -> src/Paramore.Fences.Core move. Closes P2.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Phase 3 of the fork migration. Package metadata, descriptions and the reader-facing documents now describe Fences rather than Polly. eng/Library.targets: Company and Authors become Brighter Command, and PackageProjectUrl and PackageReleaseNotes point at BrighterCommand/Fences. Copyright retains App vNext's notice alongside ours rather than replacing it — guardrail G2 constrains the authorship fields, but BSD-3-Clause clause 1 requires the upstream notice on a derivative work, and LICENSE already carries both lines. All five package descriptions open with the lineage sentence immediately followed by the non-affiliation line, per guardrails G1 and G3. PackageTags drop Simmy and the dotted Paramore.Fences token left over from the rename. MinVerMinimumMajorMinor moves to 9.0, so the packages now build as 9.0.0-alpha.*. docs/migration-from-polly.md is new: the package-ID and namespace maps, the one renamed public type, the telemetry names — which is the only part a consumer cannot fix with a find-and-replace over their own source — the strong-name change, and the bounded claim about Microsoft.Extensions.Http.Resilience, which keeps Polly in the graph regardless. This closes P7.2 early. README.md loses its broken link syntax, its empty NuGet badges, the BrigherCommand typo and its wrong package IDs, and gains the fork provenance note and a migration section. package-readme.md was still wholly Polly's; its NuGet version badge is removed rather than repointed because nothing is published yet. docs/index.md loses the assertion that Polly is part of the .NET Foundation, which was never true of Fences. The docfx site is titled Fences, the project, not Paramore.Fences, the package. Two decisions recorded in the plan. P3.2a: carry the PublicAPI.Shipped.txt baseline forward rather than emptying it, which keeps the pre-rename oracle comparable and means the D12 rename lives in Shipped.txt. P3.7: the pollydocs.org links move to Phase 7, where the wordlist entry they depend on is changed in the same commit — the plan had contradicted itself here. Full build green: 3044 passed and 1 skipped on each of net8.0, net9.0 and net10.0, matching the baseline, and the public API diff is still the one type rename and nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Phase 4 of the fork migration. Every contributor-facing document was still Polly's, and several of them bound contributors to the .NET Foundation or routed them to Polly's Slack and issue tracker. None of that is ours to offer. Rewrite CONTRIBUTING.md on Brighter's section structure but with this repository's actual conventions, read out of .editorconfig, eng/analyzers and Stylecop.json rather than assumed from Brighter: PascalCase constants, no licence header region, expression-bodied members enforced as errors, test/ not tests/, Shouldly and NSubstitute and FsCheck, Stryker at 100%, and the .PublicAPI discipline that has no Brighter equivalent. Public API changes get their own section because it is what contributors and coding agents get wrong most often. Replace CODE_OF_CONDUCT.md with the Contributor Covenant, matching Brighter, and say plainly that Fences is not a .NET Foundation project. Rewrite SECURITY.md and .github/IRP.md around advisories on this repository, with the four maintainers as fallback. The incident response checklist gains a triage branch for a vulnerability inherited from Polly: if the code predates the fork then Polly and everything downstream of it is affected too, and disclosure has to be coordinated upstream. Add CLA.txt and wire up CLA Assistant. Sections 1 to 6 of the agreement are Brighter's verbatim; only the project name and the execution instruction change, because clahub has been defunct for years. Signatures are stored on a cla-signatures branch of this repository, so the workflow needs no token beyond GITHUB_TOKEN. Rework the issue forms and pull request template, and drop the legacy .github/ISSUE_TEMPLATE.md that the forms directory superseded. The bug form now asks whether the behaviour also occurs in Polly, which tells us straight away whether a defect is ours or inherited. Add CODEOWNERS. Nothing under src/, test/, eng/ or cake.cs changed, so the build cannot have regressed. markdownlint-cli2 reports no issues on any changed Markdown file and every changed YAML file parses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
The rename script mechanically turned Polly's cname, www.pollydocs.org, into www.fencesdocs.org — a domain nobody owns. Remove it. D8 already put the site on GitHub Pages, and there is no point buying a domain while Phase 8 is held pending the Polly negotiation: if those talks succeed the fork may be retired, and brightercommand.github.io/Fences costs nothing and works today. The line had never taken effect. The site is already served from https://brightercommand.github.io/Fences/ and the repository's Pages configuration reports cname: null, so this only stops the next deployment pointing somewhere that does not resolve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
The gh-pages workflow lints and spellchecks every Markdown file, but the step had never been run against what Phases 1 to 3 added, and it was failing on four tracked files. Give NOTICE.md's licence fence a language, and space the separator row of ADR 0002's decision table. Both ADRs tripped MD025 by having a front matter title and a body heading. That is not a defect in the documents: the front matter title is page metadata for docfx and the body heading is the document's own, which is what Nygard's format calls for. Configure MD025 to stop counting front matter as an h1, so every ADR we write from here passes rather than just these two. Exclude fork-migration-plan.md from both gates. It is a working document that will be retired when the migration completes, MD004 wanted 86 of its bullets rewritten for style alone, and it carries around forty words the dictionary does not know. Nobody editing a migration decision should be thinking about bullet characters. Add sixteen more words to the list for the tracked files that remain in scope. Note that aspell is not available locally, so unlike the Markdown lint the spelling side is reasoned from the pipeline's filters rather than proven. Verified with the tool the workflow pins, markdownlint-cli2 0.23.2, over its exact glob set: no tracked file fails. PROMPT.md and baseline/README.md still fail but are gitignored, so CI never checks them out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Fences had Polly's AGENTS.md and an interim CLAUDE.md stub pointing at the migration plan. Neither told an agent what this repository's conventions actually are, and the conventions here are not Brighter's — several are its exact opposite. An agent carrying Brighter context into this repo gets constants, licence headers, test naming and InternalsVisibleTo wrong. Add `.agent_instructions/`, inferred from `.editorconfig`, `eng/analyzers/`, `eng/*.targets` and `cake.cs` rather than imported from Brighter, and agreeing with CONTRIBUTING.md, which stays the source of truth. `public_api.md` has no Brighter equivalent and exists because the `.PublicAPI/` baseline is what agents get wrong most often here; the layout is flat, one pair of files per project, contrary to what the migration plan assumed. Rewrite AGENTS.md as an index into it, replace the CLAUDE.md stub, and add `.github/copilot-instructions.md`. Port `.claude/commands/` from Brighter — tdd, refactor, adr, spec and bugfix — with the adaptations that would otherwise rot: `tests/` to `test/`, `master` to `main`, `When_X_Then_Y` to `Member_Scenario_Outcome`, one-test-per-file to grouping by type under test, `ALL_CAPS` constants and licence headers removed, Docker test infrastructure dropped because there is none, and the messaging examples rewritten in resilience terms. The verify steps gain this repo's real gates: the `.PublicAPI/` baseline, analyser cleanliness, the Stryker score, and re-running mdsnippets when `src/Snippets` changes. `generate_adr_index.awk` comes with them, so `docs/adr/index.md` is now generated. `specs/` and `bugfixes/` hold the state the workflows write. `.claude/settings.json` allows the read-only and build commands this repo actually uses; `dotnet nuget push` is explicitly denied while Phase 8 is held. Markdown lint is clean over the workflow's exact glob set. The word list grows by 68 entries; the spell check still cannot be run locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
A security review of the Phase 5 commit flagged allowlist entries that grant more than their names suggest. Remove `Bash(sed:*)`, which nothing in this repository uses, and `Bash(sort:*)`. Narrow `Bash(find:*)` — which permits `-exec` and `-delete` — to the three read-only forms actually needed. Pin the `npx markdownlint-cli2` allow to `markdownlint-cli2@0.23.2`, matching the version CI resolves, so an unpinned npx cannot fetch whatever the registry currently serves; `.agent_instructions/documentation.md` prints the pinned form for the same reason. Add denies for `git push --force`, `git reset --hard`, `git clean -fdx`, `gh pr merge` and `gh release create`/`upload`. Deny beats allow, including a command file's own `allowed-tools` frontmatter — and the ported commands declare `Bash(git:*)`, `Bash(gh:*)`, `Bash(dotnet:*)` and, in two cases, bare `Bash`. The frontmatter is left as ported so the files stay close to Brighter's; the deny list is what constrains them. Kept knowingly, and recorded in PROMPT.md: `Bash(awk:*)`, because the ADR index generator needs it; the dotnet and Cake build commands, because MSBuild runs `Exec` tasks and Cake scripts are arbitrary C#, so there is no way to allow building this repository without it; and `cat`/`echo`, whose redirection permits writes. `Bash(dotnet nuget push:*)` was already denied and stays denied, so the Phase 8 publication hold survives the `Bash(dotnet:*)` the spec and bugfix commands grant themselves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Fences carries Polly's history but has no upstream remote, so drift is invisible unless someone checks for it. Upstream main was 5 commits ahead of our fork point 47e3b41: all Dependabot, no source changes, no bug fixes, no public API change. Every one of our pre-bump values matched upstream's exactly, so this is a clean apply rather than a merge — 8 versions in Directory.Packages.props and 3 github/codeql-action pins (v4.37.6 to v4.37.7) across code-ql.yml and ossf-scorecard.yml. Polly.Core stays at 8.7.0, which is still upstream's latest release. The direct reference from src/Snippets and samples/Chaos is deliberate and is not what this commit is about. Verified with a full Cake Default run: 3044 passed / 1 skipped on each of net8.0, net9.0 and net10.0, matching the pre-rename baseline; solution build clean; AOT validation green; public API diff still the D12 rename and nothing else. The xunit.runner.visualstudio 3.1.5 to 4.0.0 major bump changed no test outcome. One new warning comes with Microsoft.NET.Test.Sdk 18.9.0: compiling cake.cs emits CS7022, because the script declares ProjectType=Test and 18.9.0 injects an entry point that collides with its top-level statements. It is cosmetic and does not fail the build, but it is new and it is recorded in PROMPT.md. Fences inherited Polly's dependabot.yml with the same groupings, so once this branch reaches main these bumps arrive on their own. This hand-sync was only needed because the branch is unmerged and Dependabot has raised nothing yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Polly's workflows were good, and most of them survive. What could not survive was everything wired to infrastructure Fences does not have: an Azure Key Vault code-signing certificate, a Codecov account, and two GitHub Apps in the App-vNext organisation. Those steps were not merely misnamed — they could not run at all. Delete three workflows outright. updater-approve.yml gates on a bot account that will never exist; nuget-packages-published.yml is driven by the repository_dispatch that goes with the app token; update-dotnet-sdk.yml calls a reusable workflow out of a Polly maintainer's personal repository, which is an external dependency and an app token we would otherwise have to provision. Dependabot still covers NuGet packages and global.json can be bumped by hand. Drop Authenticode signing, per D6, matching Brighter and Darker. The sign job goes, and with it eng/signing/filelist.txt and the authenticodelint and sign tool entries. validate-signed-packages becomes attest-packages: with signing gone it duplicated validate-packages' dotnet validate run and its signature step had nothing to inspect, so what survives is the SBOM download, the extract and actions/attest, still gated on a tag. Drop Codecov, per P6.3, rather than provision another secret on day one. Coverage is still collected by coverlet and published as the coverage-<os> artefact and a job step summary. The badge goes from package-readme.md, the claim from CONTRIBUTING.md, and codecov/codecov-action from the Dependabot auto-approve list. Resolve the POLLY_UPDATER_BOT and POLLY_REVIEWER_BOT secrets by falling back to GITHUB_TOKEN in the three workflows that outlive them, rather than creating two GitHub Apps — the same call already made for cla.yml. Two consequences are worth knowing. Pull requests opened by after-release.yml and on-push-do-docs.yml will not trigger build.yml, because GitHub suppresses workflow runs for events raised with GITHUB_TOKEN. And GITHUB_TOKEN cannot hold workflows: write, which the app token did, so dependabot-approve.yml cannot auto-merge a pull request touching .github/workflows; the github-actions group needs merging by hand. Delete eng/bump-version.ps1 rather than repair it. It was broken twice over: System.Version cannot parse a SemVer pre-release such as 9.0.0-alpha001, and there are no Paramore.Fences* PackageVersion entries in Directory.Packages.props for it to update, because our samples use ProjectReference where Polly's took package references. It had nothing to do. Arm publish-nuget rather than gate it, per D13, which reverses the Phase 8 hold. Holding traded away two things it should not have: the package identifiers, which anyone can take while we wait, and user feedback, which cannot come from a repository nobody can consume. Both are bought back by publishing pre-releases, which make no compatibility promise. Versions are 9.0.0-alpha001 onwards — zero-padded and undotted, because 9.0.0-alpha.001 is invalid SemVer and the padding is what makes alpha010 sort after alpha009. None of this is configured in MSBuild; MinVer takes the tag verbatim. README.md and package-readme.md now both say that Fences may be retired if App vNext reverses the Open Source Maintainers Fee, and that migrating back is the same change in reverse. D13 rests on that wording. Publishing by hand remains denied in .claude/settings.json: CI publishes, from a tag, and nothing else. The fork guards and the SDK pin needed verification, not edits. NUGET_USER plus a nuget.org trusted-publisher policy is now the only secret the repository still needs; STRYKER_DASHBOARD_API_KEY is inert, because dashboard is not among the configured Stryker reporters. No file under src/, test/ or eng/analyzers/ changed, so the suite was not re-run. Verified with dotnet tool restore, dotnet mdsnippets --validate-content (no rewrites), a YAML parse of all thirteen remaining workflows, and markdownlint over CI's exact glob set, which is clean on every tracked file. actionlint and zizmor still cannot run in this environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
The rename script deliberately skipped every Markdown file, because renaming prose mechanically manufactures falsehoods rather than merely reading clumsily. That left the documentation describing Polly while the code described Fences. This is the editorial pass that reconciles them. Four things were broken rather than merely stale, and none were in the plan. Thirty-one `xref:Polly.*` cross-references pointed at a namespace that no longer exists; they are now `xref:Paramore.Fences.*` and resolve against the generated API metadata. `docs/community/toc.yml` referenced `fences-contrib.md`, a file that never existed — Phase 2 renamed the structural toc but not the prose page it pointed at. The telemetry documentation was factually wrong, describing a `Polly` meter and logger and `resilience.polly.*` instruments where the code emits `Paramore.Fences` and `resilience.fences.*`. And the illustrative stack traces showed `at Polly.ResiliencePipeline` in `/_/src/Polly.Core/`. Every `pollydocs.org` link becomes the equivalent path under `https://brightercommand.github.io/Fences/`, verified live: the extensionless deep-link form resolves and a bogus path returns a real 404. The ones visible in `docs/**` and `README.md` all sat inside generated snippet regions, so the fix is in `src/Snippets/Docs/*.cs` with the Markdown regenerated. `fencesdocs` leaves the spellcheck wordlist in the same commit, as P7.3 required. Prune `docs/community/`. `polly-contrib.md` describes Polly's GitHub organisation and Slack, which Fences has no equivalent of. `resources.md` was thirty-three third-party article and podcast titles that cannot be rewritten without misquoting their authors. `git-workflow.md`, `http-client-integrations.md` and `libraries-and-contributions.md` are kept and rewritten; the last of these now says whose acknowledgements it carries. Delete the Polly- and Simmy-branded binaries: ten cheat-sheet PDFs, their ten PowerPoint sources, and the Simmy logo. They cannot be regenerated here, and shipping another project's branding from the Fences site is the most visible inconsistency available to us. Recreating the cheat sheets is separate work. Some documentation stays Polly-facing on purpose. `http-client-integrations.md` and `samples/Chaos/README.md` now lead with the bounded claim: because `Microsoft.Extensions.Http.Resilience` hands its callback a Polly `ResiliencePipelineBuilder`, their samples use Polly's API and cannot do otherwise. `migration-v8.md` and `v7/extensibility.md` are Polly's v7-to-v8 material, reframed as API generations rather than product versions — Fences ships the pre-v8 API as `Paramore.Fences`, so the guide is genuinely useful, but there was never a Fences v7 and both pages say so. Upstream citations survive untouched. Fix `generate_adr_index.awk`, which was emitting two CI failures: a relative link escaping the docfx root, and a table delimiter that MD060 rejects. A previous session had hand-fixed the generated file, which that file forbids, so regenerating reintroduced both. The bare-substitution trap is real and worth recording. Even behind a sentinel list protecting the upstream citations, the automated pass produced "Fences is a community fork of [Fences]", claimed Polly-Samples were "written for Fences", and corrupted a Polly wiki URL into a page that does not exist. All were caught by reading the diff. Verified: `docfx metadata` and `docfx build` both exit 0 with 0 warnings from a clean `_site` — the first successful site build since the rename — with the resolved xrefs spot-checked in the generated HTML. `dotnet mdsnippets` regenerated with no drift, `dotnet build src/Snippets` is 0 warnings / 0 errors, and markdownlint over CI's exact glob set is clean on every tracked file. No behavioural code changed; the only files under `src/` touched are READMEs and comments inside snippet regions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Trusted publishing needs an owner chosen before the first push, and the choice is effectively irreversible: a policy applies to all packages owned by the selected owner, and all five Paramore.Fences IDs are new, so the first push creates them owned by whoever the short-lived key is scoped to. Getting this wrong means nuget.org's manual co-owner invite flow to correct it. Record D14: the packages and the policy are owned by the BrighterCommand account, not a personal one. P8.1 is the strongest reason — a prefix reservation is granted to an owner, and the application's own argument is that 120 of the 121 Paramore.* packages belong to BrighterCommand. Publishing Fences personally would exempt it from the reservation being applied for. Beyond that: all 123 existing Paramore.* packages are owned by BrighterCommand, verified against the nuget.org search API; and D11 names four maintainers, only one of whom could administer personally-owned packages. Expand P8.3 from a one-line reminder into the full walkthrough, since it is now the only thing between the branch and a first publish: the five policy fields with our values, the two GitHub-side items that do not exist yet, the 1-hour key lifetime and single-use token, and the 7-day pending-activation window that should not apply to us because the repository is public and not a fork. Two uncertainties are recorded rather than papered over. NUGET_USER is set to BrighterCommand on the reasoning that nuget.org organizations and users share one account namespace, but neither Microsoft Learn nor the NuGet/login README states what an org-owned policy expects — so the note says to try the personal username if the exchange returns 403. And whether BrighterCommand is formally an organization or a shared user account could not be determined from its profile page; the Trusted Publishing page settles it on sight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Fences-Policy was created on nuget.org on 2026-08-24. Every field matches the workflow: package owner BrighterCommand per D14, glob Paramore.Fences* (which covers all five IDs including the bare Paramore.Fences), workflow build.yml, environment NuGet.org matching publish-nuget's environment key exactly. The numeric GitHub IDs it captured, Repository Owner #24714628 and Repository #1338118962, match the GitHub API. Because it captured those IDs on creation the policy is permanently active, not temporarily active for seven days, so the pending-activation caveat P8.3 warned about never applied. The open question about whether BrighterCommand is an organization is also closed: it was selectable as package owner, and GitHub reports the owner type as Organization. Record two things learned since. Guessing NUGET_USER wrong is cheap, because NuGet/login runs before dotnet nuget push, so a bad value fails the job with nothing published and the failed job can be re-run once the secret is fixed. And because we publish from a tag, the NuGet.org environment's deployment branches and tags rule must stay permissive or carry a matching tag pattern, or the job will never start. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
| steps: | ||
|
|
||
| - name: Check the contributor has signed the CLA | ||
| uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 |
Three failures on PR #2, the first time any of this work has run through CI. **Windows build.** `__ValidateDocs` failed with a missing snippet: `.agent_instructions/documentation.md` illustrates what a MarkdownSnippets region looks like, and does so with a literal `<!-- snippet: ... -->` marker at the start of a line inside a fenced block. MarkdownSnippets matches the marker regardless of the fence and cannot find a matching region, because none exists — the name is an example. It passed on macOS and Ubuntu because MarkdownSnippets does not descend into dot-prefixed directories there, they being hidden, but does on Windows. That silent platform divergence is the real defect, so the fix is to exclude `.agent_instructions` and `.claude` in `mdsnippets.json`: they are agent guidance, never published documentation, and MarkdownSnippets has no business rewriting them on any platform. The equivalent markers in `CONTRIBUTING.md` are inline in backticks and were never matched. **Spellcheck.** Sixteen words, from the one CI gate that cannot be run locally — `aspell` is not installable here, so `.github/wordlist.txt` had been grown on reasoning alone across four sessions. Fifteen are added to the wordlist. Most predate this branch's documentation work and are simply prose that had never been checked: `sexualized` is Contributor Covenant text and must not be edited, and `Brighter's`, `Nygard's` and `vNext's` are possessives of proper nouns. The sixteenth, `Fences's`, is not a dictionary gap but an artefact of the Phase 7 rename pass turning "Polly's" into it. A name ending in s does not take that possessive. The two occurrences in spellchecked documentation are reworded to avoid the possessive entirely, and the remaining nine become `Fences'`. Also learned, and worth recording: the spellcheck does **not** cover dot-directories. Zero files under `.agent_instructions` or `.claude` were processed, so the claim that both are inside both CI gates was wrong. `dependency-review` also failed, but on a repository setting rather than anything in the tree — Dependency graph is not enabled — so it is untouched here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
The remaining CI failure was the spell check, which every previous session recorded as impossible to run locally because `aspell` was not installed. That was wrong: `brew` is available. Installing it, plus `pyspelling` and `pymdown-extensions` in a virtualenv, runs CI's exact configuration in seconds. Doing so turns an iterative push-and-see loop into a local check, and it found a word CI had not reported yet. Six entries are added to the wordlist, all of them legitimate prose in the two ADRs, which were written in Phase 1 and had never been spell-checked: migrators, Pryce's, reviewable, unprefixed, unquantified and unresolvable. Tracked-file failures are now zero locally. Record the recipe in `.agent_instructions/documentation.md`, with the two things that make it usable. `pymdown-extensions` is required, because `spellcheck.yml` asks for `pymdownx.superfences` and `pyspelling` dies without it. And failures must be filtered to files `git ls-files` lists: `pyspelling` walks the working tree, so it reads gitignored files that CI never checks out, which is the difference between 51 reported words and the 6 that mattered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
Running the spell check locally makes `pyspelling` compile `.github/wordlist.txt` into a binary `dictionary.dic` in the repository root. It is build output, not source, and the previous commit picked it up. Remove it and ignore it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebrands the detached Polly 8.7.0 fork as Fences and makes it an independent project. Seventeen commits, phases 0–7 of
fork-migration-plan.md.This is the first time any of this work has run through CI, so the point of the pull request is as much to exercise the rewritten build as to review the diff.
What this does
NOTICE.md,ACKNOWLEDGEMENTS.mdand ADR 0002 record provenance.LICENSEretains App vNext's copyright alongside ours, as BSD-3-Clause requires.CHANGELOG.mdis deliberately untouched — it is the historical record of upstream.Polly.*→Paramore.Fences.*across 885 files: namespaces, assemblies, package IDs, project and folder names, telemetry (resilience.polly.*→resilience.fences.*). A newFences.snk;Polly.snkis gone. Exactly one public type is renamed,PollyServiceCollectionExtensions→ResilienceServiceCollectionExtensions.CONTRIBUTING.md,CODE_OF_CONDUCT.md,SECURITY.md,CLA.txt,CODEOWNERS, issue forms..agent_instructions/,AGENTS.md,CLAUDE.md,.claude/commands/.GITHUB_TOKEN.publish-nugetis armed and tag-gated.xref:cross-references, a broken toc entry, and telemetry docs that described names the code no longer emits.What is deliberately still Polly
Not misses:
Microsoft.Extensions.Http.Resiliencehas a hard dependency on Polly, and hands its callback a PollyResiliencePipelineBuilder. Fences cannot remove Polly from an application that callsAddStandardResilienceHandler.docs/community/http-client-integrations.mdandsamples/Chaosuse Polly's API on purpose and say so.docs/migration-v8.mdanddocs/v7/extensibility.mdare Polly's v7→v8 material, reframed as API generations — Fences ships the pre-v8 API asParamore.Fences, but there was never a Fences v7.App-vNext/Pollyissues and wiki,Polly-Contrib,Polly.Contrib.WaitAndRetry.CHANGELOG.mddescribes Polly releases up to the fork point.Verification so far
docfx metadataanddocfx buildboth exit 0 with 0 warnings from a clean_site.dotnet mdsnippets --validate-contentclean; markdownlint over CI's exact glob set clean on every tracked file.actionlint,zizmor, and theaspellspellcheck. All 13 workflows were YAML-parsed instead.After merge
Fences publishes pre-releases —
9.0.0-alpha001onwards — to secure the package identifiers and gather feedback.README.mdandpackage-readme.mdboth state that the fork may be retired if App vNext reverses the Open Source Maintainers Fee, and that migrating back is the same change in reverse.🤖 Generated with Claude Code
https://claude.ai/code/session_014HCSZBA6HSzikr1XTg7bPp