<?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: Altavista360</title>
    <description>The latest articles on DEV Community by Altavista360 (@altavista360).</description>
    <link>https://dev.to/altavista360</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%2F4161303%2F72ec1fc9-783f-47b3-b01d-3188b5a9f850.png</url>
      <title>DEV Community: Altavista360</title>
      <link>https://dev.to/altavista360</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/altavista360"/>
    <language>en</language>
    <item>
      <title>Turning four compliance and lead-cost checks into a local, read-only MCP server</title>
      <dc:creator>Altavista360</dc:creator>
      <pubDate>Sun, 25 Oct 2026 08:13:35 +0000</pubDate>
      <link>https://dev.to/altavista360/turning-four-compliance-and-lead-cost-checks-into-a-local-read-only-mcp-server-1bo2</link>
      <guid>https://dev.to/altavista360/turning-four-compliance-and-lead-cost-checks-into-a-local-read-only-mcp-server-1bo2</guid>
      <description>&lt;p&gt;We run a small call-centre outsourcing brokerage, and a few questions kept coming back to us. Is this telemarketing consent still valid under France's new one-year rule? Which lead source is actually cheapest once you count the meetings that took place, not the leads? What is missing in a GDPR set-up when customer service is outsourced outside the EU?&lt;/p&gt;

&lt;p&gt;Over the last weeks we turned those questions into small, standard-library Python packages. This weekend we wrapped four of them in an MCP server, so that Claude, Cursor and other MCP clients can call them directly: &lt;strong&gt;kanibal-tools-mcp&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add kanibal-tools &lt;span class="nt"&gt;--&lt;/span&gt; uvx kanibal-tools-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Claude Desktop, Cursor or any other client, the configuration is the usual one:&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;"kanibal-tools"&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;"uvx"&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;"kanibal-tools-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;h2&gt;
  
  
  The four tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;check_telemarketing_consent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;France only: is a consent to telephone canvassing still valid under décret 2026-662? Returns &lt;code&gt;valide&lt;/code&gt;, &lt;code&gt;expire&lt;/code&gt;, &lt;code&gt;revoque&lt;/code&gt; or &lt;code&gt;preuve_manquante&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rank_lead_sources_by_kept_meeting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ranks B2B lead sources, from CSV text, on cost per kept qualified meeting instead of cost per lead.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gdpr_offshore_gap_report&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GDPR gap report for customer service outsourced outside the EU/EEA (transfer basis, transfer impact assessment, Art. 28 clauses, security, breach notification).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select_least_cost_capable_model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;From a catalogue you supply, picks the model with the lowest declared cost rank among those that have every required capability and are available and authorised.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each one wraps a package that is published on its own: &lt;a href="https://github.com/kanibal-studio/demarchage-consent" rel="noopener noreferrer"&gt;demarchage-consent&lt;/a&gt;, &lt;a href="https://github.com/kanibal-studio/cost-per-kept-meeting" rel="noopener noreferrer"&gt;cost-per-kept-meeting&lt;/a&gt;, &lt;a href="https://github.com/kanibal-studio/rgpd-offshore-check" rel="noopener noreferrer"&gt;rgpd-offshore-check&lt;/a&gt; and &lt;a href="https://github.com/kanibal-studio/least-cost-capable" rel="noopener noreferrer"&gt;least-cost-capable&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choices
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local, read-only, closed-world.&lt;/strong&gt; Every tool computes from its arguments only: no network call, no file access, no state kept between calls. The tools say so to the client through MCP annotations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LOCAL_READ_ONLY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;readOnlyHint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;destructiveHint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;idempotentHint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;openWorldHint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fail-closed.&lt;/strong&gt; A missing or unreadable consent date is &lt;code&gt;preuve_manquante&lt;/code&gt;, never &lt;code&gt;valide&lt;/code&gt;. In the GDPR checklist, only a JSON &lt;code&gt;true&lt;/code&gt; satisfies a check: an unanswered item is reported as "to verify" and still counts as blocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bounded claims.&lt;/strong&gt; The consent and GDPR tools are checklists, not legal advice. A low cost per kept meeting does not prove profitability. The model picker only uses the facts in the catalogue it receives; it does not call providers or promise savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing through a real MCP session
&lt;/h2&gt;

