<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mpyw</title>
    <description>The latest articles on DEV Community by mpyw (@mpyw).</description>
    <link>https://dev.to/mpyw</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F46604%2F44efe870-be44-4c01-8510-50283caa490e.png</url>
      <title>DEV Community: mpyw</title>
      <link>https://dev.to/mpyw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mpyw"/>
    <language>en</language>
    <item>
      <title>declscope: a linter that brings file-scoped private to Go's flat packages</title>
      <dc:creator>mpyw</dc:creator>
      <pubDate>Wed, 16 Sep 2026 21:18:02 +0000</pubDate>
      <link>https://dev.to/mpyw/declscope-a-linter-that-brings-file-scoped-private-to-gos-flat-packages-2en9</link>
      <guid>https://dev.to/mpyw/declscope-a-linter-that-brings-file-scoped-private-to-gos-flat-packages-2en9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is an English version of &lt;a href="https://zenn.dev/mpyw/articles/go-declscope-file-scoped-private" rel="noopener noreferrer"&gt;an article I wrote in Japanese&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The flat package
&lt;/h2&gt;

&lt;p&gt;Opinions differ, but one school in Go says: few packages, large ones. &lt;strong&gt;Flat packages.&lt;/strong&gt; Go against it, and split a package just to get a boundary, and you pay for it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Import cycles&lt;/strong&gt;, and the interfaces you write only to invert a dependency and break them&lt;/li&gt;
&lt;li&gt;Every name the two halves share has to be exported. You wanted a boundary between two files, and you published an API. Keeping the exposure down means &lt;strong&gt;an &lt;code&gt;internal/&lt;/code&gt; layer at every boundary you draw&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A wrong boundary costs more. Moving a declaration between files disturbs little. Moving it between packages tends to break every importer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So most Go code is said to be healthier flat. Going flat does not make the problem go away, though. The flat-package school cuts both ways. Go has exactly two levels of visibility.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Reach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Every importing package&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unexported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Every file in the package&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no third level, no file scope. In a flat package, the smallest helper is a package-wide name, and any field can be written from anywhere in the package.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This helper belongs to this file, so do not call it from another file.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The convention may well be sound. The only place it exists is in the head of the person who wrote the package. You can restate it in a comment every time, and the compiler still does not know it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can an AI agent keep the discipline?
&lt;/h2&gt;

&lt;p&gt;In the age of agentic AI, that unwritten understanding tears easily. An agent finds an unexported helper in scope, so it calls it. It sees an unexported field, so it writes to it. Each choice compiles, and a quick review lets it through. Comments help somewhat, never completely. AI writes fast, so the debt piles up just as fast.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Just write it in CLAUDE.md,"&lt;/strong&gt; you say? I did. Even then, I could not make it hold. A convention in natural language cannot be checked deterministically, so it can only ever work probabilistically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let a machine check the convention instead. The package stays flat, the boundary stays inside it, and a dedicated linter does the checking. When an agent crosses a boundary, it is told what it crossed and how to repair it, and the next agent reads that decision. Run that loop.&lt;/p&gt;

&lt;p&gt;So I built it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mpyw/declscope" rel="noopener noreferrer"&gt;https://github.com/mpyw/declscope&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mise use &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="s2"&gt;"github:mpyw/declscope"&lt;/span&gt;   &lt;span class="c"&gt;# go install and go tool work too&lt;/span&gt;
declscope ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it reports
&lt;/h2&gt;

