<?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: Fernando Paladini</title>
    <description>The latest articles on DEV Community by Fernando Paladini (@paladini).</description>
    <link>https://dev.to/paladini</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%2F1065831%2F79b4d650-5838-4481-a62f-8f03f4010512.jpeg</url>
      <title>DEV Community: Fernando Paladini</title>
      <link>https://dev.to/paladini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paladini"/>
    <language>en</language>
    <item>
      <title>Stop Guessing localhost Ports with portmap and TypeScript</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sat, 24 Oct 2026 12:31:02 +0000</pubDate>
      <link>https://dev.to/paladini/stop-guessing-localhost-ports-with-portmap-and-typescript-4876</link>
      <guid>https://dev.to/paladini/stop-guessing-localhost-ports-with-portmap-and-typescript-4876</guid>
      <description>&lt;p&gt;AI coding agents are very good at remembering common conventions. That is also why they can be very confident and completely wrong about &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One project may expose a Vite frontend on port 5173. Another may run an API on 8080. A Docker mapping can make the host port different from the container port, while an &lt;code&gt;.env.local&lt;/code&gt; file may still point to a service that is not running today. The resulting error often looks like CORS, authentication, or a broken API client when the real problem is a stale URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/portmap" rel="noopener noreferrer"&gt;portmap&lt;/a&gt; is a small MIT-licensed TypeScript CLI and read-only MCP server for this local-development problem. It combines declared configuration, optional OS listener data, and environment references into a &lt;code&gt;.portmap.json&lt;/code&gt; report with findings such as &lt;code&gt;PRT-04&lt;/code&gt; for an environment URL that points at a port with no listener.&lt;/p&gt;

&lt;p&gt;This tutorial uses the current repository configuration and the checked-in mismatch fixture. You will build portmap from source, run a deterministic static scan, inspect the generated findings, and understand where the tool deliberately stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/paladini/portmap.git
cd portmap
npm ci
npm run build
node dist/cli.js declare fixtures/mismatch --json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output identifies a declared Vite port and two unresolved localhost environment URLs. That gives an agent or a developer concrete evidence before changing application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20 or newer. The repository declares &lt;code&gt;engines.node&lt;/code&gt; as &lt;code&gt;&amp;gt;=20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;npm and Git.&lt;/li&gt;
&lt;li&gt;A Windows, macOS, or Linux development machine for the documented runtime listener support. The static &lt;code&gt;declare&lt;/code&gt; command does not need a running service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository is public, non-archived, non-forked, and uses the MIT license. At the time of writing, the repository reports version &lt;code&gt;0.1.0&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt; and does not provide a GitHub release, so the reproducible path below pins the source checkout by its current default branch rather than claiming a released package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build portmap from the repository
&lt;/h2&gt;

&lt;p&gt;Clone the project and install the lockfile exactly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/portmap.git
&lt;span class="nb"&gt;cd &lt;/span&gt;portmap
npm ci
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build runs TypeScript with the repository's &lt;code&gt;tsconfig.json&lt;/code&gt; and produces the &lt;code&gt;dist&lt;/code&gt; directory. The CLI entry point is &lt;code&gt;dist/cli.js&lt;/code&gt;; the package metadata also exposes a &lt;code&gt;portmap&lt;/code&gt; binary when the project is installed as a package.&lt;/p&gt;

&lt;p&gt;The current README documents &lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;npm run build&lt;/code&gt;, and &lt;code&gt;node dist/cli.js&lt;/code&gt; as the source-based installation path. It also documents &lt;code&gt;npx portmap&lt;/code&gt; as a future npm option, so do not use the latter as if a published npm package were already verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run a static topology check
&lt;/h2&gt;

&lt;p&gt;The safest first check is &lt;code&gt;declare&lt;/code&gt;. It reads project files without requiring the application's processes to be running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js &lt;span class="nb"&gt;declare &lt;/span&gt;fixtures/mismatch &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fixture is intentionally broken. Its configuration declares a Vite server on port 5173, while &lt;code&gt;.env.local&lt;/code&gt; contains references to &lt;code&gt;http://localhost:3000&lt;/code&gt; and &lt;code&gt;http://localhost:8080&lt;/code&gt;. Because no matching service is declared for those references, the report contains unresolved environment edges.&lt;/p&gt;

&lt;p&gt;The important part of the JSON is the relationship between the declared service, references, and findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"services"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"declared"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5173&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"vite.config.ts:server.port"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"down"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRT-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRT-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".env.local:1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete report includes the second &lt;code&gt;PRT-04&lt;/code&gt; for the port 8080 reference. The fixture has no listener data because this static command does not inspect the operating system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the result in a real project
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;declare&lt;/code&gt; against your application first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node /path/to/portmap/dist/cli.js &lt;span class="nb"&gt;declare&lt;/span&gt; /path/to/your-app &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the report is useful as persistent agent context, write it to the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node /path/to/portmap/dist/cli.js &lt;span class="nb"&gt;declare&lt;/span&gt; /path/to/your-app &lt;span class="nt"&gt;--write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;.portmap.json&lt;/code&gt;. The repository's schema documentation describes it as the artifact agents can read instead of guessing a URL. Whether you commit it is a project decision: committing it can make a known local topology available to future sessions, while ignoring it avoids storing machine-specific runtime data.&lt;/p&gt;

&lt;p&gt;When processes are running, the full command adds an OS listener scan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node /path/to/portmap/dist/cli.js scan /path/to/your-app &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full scan can report declared ports that are not listening, listeners without declared configuration, mismatched Docker host and container ports, and environment references that do not resolve. The README also documents &lt;code&gt;workspace&lt;/code&gt; for projects in sibling folders and &lt;code&gt;listen&lt;/code&gt; for inspecting listeners directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a read-only MCP server
&lt;/h2&gt;

&lt;p&gt;portmap includes a read-only MCP stdio server. After building, the command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node /path/to/portmap/dist/cli.js mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Cursor or Claude Code, the documented configuration points the MCP command at the built CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"portmap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/portmap/dist/cli.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exposed tools are intentionally narrow: retrieve a full report, get a smaller service graph, resolve a URL, or filter findings. The server does not start or stop services, modify application files, or infer ports with an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the reconciliation is useful
&lt;/h2&gt;

&lt;p&gt;The core design separates three kinds of evidence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Declared configuration tells you what the project says should run. portmap looks at package scripts, Vite configuration, environment URLs, and common Docker Compose port mappings.&lt;/li&gt;
&lt;li&gt;Runtime discovery tells you what the operating system says is listening. This is optional because a static check is still valuable before startup.&lt;/li&gt;
&lt;li&gt;Reconciliation connects declarations, listeners, and environment references into services, edges, and findings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation makes a failure actionable. &lt;code&gt;PRT-04&lt;/code&gt; means an environment URL points at a port with no listener, while &lt;code&gt;PRT-02&lt;/code&gt; warns about a listener without a corresponding declaration. Those are different repair paths, even though both can surface as a browser network error.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI verification
&lt;/h2&gt;

&lt;p&gt;The CLI can turn findings into a non-zero exit status. For example, this command fails when at least one error remains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--min-findings&lt;/span&gt; 1 &lt;span class="nt"&gt;--min-severity&lt;/span&gt; error &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this as a local-development guard, not as production monitoring. The project is designed for local service topology and explicitly does not provide uptime or production health checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;There are several important boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime-only ports that are hardcoded in JavaScript without a discoverable configuration can produce a &lt;code&gt;PRT-02&lt;/code&gt; warning or remain undiscovered.&lt;/li&gt;
&lt;li&gt;WSL and Docker networking can make a container listener invisible from the host perspective.&lt;/li&gt;
&lt;li&gt;The current YAML Compose support handles common &lt;code&gt;ports:&lt;/code&gt; patterns, not every Compose feature.&lt;/li&gt;
&lt;li&gt;PID-to-repository attribution is heuristic. Low-confidence matches are flagged rather than presented as certain.&lt;/li&gt;
&lt;li&gt;The MCP server is read-only, but a local MCP client still decides which project path to pass to it. Review client configuration and avoid exposing sensitive directories unnecessarily.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool reads local configuration and socket information. It is not a secret manager, a process supervisor, or a security guarantee. Keep &lt;code&gt;.env&lt;/code&gt; values out of reports and repositories when they contain credentials; the useful signal is the referenced host and port, not the secret itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducible verification
&lt;/h2&gt;

&lt;p&gt;From the clean checkout, verify the source path and fixture again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run build
node dist/cli.js &lt;span class="nb"&gt;declare &lt;/span&gt;fixtures/mismatch &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mismatch-report.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm that the JSON parses and contains &lt;code&gt;PRT-01&lt;/code&gt; plus the unresolved &lt;code&gt;PRT-04&lt;/code&gt; findings. For your own project, change one environment URL to the actual declared port, rerun the command, and compare the findings. That gives you a small, reproducible experiment instead of relying on whether an agent happens to guess correctly.&lt;/p&gt;

&lt;p&gt;The repository also includes &lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;demo:mismatch&lt;/code&gt;, and &lt;code&gt;demo:workspace&lt;/code&gt; scripts. In a clean Windows checkout, the build and static fixture declaration are useful smoke checks; if a runtime listener check behaves differently on your platform, treat that as an environment-specific result and inspect the generated report rather than assuming the listener model is universal.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does portmap replace Docker or a process manager?
&lt;/h3&gt;

&lt;p&gt;No. It reports topology. It does not start, stop, or restart services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it require an AI model?
&lt;/h3&gt;

&lt;p&gt;No. The repository describes the scanner and MCP server as deterministic and zero-LLM. The MCP interface is for letting an agent consume evidence, not for outsourcing discovery to a model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I install it from npm today?
&lt;/h3&gt;

&lt;p&gt;The README lists npm installation as a future option. The verified current path is cloning the repository, running &lt;code&gt;npm ci&lt;/code&gt;, and building locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should &lt;code&gt;.portmap.json&lt;/code&gt; be committed?
&lt;/h3&gt;

&lt;p&gt;Only if the values are stable and safe to share. A committed static report can help future agents, but machine-specific listeners and environment details may be inappropriate for a repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The practical lesson is simple: make local topology an input to debugging. Build portmap from the documented source path, run &lt;code&gt;declare&lt;/code&gt; before starting a long investigation, and let the report show which URLs are declared, which are actually reachable, and which assumptions are unsupported.&lt;/p&gt;

&lt;p&gt;If you try this in a monorepo, which mismatch would be most valuable to catch first: stale environment URLs, Docker port remapping, or cross-workspace references?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: AI assistance was used to organize and edit this tutorial. Repository behavior, commands, metadata, limitations, and verification claims were checked against the current public source and the clean checkout described above.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>tutorial</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Build a Mobile Guitar Tuner with React, Express, and PostgreSQL</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Fri, 23 Oct 2026 12:35:12 +0000</pubDate>
      <link>https://dev.to/paladini/build-a-mobile-guitar-tuner-with-react-express-and-postgresql-1f5m</link>
      <guid>https://dev.to/paladini/build-a-mobile-guitar-tuner-with-react-express-and-postgresql-1f5m</guid>
      <description>&lt;p&gt;If you want a guitar tuner that is easy to open on a phone or smartwatch, a small local web app can be more practical than a native installation. The hard part is not drawing six buttons. It is keeping the local setup reproducible while making the browser experience usable on a small screen.&lt;/p&gt;

&lt;p&gt;This tutorial walks through the current &lt;code&gt;main&lt;/code&gt; branch of &lt;a href="https://github.com/paladini/guitar-tune-trainer" rel="noopener noreferrer"&gt;Guitar Tune Trainer&lt;/a&gt;, an MIT-licensed open-source project owned by &lt;a href="https://github.com/paladini" rel="noopener noreferrer"&gt;Fernando Paladini&lt;/a&gt;. You will install the project, start its PostgreSQL service, run the checks and production build, and understand what the repository currently does and does not provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The project is a React and Vite front end served by an Express application. Its documented local path is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Node.js 18+, npm, and PostgreSQL support.&lt;/li&gt;
&lt;li&gt;Clone the repository and run &lt;code&gt;npm install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Start PostgreSQL with the included Docker Compose file, or use another local database.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;.env.sample&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt; and set a local &lt;code&gt;SESSION_SECRET&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm run db:push&lt;/code&gt;, then &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The current repository also passes &lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;npm run check&lt;/code&gt;, and &lt;code&gt;npm run build&lt;/code&gt; on commit &lt;code&gt;26cfe55744aad30f2d30f03cdf70244bc0a91809&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18 or newer&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;li&gt;Docker Desktop with Compose, or a reachable PostgreSQL database&lt;/li&gt;
&lt;li&gt;A modern browser if you want to use the mobile-oriented interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository has no GitHub release or stable tag at the time of writing. Treat the commands below as instructions for the current &lt;code&gt;main&lt;/code&gt; branch, not as a promise that a future release will have identical dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start PostgreSQL locally
&lt;/h2&gt;

&lt;p&gt;The repository includes a small &lt;code&gt;docker-compose.yml&lt;/code&gt; that starts PostgreSQL 16 with a named volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checked-in Compose configuration uses these local development values:&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;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;guitar_tune_trainer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It publishes PostgreSQL on port &lt;code&gt;5432&lt;/code&gt;. These are development credentials from the repository configuration. Do not reuse them on a shared or internet-exposed database.&lt;/p&gt;

&lt;p&gt;If port &lt;code&gt;5432&lt;/code&gt; is already occupied, either stop the conflicting service or change the host-side port mapping. If you change the host port, update the connection string accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and configure the app
&lt;/h2&gt;

&lt;p&gt;Clone the project and install the lockfile dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/guitar-tune-trainer.git
&lt;span class="nb"&gt;cd &lt;/span&gt;guitar-tune-trainer
npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current &lt;code&gt;package.json&lt;/code&gt; identifies the package as version &lt;code&gt;1.0.0&lt;/code&gt;, uses ES modules, and declares the MIT license. Its scripts include &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;check&lt;/code&gt;, and &lt;code&gt;db:push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create the local environment file from the checked-in example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.sample .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use this equivalent command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Copy-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgres://postgres:postgres@localhost:5432/guitar_tune_trainer
SESSION_SECRET=your-session-secret
PORT=5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your-session-secret&lt;/code&gt; with a random value before using the app beyond a disposable local session. Keep &lt;code&gt;.env&lt;/code&gt; out of version control. The repository's &lt;code&gt;.gitignore&lt;/code&gt; is intended to support that local configuration pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply the database schema and run the app
&lt;/h2&gt;