&lt;p&gt;The tests do not call the Python functions directly. They go through an in-memory MCP client session, so they also check the tool names, schemas and annotations that a client sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.shared.memory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_connected_server_and_client_session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kanibal_tools_mcp.server&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_tools&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;create_connected_server_and_client_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mcp_server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_tools&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One small surprise: &lt;code&gt;FastMCP&lt;/code&gt; takes no version argument, so the server reported the SDK version (1.30.0) instead of its own. Setting it on the low-level server fixed it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mcp_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__version__&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publishing: PyPI, the MCP Registry, Glama
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PyPI&lt;/strong&gt; uses Trusted Publishing from a &lt;code&gt;release.yml&lt;/code&gt; workflow. The publish job runs in a GitHub environment called &lt;code&gt;pypi&lt;/code&gt; that requires a maintainer's approval and only accepts &lt;code&gt;v*&lt;/code&gt; tags, so no token is stored anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The official MCP Registry&lt;/strong&gt; lists the server as &lt;code&gt;io.github.kanibal-studio/kanibal-tools-mcp&lt;/code&gt;. Two details mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The registry checks ownership of a PyPI package through an &lt;code&gt;mcp-name: io.github.kanibal-studio/kanibal-tools-mcp&lt;/code&gt; line in the README that PyPI shows.&lt;/li&gt;
&lt;li&gt;The interactive &lt;code&gt;mcp-publisher login github&lt;/code&gt; flow cannot currently publish under an organisation namespace. Publishing from GitHub Actions with &lt;code&gt;mcp-publisher login github-oidc&lt;/code&gt; can, so the release workflow does it right after the PyPI job, and waits until PyPI serves the new version first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Glama&lt;/strong&gt; runs its own checks before listing a server. For a repository that belongs to an organisation, it first wants a &lt;code&gt;glama.json&lt;/code&gt; at the root naming the maintainers. Its Dockerfile is generated from a small form. The first build failed: &lt;code&gt;uv pip install --system .&lt;/code&gt; targeted Debian's Python, which is marked as externally managed. A dedicated virtual environment fixed it:&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="s2"&gt;"uv venv /opt/venv --python 3.12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uv pip install --python /opt/venv/bin/python ."&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;with &lt;code&gt;/opt/venv/bin/python -m kanibal_tools_mcp&lt;/code&gt; as the start command. Glama then started the server and listed its four tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and tell us what is missing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/kanibal-studio/kanibal-tools-mcp" rel="noopener noreferrer"&gt;https://github.com/kanibal-studio/kanibal-tools-mcp&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/kanibal-tools-mcp/" rel="noopener noreferrer"&gt;https://pypi.org/project/kanibal-tools-mcp/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Product Hunt: &lt;a href="https://www.producthunt.com/products/kanibal-tools-mcp" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/kanibal-tools-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We would like feedback on the tool interfaces, especially from people who run outbound or customer-service teams across EU borders.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Coordinating several AI coding agents on one machine with a 230-line SQLite tool</title>
      <dc:creator>Altavista360</dc:creator>
      <pubDate>Sat, 24 Oct 2026 21:26:24 +0000</pubDate>
      <link>https://dev.to/altavista360/coordinating-several-ai-coding-agents-on-one-machine-with-a-230-line-sqlite-tool-5chd</link>
      <guid>https://dev.to/altavista360/coordinating-several-ai-coding-agents-on-one-machine-with-a-230-line-sqlite-tool-5chd</guid>
      <description>&lt;p&gt;When you run several AI coding agents at once on the same machine (several Claude Code sessions, a worker plus an integrator, or a mix of tools), two problems show up quickly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Messages get lost.&lt;/strong&gt; One agent finishes something, says so in its own terminal, and nobody else ever sees it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two agents edit the same file.&lt;/strong&gt; Each one is convinced it owns &lt;code&gt;src/add.py&lt;/code&gt;, and the second write quietly wins.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We hit both while building our own internal tooling, so we wrote a deliberately small answer to them: &lt;a href="https://github.com/kanibal-studio/agent-collab-kit" rel="noopener noreferrer"&gt;agent-collab-kit&lt;/a&gt;, one Python file of about 230 lines, standard library and SQLite only, MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Durable messages with explicit acknowledgements
&lt;/h2&gt;

