From 4688fc5ffb61a3a43ab409fcd1843988a29239f9 Mon Sep 17 00:00:00 2001 From: Dylan de Beer Date: Mon, 10 Aug 2026 14:01:12 +0200 Subject: [PATCH] docs: describe this project to the portfolio Carries the prose already written under .dylan into the .folio format the portfolio reads: the description becomes an overview, the story and the analyzer entry become sections, each in both published locales. Package identifiers are dropped, since GitHub releases already reach the portfolio, and featured now lives in the central config rather than here. --- .folio/content/en/analyzer.md | 22 ++++++++++++++++++++++ .folio/content/en/overview.md | 9 +++++++++ .folio/content/en/story.md | 25 +++++++++++++++++++++++++ .folio/content/nl/analyzer.md | 22 ++++++++++++++++++++++ .folio/content/nl/overview.md | 9 +++++++++ .folio/content/nl/story.md | 27 +++++++++++++++++++++++++++ .folio/locales/en.toml | 5 +++++ .folio/locales/nl.toml | 5 +++++ .folio/project.toml | 28 ++++++++++++++++++++++++++++ 9 files changed, 152 insertions(+) create mode 100644 .folio/content/en/analyzer.md create mode 100644 .folio/content/en/overview.md create mode 100644 .folio/content/en/story.md create mode 100644 .folio/content/nl/analyzer.md create mode 100644 .folio/content/nl/overview.md create mode 100644 .folio/content/nl/story.md create mode 100644 .folio/locales/en.toml create mode 100644 .folio/locales/nl.toml create mode 100644 .folio/project.toml diff --git a/.folio/content/en/analyzer.md b/.folio/content/en/analyzer.md new file mode 100644 index 0000000..aa55e2b --- /dev/null +++ b/.folio/content/en/analyzer.md @@ -0,0 +1,22 @@ +# An analyzer for the one mistake this style makes easy + +## situation + +Returning failures as values instead of exceptions means a discarded `Result` is silently +a swallowed failure. The compiler has nothing to say about it. + +## task + +Catch discarded results at build time, without asking anyone to install or enable +anything. + +## action + +Shipped `LOOM0001` inside `CodeByDylan.Loom.Results`, so it arrives with the package. +Discarding on purpose stays legal by writing `_ =`, which makes the intent visible in +review. + +## result + +Its first run against existing code found eight unchecked discards in this repository's +own test suite. diff --git a/.folio/content/en/overview.md b/.folio/content/en/overview.md new file mode 100644 index 0000000..ab9112b --- /dev/null +++ b/.folio/content/en/overview.md @@ -0,0 +1,9 @@ +# Overview + +Ten foundational **.NET packages**, versioned in lockstep, each doing one thing and +depending only on the packages below it. + +Clean Architecture with vertical slice structure, made opinionated on purpose: one way +to report a failure, one way to run a handler, one way to turn a failure into a status +code. Where a decision is forced, Loom makes it — where it is taste, it hands you the +object and gets out of the way. \ No newline at end of file diff --git a/.folio/content/en/story.md b/.folio/content/en/story.md new file mode 100644 index 0000000..0c82502 --- /dev/null +++ b/.folio/content/en/story.md @@ -0,0 +1,25 @@ +# Story + +Every new .NET service started the same way. A `Result` type, rewritten slightly +differently than last time. A folder layout argued about again. A decision about whether +a not-found is an exception or a return value, made again, and made differently. + +None of those are hard problems. That is exactly why re-deciding them is wasteful — the +cost is not the thinking, it is that two services in the same solution end up disagreeing +about what a failure looks like. + +So Loom takes the decisions away. There is one `Error`, with a stable code and one of six +categories, and that category is what decides the status code, the log level, and whether +a retry makes sense. An endpoint becomes a thin adapter with no mapping logic of its own. +Handlers are injected and called directly, so "go to definition" lands on the handler +instead of a registry. + +The harder half was deciding what to leave out. No mediator, because indirection you +cannot navigate is a cost paid on every read. No repository, because `DbContext` is +already a unit of work and wrapping it destroys the `IQueryable` composition that makes +specifications work. No option to log a request's contents — not defaulted off, but +absent, because an option is an invitation. + +Ten packages rather than one, so nothing drags in a framework you did not ask for. They +version in lockstep because a matrix of compatible versions is its own maintenance +problem, and this is meant to remove those, not add one. \ No newline at end of file diff --git a/.folio/content/nl/analyzer.md b/.folio/content/nl/analyzer.md new file mode 100644 index 0000000..a3eba1d --- /dev/null +++ b/.folio/content/nl/analyzer.md @@ -0,0 +1,22 @@ +# Een analyzer voor de ene fout die deze stijl makkelijk maakt + +## situation + +Fouten als waarde teruggeven in plaats van als exception betekent dat een weggegooide +`Result` stilzwijgend een genegeerde fout is. De compiler heeft er niets over te zeggen. + +## task + +Weggegooide resultaten opvangen tijdens het bouwen, zonder dat iemand iets hoeft te +installeren of aan te zetten. + +## action + +`LOOM0001` meegeleverd in `CodeByDylan.Loom.Results`, zodat de analyzer met het pakket +meekomt. Bewust weggooien blijft toegestaan door `_ =` te schrijven, waardoor die +bedoeling zichtbaar wordt tijdens review. + +## result + +De eerste run op bestaande code vond acht ongecontroleerde weggooiacties in de testsuite +van deze repository zelf. diff --git a/.folio/content/nl/overview.md b/.folio/content/nl/overview.md new file mode 100644 index 0000000..a7cf8d8 --- /dev/null +++ b/.folio/content/nl/overview.md @@ -0,0 +1,9 @@ +# Overzicht + +Tien fundamentele **.NET-pakketten**, in lockstep geversioneerd, die elk één ding doen +en alleen afhangen van de pakketten eronder. + +Clean Architecture met vertical slices, bewust uitgesproken: één manier om een fout te +melden, één manier om een handler uit te voeren, één manier om een fout naar een +statuscode te vertalen. Waar een keuze onvermijdelijk is, maakt Loom hem — waar het +smaak is, krijg je het object terug en gaat Loom opzij. \ No newline at end of file diff --git a/.folio/content/nl/story.md b/.folio/content/nl/story.md new file mode 100644 index 0000000..3dcea1e --- /dev/null +++ b/.folio/content/nl/story.md @@ -0,0 +1,27 @@ +# Verhaal + +Elke nieuwe .NET-service begon op dezelfde manier. Een `Result`-type, net iets anders +geschreven dan de vorige keer. Een mappenstructuur waar opnieuw over werd gediscussieerd. +De vraag of "niet gevonden" nu een exception is of een retourwaarde, opnieuw beantwoord en +opnieuw anders. + +Geen van die dingen is een moeilijk probleem. Juist daarom is het zonde om ze telkens +opnieuw te beslissen — de kosten zitten niet in het nadenken, maar in het feit dat twee +services binnen dezelfde solution het oneens blijken over hoe een fout eruitziet. + +Loom neemt die beslissingen dus weg. Er is één `Error`, met een vaste code en één van zes +categorieën, en die categorie bepaalt de statuscode, het logniveau en of opnieuw proberen +zinvol is. Een endpoint wordt daarmee een dunne adapter zonder eigen vertaallogica. +Handlers worden geïnjecteerd en direct aangeroepen, zodat "go to definition" bij de handler +uitkomt in plaats van bij een registry. + +De lastigere helft was bepalen wat er níét in moest. Geen mediator, want indirectie die je +niet kunt volgen kost je bij elke keer lezen. Geen repository, want `DbContext` is al een +unit of work, en het inpakken ervan doet de `IQueryable`-compositie teniet waar +specifications juist op leunen. Geen optie om de inhoud van een request te loggen — niet +standaard uitgeschakeld, maar afwezig, want een optie is een uitnodiging. + +Tien pakketten in plaats van één, zodat niets een framework binnenhaalt waar je niet om +hebt gevraagd. Ze worden in lockstep geversioneerd, omdat een matrix van compatibele +versies zelf weer onderhoud oplevert — en dit is bedoeld om dat soort onderhoud weg te +nemen, niet om er iets aan toe te voegen. \ No newline at end of file diff --git a/.folio/locales/en.toml b/.folio/locales/en.toml new file mode 100644 index 0000000..c810c23 --- /dev/null +++ b/.folio/locales/en.toml @@ -0,0 +1,5 @@ +project.name = "Loom" +project.tagline = "Ten .NET packages that make the boring decisions once" + +link.docs = "Guidance for projects built on Loom" +link.package = "Packages on NuGet" diff --git a/.folio/locales/nl.toml b/.folio/locales/nl.toml new file mode 100644 index 0000000..37278db --- /dev/null +++ b/.folio/locales/nl.toml @@ -0,0 +1,5 @@ +project.name = "Loom" +project.tagline = "Tien .NET-pakketten die de saaie keuzes één keer maken" + +link.docs = "Richtlijnen voor projecten op Loom" +link.package = "Pakketten op NuGet" diff --git a/.folio/project.toml b/.folio/project.toml new file mode 100644 index 0000000..5159dbd --- /dev/null +++ b/.folio/project.toml @@ -0,0 +1,28 @@ +version = 1 + +[project] +slug = "loom" +status = "active" +role = "author" +started = "2026-07" +tags = ["csharp", "dotnet", "tooling"] + +[[links]] +type = "docs" +url = "https://github.com/CodeByDylan/Loom/tree/main/docs/agents" + +[[links]] +type = "package" +url = "https://www.nuget.org/packages?q=CodeByDylan.Loom" + +[[sections]] +id = "overview" +file = "overview.md" + +[[sections]] +id = "story" +file = "story.md" + +[[sections]] +id = "analyzer" +file = "analyzer.md"