&lt;p&gt;Picture one &lt;code&gt;database&lt;/code&gt; package, with a repository implementation per entity. A common enough layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;user_repository.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRowContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;`SELECT id, email FROM users WHERE id = ?`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scanUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// turn a raw result set into a domain entity&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;scanUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;order_repository.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;OrderRepository&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// elided: run the query and get rows&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;scanOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// elided&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// turn a raw result set into a domain entity&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;scanOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuyerEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two helpers do the same job. They pack a raw result set into a domain entity, nothing more. Really, both want to be called &lt;code&gt;scan&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But a flat package can declare &lt;strong&gt;only one &lt;code&gt;scan&lt;/code&gt;.&lt;/strong&gt; So we write them apart by hand, as &lt;code&gt;scanUser&lt;/code&gt; and &lt;code&gt;scanOrder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Look closely at what just happened. The &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Order&lt;/code&gt; in those names state ownership: which repository each helper belongs to. Something we did to dodge a collision has turned into a convention.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;the compiler does not know that convention.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The crossing an AI agent makes
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;normalizeEmail&lt;/code&gt; was written in &lt;code&gt;user_repository.go&lt;/code&gt;, for &lt;code&gt;scanUser&lt;/code&gt;. Yet &lt;code&gt;scanOrder&lt;/code&gt; calls it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuyerEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// this one belongs to user_repository.go&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler accepts it. It is just a call to an unexported function in the same package. A review can easily miss it, and &lt;strong&gt;an agent simply uses whatever it finds in scope.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;declscope catches it.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declscope ./...
&lt;span class="go"&gt;user_repository.go:21:6: func normalizeEmail is private to namespace "userRepository", but is used from namespace "orderRepository"
order_repository.go:21:20:      used here, in namespace "orderRepository"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A crossing has two answers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Keep the boundary&lt;/td&gt;
&lt;td&gt;Move the call inside the namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share on purpose&lt;/td&gt;
&lt;td&gt;Write &lt;code&gt;//declscope:package&lt;/code&gt;. &lt;code&gt;-fix&lt;/code&gt; inserts it for you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Which one fits here? Normalizing an email address belongs to neither repository. So what we actually want is &lt;strong&gt;a third place&lt;/strong&gt;: create &lt;code&gt;email.go&lt;/code&gt; and move it there.&lt;/p&gt;