&lt;p&gt;Every message lives in a local SQLite file until the recipient acknowledges it. Here is the demo from the README, run against a throwaway database:&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="nv"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/demo.db
python3 agent_collab.py &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; init
python3 agent_collab.py &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; send &lt;span class="nt"&gt;--sender&lt;/span&gt; worker-a &lt;span class="nt"&gt;--recipient&lt;/span&gt; integrator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; demo-result-1 &lt;span class="nt"&gt;--body&lt;/span&gt; &lt;span class="s1"&gt;'Addition is ready for review'&lt;/span&gt;
python3 agent_collab.py &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; inbox &lt;span class="nt"&gt;--recipient&lt;/span&gt; integrator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="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;"2fd715fc-..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idempotency_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo-result-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;"sender"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"worker-a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recipient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integrator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Addition is ready for review"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"acknowledged_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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 idempotency key matters for agents: an agent that retries after a crash and sends the same sender, recipient and body gets the existing message back instead of a duplicate. Reusing the key for different content is an error. &lt;code&gt;ack&lt;/code&gt; marks the message as received. It does not prove the work was delivered or tested, and the README says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leases on exact resource names
&lt;/h2&gt;

&lt;p&gt;Before editing, an agent claims the exact resource it is about to touch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 agent_collab.py &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; claim &lt;span class="nt"&gt;--resource&lt;/span&gt; src/add.py &lt;span class="nt"&gt;--owner&lt;/span&gt; worker-a &lt;span class="nt"&gt;--ttl&lt;/span&gt; 300
&lt;span class="c"&gt;# {"resource": "src/add.py", "owner": "worker-a", "generation": "6ca2d25b-...", ...}&lt;/span&gt;

python3 agent_collab.py &lt;span class="nt"&gt;--db&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; claim &lt;span class="nt"&gt;--resource&lt;/span&gt; src/add.py &lt;span class="nt"&gt;--owner&lt;/span&gt; worker-b &lt;span class="nt"&gt;--ttl&lt;/span&gt; 300
&lt;span class="c"&gt;# resource has an active lease   (exit code 1)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each claim gets a fresh &lt;strong&gt;generation UUID&lt;/strong&gt;. Renewing or releasing requires the matching owner &lt;em&gt;and&lt;/em&gt; generation, and an unexpired lease. That closes a nasty case: an agent that was paused, whose lease expired and was taken over, cannot later renew or release its successor's lease, even under the same owner name. Every mutation runs inside SQLite &lt;code&gt;BEGIN IMMEDIATE&lt;/code&gt;, and the test suite includes two real processes racing for one claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;p&gt;This is the part we care most about stating clearly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leases are cooperative.&lt;/strong&gt; An agent that ignores them is not stopped. There is no filesystem fencing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiry does not stop a worker.&lt;/strong&gt; A paused worker may resume after a takeover, so workers must check ownership and stop when they lose it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No identities.&lt;/strong&gt; Names and UUIDs are cooperative tokens, not authentication. Anyone with access to the database file can impersonate a participant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One trusted machine, local disk.&lt;/strong&gt; Network filesystems and separate hosts are unsupported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No orchestration.&lt;/strong&gt; It does not launch agents, call a model provider or run Git.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice we pair it with one rule: a single integrator reviews and merges, and a shared resource such as &lt;code&gt;__commit__&lt;/code&gt; serializes integration. The repository includes a fictitious &lt;code&gt;COLLAB.md&lt;/code&gt; board and a short method for letting project agents propose improvements under supervision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four other small tools from the same place
&lt;/h2&gt;