&lt;p&gt;The repository's Drizzle configuration reads &lt;code&gt;DATABASE_URL&lt;/code&gt;, and the documented schema currently defines a &lt;code&gt;users&lt;/code&gt; table with a serial ID, unique username, and password field. Apply that schema with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run db:push
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Express entry point defaults to port &lt;code&gt;5000&lt;/code&gt;, listens on &lt;code&gt;0.0.0.0&lt;/code&gt;, and serves the Vite development app after registering the server routes. Open &lt;a href="http://localhost:5000" rel="noopener noreferrer"&gt;http://localhost:5000&lt;/a&gt; in a browser.&lt;/p&gt;

&lt;p&gt;The HTML entry point is explicitly mobile-oriented. It sets a responsive viewport, describes the page as a smartwatch and mobile guitar tuner, links a web manifest, and includes Apple web-app metadata. The README describes the project as a PWA with offline support. Verify those capabilities in the browser you intend to use rather than assuming every browser exposes the same install prompt or audio behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the current code actually provides
&lt;/h2&gt;

&lt;p&gt;The project is easiest to understand as two layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client is a React application bundled with Vite and styled with Tailwind CSS and component primitives.&lt;/li&gt;
&lt;li&gt;The server is an Express host with Drizzle-related database configuration and a memory-backed user storage implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction matters. The current &lt;code&gt;server/routes.ts&lt;/code&gt; creates the HTTP server but does not register application API endpoints. The storage class keeps users in a process-local &lt;code&gt;Map&lt;/code&gt;, while the shared schema describes PostgreSQL tables. The repository therefore demonstrates a runnable full-stack shell and a mobile tuner experience, but it is not yet a complete account-backed production service.&lt;/p&gt;

&lt;p&gt;This is also why the first verification target should be the local page and build, not a claim that user data is persisted across restarts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducible verification
&lt;/h2&gt;

&lt;p&gt;From a clean checkout, run the same checks used for this tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run check
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; installs the exact versions recorded in &lt;code&gt;package-lock.json&lt;/code&gt;. &lt;code&gt;npm run check&lt;/code&gt; runs TypeScript checking through &lt;code&gt;tsc&lt;/code&gt;. &lt;code&gt;npm run build&lt;/code&gt; runs the Vite client build and bundles the Express server with esbuild into &lt;code&gt;dist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the current commit, all three commands completed successfully. The install also reported 35 audit findings in the dependency tree: 3 low, 11 moderate, 20 high, and 1 critical. That result is a reason to review and update dependencies before exposing the app publicly, not a reason to claim that the application is secure.&lt;/p&gt;

&lt;p&gt;After the build, you can use the production-shaped command locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application still requires the configured environment and database assumptions. Stop it with &lt;code&gt;Ctrl+C&lt;/code&gt; when the smoke test is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and boundaries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PostgreSQL does not start
&lt;/h3&gt;

&lt;p&gt;Check whether port &lt;code&gt;5432&lt;/code&gt; is already in use and inspect the Compose logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
docker compose logs db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use a managed PostgreSQL provider instead, set &lt;code&gt;DATABASE_URL&lt;/code&gt; to that provider's connection string and follow its TLS requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The browser cannot access audio features
&lt;/h3&gt;

&lt;p&gt;The repository presents reference-tone functionality as part of the tuner experience, but browser audio policies vary. Test on the target phone or watch, allow audio when prompted, and do not assume that a desktop result reproduces on a wearable browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database changes appear not to persist
&lt;/h3&gt;

&lt;p&gt;The schema and database configuration do not mean every feature is backed by PostgreSQL. The current route file has no registered application endpoints, and &lt;code&gt;MemStorage&lt;/code&gt; stores users only in memory. Treat persistence as an unfinished boundary until the implementation changes and is verified.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development credentials leak into deployment
&lt;/h3&gt;

&lt;p&gt;The Compose file intentionally uses simple local credentials. Replace them, restrict network exposure, use a secret manager, and rotate any credential that may have been copied outside a disposable development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I run it without Docker?
&lt;/h3&gt;

&lt;p&gt;Yes. The README lists local, Docker, or Neon PostgreSQL as options. Set &lt;code&gt;DATABASE_URL&lt;/code&gt; to the database you control and run the same schema command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is there a stable release to pin?
&lt;/h3&gt;

&lt;p&gt;No GitHub release or tag was present during this review. Pin a commit when reproducibility matters, and recheck the README and package metadata before upgrading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this a production-ready authentication system?
&lt;/h3&gt;

&lt;p&gt;No. The current repository contains a schema and in-memory storage skeleton, not a completed authentication flow. Do not put real credentials into it without implementing and reviewing authentication, password hashing, session handling, authorization, and database persistence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Guitar Tune Trainer is a useful example of a small mobile-first full-stack web project: the command surface is compact, the browser entry point is designed for small screens, and the build is easy to verify locally. Its honest value today is the runnable interface and development scaffold. Its server-side persistence and security model still need deliberate engineering before deployment.&lt;/p&gt;

&lt;p&gt;What would you build next: browser microphone pitch detection, a completed persisted account flow, or a smaller offline-only tuner with no server at all?&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assistance disclosure
&lt;/h2&gt;

&lt;p&gt;AI assistance was used to organize this tutorial and review the documented commands against the public repository. The repository behavior, configuration values, limitations, and validation results were checked against the cited primary sources and a clean local checkout.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Turn Spec Drift into a Pull Request Check with SpecGov</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:34:30 +0000</pubDate>
      <link>https://dev.to/paladini/turn-spec-drift-into-a-pull-request-check-with-specgov-1eo7</link>
      <guid>https://dev.to/paladini/turn-spec-drift-into-a-pull-request-check-with-specgov-1eo7</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Spec-driven development is useful only while the code and its governing artifacts stay aligned. SpecGov is a local, MIT-licensed CLI and GitHub Action that maps implementation paths to requirements, ADRs, docs, or specs, then reports when a code change bypasses them.&lt;/p&gt;

&lt;p&gt;This tutorial uses the stable &lt;code&gt;v0.1.0&lt;/code&gt; release to build a small manifest, run a local scan, simulate a pull request check, and add the same check to GitHub Actions. The workflow is advisory first, so a team can learn where its mappings are incomplete before making them blocking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;By the end, a repository will have a &lt;code&gt;.specgov.yml&lt;/code&gt; manifest that says which artifacts govern &lt;code&gt;src/auth/**&lt;/code&gt;. SpecGov can then report a warning when a pull request changes &lt;code&gt;src/auth/session.ts&lt;/code&gt; without changing a related specification or design document.&lt;/p&gt;

&lt;p&gt;SpecGov does not replace a spec-authoring method. It works with existing Markdown contracts, ADRs, &lt;code&gt;.specs&lt;/code&gt; folders, Kiro specs, and Spec Kit plans. It reads local files and Git metadata. The project documentation describes it as framework agnostic, PR native, advisory first, and private by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20 or newer. The package declares &lt;code&gt;node &amp;gt;=20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A Git repository with a few documentation or specification files.&lt;/li&gt;
&lt;li&gt;Git, if you want to run the pull request comparison locally.&lt;/li&gt;
&lt;li&gt;A GitHub repository if you want to add the Action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stable release is &lt;code&gt;specgov@0.1.0&lt;/code&gt;, published under the MIT license. The current repository also contains newer pre-release work, so pin the version when you need repeatable behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the stable CLI
&lt;/h2&gt;

&lt;p&gt;You can install the published CLI globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; specgov@0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use it without changing the global environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx specgov@0.1.0 &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI exposes &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;scan&lt;/code&gt;, &lt;code&gt;check-pr&lt;/code&gt;, &lt;code&gt;trace&lt;/code&gt;, and &lt;code&gt;drift&lt;/code&gt;. The first four are enough for a useful pull request gate. The &lt;a href="https://github.com/paladini/specgov/blob/v0.1.0/README.md" rel="noopener noreferrer"&gt;v0.1.0 README&lt;/a&gt; documents the complete command surface and exit codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a small manifest
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.specgov.yml&lt;/code&gt; at the repository root. Start with one high-value mapping instead of trying to govern every path immediately:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;advisory&lt;/span&gt;

&lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs/**/*.md"&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;documentation&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adr/**/*.md"&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;decision&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;architecture&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.specs/**/*.md"&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specification&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;