&lt;p&gt;Except &lt;code&gt;boundary&lt;/code&gt; does not go quiet on the move alone. Put it in &lt;code&gt;email.go&lt;/code&gt; and it is still used from both &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;order&lt;/code&gt;. You move it out, and then you &lt;strong&gt;state that it is shared&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;email.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="c"&gt;//declscope:package&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line stays in the source as &lt;strong&gt;a statement that this is shared&lt;/strong&gt;. The next person to open the file reads it first, and so does the next agent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-fix&lt;/code&gt; can insert &lt;code&gt;//declscope:package&lt;/code&gt; for you, &lt;strong&gt;but not in this case.&lt;/strong&gt; All &lt;code&gt;-fix&lt;/code&gt; can do is widen a declaration where it stands. It cannot move one across files. Leave it to the tool and &lt;code&gt;normalizeEmail&lt;/code&gt; becomes shared while sitting in &lt;code&gt;user_repository.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-fix&lt;/code&gt; always widens, because that is the repair a tool can apply mechanically. &lt;strong&gt;Deciding where a declaration should live is exactly the judgment you want an AI agent to make.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Only two things to learn
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The file name is the namespace
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;namespace&lt;/strong&gt; is the unit within which a &lt;code&gt;private&lt;/code&gt; declaration may be used. By default each file is its own namespace, named after the file in lowerCamelCase (&lt;code&gt;user_repository.go&lt;/code&gt; becomes &lt;code&gt;userRepository&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Suffixes that follow Go's own conventions are dropped: &lt;code&gt;*_test.go&lt;/code&gt;, &lt;code&gt;*_windows.go&lt;/code&gt;, &lt;code&gt;*_darwin.go&lt;/code&gt; and the like.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When one unit spans several files, write a directive before the package clause to join a &lt;strong&gt;shared namespace&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;user_repository.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//declscope:namespace user&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The core namespace
&lt;/h4&gt;

&lt;p&gt;There is one more: &lt;strong&gt;a namespace with no name&lt;/strong&gt;, one per package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;client.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//declscope:core&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Several files may carry &lt;code&gt;//declscope:core&lt;/code&gt;, and they share the one core namespace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To &lt;code&gt;boundary&lt;/code&gt;, the core is just one more unnamed namespace.&lt;/strong&gt; Nothing is special about it. A core declaration is private to the core by default, and reaching it from outside is reported like any other crossing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;client.go:4:6: func doSomething is private to the core namespace, but is used from namespace "other"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it matters is &lt;code&gt;qualify&lt;/code&gt;, below. &lt;strong&gt;The core has no name, so &lt;code&gt;qualify&lt;/code&gt; asks nothing of its declarations.&lt;/strong&gt; It is the escape hatch for the file that carries the package's own subject, and for utilities used from everywhere, so that neither is forced to wear a meaningless prefix.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Files in the core share one namespace, so they can reach each other's private declarations. Throw everything into the core and that part is a flat package again. Use it sparingly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  There are only two scopes: &lt;code&gt;package&lt;/code&gt; and &lt;code&gt;private&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Rust equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Usable anywhere in the package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pub(super)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Usable only inside its own namespace&lt;/td&gt;
&lt;td&gt;No modifier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no &lt;code&gt;public&lt;/code&gt;. Go already spells that with a capital letter, and a use beyond the package edge is outside what declscope examines.&lt;/p&gt;

&lt;p&gt;By default an exported declaration is &lt;code&gt;package&lt;/code&gt;, and everything else is &lt;code&gt;private&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four rules
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;The question it asks&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;boundary&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;May this file touch that declaration?&lt;/td&gt;
&lt;td&gt;Always on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;qualify&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reading this name on its own, can you tell which concern owns it?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Off&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;surplus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Is that shared declaration really needed?&lt;/td&gt;
&lt;td&gt;On&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;directive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Does that directive decide anything?&lt;/td&gt;
&lt;td&gt;Always on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The bottom two need little explanation. &lt;code&gt;surplus&lt;/code&gt; reports a &lt;code&gt;//declscope:package&lt;/code&gt; with no visible use from another namespace. &lt;code&gt;directive&lt;/code&gt; reports a directive that binds nothing, or is malformed. Both audit whether what you wrote is still earning its keep.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;boundary&lt;/code&gt; rule
&lt;/h3&gt;

&lt;p&gt;This is what we saw at the top. It reports &lt;strong&gt;a &lt;code&gt;private&lt;/code&gt; declaration used from outside its namespace&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It covers package-level declarations and a type's members, which are a struct's fields and an interface's method names. And the namespace has no exceptions to how it is decided. It is &lt;strong&gt;the file the declaration is physically written in&lt;/strong&gt;, and nothing else.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A member is written inside its type's declaration, so it belongs to &lt;strong&gt;the file the type is in&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A method with a receiver belongs to &lt;strong&gt;the file it is written in&lt;/strong&gt;, wherever its type lives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Splitting a type's methods across files is ordinary Go. Let us add a small query builder to the same &lt;code&gt;database&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;statement.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="c"&gt;// Statement carries the state of a query under construction&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Statement&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Table&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;wheres&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;   &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"SELECT * FROM "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheres&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" WHERE "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" AND "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;query.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="c"&gt;// the DSL that stacks conditions. Methods on Statement, gathered here&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type goes in &lt;code&gt;statement.go&lt;/code&gt;, and the DSL that stacks conditions goes in &lt;code&gt;query.go&lt;/code&gt;. This shape turns up in well-known Go libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declscope ./...
&lt;span class="go"&gt;statement.go:6:5: field Statement.wheres is private to namespace "statement", but is used from namespace "query"
query.go:5:5:   used here, in namespace "query"
query.go:5:23:  used here, in namespace "query"
statement.go:7:5: field Statement.args is private to namespace "statement", but is used from namespace "query"
query.go:6:5:   used here, in namespace "query"
query.go:6:21:  used here, in namespace "query"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting &lt;code&gt;Where&lt;/code&gt; in &lt;code&gt;query.go&lt;/code&gt; is not itself held against you. What got reported is &lt;code&gt;wheres&lt;/code&gt; and &lt;code&gt;args&lt;/code&gt;, and they were recorded &lt;strong&gt;against &lt;code&gt;statement.go&lt;/code&gt;, where they are declared&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These two files are really one concern split in two, so the thing to do is merge the namespaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;query.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//declscope:namespace statement&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;query.go&lt;/code&gt; joins the &lt;code&gt;statement&lt;/code&gt; namespace, and the errors are gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;qualify&lt;/code&gt; rule
&lt;/h3&gt;

&lt;p&gt;It is off by default, &lt;strong&gt;but I recommend turning it on.&lt;/strong&gt; It asks that a package-level declaration carry its file's namespace somewhere in its name. Where &lt;code&gt;boundary&lt;/code&gt; asks "may this file touch that declaration?", &lt;code&gt;qualify&lt;/code&gt; asks &lt;strong&gt;"reading this name on its own, can you tell which concern owns it?"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is not a demand for a prefix. Forcing a prefix often produces unnatural names, so the check is deliberately flexible about where the namespace appears.&lt;/p&gt;

&lt;p&gt;This naming rule has no effect whatsoever on the &lt;code&gt;boundary&lt;/code&gt; rule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, &lt;strong&gt;struct fields and methods in the same namespace as their type are out of scope.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take the query builder from above, where the two sit side by side. &lt;code&gt;Build&lt;/code&gt; is in &lt;code&gt;statement.go&lt;/code&gt; with its type, and &lt;code&gt;Where&lt;/code&gt; is in &lt;code&gt;query.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;statement.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Statement&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Table&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;wheres&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="c"&gt;// out of scope&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;// out of scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;query.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;// in scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declscope ./...
&lt;span class="gp"&gt;query.go:4:21: method Where does not carry namespace "query" anywhere in its name;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;               rename it to QueryWhere, or to another name that carries "query"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing is asked of &lt;code&gt;Build&lt;/code&gt;, and only &lt;code&gt;Where&lt;/code&gt; is reported. &lt;strong&gt;Two methods on the same type, in the same shape, treated differently.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What divides them is whether the method is &lt;strong&gt;foreign&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A method in the same namespace as its struct is already identified well enough by the struct's own naming. It is not made to carry the convention a second time in its own name.&lt;/li&gt;
&lt;li&gt;A method in a different namespace from its struct is treated as a &lt;strong&gt;foreign method&lt;/strong&gt;, and is asked to carry that namespace's name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course &lt;code&gt;QueryWhere&lt;/code&gt;, the name the tool suggests, is plainly not the answer. One of these would be better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merge the namespaces into one &lt;code&gt;statement&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rename &lt;code&gt;query.go&lt;/code&gt; to the narrower topic it actually covers, such as &lt;code&gt;where.go&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;For a second example, go back to &lt;code&gt;user_repository.go&lt;/code&gt; from the top of this article. &lt;code&gt;scanUser&lt;/code&gt; is declared there, on line 11. Turn this rule on, and the first thing you hear is that &lt;code&gt;scanUser&lt;/code&gt; is up for renaming.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declscope ./...
&lt;span class="gp"&gt;user_repository.go:11:6: func scanUser does not carry namespace "userRepository" anywhere in its name;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;                         rename it to userRepositoryScanUser, or to another name that carries "userRepository"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;userRepositoryScanUser&lt;/code&gt; is dreadful, obviously. But &lt;strong&gt;the report itself is right, and what is wrong is the rename it suggests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about it. Is the concern this file covers &lt;code&gt;userRepository&lt;/code&gt;? It is not. It is &lt;strong&gt;&lt;code&gt;user&lt;/code&gt;&lt;/strong&gt;. &lt;code&gt;Repository&lt;/code&gt; is in the file name and names no concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;user_repository.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//declscope:namespace user&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the namespace is &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;scanUser&lt;/code&gt; carries it, and the report goes quiet. The same goes for &lt;code&gt;order_repository.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That leaves &lt;code&gt;normalizeEmail&lt;/code&gt;. It cannot carry &lt;code&gt;user&lt;/code&gt;. And &lt;strong&gt;it cannot because it does not belong to user.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;email.go&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;

&lt;span class="c"&gt;//declscope:package&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;normalizeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move it to &lt;code&gt;email.go&lt;/code&gt; and the namespace becomes &lt;code&gt;email&lt;/code&gt;, which &lt;code&gt;normalizeEmail&lt;/code&gt; carries, so the error goes away.&lt;/p&gt;

&lt;p&gt;That clears every error in the package. &lt;strong&gt;What &lt;code&gt;qualify&lt;/code&gt; asks is not "change the name" but "do this name and the place it sits agree with each other?"&lt;/strong&gt; The answer may be a new name, or a new namespace, or a new file.&lt;/p&gt;

&lt;h4&gt;
  
  
  The settings I recommend
&lt;/h4&gt;

&lt;p&gt;The same ones declscope holds itself to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.declscope.yaml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;naming&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;qualify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ondemand&lt;/span&gt;   &lt;span class="c1"&gt;# required once a package has a second namespace&lt;/span&gt;
    &lt;span class="na"&gt;exported&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;      &lt;span class="c1"&gt;# reach exported declarations too&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;qualify: ondemand&lt;/code&gt; switches on for a package with two or more namespaces, and stays off for a package with one. A prefix repeated across everything distinguishes nothing, so it would be pointless there. This is usually the setting you want.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exported: true&lt;/code&gt; touches the public API, so adopting it will not always go smoothly. In a repository built mainly around &lt;code&gt;internal&lt;/code&gt;, it may go in with little disruption. On a new project I would take it every time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that &lt;strong&gt;a rename is never suggested for an exported declaration.&lt;/strong&gt; It is only reported. Uses outside the package are invisible to declscope, so it cannot judge whether rewriting is safe. Running &lt;code&gt;-fix&lt;/code&gt; will never change your public API behind your back.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Adopting it on an existing codebase
&lt;/h2&gt;

&lt;p&gt;For a codebase that already has crossings in bulk, use a &lt;strong&gt;baseline&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declscope baseline ./...       &lt;span class="c"&gt;# writes .declscope-baseline.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today's violations are recorded, and from then on &lt;strong&gt;only new ones&lt;/strong&gt; are reported. An entry is keyed by package, rule, namespace and declaration name, never by position, so it survives code moving within a file.&lt;/p&gt;

&lt;p&gt;And a baseline &lt;strong&gt;suppresses without endorsing&lt;/strong&gt;. Nothing is written into the source, so the rules still apply to every new declaration, and an entry disappears only when the violation is fixed. What you cleaned up shows up in &lt;code&gt;git diff&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it with an AI agent
&lt;/h2&gt;

&lt;p&gt;Back to the motivation. declscope runs &lt;strong&gt;wherever an agent's edits are checked&lt;/strong&gt;: in CI, and inside the agent's own build loop.&lt;/p&gt;

&lt;p&gt;On an existing codebase, take a baseline once at the start. The agent is then shown only the boundaries its own edits crossed.&lt;/p&gt;

&lt;p&gt;Two things separate this from a convention that is merely written down.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Effect on the agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The diagnostic names the namespace that was crossed&lt;/td&gt;
&lt;td&gt;It learns why the use is wrong, and the repair is mechanical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A directive is a durable record of intent&lt;/td&gt;
&lt;td&gt;The next agent inherits the decision instead of re-deriving it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second one is what I was really after. "This helper may be shared" used to be a decision that died in a review comment, or in somebody's memory. With declscope it stays in the source as one line of &lt;code&gt;//declscope:package&lt;/code&gt;, and it is &lt;strong&gt;the first thing the next agent reads when it opens the file&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The adoption know-how ships as a skill too
&lt;/h3&gt;

&lt;p&gt;A README is where you write what declscope &lt;em&gt;is&lt;/em&gt;. But &lt;strong&gt;what an agent trips over when introducing it to an existing repository turned out to be entirely different knowledge.&lt;/strong&gt; That part ships as a skill.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh skill &lt;span class="nb"&gt;install &lt;/span&gt;mpyw/declscope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the sort of thing it says.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A failed build reports zero diagnostics.&lt;/strong&gt; And that is indistinguishable from success. Get &lt;code&gt;go build ./...&lt;/code&gt; passing before you read any count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A baseline turns every count into zero.&lt;/strong&gt; Move it aside before you measure, or the codebase will look clean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero from &lt;code&gt;qualify&lt;/code&gt; does not mean clean.&lt;/strong&gt; It is off by default, so never report "no problems" from a zero without checking the config. The config is looked up from the analyzed package upwards, so a repository can hold several. Find them all&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;//declscope:namespace&lt;/code&gt; goes before the package clause.&lt;/strong&gt; After it, the directive is silently inert. When nothing you do moves the diagnostics, suspect the placement first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The configuration is the repository owner's decision, not the agent's.&lt;/strong&gt; Ask before writing a config file, and wait for the answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also gives an order of work. &lt;strong&gt;Clear &lt;code&gt;boundary&lt;/code&gt; first, and clear it by moving the boundary rather than widening everything. Then measure again.&lt;/strong&gt; While you clear &lt;code&gt;boundary&lt;/code&gt;, two namespaces may merge into one, which takes &lt;code&gt;ondemand&lt;/code&gt; out of force, and &lt;code&gt;qualify&lt;/code&gt; reports can vanish in a chain. Do it the other way round and you spend the day renaming things that were about to disappear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;declscope &lt;strong&gt;reads one package at a time, and counts a use only where a name is written&lt;/strong&gt;. So the following are invisible to it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operations on a struct's whole value (copying, comparing and zeroing name no field)&lt;/li&gt;
&lt;li&gt;Reflection, &lt;code&gt;//go:linkname&lt;/code&gt;, generated code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A declaration nobody uses.&lt;/strong&gt; &lt;code&gt;boundary&lt;/code&gt; works by finding uses, so unused code produces nothing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the last one, pair it with a linter for unused code, such as &lt;a href="https://pkg.go.dev/golang.org/x/tools/cmd/deadcode" rel="noopener noreferrer"&gt;&lt;code&gt;deadcode&lt;/code&gt;&lt;/a&gt;. To draw boundaries between packages, that is &lt;a href="https://github.com/OpenPeeDeeP/depguard" rel="noopener noreferrer"&gt;&lt;code&gt;depguard&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;depguard&lt;/code&gt; keeps the package graph honest, declscope keeps each package honest inside, and &lt;code&gt;deadcode&lt;/code&gt; strips what neither needs to reach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhux7xmsjxu288zo0v0rm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhux7xmsjxu288zo0v0rm.png" alt="A Go program drawn as nested frames. Between the api and database packages, depguard asks whether one package may import another. A green arrow runs from api to database, and a red one back from database to api is crossed out. Inside database, between user_repository.go and order_repository.go, declscope asks whether one file may reach another's declaration. A red arrow from scanOrder to normalizeEmail is crossed out. At the edge of the program, deadcode asks whether anything is reachable at all. The mail package sits greyed out with no arrow entering it, captioned unreachable." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go has two levels of visibility, and no file-scoped private.&lt;/strong&gt; Stay flat, and "this one belongs to this file" exists only in somebody's head&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An AI agent does not know that convention, and breaks it far faster than people used to.&lt;/strong&gt; A convention in natural language cannot be checked deterministically, so it only ever works probabilistically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;declscope checks that convention deterministically, with the package still flat.&lt;/strong&gt; A crossing always has two answers: keep the boundary, or state that the sharing is deliberate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn &lt;code&gt;qualify&lt;/code&gt; on as well.&lt;/strong&gt; Its reports do not say "change the name". They ask whether this file is a sensible place to declare this&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A directive is a durable record of a decision somebody made.&lt;/strong&gt; The next agent inherits it instead of re-deriving it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The adoption procedure ships as a skill.&lt;/strong&gt; An author can now write and distribute the instructions an agent is meant to read&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/mpyw/declscope" rel="noopener noreferrer"&gt;https://github.com/mpyw/declscope&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If anything is unclear, or a rule feels too strict, please tell me in an issue or in the comments.&lt;/p&gt;

</description>
      <category>go</category>
      <category>linter</category>
      <category>staticanalysis</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