&lt;p&gt;We publish the reusable pieces of our stack under &lt;a href="https://github.com/kanibal-studio" rel="noopener noreferrer"&gt;kanibal-studio&lt;/a&gt;, each with its own tests and the same habit of saying what it does not do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kanibal-studio/least-cost-capable" rel="noopener noreferrer"&gt;least-cost-capable&lt;/a&gt;: a portable Agent Skill that picks the lowest declared cost rank among the models in your catalogue that actually have the capabilities a task needs. Unknown evidence stays "unresolved".&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kanibal-studio/cost-per-kept-meeting" rel="noopener noreferrer"&gt;cost-per-kept-meeting&lt;/a&gt;: ranks B2B lead sources by cost per kept qualified meeting instead of cost per lead.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kanibal-studio/rgpd-offshore-check" rel="noopener noreferrer"&gt;rgpd-offshore-check&lt;/a&gt;: a GDPR gap report for outsourcing customer service outside the EU. Not legal advice.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kanibal-studio/demarchage-consent" rel="noopener noreferrer"&gt;demarchage-consent&lt;/a&gt;: French cold-call consent validity under decree 2026-662, on PyPI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues and criticism are welcome, especially about the lease semantics.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
      <category>agents</category>
    </item>
    <item>
      <title>Aymen, Altavista360 : automatiser la relation client sans perdre de vue le résultat</title>
      <dc:creator>Altavista360</dc:creator>
      <pubDate>Sun, 04 Oct 2026 09:39:17 +0000</pubDate>
      <link>https://dev.to/altavista360/presentation-de-gold-construire-une-entreprise-ia-en-verifiant-les-effets-reels-4cl7</link>
      <guid>https://dev.to/altavista360/presentation-de-gold-construire-une-entreprise-ia-en-verifiant-les-effets-reels-4cl7</guid>
      <description>&lt;p&gt;Bonjour DEV,&lt;/p&gt;

&lt;p&gt;Je m’appelle Aymen et je porte Altavista360, une activité de relation client, de centres d’appels et d’acquisition. Je m’intéresse à la façon dont l’IA et l’automatisation peuvent rendre ces services plus utiles et plus fiables au quotidien. Je ne présente pas l’autonomie complète ni un résultat commercial comme acquis.&lt;/p&gt;

&lt;p&gt;La question technique qui m’occupe est simple : &lt;strong&gt;comment vérifier qu’un agent ou un automatisme a réellement produit l’effet attendu ?&lt;/strong&gt; Une réponse réussie, une tâche mise en file et une action visible sont trois états différents.&lt;/p&gt;

&lt;p&gt;Quelques principes que j’applique :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Séparer les preuves.&lt;/strong&gt; Le code existe, un test passe, une version est chargée, l’effet public est visible, puis un résultat métier est mesuré. Chaque étape demande sa propre observation. Un HTTP 200 ne prouve pas à lui seul qu’une page contient l’information attendue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tester hors des données réelles.&lt;/strong&gt; Les scénarios de concurrence et d’échec utilisent des données isolées. Désactiver temporairement un mécanisme dans un test permet de vérifier que le test détecte bien sa disparition, puis la source est restaurée et vérifiée.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regarder le chemin réellement exécuté.&lt;/strong&gt; Une option de configuration n’est utile que si les appelants la lisent. Je compare le réglage déclaré, la clé effectivement résolue et le comportement du composant, sans exposer de secret.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nommer l’inconnu.&lt;/strong&gt; Si une mesure publique échoue, le résultat reste non mesurable. Le remplacer par “tout va bien” masquerait le prochain problème.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendre une modification réversible.&lt;/strong&gt; Préparer le retour arrière et vérifier l’état chargé aide à distinguer une correction locale d’une amélioration réellement en exploitation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ces principes servent autant aux flux de publication qu’aux outils de mesure et aux échanges entre composants. Ils ne remplacent pas la qualité du service : l’étape suivante reste de suivre les visites, les demandes et les opérations réellement exécutées, sans transformer les tests en clients fictifs.&lt;/p&gt;

&lt;p&gt;Mes trois sites ont des fonctions distinctes : &lt;a href="https://altavista360.com/" rel="noopener noreferrer"&gt;Altavista360&lt;/a&gt; pour l’activité de relation client et d’acquisition, &lt;a href="https://altavista-conseil.com/" rel="noopener noreferrer"&gt;Altavista Conseil&lt;/a&gt; pour les parcours de demande et de mise en relation, et &lt;a href="https://infos.kanibal-studio.com/" rel="noopener noreferrer"&gt;K-Pulse 360&lt;/a&gt; pour les articles et guides francophones.&lt;/p&gt;

&lt;p&gt;Je souhaite partager ici des retours techniques concrets sur les agents, les tests, l’observabilité et l’automatisation. Quelle preuve vous manque le plus souvent entre “la commande a réussi” et “l’effet attendu est réellement présent” ?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Présentation rédigée avec une assistance IA. Les principes décrivent ma démarche de développement ; ils ne constituent pas une promesse d’autonomie complète ou de revenu.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>testing</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