&lt;span class="na"&gt;mappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/auth/**"&lt;/span&gt;
    &lt;span class="na"&gt;specs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs/auth/**"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adr/auth/**"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.specs/features/auth/**"&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authentication behavior must stay aligned with its specs.&lt;/span&gt;

&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;require_spec_impact_for_code_changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;require_lifecycle_status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;require_owner_for_active_specs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stale_after_days&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180&lt;/span&gt;

&lt;span class="na"&gt;ignore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node_modules/**"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist/**"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.git/**"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;artifacts&lt;/code&gt; entries tell SpecGov what belongs to the repository's governance layer. A &lt;code&gt;mappings&lt;/code&gt; entry connects implementation paths to the artifacts that must move with them. The mapping is explicit: an unrelated changed document does not satisfy the rule by accident.&lt;/p&gt;

&lt;p&gt;Keep &lt;code&gt;mode: advisory&lt;/code&gt; while the manifest is new. Advisory mode reports warnings but returns success for those warnings. Strict mode returns exit code 1 when governance findings should block a pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the local checks
&lt;/h2&gt;

&lt;p&gt;First scan the repository and emit JSON for a script or CI job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;specgov scan &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then simulate a pull request that changes an authentication file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;specgov check-pr &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--changed-file&lt;/span&gt; src/auth/session.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the manifest above and no related spec change, the result should contain a &lt;code&gt;SPEC_IMPACT_MISSING&lt;/code&gt; warning. That warning is useful evidence, not a failure in advisory mode. It tells the author which code path changed and which spec roots should be considered.&lt;/p&gt;

&lt;p&gt;For a human-readable report, omit &lt;code&gt;--format json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;specgov check-pr &lt;span class="nt"&gt;--changed-file&lt;/span&gt; src/auth/session.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also create a trace index for automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;specgov trace &lt;span class="nt"&gt;--out&lt;/span&gt; .specgov.trace.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact report depends on your repository. The important verification is reproducible: run the same command against the same checkout and inspect the finding code, related files, status, and exit code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the GitHub Action
&lt;/h2&gt;

&lt;p&gt;Once the local mapping makes sense, add a workflow such as &lt;code&gt;.github/workflows/specgov.yml&lt;/code&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SpecGov&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;specgov&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;paladini/specgov@v0.1.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;advisory&lt;/span&gt;
          &lt;span class="na"&gt;base-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.base.sha }}&lt;/span&gt;
          &lt;span class="na"&gt;head-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.head.sha }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository's stable README uses the same action contract and recommends pinning the Action to a version tag for repeatable CI behavior. The checkout needs full history because the check compares the pull request's base and head references.&lt;/p&gt;

&lt;p&gt;When the mapping has earned trust, change &lt;code&gt;mode&lt;/code&gt; to &lt;code&gt;strict&lt;/code&gt; for the paths you are ready to enforce. Treat that as a policy decision: a strict rule can block a pull request, so the mapped artifact roots must be maintained with the same care as source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;The useful abstraction is a small graph:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Artifact globs identify the documents that can govern behavior.&lt;/li&gt;
&lt;li&gt;Code-to-spec mappings connect implementation paths to those artifacts.&lt;/li&gt;
&lt;li&gt;Git changes provide the pull request scope.&lt;/li&gt;
&lt;li&gt;SpecGov compares the scope with the mappings and emits Markdown or JSON evidence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is more precise than checking whether a repository contains a file named &lt;code&gt;SPEC.md&lt;/code&gt;. It also avoids asking an agent or model to guess which document matters. The manifest makes the relationship explicit and reviewable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and limitations
&lt;/h2&gt;

&lt;p&gt;An empty glob or a typo in a path can make a mapping look complete while matching nothing. Run &lt;code&gt;scan&lt;/code&gt; and review its artifact list before enabling strict mode.&lt;/p&gt;

&lt;p&gt;SpecGov does not prove that a requirement is correct, that an implementation satisfies the requirement semantically, or that a declared producer really authored an artifact. It reports structural relationships and policy findings. A passing scan is not a correctness guarantee.&lt;/p&gt;

&lt;p&gt;The stable release is marked pre-release in its README, and the CLI, report shape, and Action inputs may change before a first mature release. Pin &lt;code&gt;0.1.0&lt;/code&gt; in tutorials and workflows, then review changes before upgrading.&lt;/p&gt;

&lt;p&gt;The security boundary is intentionally narrow. Version 0.1 runs locally or in the GitHub runner, does not require an API key, does not call a model, and does not upload repository contents. That does not make the surrounding runner trusted automatically. Review third-party Actions, protect workflow changes, and avoid exposing private reports in public logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does SpecGov require GitHub?
&lt;/h3&gt;

&lt;p&gt;No. The CLI can run locally in any Git repository. GitHub Actions is an integration point for pull request review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it support only one specification framework?
&lt;/h3&gt;

&lt;p&gt;No. The stable release supports custom folders and mappings, including docs, ADRs, &lt;code&gt;.specs&lt;/code&gt;, Kiro-style specs, and Spec Kit-style plans.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should every repository start in strict mode?
&lt;/h3&gt;

&lt;p&gt;No. Start advisory, fix empty or overly broad mappings, observe real findings, and make only trusted paths strict.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a warning mean the code is unsafe?
&lt;/h3&gt;

&lt;p&gt;No. It means the configured governance relationship is incomplete for the changed path. A reviewer still needs to decide whether the code, artifact, or mapping should change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Spec drift becomes actionable when a repository can state which artifacts govern which code paths. SpecGov turns that statement into a deterministic local check and a pull request report. Start with one mapping, keep it advisory, verify the findings, and tighten enforcement only when the repository's own evidence supports it.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: I used AI assistance to organize this tutorial and edit its prose. The project behavior, commands, configuration, version, license, limitations, and verification results were checked against the public repository, the stable &lt;code&gt;v0.1.0&lt;/code&gt; tag, package metadata, and local test/build commands.&lt;/p&gt;

&lt;p&gt;What is the first code-to-artifact mapping in your repository that would benefit from an advisory check?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubactions</category>
      <category>tutorial</category>
      <category>traceability</category>
    </item>
    <item>
      <title>Learn Your Writing Style Locally with AuthorStyle and Python</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:35:27 +0000</pubDate>
      <link>https://dev.to/paladini/learn-your-writing-style-locally-with-authorstyle-and-python-15og</link>
      <guid>https://dev.to/paladini/learn-your-writing-style-locally-with-authorstyle-and-python-15og</guid>
      <description>&lt;p&gt;If you want an AI system to write in a style that feels like yours, the first problem is not a clever prompt. It is deciding what "your style" means and how to test whether a generated passage resembles it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/authorstyle" rel="noopener noreferrer"&gt;AuthorStyle&lt;/a&gt; takes a research-oriented approach to that problem. It is a Python command-line tool that learns recurring patterns from a local corpus, records evidence for its style profile, and evaluates generated candidates against held-out writing. The project is alpha software, so the useful outcome is an inspectable experiment, not a promise of a perfect voice clone.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Install AuthorStyle 0.2.1, create a profile, ingest a directory of Markdown or other supported documents, and analyze it locally. The minimal path produces corpus statistics and a Style Card with measurable observations such as sentence rhythm, paragraph rhythm, lexical diversity, and punctuation rates.&lt;/p&gt;

&lt;p&gt;This tutorial uses the repository's English fixtures so the workflow is reproducible without supplying private writing. Replace that directory with your own corpus only after reviewing the privacy mode and data-handling boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.11 or newer.&lt;/li&gt;
&lt;li&gt;Git.&lt;/li&gt;
&lt;li&gt;A local corpus you are allowed to process, or the repository's fixture corpus.&lt;/li&gt;
&lt;li&gt;A terminal. The commands below include a Windows PowerShell path and a POSIX shell path where activation differs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AuthorStyle 0.2.1 is published in the repository's &lt;code&gt;v0.2.1&lt;/code&gt; tag. The package declares the MIT license and supports Windows, macOS, and Linux through Python's normal environment model. The project is marked alpha in its package metadata, which matters when deciding how much to rely on generated output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the tested version
&lt;/h2&gt;

&lt;p&gt;Clone the repository and create an isolated virtual environment. Pinning the tag keeps the example aligned with the documented release instead of silently following unreleased &lt;code&gt;main&lt;/code&gt; changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v0.2.1 https://github.com/paladini/authorstyle.git
&lt;span class="nb"&gt;cd &lt;/span&gt;authorstyle
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, activate the environment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v0.2.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/paladini/authorstyle.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="nx"&gt;\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".[dev]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The development extra installs the test tools in addition to the runtime dependencies. The optional &lt;code&gt;embeddings&lt;/code&gt; and &lt;code&gt;nlp&lt;/code&gt; extras are separate; the basic workflow does not require them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a local profile
&lt;/h2&gt;

&lt;p&gt;Start with a profile name and ingest the fixture corpus. A profile is stored below &lt;code&gt;~/.authorstyle/profiles/&amp;lt;profile_id&amp;gt;/&lt;/code&gt; according to the README.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;authorstyle init demo
authorstyle ingest demo ./fixtures/corpus/en
authorstyle corpus stats demo
authorstyle analyze demo
authorstyle profile show demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, the same commands use the installed console script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ingest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\fixtures\corpus\en&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;corpus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;analyze&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;authorstyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository's current fixtures produced three documents and seven chunks during verification. The exact counts will differ for your corpus. &lt;code&gt;corpus stats&lt;/code&gt; reports documents, chunks, words, canonical families, and modes, while &lt;code&gt;profile show&lt;/code&gt; summarizes the resulting global profile.&lt;/p&gt;

&lt;p&gt;The important detail is the order. Ingestion normalizes and records source material, analysis builds the profile and Style Card, and the display commands let you inspect what was actually learned. You are not handing the tool a paragraph and asking it to guess a personality in one step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Style Card measures
&lt;/h2&gt;

&lt;p&gt;AuthorStyle combines explicit stylometry with pluggable style and semantic encoders. The documented methodology separates the two concepts: style describes how text is written, while semantic signals describe what it is about. The current methodology lists sentence and paragraph rhythm, punctuation rates, lexical diversity, Markdown structure, and technical prose or code ratios among the explicit features.&lt;/p&gt;

&lt;p&gt;A generated Style Card can contain observations like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Sentence rhythm&lt;/span&gt;
Typical sentences center around 18.0 words (q25=12.0, q75=24.0).
Evidence metrics: median_sentence_length

&lt;span class="gu"&gt;## Punctuation and typography&lt;/span&gt;
Notable punctuation rates: comma=4.20/100w, question=1.10/100w
Evidence metrics: punct_comma_per_100_words, punct_question_per_100_words
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those numbers are evidence about the supplied corpus, not universal facts about the author. If the corpus contains only API documentation, the profile may reflect that document type as much as the writer's broader voice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a real corpus safely
&lt;/h2&gt;

&lt;p&gt;AuthorStyle supports &lt;code&gt;.md&lt;/code&gt;, &lt;code&gt;.txt&lt;/code&gt;, &lt;code&gt;.html&lt;/code&gt;, and &lt;code&gt;.docx&lt;/code&gt; files. Markdown can include YAML front matter with fields such as &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;, and &lt;code&gt;mode&lt;/code&gt;. A &lt;code&gt;.authorstyleignore&lt;/code&gt; file can exclude paths using gitignore-style rules.&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;technical&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;article"&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2025-04-02&lt;/span&gt;
&lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devto&lt;/span&gt;
&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;technical&lt;/span&gt;
&lt;span class="na"&gt;published&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;databases&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the corpus representative and permitted for local processing. Remove private documents that do not belong in the experiment, and keep source provenance intact so you can identify why a profile changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate and evaluate, with the boundary visible
&lt;/h2&gt;

&lt;p&gt;After analysis, the documented commands for generation and evaluation are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;authorstyle write demo &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Write about distributed databases"&lt;/span&gt;
authorstyle evaluate demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generation stores candidates, component scores, retrieved exemplars, and provider metadata for reproducibility. Evaluation uses document-level held-out splits, so chunks from one document are not placed in both profile and test sets. The project compares baselines such as &lt;code&gt;generic&lt;/code&gt;, &lt;code&gt;card&lt;/code&gt;, &lt;code&gt;examples&lt;/code&gt;, &lt;code&gt;hybrid&lt;/code&gt;, and &lt;code&gt;full&lt;/code&gt;, and reports style distance, explicit stylometric fit, content adherence, naturalness heuristics, originality, and calibration percentiles.&lt;/p&gt;

&lt;p&gt;Do not interpret a high score as proof that a passage is authentically yours. The repository explicitly warns that it models recurring patterns from the supplied corpus, does not guarantee topic-independent style transfer, and does not automatically infer beliefs or personality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;The default &lt;code&gt;LOCAL_ONLY&lt;/code&gt; mode permits only local or mock providers. &lt;code&gt;REMOTE_PROFILE_ONLY&lt;/code&gt; sends the task and Style Card but not raw corpus excerpts. &lt;code&gt;REMOTE_EXEMPLARS&lt;/code&gt; also sends selected exemplar passages. These are materially different boundaries, so choose the mode deliberately rather than treating a remote provider as an implementation detail.&lt;/p&gt;

&lt;p&gt;The security policy also calls out prompt injection risks in Style Cards and generation prompts, unsafe file handling, accidental corpus disclosure, and unexpected network access when &lt;code&gt;LOCAL_ONLY&lt;/code&gt; is configured. Never commit &lt;code&gt;.env&lt;/code&gt; files, API keys, private corpora, or generated profile data unless you have a specific reason and have reviewed the consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducible verification
&lt;/h2&gt;

&lt;p&gt;Run the project's test suite from the tagged checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;v0.2.1&lt;/code&gt; checkout passed 24 tests in the verification environment. It also passed the CLI help command and the fixture path from initialization through analysis. Re-run those checks after changing dependencies or using a different Python version. A passing test suite validates the repository's current test cases; it does not validate the quality of your personal corpus or guarantee useful generated prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common failure modes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The profile reflects the topic instead of the voice
&lt;/h3&gt;

&lt;p&gt;This is a documented risk. Separate style and semantic embeddings help, but recurring subjects can still correlate with style signals. Add varied documents and inspect modes instead of treating one narrow corpus as a complete author model.&lt;/p&gt;

&lt;h3&gt;
  
  
  A mode has too little evidence
&lt;/h3&gt;

&lt;p&gt;Mode profiles require matching front-matter metadata. The methodology says insufficient evidence triggers warnings and fallback toward the global profile. That fallback is safer than pretending a small sample is a reliable mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  A remote provider receives too much
&lt;/h3&gt;

&lt;p&gt;Review the configured privacy mode before running &lt;code&gt;write&lt;/code&gt;. Start with &lt;code&gt;LOCAL_ONLY&lt;/code&gt; when testing ingestion and analysis. Remote generation is optional and must be explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  The output copies a recognizable phrase
&lt;/h3&gt;

&lt;p&gt;The project includes originality checks for long exact spans and word n-gram overlap. Inspect the overlap diagnostics and keep generated text under human review, especially when the corpus contains published or third-party material.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does AuthorStyle fine-tune an LLM?
&lt;/h3&gt;

&lt;p&gt;No. The README describes a local research CLI with profiles, exemplars, encoders, candidate generation, scoring, and evaluation. Fine-tuning is not the documented quick-start path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can it infer my beliefs or persona?
&lt;/h3&gt;

&lt;p&gt;No. Persona extraction is deferred in the current MVP. A style profile should not be treated as a biography.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it production-ready?
&lt;/h3&gt;

&lt;p&gt;The package metadata marks the project as alpha. Treat it as an experiment that produces inspectable evidence and keep backups of any corpus or profile you care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The useful lesson in AuthorStyle is methodological: define style as measurable observations, keep the source corpus under a clear privacy boundary, and evaluate against held-out documents. That makes the experiment easier to inspect than a prompt-only imitation workflow, while still leaving room for the project's alpha-stage limitations.&lt;/p&gt;

&lt;p&gt;If you try this with your own corpus, which verification signal would you trust first: sentence-level stylometry, document-level holdout evaluation, or originality diagnostics?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; This tutorial was researched and drafted with AI assistance. Commands, project details, limitations, and verification results were checked against the public AuthorStyle repository, its tagged release, and a local test run before publication.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>python</category>
      <category>cli</category>
      <category>tutorial</category>
      <category>stylometry</category>
    </item>
    <item>
      <title>Turn Missing UI States into a CI Gate with unhappypath</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:36:34 +0000</pubDate>
      <link>https://dev.to/paladini/turn-missing-ui-states-into-a-ci-gate-with-unhappypath-5gef</link>
      <guid>https://dev.to/paladini/turn-missing-ui-states-into-a-ci-gate-with-unhappypath-5gef</guid>
      <description>&lt;p&gt;AI-assisted development makes it easy to finish the success case and forget everything that happens when the network is slow, the result is empty, a record is missing, or a user clicks twice. The application can look complete in a demo while real users still encounter blank screens and silent failures.&lt;/p&gt;

&lt;p&gt;Those gaps are hard to review consistently. A code review may notice a missing loading state in one component and miss the same problem in another route. End-to-end tests can catch some failures, but they need a running application, test data, and carefully designed scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/unhappypath" rel="noopener noreferrer"&gt;unhappypath&lt;/a&gt; takes a narrower approach. It statically scans a React or Next.js codebase, reports missing UI branches, and produces a deterministic score that can be used as a CI threshold. It does not run a browser or ask an LLM to judge the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Build the current public repository, scan the included broken and good fixtures, and compare the results. The broken fixture scores 38/100 with 14 findings. The good fixture scores 100/100 with zero findings. Then add the min-score option to your own check so a change below your team's threshold exits with status 1.&lt;/p&gt;

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

&lt;p&gt;The current README describes four dimensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routes: Next.js App Router loading, error, not-found, and global-error boundaries&lt;/li&gt;
&lt;li&gt;Queries: loading, error, and empty states around data fetching&lt;/li&gt;
&lt;li&gt;Mutations: failure handling and pending or disabled feedback for actions&lt;/li&gt;
&lt;li&gt;Forms: submitting or pending feedback during submission&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scanner reports rule IDs such as RTE-01, RTE-02, RTE-03, QRY-01, QRY-02, QRY-03, FRM-01, and MUT-02. Each finding includes a file and line location, so the score is a triage signal rather than a replacement for reading the code.&lt;/p&gt;

&lt;p&gt;The project is version 0.1.0 in its package metadata, uses TypeScript, requires Node.js 20 or newer, and declares the MIT license. The current default branch has no release tag and no package-lock file, so the commands below use the public main checkout and npm install.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20 or newer&lt;/li&gt;
&lt;li&gt;npm and Git&lt;/li&gt;
&lt;li&gt;A React or Next.js project to scan&lt;/li&gt;
&lt;li&gt;A terminal that can read the source tree&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scanner is designed for source code, not a running server. It does not need credentials, a database, a browser, or network access to analyze the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and build from source
&lt;/h2&gt;

&lt;p&gt;Clone the repository and build the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/unhappypath.git
&lt;span class="nb"&gt;cd &lt;/span&gt;unhappypath
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiled entry point is dist/cli.js. Check the current command surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documented invocation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js /path/to/your-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The path is optional and defaults to the current directory. The CLI also supports JSON, Markdown, min-score, badge, out, and quiet options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the broken and good scores
&lt;/h2&gt;

&lt;p&gt;The repository includes two fixtures that make the behavior easy to verify without creating a sample application.&lt;/p&gt;

&lt;p&gt;Run the intentionally incomplete fixture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run demo:broken
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current output reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Score: 38/100
14 findings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It detects a Next.js App Router project, TanStack Query, and React. The findings include missing route boundaries, query states, a form submitting state, and an async button pending state.&lt;/p&gt;

&lt;p&gt;Now run the covered fixture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run demo:good
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its current output reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Score: 100/100
0 findings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two demos give you a reproducible contract: the scanner distinguishes a fixture with deliberate gaps from one that includes the expected branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan your application
&lt;/h2&gt;

&lt;p&gt;From the built checkout, scan a project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js /path/to/your-next-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use JSON when another tool will consume the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js /path/to/your-next-app &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; unhappypath-report.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A report contains the total score, dimension scores, detected technologies, and findings. A finding is intended to point you toward a code change. For example, a query finding may tell you that a useQuery call lacks a loading, error, or empty branch. A route finding may point to a missing loading.tsx, error.tsx, or not-found.tsx.&lt;/p&gt;

&lt;p&gt;Do not treat a finding as proof that the user experience is poor. Static analysis can detect the presence or absence of recognizable branches, but it cannot judge whether the message is understandable, whether a retry really succeeds, or whether the layout remains usable on a small screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn the score into a CI gate
&lt;/h2&gt;

&lt;p&gt;The useful automation boundary is the min-score option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node dist/cli.js &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--min-score&lt;/span&gt; 75
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documented exit behavior is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0 when the score meets the threshold&lt;/li&gt;
&lt;li&gt;1 when the score is below the threshold&lt;/li&gt;
&lt;li&gt;2 for a usage error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository README also shows this GitHub Actions pattern:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx unhappypath . --min-score &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The npx example is a documented future path because the current README lists npm publishing as a roadmap item. Until a package is published, use the compiled local CLI in your own workflow:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node unhappypath/dist/cli.js . --min-score &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command assumes the workflow has already cloned or built the scanner. Choose the threshold as a team policy. The README describes 75 as launchable and 90 as production UX, but those labels are project guidance, not a guarantee that an application is ready to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix findings in a useful order
&lt;/h2&gt;

&lt;p&gt;The README recommends fixing routes, queries, mutations, and forms in that order. Route-level boundaries can prevent a whole screen from becoming blank, while component-level feedback handles narrower interactions.&lt;/p&gt;

&lt;p&gt;For a missing route loading state, add the convention expected by the framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
  dashboard/
    page.tsx
    loading.tsx
    error.tsx
    not-found.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a query, make the states explicit before rendering the successful result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingPanel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RetryPanel&lt;/span&gt; &lt;span class="na"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;refetch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EmptyPanel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Results&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a mutation or form, disable the action while the request is pending and show the user what is happening. The scanner can identify recognizable omissions, but the final copy and interaction still belong to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a deterministic score helps
&lt;/h2&gt;

&lt;p&gt;The tool intentionally avoids an LLM in the scanning loop. It uses filesystem discovery and TypeScript-oriented analysis, then produces the same kind of report for the same source tree. That makes a failing CI check easier to reproduce and review.&lt;/p&gt;

&lt;p&gt;This keeps responsibility clear. The scanner finds candidate gaps. A developer decides whether a branch is required, fixes the code, and runs application tests that prove the behavior. unhappypath complements browser tests and manual QA; it does not replace them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and security boundaries
&lt;/h2&gt;

&lt;p&gt;The project is static analysis only. It does not run your app in a browser, simulate a slow network or a 500 response, verify UX copy, or prove that a retry action works. It currently focuses on the documented React and Next.js patterns. The roadmap lists Vue, Nuxt, SvelteKit, Remix conventions, MCP support, and npm publishing as future work.&lt;/p&gt;

&lt;p&gt;Because it reads source files, run it only against directories you intend to inspect. It does not need network access for the scan, but dependencies still come from npm during installation. Pin and review dependencies according to your normal project policy.&lt;/p&gt;

&lt;p&gt;Do not use the score as a security guarantee or as a substitute for accessibility, performance, integration, or end-to-end checks. A project can score well and still have a broken API, misleading error message, or inaccessible interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does the scanner need a running Next.js app?
&lt;/h3&gt;

&lt;p&gt;No. It scans source code and filesystem conventions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it replace Playwright?
&lt;/h3&gt;

&lt;p&gt;No. Use browser tests for runtime behavior. The scanner is a fast static complement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I fail CI on findings instead of score?
&lt;/h3&gt;

&lt;p&gt;The documented CLI gate is min-score. Choose a threshold that reflects the minimum coverage you want, then use the report to inspect individual findings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is npx unhappypath ready to use?
&lt;/h3&gt;

&lt;p&gt;The current README labels npm publishing as a future option. Build from the repository until the project documents a published package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;A successful demo is only one UI state. Run unhappypath before calling an AI-built React or Next.js screen complete, use its file-and-line findings to guide fixes, and enforce a minimum score where a deterministic CI signal is useful. Then verify important paths with real tests and a browser.&lt;/p&gt;

&lt;p&gt;What minimum score would be meaningful for your team: a first threshold around 75, a stricter 90, or a dimension-specific policy?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclosure: AI assistance was used to organize and edit this tutorial. The commands, version, output, limitations, and security boundaries were checked against the current public paladini/unhappypath README, package metadata, license, source checkout, and local build, test, and demo results.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>tutorial</category>
      <category>staticanalysis</category>
    </item>
    <item>
      <title>Run a Safe Local Review of Env Team Vault Before Sharing .env Files</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Mon, 19 Oct 2026 22:00:33 +0000</pubDate>
      <link>https://dev.to/paladini/run-a-safe-local-review-of-env-team-vault-before-sharing-env-files-8d2</link>
      <guid>https://dev.to/paladini/run-a-safe-local-review-of-env-team-vault-before-sharing-env-files-8d2</guid>
      <description>&lt;p&gt;Team environment variables are easy to share badly. A &lt;code&gt;.env&lt;/code&gt; file sent through chat is convenient, but it is difficult to audit, easy to lose track of, and often contains more access than a teammate needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/env-team-vault" rel="noopener noreferrer"&gt;Env Team Vault&lt;/a&gt; is an open-source, self-hosted Node.js and TypeScript application that organizes teams, applications, variables, API tokens, and audit events around local development configuration. The useful question is not only how to install it. The useful question is whether the current code and package are ready for the boundary where you want to use them.&lt;/p&gt;

&lt;p&gt;This tutorial shows a safe evaluation path for the current &lt;code&gt;main&lt;/code&gt; branch and package version &lt;code&gt;1.0.0&lt;/code&gt;. You will build the project, inspect the CLI flow, and verify the environment-file route. This is a reproducible review, not a production recommendation.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clone &lt;code&gt;paladini/env-team-vault&lt;/code&gt; and install its locked dependencies.&lt;/li&gt;
&lt;li&gt;Run the TypeScript build and inspect the CLI package before sending real values.&lt;/li&gt;
&lt;li&gt;The project documents Docker Compose, a web UI, SQLite with Prisma, and &lt;code&gt;env-team-vault sync&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The current root CLI imports &lt;code&gt;axios&lt;/code&gt;, but &lt;code&gt;axios&lt;/code&gt; is not declared in the root &lt;code&gt;package.json&lt;/code&gt;, so the documented &lt;code&gt;npx env-team-vault --help&lt;/code&gt; check currently fails after &lt;code&gt;npm ci&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The API controller also has an unauthenticated compatibility branch. Treat the current code as a local-development experiment until that boundary is fixed and tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need Git, Node.js, npm, and a disposable working directory. The repository declares Node.js type definitions for Node 20, while its Dockerfile starts from Node 18 Alpine. Use a current supported Node release for the local build, and record the exact version in your review.&lt;/p&gt;

&lt;p&gt;Do not put real API keys, production database URLs, or customer data into a first run. Use fake values and a disposable SQLite database.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Clone the current source and install it
&lt;/h2&gt;

&lt;p&gt;The repository README documents Docker Compose and a manual npm setup. Use the owner repository URL, not the placeholder &lt;code&gt;your-org&lt;/code&gt; URL in its copy-paste examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/env-team-vault.git
&lt;span class="nb"&gt;cd &lt;/span&gt;env-team-vault
npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lockfile pins the application package at version &lt;code&gt;1.0.0&lt;/code&gt; and includes its Prisma, Express, Passport, SQLite session, and TypeScript dependency tree. &lt;code&gt;npm ci&lt;/code&gt; tests the package as committed rather than silently resolving a new dependency graph.&lt;/p&gt;

&lt;p&gt;Before continuing, inspect the installation result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm audit
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build compiles &lt;code&gt;src/main.ts&lt;/code&gt; and the application layers into &lt;code&gt;dist&lt;/code&gt;. It does not prove that the web server is configured correctly, that the database has been initialized, or that the CLI can run. Keep those checks separate.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understand the local architecture
&lt;/h2&gt;

&lt;p&gt;The application combines Express with server-rendered EJS views. Prisma stores teams, users, applications, variables, API tokens, and audit logs in SQLite. The schema makes variable keys unique per application.&lt;/p&gt;

&lt;p&gt;The main configuration values are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=development
PORT=3000
SESSION_SECRET=use-a-random-local-value
DATABASE_URL=file:./prod.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source defaults the HTTP port to &lt;code&gt;3000&lt;/code&gt;, enables the SQLite-backed session store, and uses a fallback session secret when &lt;code&gt;SESSION_SECRET&lt;/code&gt; is absent. That fallback is convenient for a local experiment but is not an acceptable production setting.&lt;/p&gt;

&lt;p&gt;The documented Docker path exposes port 3000 and persists the Prisma database and sessions in named volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;, register a disposable account, create a team, and create an application. Do not add a real secret while evaluating the flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Verify the documented CLI before trusting it
&lt;/h2&gt;

&lt;p&gt;The README documents a sync command that writes the response into a local &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx env-team-vault &lt;span class="nb"&gt;sync&lt;/span&gt; &amp;lt;APP_ID&amp;gt; &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000 &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;API_TOKEN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current root &lt;code&gt;bin/cli.js&lt;/code&gt; imports &lt;code&gt;axios&lt;/code&gt;, &lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;yargs&lt;/code&gt;, and Node path utilities. The root &lt;code&gt;package.json&lt;/code&gt; declares &lt;code&gt;yargs&lt;/code&gt; and the other server dependencies, but it does not declare &lt;code&gt;axios&lt;/code&gt;. A fresh &lt;code&gt;npm ci&lt;/code&gt; followed by &lt;code&gt;npx env-team-vault --help&lt;/code&gt; therefore reaches the CLI file and fails with &lt;code&gt;Cannot find module 'axios'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That failure separates a documented interface from a verified interface. Do not work around it by installing an undeclared package and then report the repository as working. A fix should add the dependency, update the lockfile, and add a smoke test for the executable path.&lt;/p&gt;

&lt;p&gt;The CLI also writes the server response directly to &lt;code&gt;.env&lt;/code&gt; in the current directory. That is convenient, but it can overwrite an existing file. Use a disposable directory until the command has a confirmed backup or safe-write policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Inspect the authentication boundary
&lt;/h2&gt;

&lt;p&gt;The API route documented for the CLI is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/vault/:appId
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; header is present, &lt;code&gt;VaultController&lt;/code&gt; looks up the API token, checks revocation, verifies team membership, verifies that the application belongs to that team, and records a &lt;code&gt;SYNC_ENV&lt;/code&gt; audit event.&lt;/p&gt;

&lt;p&gt;However, the same controller has an &lt;code&gt;else&lt;/code&gt; branch for requests without an authorization header. It checks only that the application exists and then returns the environment content. The route is registered as a public Express route, without the authentication middleware used by other parts of the application.&lt;/p&gt;

&lt;p&gt;This is the most important boundary in the current review. The README calls the project secure and audited, but the implementation currently permits an unauthenticated request for a known application ID. Do not expose this route to an untrusted network or put production secrets behind it. A safe contribution would remove the compatibility branch, require a valid token, add an integration test for unauthorized requests, and then document the changed contract.&lt;/p&gt;

&lt;p&gt;This conclusion comes from the current controller and route source, not from treating the README's security language as a guarantee. Source behavior wins when documentation and implementation differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Verify the expected result without real secrets
&lt;/h2&gt;

&lt;p&gt;For a local evaluation, the expected result is a buildable application and a reviewable data flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The server starts on port 3000 after the database has been initialized.&lt;/li&gt;
&lt;li&gt;A disposable account can create a team and an application.&lt;/li&gt;
&lt;li&gt;A fake variable such as &lt;code&gt;DEMO_VALUE=not-a-secret&lt;/code&gt; can be associated with that application.&lt;/li&gt;
&lt;li&gt;A valid API token can be used to request the application's &lt;code&gt;.env&lt;/code&gt; representation.&lt;/li&gt;
&lt;li&gt;The request creates an audit record when the token path is used.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The source formats the response as plain text, so a successful response should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEMO_VALUE=not-a-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the HTTP status, response content type, and resulting file. Then inspect the audit log in the application. Never use a successful response as evidence that the deployment is safe by itself. The unauthenticated branch and the default session secret still need to be addressed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this review order works
&lt;/h2&gt;

&lt;p&gt;The order follows operational risk. &lt;code&gt;npm ci&lt;/code&gt; and &lt;code&gt;npm run build&lt;/code&gt; establish a baseline. The CLI check tests whether the documented command exists in the dependency graph. The controller review tests authorization before any real value crosses the boundary.&lt;/p&gt;

&lt;p&gt;This is also why a green TypeScript build is not enough. TypeScript checks the files included in compilation. It does not guarantee that a JavaScript CLI has every runtime dependency, that a database migration exists, or that an HTTP route enforces the intended authorization policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and honest limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The CLI reports a missing module
&lt;/h3&gt;

&lt;p&gt;Confirm the package manifest and lockfile before installing anything manually. The current root CLI's undeclared &lt;code&gt;axios&lt;/code&gt; import is a repository issue, not a user configuration error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prisma has no migration to apply
&lt;/h3&gt;

&lt;p&gt;The current repository contains &lt;code&gt;prisma/schema.prisma&lt;/code&gt; but no migration directory in the checked-in Prisma folder. Treat database initialization as a development setup step and review the resulting database before using the application. Do not point this first run at an existing production database.&lt;/p&gt;

&lt;h3&gt;
  
  
  A token request returns 401 or 403
&lt;/h3&gt;

&lt;p&gt;Check the &lt;code&gt;Bearer&lt;/code&gt; prefix, token revocation state, user team membership, and application team ownership. The controller intentionally rejects invalid or revoked tokens on the authenticated path.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need production secret management
&lt;/h3&gt;

&lt;p&gt;This project is explicitly positioned for local development variables. It is not a replacement for a mature production secret manager. The current unauthenticated route, fallback session secret, local SQLite storage, and missing CLI dependency are enough reason to keep the evaluation local and disposable.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Env Team Vault a production secrets manager?
&lt;/h3&gt;

&lt;p&gt;No. The README describes a self-hosted local-development tool, and the current route implementation has an unauthenticated compatibility path. Treat it as an experiment or contribution target until that behavior changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use &lt;code&gt;npm ci&lt;/code&gt; instead of &lt;code&gt;npm install&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; uses the committed lockfile and exposes undeclared runtime dependencies more reliably. It makes the first verification closer to what a clean deployment or package consumer will see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I publish the &lt;code&gt;.env&lt;/code&gt; file generated by the CLI?
&lt;/h3&gt;

&lt;p&gt;No. Keep it outside version control, use fake values while testing, and check your ignore rules before running a sync command in a repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where is the source of truth for behavior?
&lt;/h3&gt;

&lt;p&gt;Use the &lt;a href="https://github.com/paladini/env-team-vault/blob/main/README.md" rel="noopener noreferrer"&gt;repository README&lt;/a&gt; for the documented workflow, then compare it with the &lt;a href="https://github.com/paladini/env-team-vault/blob/main/package.json" rel="noopener noreferrer"&gt;package manifest&lt;/a&gt;, &lt;a href="https://github.com/paladini/env-team-vault/blob/main/prisma/schema.prisma" rel="noopener noreferrer"&gt;Prisma schema&lt;/a&gt;, &lt;a href="https://github.com/paladini/env-team-vault/blob/main/bin/cli.js" rel="noopener noreferrer"&gt;CLI implementation&lt;/a&gt;, and &lt;a href="https://github.com/paladini/env-team-vault/blob/main/src/infra/http/controllers/VaultController.ts" rel="noopener noreferrer"&gt;vault controller&lt;/a&gt; for current behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Env Team Vault has a clear local-development idea: keep team variables in one self-hosted application, expose a CLI, and record synchronization events. The responsible way to evaluate it is to verify each layer independently. The current build succeeds, but the documented CLI has an undeclared runtime dependency and the API has an unauthenticated response path. Those findings should shape how you use the project and what you fix next.&lt;/p&gt;

&lt;p&gt;This tutorial was prepared with AI assistance for research organization and editorial review. The repository behavior, commands, and limitations were checked against the current public source and a fresh local checkout.&lt;/p&gt;

&lt;p&gt;Would you use a tool like this for disposable development environments, and which authorization test would you add first?&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>tutorial</category>
      <category>envvars</category>
    </item>
    <item>
      <title>Install and Validate a Reusable AI Agent Skill from GitHub</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:35:27 +0000</pubDate>
      <link>https://dev.to/paladini/install-and-validate-a-reusable-ai-agent-skill-from-github-1dhj</link>
      <guid>https://dev.to/paladini/install-and-validate-a-reusable-ai-agent-skill-from-github-1dhj</guid>
      <description>&lt;p&gt;AI assistants become easier to trust when their repeated work is packaged as a small, reviewable skill instead of copied into every prompt. The challenge is that an installed skill can influence future work, while a poorly structured repository can quietly add stale instructions, unsafe examples, or credentials.&lt;/p&gt;

&lt;p&gt;This tutorial shows a practical installation and validation path using &lt;a href="https://github.com/paladini/agent-skills" rel="noopener noreferrer"&gt;Fernando Paladini's agent-skills repository&lt;/a&gt;. The repository contains reusable skills for technical work, including a DEV.to publishing skill, plus sanitized MCP configuration examples. We will install one skill locally, inspect what it contains, and run the repository's documented checks without treating the project as a trusted executable.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Clone the repository, copy the &lt;code&gt;devto-publish-expert&lt;/code&gt; directory into your agent's skills directory, then validate the skill and the committed MCP examples. The result is a reusable local capability with a visible instruction file, deterministic checks, and no need to commit real credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git.&lt;/li&gt;
&lt;li&gt;Python 3.11 or newer for the repository's TOML-aware validation script.&lt;/li&gt;
&lt;li&gt;A local agent host that supports skills stored as directories with a &lt;code&gt;SKILL.md&lt;/code&gt; entrypoint.&lt;/li&gt;
&lt;li&gt;A writable skills directory for your agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository is public, non-archived, non-forked, non-empty, and MIT licensed. At the time of writing, its default branch is &lt;code&gt;main&lt;/code&gt; and it does not publish a tagged release. That means this tutorial uses the current documented &lt;code&gt;main&lt;/code&gt; workflow, not a versioned package contract. Pin a commit in your own automation if you need repeatable installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install one skill
&lt;/h2&gt;

&lt;p&gt;The repository README documents a focused installation model: copy only the skill directory you need. That keeps unrelated skills and configuration examples out of the runtime search path.&lt;/p&gt;

&lt;p&gt;On macOS or Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/agent-skills.git
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; agent-skills/skills/devto-publish-expert ~/.codex/skills/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/paladini/agent-skills.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Copy-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Recurse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;agent-skills\skills\devto-publish-expert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="nx"&gt;\.codex\skills\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The copied directory should contain &lt;code&gt;SKILL.md&lt;/code&gt;, a &lt;code&gt;references/&lt;/code&gt; directory, and the supporting scripts used by the skill. Restart the agent host after installation when it only discovers skills during startup. If your host uses another skills directory, adapt the destination while preserving the skill's internal layout.&lt;/p&gt;

&lt;p&gt;The important boundary is that installation copies instructions and helper files. It does not grant the skill permission to publish, modify repositories, or access secrets. Those permissions come from the host and from the workflow that invokes the skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the skill structure
&lt;/h2&gt;

&lt;p&gt;The repository README separates skill validation from article validation. Use the Agent Skills validator supplied by your host or skill tooling, then run the repository's bundled unit tests for the skill scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python path/to/skill-creator/scripts/quick_validate.py skills/devto-publish-expert
python &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; skills/devto-publish-expert/scripts &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"test_*.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command is structural validation. It should answer questions such as whether the skill has the expected entrypoint and layout. The second command checks deterministic helper behavior. These are different signals: a directory can be shaped correctly while a helper script still contains a regression.&lt;/p&gt;

&lt;p&gt;For a local installation, also inspect the entrypoint before using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'1,220p'&lt;/span&gt; skills/devto-publish-expert/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On PowerShell, the equivalent is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\skills\devto-publish-expert\SKILL.md&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TotalCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;220&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the instructions as policy, not as an authority escalation. A skill can describe a safe workflow, but the host still needs to enforce the final boundary around external writes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check sanitized MCP examples
&lt;/h2&gt;

&lt;p&gt;The same repository stores example MCP configurations for restoring publishing workflows. Examples are useful only when a reader can distinguish placeholders from live authentication data.&lt;/p&gt;

&lt;p&gt;Run the repository's configuration checker from its root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/validate_mcp_configs.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checker expects three JSON examples and three TOML examples. It parses both formats, verifies the expected top-level server objects, looks for likely credential patterns, and requires the placeholder &lt;code&gt;REPLACE_WITH_LOCAL_SECRET&lt;/code&gt; in &lt;code&gt;.env.example&lt;/code&gt;. A successful check does not prove that an external account is configured correctly. It proves that the committed examples have the expected shape and do not contain obvious secrets.&lt;/p&gt;

&lt;p&gt;If you adapt an example, keep the real value outside Git. For example, use a local environment file that is ignored by the repository, or configure the connector through your desktop agent's secret store. Never replace the placeholder in a public example and commit the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected result and reproducible verification
&lt;/h2&gt;

&lt;p&gt;After installation, verification should leave you with three independently checkable facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The skill directory is present in the host's skills location.&lt;/li&gt;
&lt;li&gt;The structural and helper checks complete without errors.&lt;/li&gt;
&lt;li&gt;The MCP example checker accepts the repository's committed configuration samples.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can record the exact source revision used for installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; agent-skills rev-parse HEAD
git &lt;span class="nt"&gt;-C&lt;/span&gt; agent-skills status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command gives you a commit to pin in documentation or automation. The second confirms that your clone has no local changes before you reason about its contents. Because the repository currently documents its workflow on &lt;code&gt;main&lt;/code&gt; and has no release tag, this revision check is especially important for repeatable setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;The useful design choice is the narrow installation boundary. A skill is a directory with a clear entrypoint and optional references and scripts. Copying one directory makes the dependency visible and reviewable. It also avoids treating every file in a larger repository as an instruction that must be loaded for every task.&lt;/p&gt;

&lt;p&gt;The validation split is equally important. Structure, helper behavior, and configuration safety are separate concerns. Keeping them as separate checks makes failures easier to interpret and reduces the temptation to declare a skill safe because one broad command happened to pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;If the host does not discover the copied skill, check the destination path and restart the host. Do not duplicate the same skill under several locations until discovery happens; duplicate installations can make it unclear which revision is active.&lt;/p&gt;

&lt;p&gt;If Python cannot parse the MCP checker, use a supported Python version rather than weakening the script or skipping validation. The checker imports &lt;code&gt;tomllib&lt;/code&gt;, so the documented runtime should provide that module.&lt;/p&gt;

&lt;p&gt;If validation reports a possible credential, stop and inspect the exact file. Do not suppress the warning just to obtain a green run. Remove the secret, restore the documented placeholder, rotate any exposed credential, and then validate again.&lt;/p&gt;

&lt;p&gt;This workflow is local and reviewable, but it is not a security guarantee. A skill can still contain incorrect instructions, and a host can still grant excessive permissions. Review &lt;code&gt;SKILL.md&lt;/code&gt;, pin the source commit, minimize connector scopes, and keep publishing or repository writes behind an explicit workflow gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to install every skill?
&lt;/h3&gt;

&lt;p&gt;No. Copy only the directory required by your workflow. Smaller installation scope makes updates and review simpler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the current &lt;code&gt;main&lt;/code&gt; branch a stable release?
&lt;/h3&gt;

&lt;p&gt;No release tag was present when this tutorial was checked. Treat &lt;code&gt;main&lt;/code&gt; as the current documented source and pin a commit when reproducibility matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can the MCP examples be used with real credentials?
&lt;/h3&gt;

&lt;p&gt;They can be adapted locally, but the committed examples are templates. Keep real credentials in a secret store or ignored local configuration and never publish them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does structural validation prove that the skill is correct?
&lt;/h3&gt;

&lt;p&gt;No. It checks expected shape and selected helper behavior. Human review is still required for claims, permissions, and external side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;A reusable AI skill is easiest to maintain when installation, source revision, structure, helper behavior, and credential hygiene are all visible. Start with one directory, validate each boundary separately, and pin the commit before making it part of a repeatable agent workflow.&lt;/p&gt;

&lt;p&gt;What additional check would you require before allowing a locally installed skill to perform an external write?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: I used an AI assistant to organize this tutorial and check its Markdown structure. The repository facts, commands, and security boundaries are based on the current public source and documented examples linked above.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
      <category>agents</category>
    </item>
    <item>
      <title>Install Active Admin Claude Theme 0.2.0 in Rails</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:34:54 +0000</pubDate>
      <link>https://dev.to/paladini/install-active-admin-claude-theme-020-in-rails-65f</link>
      <guid>https://dev.to/paladini/install-active-admin-claude-theme-020-in-rails-65f</guid>
      <description>&lt;p&gt;If an Active Admin backend works but still looks like a default admin panel, a theme gem can improve the interface without forcing a full frontend rewrite. The difficult part is choosing the correct integration path. Active Admin 3 uses Sass and Sprockets, while Active Admin 4 uses Tailwind CSS v4 and a different asset layout.&lt;/p&gt;

&lt;p&gt;This tutorial installs &lt;a href="https://github.com/paladini/activeadmin-claude-theme" rel="noopener noreferrer"&gt;activeadmin-claude-theme&lt;/a&gt; version 0.2.0 in a Rails application, explains the version-aware generator, and shows how to customize its tokens. The gem is a community project inspired by Claude aesthetics. It is not affiliated with or endorsed by Anthropic.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Add the gem to an existing Active Admin application, run &lt;code&gt;rails generate activeadmin_claude_theme:install&lt;/code&gt;, rebuild CSS when using Active Admin 4, and verify the generated asset entry before restarting Rails. Active Admin 4 keeps dark mode and uses CSS variables. Active Admin 3 receives a Sass theme and light mode only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The release documentation lists these requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby 3.2 or newer&lt;/li&gt;
&lt;li&gt;Rails 7.2 or newer&lt;/li&gt;
&lt;li&gt;Active Admin 4.0.0.beta22 or newer for the Tailwind path&lt;/li&gt;
&lt;li&gt;Active Admin 3.2 or newer for the Sass path&lt;/li&gt;
&lt;li&gt;Node.js and the Active Admin npm toolchain for Active Admin 4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is distributed under the MIT license. The &lt;code&gt;0.2.0&lt;/code&gt; release is available on &lt;a href="https://github.com/paladini/activeadmin-claude-theme/releases/tag/v0.2.0" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://rubygems.org/gems/activeadmin-claude-theme/versions/0.2.0" rel="noopener noreferrer"&gt;RubyGems&lt;/a&gt;. The gem declares runtime dependencies on Active Admin &lt;code&gt;&amp;gt;= 3.2.0, &amp;lt; 5&lt;/code&gt; and Rails &lt;code&gt;&amp;gt;= 7.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This guide assumes that Active Admin is already installed. It does not replace the official &lt;a href="https://activeadmin.info/0-installation.html" rel="noopener noreferrer"&gt;Active Admin installation guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the theme in Active Admin 4
&lt;/h2&gt;

&lt;p&gt;Active Admin 4 uses Tailwind CSS v4. The theme's release documentation uses &lt;code&gt;4.0.0.beta22&lt;/code&gt; as its documented example, so keep the Active Admin and npm versions aligned with the application you are actually maintaining.&lt;/p&gt;

&lt;p&gt;Add the gem to your &lt;code&gt;Gemfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin-claude-theme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the bundle and run the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rails generate activeadmin_claude_theme:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator detects Active Admin's major version. For Active Admin 4, it prepares the Tailwind entry and the theme assets. Rebuild the stylesheet and restart Rails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build:css
bin/rails server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important detail is the separation between source and served CSS. The documented layout is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/assets/tailwind/active_admin.css
app/assets/builds/active_admin.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first file is the Tailwind source. The second is the generated stylesheet served by the application. If the source file is placed directly in the served Propshaft path, the build can produce confusing results or fail to expose the expected &lt;code&gt;active_admin&lt;/code&gt; asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the theme in Active Admin 3
&lt;/h2&gt;

&lt;p&gt;Active Admin 3 follows the Sprockets and Sass path. Add the same gem, but make sure the application has the Sass dependencies documented by the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.5"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"sprockets-rails"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"sassc-rails"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"devise"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin-claude-theme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the bundle and run the same generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rails generate activeadmin_claude_theme:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Active Admin 3, the generator updates &lt;code&gt;app/assets/stylesheets/active_admin.scss&lt;/code&gt; to use the theme entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin_claude_theme/aa3/base"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Rails after the asset change. Do not copy the Active Admin 4 Tailwind setup into an Active Admin 3 application. The two integration paths intentionally use different asset pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the design tokens
&lt;/h2&gt;

&lt;p&gt;The theme exposes a small set of semantic tokens instead of requiring selectors to be rewritten throughout the application. On Active Admin 4, override CSS variables after the theme import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--claude-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d4845f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--claude-canvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--claude-canvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#121110&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;On Active Admin 3, set Sass variables before importing the theme base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$claude-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#d4845f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$claude-canvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin_claude_theme/aa3/base"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The release's &lt;a href="https://github.com/paladini/activeadmin-claude-theme/blob/v0.2.0/DESIGN.md" rel="noopener noreferrer"&gt;design reference&lt;/a&gt; documents the palette, typography, spacing, and token naming. The default primary color is &lt;code&gt;#cc785c&lt;/code&gt;, the canvas is &lt;code&gt;#faf9f5&lt;/code&gt;, and the semantic token prefix is &lt;code&gt;--claude-&lt;/code&gt; for CSS variables.&lt;/p&gt;

&lt;p&gt;Prefer semantic tokens for global changes. If a single screen needs a special treatment, use a narrow application-level selector rather than changing the theme's generated files. This keeps upgrades reviewable and makes it clear which choices belong to the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the generated integration
&lt;/h2&gt;

&lt;p&gt;After the generator finishes, verify the files before debugging the browser. For an Active Admin 4 application, check that the Tailwind source and build output exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Test-Path app/assets/tailwind/active_admin.css
Test-Path app/assets/builds/active_admin.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a Unix-like shell, the equivalent check is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; app/assets/tailwind/active_admin.css
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; app/assets/builds/active_admin.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then rebuild and inspect the generated CSS for one token that you changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build:css
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"claude-primary"&lt;/span&gt; app/assets/builds/active_admin.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use this instead of &lt;code&gt;grep&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Select-String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;app/assets/builds/active_admin.css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-primary"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Active Admin 3, verify the Sass import and run the normal Rails asset precompile or development server path used by the host application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin_claude_theme/aa3/base"&lt;/span&gt; app/assets/stylesheets/active_admin.scss
bin/rails assets:precompile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected result is an Active Admin page using the theme's warm palette. On Active Admin 4, the built-in dark mode and mobile drawer remain Active Admin features. The theme's &lt;a href="https://github.com/paladini/activeadmin-claude-theme/blob/v0.2.0/docs/aa4-override-map.md" rel="noopener noreferrer"&gt;AA4 override map&lt;/a&gt; explains which partials the gem overrides and which hooks it deliberately preserves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common failure modes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The page is unstyled after installation
&lt;/h3&gt;

&lt;p&gt;For Active Admin 4, run &lt;code&gt;npm run build:css&lt;/code&gt; or the application's documented &lt;code&gt;bin/rails css:build&lt;/code&gt; command, then restart Rails. Also confirm that the source is under &lt;code&gt;app/assets/tailwind&lt;/code&gt; and the generated file is under &lt;code&gt;app/assets/builds&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The generator changes the wrong file
&lt;/h3&gt;

&lt;p&gt;The generator detects the installed Active Admin major version. Check the bundle before running it. An application with a stale or conflicting Active Admin dependency can be classified differently from what you expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Active Admin 3 reports a Sass error
&lt;/h3&gt;

&lt;p&gt;Confirm that &lt;code&gt;sassc-rails&lt;/code&gt; or a compatible Sass pipeline is installed and that the file imports &lt;code&gt;activeadmin_claude_theme/aa3/base&lt;/code&gt; after your own variable overrides.&lt;/p&gt;

&lt;h3&gt;
  
  
  You expect dark mode on Active Admin 3
&lt;/h3&gt;

&lt;p&gt;That is not supported by this release. The project documents dark mode for Active Admin 4 only because Active Admin 3 does not provide the same native dark-mode mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  You treat the theme as an official Claude product
&lt;/h3&gt;

&lt;p&gt;Do not. The README and FAQ explicitly describe it as a community theme. The name and visual inspiration do not provide any Anthropic affiliation, endorsement, security guarantee, or product compatibility promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can one Gemfile support both Active Admin 3 and 4?
&lt;/h3&gt;

&lt;p&gt;The gem supports both major versions, but an individual application should use one Active Admin integration path at a time. The project's own development setup uses separate dummy applications and dependency files for its version coverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the theme replace all Active Admin templates?
&lt;/h3&gt;

&lt;p&gt;No. The AA4 documentation describes minimal partial overrides and preserved Active Admin hooks. It remaps Tailwind colors, adds semantic tokens, and customizes selected header and navigation partials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use it with Propshaft?
&lt;/h3&gt;

&lt;p&gt;The FAQ documents Propshaft support for Active Admin 4 when the standard Tailwind and cssbundling setup is followed. Active Admin 3 typically remains on Sprockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I customize first?
&lt;/h3&gt;

&lt;p&gt;Start with &lt;code&gt;--claude-primary&lt;/code&gt; and &lt;code&gt;--claude-canvas&lt;/code&gt; on Active Admin 4, or their Sass equivalents on Active Admin 3. These two choices demonstrate the token flow without coupling your application to internal selectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The useful part of &lt;code&gt;activeadmin-claude-theme&lt;/code&gt; is not only its palette. Its version-aware generator makes the Active Admin 3 and 4 boundary explicit, while its token model gives Rails teams a small, reviewable customization surface. Install against the release that matches your application, verify the generated asset path, and treat Active Admin 3 and 4 as separate pipelines.&lt;/p&gt;

&lt;p&gt;Have you kept one Active Admin application compatible with both major asset pipelines, or did you choose a clean upgrade to Active Admin 4? Share the constraint that shaped the decision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: AI assistance was used to organize this tutorial and draft prose. Project behavior, versions, commands, limitations, and links were checked against the public repository, the &lt;code&gt;v0.2.0&lt;/code&gt; release, the gem metadata, and the project documentation.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>tutorial</category>
      <category>activeadmin</category>
    </item>
    <item>
      <title>Score Any Public Repository Reproducibly with harness-maturity-analysis</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sun, 11 Oct 2026 12:31:02 +0000</pubDate>
      <link>https://dev.to/paladini/score-any-public-repository-reproducibly-with-harness-maturity-analysis-493g</link>
      <guid>https://dev.to/paladini/score-any-public-repository-reproducibly-with-harness-maturity-analysis-493g</guid>
      <description>&lt;p&gt;If you are evaluating how well a repository supports AI-assisted development, it is easy to mix two different jobs: measuring one repository quickly and adding a research observation to a maintained corpus. Those jobs need different levels of evidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/harness-maturity-analysis" rel="noopener noreferrer"&gt;harness-maturity-analysis&lt;/a&gt; gives you both paths. Its corpus workflow pins repositories to exact commits and records scanner versions. Its ad hoc command lets you inspect any local path or public repository without writing to the corpus.&lt;/p&gt;

&lt;p&gt;This tutorial focuses on the second path. You will run a deterministic local score, read the dimension breakdown, identify the highest-value gaps, and understand what the result does not prove.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Clone the project, install its development dependencies, then run the documented &lt;code&gt;score&lt;/code&gt; script with a repository URL or local path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/harness-maturity-analysis
&lt;span class="nb"&gt;cd &lt;/span&gt;harness-maturity-analysis
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run score &lt;span class="nt"&gt;--&lt;/span&gt; https://github.com/owner/repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command prints a maturity level, a point total, dimension percentages, detected tooling, and several unmet checks. It does not add the target to &lt;code&gt;corpus/manifest.json&lt;/code&gt; or create a generated report under &lt;code&gt;corpus/reports/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need Git, Node.js 18 or newer, and npm. The project declares the Node engine requirement in &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/package.json" rel="noopener noreferrer"&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/a&gt;. A network connection is needed when you pass a remote repository URL because the runner must obtain the source and the pinned scanner package.&lt;/p&gt;

&lt;p&gt;The project is MIT licensed. Read the &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/LICENSE" rel="noopener noreferrer"&gt;repository license&lt;/a&gt; before incorporating the scripts into another workflow.&lt;/p&gt;

&lt;p&gt;The current public README documents &lt;code&gt;harness-score@1.8.1&lt;/code&gt; as the scanner pin used by the corpus. That version is part of the research evidence, not a promise that every future run will produce the same number if the target repository changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run an ad hoc score
&lt;/h2&gt;

&lt;p&gt;From the project directory, pass either a GitHub URL or a local directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run score &lt;span class="nt"&gt;--&lt;/span&gt; https://github.com/owner/repo
npm run score &lt;span class="nt"&gt;--&lt;/span&gt; C:&lt;span class="se"&gt;\p&lt;/span&gt;ath&lt;span class="se"&gt;\t&lt;/span&gt;o&lt;span class="se"&gt;\l&lt;/span&gt;ocal&lt;span class="se"&gt;\r&lt;/span&gt;epository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command asks the project to obtain the remote source. The second analyzes files already on disk. In both cases, the ad hoc runner reads the target and sends it to the scanner; it does not install dependencies or execute code from the repository being inspected. That boundary matters when the target is unfamiliar.&lt;/p&gt;

&lt;p&gt;For a first experiment, score the analysis project itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run score &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;score&lt;/code&gt; npm script delegates to &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/corpus/score-adhoc.mjs" rel="noopener noreferrer"&gt;&lt;code&gt;corpus/score-adhoc.mjs&lt;/code&gt;&lt;/a&gt;. The script uses the study's configured scanner version, prints the score and dimension breakdown, and lists unmet checks that may offer the clearest improvement opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the output
&lt;/h2&gt;

&lt;p&gt;A result has several separate signals. The maturity level is a compact interpretation of the score. The point total explains how much of the available model the repository satisfies. Dimension percentages show whether the result is driven by context, skills, guardrails, feedback, CI, or hygiene.&lt;/p&gt;

&lt;p&gt;The detected-tooling line is descriptive. If the scanner identifies editor or agent configuration, that tells you which signals were found in the inspected files. It does not certify that a team uses the tools effectively.&lt;/p&gt;

&lt;p&gt;The unmet checks are a prioritization aid, not a backlog generated by an oracle. A missing type-checking check, for example, may be a reasonable next experiment for a JavaScript project, but it is not automatically a defect for every repository. Read the check definition and the relevant files before changing anything.&lt;/p&gt;

&lt;p&gt;The project's &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/METHODOLOGY.md" rel="noopener noreferrer"&gt;methodology&lt;/a&gt; makes the same distinction for the larger study: the score describes a repository's harness, not the competence of the organization that owns it. A low score can be appropriate for a library that was never designed as an agent-first workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the observation
&lt;/h2&gt;

&lt;p&gt;An ad hoc score is useful for exploration, but a research claim needs stronger identity. Record at least these values alongside the output:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the repository URL;&lt;/li&gt;
&lt;li&gt;the commit SHA or tag inspected;&lt;/li&gt;
&lt;li&gt;the scanner version;&lt;/li&gt;
&lt;li&gt;the command and options used;&lt;/li&gt;
&lt;li&gt;the complete output;&lt;/li&gt;
&lt;li&gt;the date and environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then rerun the same command at the same commit. If the score changes, investigate the scanner version, checkout contents, platform-specific files, and command arguments before interpreting the difference.&lt;/p&gt;

&lt;p&gt;The corpus workflow formalizes this process. &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/corpus/manifest.json" rel="noopener noreferrer"&gt;&lt;code&gt;corpus/manifest.json&lt;/code&gt;&lt;/a&gt; stores exact repository commits and the scanner version. &lt;a href="https://github.com/paladini/harness-maturity-analysis/blob/main/corpus/run.mjs" rel="noopener noreferrer"&gt;&lt;code&gt;corpus/run.mjs&lt;/code&gt;&lt;/a&gt; clones pinned source into a cache, scans it, and writes generated reports. The generated artifacts are then rebuilt from those reports instead of being edited by hand.&lt;/p&gt;

&lt;p&gt;That separation is the key design choice: use &lt;code&gt;npm run score -- ...&lt;/code&gt; when you are asking, "How does this repository look right now?" Use the corpus workflow when you are ready to make a durable, reviewable data point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the local installation
&lt;/h2&gt;

&lt;p&gt;Before trusting a local checkout, run the project's own checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test suite covers parsing, manifest shape, selection logic, report generation, history handling, site rendering, and clone behavior. Linting checks the JavaScript and data files with Biome. These checks validate the analysis tool itself; they do not validate the quality of the repository you pass to &lt;code&gt;npm run score&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After a score, check the working tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an ad hoc run, the expected result is no new corpus entry or generated report. If you intended to create study evidence and see unexpected files, stop and inspect the command and repository state before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The target is not reproducible
&lt;/h3&gt;

&lt;p&gt;A branch name points to moving content. Prefer a commit SHA or release tag when comparing results over time. A score from &lt;code&gt;main&lt;/code&gt; is a current snapshot, not a permanent fact.&lt;/p&gt;

&lt;h3&gt;
  
  
  The score looks surprisingly high or low
&lt;/h3&gt;

&lt;p&gt;Read the file-level evidence and check whether the project type matches the model's assumptions. The study explicitly warns that a repository-local harness score should not be treated as an organizational ranking.&lt;/p&gt;

&lt;h3&gt;
  
  
  The remote checkout cannot be obtained
&lt;/h3&gt;

&lt;p&gt;Check the URL, visibility, network access, and Git installation. Do not work around the failure by copying generated reports from another checkout. A missing checkout is missing evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  You are tempted to run target code
&lt;/h3&gt;

&lt;p&gt;Do not. The documented runner is designed to inspect files without executing code from the scanned repository. Keep that boundary intact, especially for untrusted projects, and treat any workflow that installs target dependencies as a different security model.&lt;/p&gt;

&lt;h3&gt;
  
  
  The corpus changes unexpectedly
&lt;/h3&gt;

&lt;p&gt;Do not hand-edit &lt;code&gt;corpus/reports/&lt;/code&gt;, &lt;code&gt;results/&lt;/code&gt;, or the Pages site. Those files are generated. Re-run the documented generator after changing an input, then review the diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does an ad hoc score change the public corpus?
&lt;/h3&gt;

&lt;p&gt;No. It is intended for a one-off reading. Corpus additions require a separate pinned and reviewable workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I compare two projects directly?
&lt;/h3&gt;

&lt;p&gt;You can compare their outputs as an exploratory exercise, but keep repository type, commit identity, scanner version, and checkout conditions visible. The number alone is not a universal quality ranking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does L4 mean the repository is production-ready?
&lt;/h3&gt;

&lt;p&gt;No. It means the inspected files satisfy the model's harness checks at that snapshot. It says nothing by itself about correctness, reliability, maintainability, licensing of dependencies, or operational safety.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this an AI judge?
&lt;/h3&gt;

&lt;p&gt;The scanner is deterministic and filesystem-oriented. It does not replace human review, and the repository's research protocol treats blind human assessment as a separate question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Use the ad hoc command to ask a focused question without polluting research history. Pin the target and scanner when the result needs to be reproduced. Most importantly, read the evidence behind the number before turning a maturity score into a technical decision.&lt;/p&gt;

&lt;p&gt;What repository would you score first, and which scanner signal would you want to validate manually before trusting the result?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: AI was used to help organize and edit this tutorial. Repository behavior, commands, versions, and limitations were checked against the project's public source and local verification output.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Customize Active Admin with CSS Design Tokens and Mobile Navigation</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sat, 10 Oct 2026 12:37:29 +0000</pubDate>
      <link>https://dev.to/paladini/customize-active-admin-with-css-design-tokens-and-mobile-navigation-209d</link>
      <guid>https://dev.to/paladini/customize-active-admin-with-css-design-tokens-and-mobile-navigation-209d</guid>
      <description>&lt;p&gt;If an Active Admin interface works but still feels like an internal tool from another decade, replacing the entire admin stack is usually too expensive. The smaller move is to change the visual system around the screens you already have.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to use &lt;a href="https://github.com/paladini/activeadmin-aaa-theme" rel="noopener noreferrer"&gt;Active Admin AAA Theme&lt;/a&gt; as a Rails theme and then customize its CSS design tokens. The useful idea is not the Apple-inspired visual language itself. It is the separation between the theme structure and the values you are likely to change: colors, type, spacing, radiuses, and shadows.&lt;/p&gt;

&lt;p&gt;The repository currently identifies itself as version &lt;code&gt;0.1.0&lt;/code&gt;, requires Ruby 3.0 or newer, and declares Rails 6.1 or newer plus Active Admin 3.0 or newer as gem dependencies. It is public, MIT licensed, and has no published GitHub release or RubyGems package at the time of writing. Treat the current branch as an early project, not as a stability guarantee. The version and dependency claims come from the &lt;a href="https://github.com/paladini/activeadmin-aaa-theme/blob/master/lib/active_admin_aaa_theme/version.rb" rel="noopener noreferrer"&gt;version file&lt;/a&gt; and &lt;a href="https://github.com/paladini/activeadmin-aaa-theme/blob/master/activeadmin-aaa-theme.gemspec" rel="noopener noreferrer"&gt;gemspec&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Add the theme gem to a Rails application, import its stylesheet and JavaScript entrypoints, then override the &lt;code&gt;--aaa-*&lt;/code&gt; variables in your own stylesheet. Verify the result in both light and dark color schemes, and check the mobile menu before treating the change as complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Rails application that already uses Active Admin.&lt;/li&gt;
&lt;li&gt;Ruby 3.0 or newer.&lt;/li&gt;
&lt;li&gt;Rails 6.1 or newer and Active Admin 3.0 or newer.&lt;/li&gt;
&lt;li&gt;Bundler and an asset pipeline or JavaScript setup that can load the documented imports.&lt;/li&gt;
&lt;li&gt;A way to inspect the admin UI at desktop and mobile widths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The theme is a presentation layer. It does not replace Active Admin authentication, authorization, database models, filters, or resource definitions. Keep those responsibilities in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the theme
&lt;/h2&gt;

&lt;p&gt;Add the gem name from the project's current README to your application's &lt;code&gt;Gemfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'activeadmin-aaa-theme'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository's gemspec declares the Rails and Active Admin dependencies, while the package metadata reports the same project version, &lt;code&gt;0.1.0&lt;/code&gt;. Because the gem is not currently published to RubyGems, a real application may need to point Bundler at the source repository or use a locally built gem until a package is released. Do not assume that the short gem name resolves from RubyGems today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect the stylesheet and JavaScript
&lt;/h2&gt;

&lt;p&gt;The README asks you to replace the default Active Admin stylesheet import in &lt;code&gt;app/assets/stylesheets/active_admin.scss&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Remove the default Active Admin base stylesheet&lt;/span&gt;
&lt;span class="c1"&gt;// @import "active_admin/base";&lt;/span&gt;

&lt;span class="c1"&gt;// Load the AAA theme&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"active_admin/aaa_theme"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then load the theme JavaScript from your Active Admin entrypoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active_admin/aaa_theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript is intentionally small. On &lt;code&gt;DOMContentLoaded&lt;/code&gt;, it looks for Active Admin's &lt;code&gt;#header&lt;/code&gt;, creates a button with the &lt;code&gt;aaa-menu-toggle&lt;/code&gt; class, and toggles &lt;code&gt;menu-open&lt;/code&gt; on the header when the button is clicked. It also closes the menu when a click happens outside the header. You can inspect this behavior in the &lt;a href="https://github.com/paladini/activeadmin-aaa-theme/blob/master/app/assets/javascripts/active_admin/aaa_theme.js" rel="noopener noreferrer"&gt;theme JavaScript source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This means the import is not decorative. If the stylesheet loads but the JavaScript entrypoint does not, desktop styling can appear correct while the mobile navigation remains incomplete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the design tokens
&lt;/h2&gt;

&lt;p&gt;The theme defines CSS custom properties under &lt;code&gt;:root&lt;/code&gt;. Put your overrides after the theme import so your application stylesheet wins in the cascade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"active_admin/aaa_theme"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;--aaa-primary-h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;260&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--aaa-primary-s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;85%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--aaa-primary-l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;55%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--aaa-border-radius-lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--aaa-font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&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;The current README documents tokens for the primary color, page and card backgrounds, primary text, medium and large radiuses, and body and display font stacks. The source stylesheet also defines transition values, sidebar width, border colors, shadows, and status colors. This gives you two useful levels of customization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change a few variables to match an existing product identity.&lt;/li&gt;
&lt;li&gt;Fork or extend selectors only when the structure itself needs to change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start with tokens. Selector overrides are more coupled to Active Admin's generated HTML and are therefore more likely to need maintenance after an Active Admin upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand dark mode before overriding colors
&lt;/h2&gt;

&lt;p&gt;The theme has two dark-mode paths. First, a &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; media query changes background, text, border, shadow, and primary-lightness variables. Second, &lt;code&gt;body.dark&lt;/code&gt; applies a similar palette when your application explicitly adds the class.&lt;/p&gt;

&lt;p&gt;That gives you a simple verification matrix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Light system preference with no &lt;code&gt;dark&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Dark system preference with no &lt;code&gt;dark&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Light preference with &lt;code&gt;body.dark&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A mobile viewport with the menu closed and open.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not override only &lt;code&gt;--aaa-bg-base&lt;/code&gt; and assume dark mode is handled. Check text, borders, focus rings, links, flash messages, tables, and form controls as well. A readable dashboard can still have an inaccessible input or a low-contrast action link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the result reproducibly
&lt;/h2&gt;

&lt;p&gt;Run the normal dependency and application setup commands documented by your Rails application. If you are using the repository's dummy application, its contribution guide points to the standard Rails database setup and server commands.&lt;/p&gt;

&lt;p&gt;After the application boots, verify the theme in the browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open an Active Admin index page with a table and filters.&lt;/li&gt;
&lt;li&gt;Confirm that the stylesheet import changes the sidebar, title bar, cards, tables, and controls.&lt;/li&gt;
&lt;li&gt;Use browser developer tools to inspect &lt;code&gt;:root&lt;/code&gt; and confirm that your overridden &lt;code&gt;--aaa-*&lt;/code&gt; values are active.&lt;/li&gt;
&lt;li&gt;Switch the operating system or browser color preference to dark and repeat the inspection.&lt;/li&gt;
&lt;li&gt;Reduce the viewport below the theme's mobile breakpoint and click the generated menu button.&lt;/li&gt;
&lt;li&gt;Click outside the header and confirm that the menu closes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a source-level check, compare your built asset output with the theme's &lt;a href="https://github.com/paladini/activeadmin-aaa-theme/blob/master/app/assets/stylesheets/active_admin/aaa_theme.scss" rel="noopener noreferrer"&gt;SCSS entrypoint&lt;/a&gt; and &lt;a href="https://github.com/paladini/activeadmin-aaa-theme/blob/master/app/assets/javascripts/active_admin/aaa_theme.js" rel="noopener noreferrer"&gt;JavaScript entrypoint&lt;/a&gt;. This catches a missing import without confusing it with a CSS specificity issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and tradeoffs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The gem cannot be found
&lt;/h3&gt;

&lt;p&gt;The repository currently has no RubyGems release even though the README includes a Gem Version badge. Check the package source before debugging Bundler. Until a release exists, use the source repository or a locally built gem according to your organization's dependency policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The menu button does not appear
&lt;/h3&gt;

&lt;p&gt;Check that the JavaScript import is part of the entrypoint loaded by the Active Admin layout. The script exits without creating a button when it cannot find &lt;code&gt;#header&lt;/code&gt;, so a wrong page, an earlier script failure, or a changed Active Admin layout can all produce the same symptom.&lt;/p&gt;

&lt;h3&gt;
  
  
  The color override has no effect
&lt;/h3&gt;

&lt;p&gt;Confirm that the override is loaded after the theme and that the variable name is exact. Inspect the computed value rather than only searching the source file. A selector that sets a concrete color can also win over a variable-based declaration.&lt;/p&gt;

&lt;h3&gt;
  
  
  A table becomes hard to use on mobile
&lt;/h3&gt;

&lt;p&gt;The theme adds responsive behavior, but it cannot make every wide Active Admin table fit a narrow screen. Test real resources with realistic column names and action links. Keep horizontal scrolling available where the data cannot be meaningfully collapsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security boundaries
&lt;/h2&gt;

&lt;p&gt;This theme changes CSS, HTML-adjacent behavior, and a small client-side menu controller. It does not provide authorization, secure sessions, CSRF protection, input validation, or protection from unsafe Active Admin resource definitions. Continue to use Rails and Active Admin security controls, review the generated asset policy, and treat third-party font loading as a separate privacy and availability decision.&lt;/p&gt;

&lt;p&gt;The README imports Google Fonts in the theme stylesheet. If your admin area must remain self-contained or avoid third-party requests, remove that import and provide local font fallbacks. The theme's MIT license permits reuse subject to its license terms, but it does not turn the project into a security-reviewed dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does this replace Active Admin?
&lt;/h3&gt;

&lt;p&gt;No. It is a theme gem that styles an existing Active Admin installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use my own brand colors?
&lt;/h3&gt;

&lt;p&gt;Yes. The documented CSS custom properties are the intended starting point for primary colors, typography, radiuses, and related visual values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does dark mode require a JavaScript framework?
&lt;/h3&gt;

&lt;p&gt;No. Automatic dark mode uses the CSS media query, and manual dark mode uses a &lt;code&gt;dark&lt;/code&gt; class on the body. The small JavaScript entrypoint is for the responsive navigation toggle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is version &lt;code&gt;0.1.0&lt;/code&gt; a stable release?
&lt;/h3&gt;

&lt;p&gt;No. It is the version declared in the repository's source. There is no published GitHub release or RubyGems package to treat as a stable distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The practical value of Active Admin AAA Theme is its tokenized boundary: install the theme once, keep application-specific branding in your own stylesheet, and verify desktop, dark mode, and mobile behavior as separate states. That approach makes visual customization easier to review and gives you a clear place to look when an upgrade changes the admin markup.&lt;/p&gt;

&lt;p&gt;If you try this pattern, which token or Active Admin component would you want the theme to expose next: navigation spacing, table density, form controls, or accessibility-focused color presets?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance disclosure: AI assistance was used to organize and edit this tutorial. Project facts, examples, limitations, and links were checked against the public repository sources listed in the article on October 10, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>tutorial</category>
      <category>css</category>
    </item>
    <item>
      <title>Add an Evidence-Backed Repository Listing to Harness Maturity Showcase</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:36:27 +0000</pubDate>
      <link>https://dev.to/paladini/add-an-evidence-backed-repository-listing-to-harness-maturity-showcase-130h</link>
      <guid>https://dev.to/paladini/add-an-evidence-backed-repository-listing-to-harness-maturity-showcase-130h</guid>
      <description>&lt;p&gt;If you publish a maturity score for an open-source repository, readers need more than a number. They need to know which commit was measured, which scanner produced the result, and where the raw evidence can be inspected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/harness-maturity-showcase" rel="noopener noreferrer"&gt;Harness Maturity Showcase&lt;/a&gt; is a small static site that makes those checks visible. It stores repository listings as data, links each listing to public evidence, and validates the rules in CI. This tutorial shows how to add one reproducible full report without changing the site's ranking code.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You will scan a public repository with the published &lt;code&gt;harness-score&lt;/code&gt; package, commit the generated JSON report to that repository, add one record to &lt;code&gt;data/projects.json&lt;/code&gt;, and run the showcase's deterministic checks. The important part is the evidence chain: repository, measured commit, scanner version, score, and public report must agree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git and a GitHub account.&lt;/li&gt;
&lt;li&gt;Node.js 20 or newer. The showcase declares &lt;code&gt;node &amp;gt;=20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A public GitHub repository that you are allowed to scan and modify.&lt;/li&gt;
&lt;li&gt;Permission to open a pull request in your fork of the showcase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The showcase is MIT-licensed. It is a static HTML, CSS, and JavaScript site with no runtime dependencies. Its own checks run with Node's built-in test runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the two contribution paths
&lt;/h2&gt;

&lt;p&gt;The project supports two kinds of entry. A full report participates in the numeric ranking. It includes &lt;code&gt;score&lt;/code&gt;, &lt;code&gt;maxScore&lt;/code&gt;, &lt;code&gt;level&lt;/code&gt;, &lt;code&gt;toolVersion&lt;/code&gt;, a measured commit SHA, and a link to a committed JSON report.&lt;/p&gt;

&lt;p&gt;A badge-only entry is different. It records a public maturity claim from a repository README, but it does not claim a numeric score. The validator rejects badge entries that include &lt;code&gt;score&lt;/code&gt; or &lt;code&gt;maxScore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use the full-report path when you can publish the scanner output and identify the exact commit. Use badge-only when the repository displays an official badge but does not publish a full report. Do not turn a badge claim into a measured result by inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start from a clean checkout
&lt;/h2&gt;

&lt;p&gt;Create a working copy of the repository you want to measure. The scanner reads files, so the result should be tied to a commit that another person can retrieve later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/your-name/your-project.git
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
git checkout &lt;span class="nt"&gt;--detach&lt;/span&gt; YOUR_COMMIT_SHA
git rev-parse HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record the 40-character SHA printed by the final command. The showcase compares reports within the same scanner version and stores the measured commit for reproducibility.&lt;/p&gt;

&lt;p&gt;The current showcase refresh documents the same rule: reports are pinned to public default-branch commits, and future refreshes use a new dated directory instead of rewriting historical evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Generate the report with the published scanner
&lt;/h2&gt;

&lt;p&gt;Run the published package from outside the repository checkout. The documented command writes JSON to standard output and leaves the scanned project unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; reports
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; harness-score@1.8.1 /path/to/pinned-checkout &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; reports/harness-score.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, the equivalent redirection is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;npx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--yes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;harness-score&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;1.8.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\path\to\pinned-checkout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;utf8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reports\harness-score.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the version that you actually ran in the showcase record. Do not copy a score from a different scanner version. The current release reports version &lt;code&gt;1.8.1&lt;/code&gt;, and the package requires Node.js 18 or newer; the showcase itself requires Node.js 20 or newer.&lt;/p&gt;

&lt;p&gt;Inspect the report before committing it. Confirm that its &lt;code&gt;tool.version&lt;/code&gt;, level, score, maximum score, and measured commit describe the checkout you intended to scan.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Publish the evidence in the scanned repository
&lt;/h2&gt;

&lt;p&gt;Commit the report to the repository you measured. A simple layout is enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── harness-score.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add harness-score.json
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add Harness Score evidence"&lt;/span&gt;
git push origin YOUR_BRANCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The report URL in the showcase must be public and stable, for example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://github.com/your-name/your-project/blob/main/harness-score.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you measure a historical commit, link to the report at that commit or to a branch that preserves the exact JSON. A link that later changes to a different report weakens the evidence chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add one full-report record
&lt;/h2&gt;

&lt;p&gt;Fork the showcase, edit &lt;code&gt;data/projects.json&lt;/code&gt;, and add one object. The contribution guide requires &lt;code&gt;source: "study"&lt;/code&gt; for a full report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-name/your-project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"community"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"toolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.8.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0123456789abcdef0123456789abcdef01234567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"study"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/your-name/your-project/blob/main/harness-score.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace every illustrative value with the corresponding value from your report. The repository name must use &lt;code&gt;owner/name&lt;/code&gt; syntax, and each repository may appear only once. The commit must be a complete 40-character hexadecimal SHA.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;category&lt;/code&gt; field is part of the site's data model, while the validator focuses on the repository name, source, numeric fields, commit, scanner version, and evidence URL. Keep the record small and avoid adding claims that the report does not contain.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Validate before opening the pull request
&lt;/h2&gt;

&lt;p&gt;From the showcase checkout, install its lockfile and run the documented check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;check&lt;/code&gt; script runs &lt;code&gt;npm run validate&lt;/code&gt; and &lt;code&gt;npm test&lt;/code&gt;. Validation checks repository names, duplicates, levels from 0 through 4, numeric bounds, scanner versions, complete commit SHAs, public GitHub evidence links, and the badge-only rules.&lt;/p&gt;

&lt;p&gt;The test suite checks the page structure, report reproducibility links, scanner-version boundaries, historical rankings, and the separation between full reports and badge-only entries. A passing check confirms the repository's data and site invariants. It does not replace human review of the evidence URL.&lt;/p&gt;

&lt;p&gt;Open a focused pull request that changes one listing. Maintainers verify that the linked evidence is public and that the values in &lt;code&gt;data/projects.json&lt;/code&gt; match it. If the measured repository changes substantially, generate a new report rather than silently editing the old one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the evidence model matters
&lt;/h2&gt;

&lt;p&gt;A score without a commit is hard to reproduce. A score without a scanner version is hard to compare. A score without a public report asks reviewers to trust a summary they cannot inspect.&lt;/p&gt;

&lt;p&gt;The showcase keeps those concerns separate. Rankings are scoped to a scanner version, full reports point to immutable evidence, and badge-only records stay outside numeric ranking. This makes a lower score interpretable: it may reflect the repository, the scanner version, or the measured commit, and the data tells you which inputs to investigate.&lt;/p&gt;

&lt;p&gt;The same design also limits what the site can claim. A passing GitHub Actions job proves that the data satisfies structural checks. It does not prove that a repository is secure, production-ready, or better than another project in every engineering dimension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and honest limits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The report is rejected because the commit is invalid
&lt;/h3&gt;

&lt;p&gt;Check that &lt;code&gt;commit&lt;/code&gt; contains exactly 40 hexadecimal characters. Do not use a short SHA, a branch name, or the commit that happened after the scan.&lt;/p&gt;

&lt;h3&gt;
  
  
  A full report does not appear in the ranking
&lt;/h3&gt;

&lt;p&gt;Confirm that &lt;code&gt;source&lt;/code&gt; is &lt;code&gt;study&lt;/code&gt;, that &lt;code&gt;toolVersion&lt;/code&gt; is a semantic version, and that &lt;code&gt;score&lt;/code&gt; and &lt;code&gt;maxScore&lt;/code&gt; are numbers within bounds. Badge-only entries intentionally do not receive numeric ranking.&lt;/p&gt;

&lt;h3&gt;
  
  
  The score changed after a refresh
&lt;/h3&gt;

&lt;p&gt;Compare the scanner version, measured commit, and repository contents. The project documents that changes between scanner versions or repository revisions are not, by themselves, evidence of improvement or regression.&lt;/p&gt;

&lt;h3&gt;
  
  
  The site check passes but the evidence is wrong
&lt;/h3&gt;

&lt;p&gt;This is a review failure, not a validator failure. Open the evidence URL, compare the JSON fields with the record, and verify that the URL is public before asking for review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security boundary
&lt;/h3&gt;

&lt;p&gt;The scanner is a read-only file inspection tool for this workflow, but running any package still deserves normal supply-chain care. Pin the version in reproducible instructions, review the package source and lockfile policy appropriate to your environment, and do not scan private repositories or publish sensitive report contents without authorization.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I submit a private repository?
&lt;/h3&gt;

&lt;p&gt;No. The evidence URL must be public, and the showcase is designed for inspectable open-source data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I submit a score from an older Harness Score version?
&lt;/h3&gt;

&lt;p&gt;Yes, when the report and metadata satisfy the schema, but rankings are separated by scanner version. Use the current published version for a new contribution unless you have a reason to preserve historical evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a passing score make a repository mature?
&lt;/h3&gt;

&lt;p&gt;It measures the checks implemented by Harness Score. It does not certify software quality, security, or operational readiness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I add several repositories in one pull request?
&lt;/h3&gt;

&lt;p&gt;The contribution guide asks contributors to keep a submission focused on one repository. Follow that review boundary unless maintainers ask for a batch change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The useful unit is not a maturity number. It is a number connected to a scanner version, a measured commit, a public report, and a validation path that another contributor can repeat.&lt;/p&gt;

&lt;p&gt;If you maintain an open-source repository with an official Harness Score result, submit the smallest complete evidence record you can defend. What additional evidence would make a maturity listing more useful to you: historical snapshots, CI status, or a clearer explanation of each check?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; I used AI assistance to organize and edit this tutorial. The commands, project behavior, version claims, and validation results were checked against the cited repository, its current documentation, and the published package metadata.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>opensource</category>
      <category>github</category>
      <category>tutorial</category>
      <category>githubpages</category>
    </item>
    <item>
      <title>Use mcp-me v0.6.0 to Share One Local AI Profile Across MCP Clients</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 07 Oct 2026 12:38:11 +0000</pubDate>
      <link>https://dev.to/paladini/use-mcp-me-v060-to-share-one-local-ai-profile-across-mcp-clients-262l</link>
      <guid>https://dev.to/paladini/use-mcp-me-v060-to-share-one-local-ai-profile-across-mcp-clients-262l</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you use more than one MCP-compatible AI client, repeating your background in every tool is tedious and easy to get wrong. &lt;code&gt;mcp-me&lt;/code&gt; v0.6.0 gives you one local profile directory, a YAML configuration file, and an MCP server that clients can read.&lt;/p&gt;

&lt;p&gt;This tutorial creates the profile, validates it, configures the same server for Cursor, VS Code, and Windsurf, and explains where Claude Desktop fits. The setup uses the published &lt;code&gt;mcp-me@0.6.0&lt;/code&gt; package, not an unreleased checkout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20 or later. The package declares &lt;code&gt;node &amp;gt;=20.0.0&lt;/code&gt; in its release metadata.&lt;/li&gt;
&lt;li&gt;npm and a terminal.&lt;/li&gt;
&lt;li&gt;One MCP-compatible client such as Cursor, VS Code with GitHub Copilot, Windsurf, or Claude Desktop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is open source under the MIT license. The &lt;a href="https://github.com/paladini/mcp-me/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;v0.6.0 release&lt;/a&gt; is the stable reference for this walkthrough, and the &lt;a href="https://github.com/paladini/mcp-me/blob/v0.6.0/package.json" rel="noopener noreferrer"&gt;package manifest&lt;/a&gt; records the version and Node.js requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create and validate one profile
&lt;/h2&gt;

&lt;p&gt;You can run the CLI without a global installation through &lt;code&gt;npx&lt;/code&gt;. This keeps the example tied to the exact version used in the tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 init
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With no directory argument, v0.6.0 uses &lt;code&gt;~/.mcp-me&lt;/code&gt; as the default profile location. The &lt;code&gt;init&lt;/code&gt; command creates the YAML templates, &lt;code&gt;.mcp-me.yaml&lt;/code&gt;, and agent instruction files. The &lt;code&gt;validate&lt;/code&gt; command checks the profile YAML files against the project's schemas.&lt;/p&gt;

&lt;p&gt;The command output should end with a successful validation message for the profile files. In a clean smoke test, the release created 11 files and validated identity, career, skills, interests, personality, goals, projects, and FAQ data.&lt;/p&gt;

&lt;p&gt;Now inspect the generated configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.mcp-me/.mcp-me.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use this equivalent command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="bp"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;\.mcp-me\.mcp-me.yaml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file separates data generators from live plugins. Start with a small configuration and replace the example values with your own public handles:&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;generators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;
  &lt;span class="na"&gt;devto&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;

&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-username&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration is not your profile data. It tells &lt;code&gt;mcp-me&lt;/code&gt; which sources to use when it generates profile files and which live plugins should be available to the server. Keep the generated YAML files reviewable, and validate after editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate profile data locally
&lt;/h2&gt;

&lt;p&gt;After editing &lt;code&gt;.mcp-me.yaml&lt;/code&gt;, run the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 generate
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/paladini/mcp-me/blob/v0.6.0/README.md" rel="noopener noreferrer"&gt;v0.6.0 README&lt;/a&gt; documents username-based generators and the default profile path. Many public sources do not need API keys, but that is source-dependent. A provider can still require authentication, rate limits, or a local export. Treat each generated file as data to review before making it available to an assistant.&lt;/p&gt;

&lt;p&gt;If you want an isolated profile for a project or experiment, pass a directory explicitly instead of changing the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 init ./my-ai-profile
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 validate ./my-ai-profile
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 generate ./my-ai-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explicit path is useful for testing because it makes the data boundary obvious. It also lets you keep separate profiles for work and personal use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect the same server to multiple clients
&lt;/h2&gt;

&lt;p&gt;The important idea is that every client points to the same command and profile directory. The server reads the profile when it starts, so the clients do not need separate copies of your YAML files.&lt;/p&gt;

&lt;p&gt;The simplest cross-client command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx -y mcp-me serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windsurf, add an entry to &lt;code&gt;~/.codeium/windsurf/mcp_config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"me"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Cursor, place the same server entry in &lt;code&gt;.cursor/mcp.json&lt;/code&gt; in a project, or in the client configuration used for global servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"me"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VS Code uses a different top-level key. In &lt;code&gt;.vscode/mcp.json&lt;/code&gt;, use &lt;code&gt;servers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"me"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These client examples are taken from the &lt;a href="https://github.com/paladini/mcp-me/tree/v0.6.0#configure-your-ai-assistant" rel="noopener noreferrer"&gt;v0.6.0 README configuration section&lt;/a&gt;. Claude Desktop can use the &lt;code&gt;.mcpb&lt;/code&gt; asset attached to the &lt;a href="https://github.com/paladini/mcp-me/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;v0.6.0 GitHub release&lt;/a&gt;, or the manual MCP configuration documented by the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the server boundary
&lt;/h2&gt;

&lt;p&gt;Before connecting a client, ask the CLI for help and validate the profile again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 &lt;span class="nt"&gt;--help&lt;/span&gt;
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start the server in a terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; mcp-me@0.6.0 serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An MCP client normally launches this process itself over standard input and output. The useful verification is therefore client-side: connect the &lt;code&gt;me&lt;/code&gt; server, ask the client to read a profile resource, and confirm that the answer matches the YAML you reviewed. Do not treat a successful process launch as proof that every generator or plugin works.&lt;/p&gt;

&lt;p&gt;If you need to make the profile location explicit, set &lt;code&gt;MCP_ME_PROFILE_DIR&lt;/code&gt; in the client process environment or pass a directory to the &lt;code&gt;serve&lt;/code&gt; command. The &lt;a href="https://github.com/paladini/mcp-me/blob/v0.6.0/README.md" rel="noopener noreferrer"&gt;release README&lt;/a&gt; documents both options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;The profile files and the &lt;code&gt;.mcp-me.yaml&lt;/code&gt; configuration form a local source of context. The MCP server exposes that context through the protocol, while each client remains responsible for deciding when to read it. This is different from copying a long instruction block into every client: the data has one canonical location, and changes can be reviewed with normal file tools.&lt;/p&gt;

&lt;p&gt;The design also leaves a useful boundary between generated data and live integrations. Generated YAML can be inspected before use. Plugins can provide current values when an assistant queries them. That separation makes it easier to disable a plugin, remove a field, or keep a sensitive category out of a profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The client cannot find &lt;code&gt;npx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;GUI applications may not inherit the same PATH as your terminal. Check the client documentation for environment configuration, or use the absolute path to your Node.js installation. The error is a process discovery problem, not a profile validation failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation fails after editing YAML
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;validate&lt;/code&gt; with the profile directory and read the first reported file. Fix the schema or value, then run validation again. Do not connect an unvalidated profile and assume the client will explain the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  A source returns incomplete data
&lt;/h3&gt;

&lt;p&gt;Generators depend on the source's public API, feed, or export format. A valid local profile does not prove that a remote source returned complete data. Review the generated file and consult the source-specific documentation before relying on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local does not mean risk-free
&lt;/h3&gt;

&lt;p&gt;The project is designed around local YAML files, but any MCP client that can read those files may place their contents into model context. Do not put passwords, private keys, access tokens, or secrets in the profile. Use the project's documented plugin settings and your operating system's file permissions. The MIT license and local execution do not constitute a security guarantee.&lt;/p&gt;

&lt;p&gt;The tutorial also uses the stable v0.6.0 release. The repository's default branch may contain newer or unreleased behavior, so check the &lt;a href="https://github.com/paladini/mcp-me/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;current changelog&lt;/a&gt; before copying commands into a new environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to install &lt;code&gt;mcp-me&lt;/code&gt; globally?
&lt;/h3&gt;

&lt;p&gt;No. &lt;code&gt;npx --yes mcp-me@0.6.0&lt;/code&gt; is enough for the commands above. A global npm installation is convenient for repeated use, but pinning the version makes a tutorial and a reproducible check clearer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can different clients use different profiles?
&lt;/h3&gt;

&lt;p&gt;Yes. Pass an explicit directory or set &lt;code&gt;MCP_ME_PROFILE_DIR&lt;/code&gt; for a client process. Use separate profiles when the audiences or sensitivity of the data differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does &lt;code&gt;mcp-me&lt;/code&gt; remember previous conversations?
&lt;/h3&gt;

&lt;p&gt;No. It exposes profile data and configured live resources through MCP. Conversation history, model behavior, and client-side context policies remain separate concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Use one reviewed profile directory as the shared context layer, validate it after every edit, and point each MCP client at the same &lt;code&gt;mcp-me serve&lt;/code&gt; command. Start with public, low-risk fields. Add generators and plugins only when you understand what data they read and what the client can send to a model.&lt;/p&gt;

&lt;p&gt;What is the first profile category you would keep local and shared across your AI tools: skills, projects, career history, or something else?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: I used AI assistance to organize and edit this tutorial. The commands, version claims, configuration examples, and smoke-test result were checked against the &lt;code&gt;mcp-me&lt;/code&gt; v0.6.0 release and its primary project documentation.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
