<?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: Alexander Ivanov</title>
    <description>The latest articles on DEV Community by Alexander Ivanov (@someone_somewhere_05cad9e).</description>
    <link>https://dev.to/someone_somewhere_05cad9e</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%2F3941191%2Feb591200-9c40-441f-a313-918124a62f41.jpg</url>
      <title>DEV Community: Alexander Ivanov</title>
      <link>https://dev.to/someone_somewhere_05cad9e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/someone_somewhere_05cad9e"/>
    <language>en</language>
    <item>
      <title>A change is committed where it is made</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Wed, 04 Nov 2026 02:18:09 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/a-change-is-committed-where-it-is-made-2bme</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/a-change-is-committed-where-it-is-made-2bme</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/a-change-is-committed-where-it-is-made/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A change worked in its own worktree — since ADR 0043 the usual change —&lt;br&gt;
could have its own harness set nowhere but in the file. The Changes tree&lt;br&gt;
drew such a change as one worked "elsewhere" and gave it a shortened menu,&lt;br&gt;
and the Pipeline's card offered only Start, the run's controls and Logs.&lt;br&gt;
Every control added to the tree and not to the card, or the other way&lt;br&gt;
round, left one host or one kind of change poorer.&lt;/p&gt;

&lt;p&gt;The owner decided the same day: when a change is made, everything needed&lt;br&gt;
is done, so the change can be worked at once as one's own.&lt;/p&gt;
&lt;h2&gt;
  
  
  The rules, now with when
&lt;/h2&gt;

&lt;p&gt;The agent workflow rules say when: commit the planning artifacts on the&lt;br&gt;
change's branch and push it with an upstream as soon as they are written;&lt;br&gt;
commit and push after every task ticked; leave nothing uncommitted or&lt;br&gt;
unpushed at the end of a turn; nothing of a change on &lt;code&gt;main&lt;/code&gt;; a refused&lt;br&gt;
push is said, not worked around.&lt;/p&gt;

&lt;p&gt;Create Change, in both hosts, commits the new change on its branch and&lt;br&gt;
pushes it at once. A refused push leaves the change made and says why&lt;br&gt;
(&lt;code&gt;OSW-GIT-203&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;git&lt;/code&gt; stage commits what the stages left before it pushes&lt;br&gt;
(&lt;code&gt;OSW-GIT-103&lt;/code&gt; where that commit fails).&lt;/p&gt;
&lt;h2&gt;
  
  
  The survey and the card
&lt;/h2&gt;

&lt;p&gt;The survey says where a change's own branch is not on the server: never&lt;br&gt;
pushed, or at another commit. Read from the refs it lists anyway, so no&lt;br&gt;
git runs in the directory.&lt;/p&gt;

&lt;p&gt;A card of a change's own worktree says so ("not on the server"), and a&lt;br&gt;
new action, &lt;strong&gt;Commit Change&lt;/strong&gt;, commits everything the worktree holds on&lt;br&gt;
the change's branch and pushes it. On the card and in Show Actions..., in&lt;br&gt;
both hosts; refused in the main checkout and on the default branch.&lt;/p&gt;

&lt;p&gt;A card of a change's own worktree offers its actions in the arrangement&lt;br&gt;
by step as on the board, and &lt;strong&gt;Run Change...&lt;/strong&gt;: the owner found on&lt;br&gt;
2026-10-10 that such a change could be acted on but never run — a card's&lt;br&gt;
Start was refused for a change this checkout does not have. It runs in&lt;br&gt;
the change's worktree, in both hosts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Messages in the new GIT group
&lt;/h2&gt;

&lt;p&gt;Messages in the register's new GIT group (&lt;code&gt;OSW-GIT-001&lt;/code&gt;..&lt;code&gt;203&lt;/code&gt;) name&lt;br&gt;
every failure, refusal and success in the commit and push path. ADR 0043&lt;br&gt;
is amended to reflect the new rules.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec-ui-cli commit change &lt;span class="nt"&gt;--cwd&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>git</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Every message has an identifier, from one register</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Fri, 30 Oct 2026 02:22:24 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/every-message-has-an-identifier-from-one-register-29l2</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/every-message-has-an-identifier-from-one-register-29l2</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/every-message-has-an-identifier-from-one-register/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What the product says to a person — a failure's reason, a refusal, a CLI&lt;br&gt;
error — was written where it is said, as a sentence. A person cannot look&lt;br&gt;
up a message they saw, a host cannot act on one except by reading its&lt;br&gt;
words, and nothing says which messages exist. A new register decides that&lt;br&gt;
every message is said as &lt;code&gt;&amp;lt;level&amp;gt; OSW-&amp;lt;GROUP&amp;gt;-&amp;lt;NNN&amp;gt;: &amp;lt;message&amp;gt;&lt;/code&gt;, from one&lt;br&gt;
place, and that the register comes first with the groups &lt;code&gt;CLI&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;QST&lt;/code&gt; and &lt;code&gt;PRM&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The register, and what it holds
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;message-register.ts&lt;/code&gt; in core holds the 14 groups, every message's&lt;br&gt;
identifier, level, words with named values, why it is said and what to&lt;br&gt;
do. &lt;code&gt;say&lt;/code&gt;, &lt;code&gt;formatMessage&lt;/code&gt;, &lt;code&gt;withMessageCode&lt;/code&gt;, &lt;code&gt;messageEntryUrl&lt;/code&gt; are the&lt;br&gt;
functions the rest of the product calls.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CLI&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;QST&lt;/code&gt; and &lt;code&gt;PRM&lt;/code&gt; messages are said from the register:&lt;br&gt;
the CLI's argument errors, the chain's refusals, limits and endings, the&lt;br&gt;
operator's questions, and the permission requests nobody can answer. Their&lt;br&gt;
words do not change; the CLI's lines lose &lt;code&gt;openspec-ui-cli:&lt;/code&gt; and gain the&lt;br&gt;
form, &lt;code&gt;error OSW-CLI-003: --cwd requires a value&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  A page you can search for
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;docs/messages.md&lt;/code&gt; is generated from the register: a page a person&lt;br&gt;
searches for the identifier they saw. If you see &lt;code&gt;error OSW-RUN-042&lt;/code&gt; in&lt;br&gt;
your terminal or panel, you can look up what it means, why it was said,&lt;br&gt;
and what to do about it, without guessing from the words alone.&lt;/p&gt;
&lt;h2&gt;
  
  
  Codes on events and in JSON
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;progress&lt;/code&gt;, &lt;code&gt;failed&lt;/code&gt; and &lt;code&gt;cancelled&lt;/code&gt; events carry &lt;code&gt;code&lt;/code&gt; beside their&lt;br&gt;
words, and so does the CLI's JSON. The CLI, the output channel and the&lt;br&gt;
panels put the identifier before the words; the panels show it as a label&lt;br&gt;
that links to its entry.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tests that hold the line
&lt;/h2&gt;

&lt;p&gt;Tests check that identifiers are well formed and in a group, that every&lt;br&gt;
identifier ever given is kept, that the page matches what the register&lt;br&gt;
says, and a ratchet on the messages still said in place — 323 before, 290&lt;br&gt;
after, and it may only fall. This ratchet prevents new ad-hoc messages&lt;br&gt;
from being added outside the register.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec-ui-cli run change &lt;span class="nt"&gt;--cwd&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'OSW-'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>devtools</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The agent asks the operator, and the chain waits</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Sun, 25 Oct 2026 02:30:55 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/the-agent-asks-the-operator-and-the-chain-waits-5b48</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/the-agent-asks-the-operator-and-the-chain-waits-5b48</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/the-agent-asks-the-operator-and-the-chain-waits/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An agent ended its run with four decisions it needed from the operator,&lt;br&gt;
and the product offered no way to answer them. A chain would have gone on&lt;br&gt;
to the next stage with them open. The owner decided the same day: questions&lt;br&gt;
are caught, the run stops and waits, nothing goes on without an answer, the&lt;br&gt;
answers are kept in the change's &lt;code&gt;decisions.md&lt;/code&gt; and in the audit log, and&lt;br&gt;
the product's own agent asks in its turn from the start.&lt;/p&gt;
&lt;h2&gt;
  
  
  The marker, the event and the command
&lt;/h2&gt;

&lt;p&gt;Every agent stage's instruction asks for &lt;code&gt;Question for the operator: &amp;lt;…&amp;gt;&lt;/code&gt;&lt;br&gt;
lines for decisions the agent needs. The product reads them as markers.&lt;br&gt;
&lt;code&gt;local-llm-acp&lt;/code&gt; gets an &lt;code&gt;ask_operator&lt;/code&gt; tool that blocks its turn until&lt;br&gt;
answered.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;question&lt;/code&gt; event and an &lt;code&gt;answerQuestion&lt;/code&gt; command are added to the&lt;br&gt;
protocol. A &lt;code&gt;question&lt;/code&gt; waiting kind appears in the run's status. A run&lt;br&gt;
that asked waits instead of completing, in a chain or alone, under every&lt;br&gt;
autonomy level. Once answered, the stage runs again with the answers —&lt;br&gt;
&lt;code&gt;update&lt;/code&gt; for propose and review, the same command otherwise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where answers live
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;openspec/changes/&amp;lt;change&amp;gt;/decisions.md&lt;/code&gt; holds every question and answer.&lt;br&gt;
The audit log records both. An agent run on a change with an open question&lt;br&gt;
is refused.&lt;/p&gt;

&lt;p&gt;Questions are answered from the card, the AI panel, the Human-Only Inbox&lt;br&gt;
and &lt;code&gt;openspec-ui-cli answer&lt;/code&gt;. The card shows open questions with answer&lt;br&gt;
fields; the panel does the same. The Inbox surfaces questions alongside&lt;br&gt;
its usual checkpoint answers. From 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;openspec-ui-cli answer &amp;lt;changeId&amp;gt; &lt;span class="nt"&gt;--question&lt;/span&gt; &lt;span class="s2"&gt;"Which branch strategy?"&lt;/span&gt; &lt;span class="nt"&gt;--answer&lt;/span&gt; &lt;span class="s2"&gt;"gitflow"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What this changes about autonomy
&lt;/h2&gt;

&lt;p&gt;Before this, an autonomy level of &lt;code&gt;full&lt;/code&gt; meant the chain ran without&lt;br&gt;
interruption. Now, a question is a hard stop regardless of autonomy level.&lt;br&gt;
The chain waits for a human answer, even at full autonomy. This is&lt;br&gt;
intentional: a question from an agent is a request for judgment, not a&lt;br&gt;
checkpoint the chain can resolve on its own.&lt;/p&gt;

&lt;p&gt;The refusal of a run past an open question is explicit. If someone tries&lt;br&gt;
to start a new agent run on a change that still has unanswered questions,&lt;br&gt;
the product says so and names the open questions. This prevents a second&lt;br&gt;
agent from running while the first is still waiting for an answer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec-ui-cli answer &amp;lt;changeId&amp;gt; &lt;span class="nt"&gt;--question&lt;/span&gt; &amp;lt;questionId&amp;gt; &lt;span class="nt"&gt;--answer&lt;/span&gt; &amp;lt;answer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Local models can now search the web and fetch pages</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:31:55 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/local-models-can-now-search-the-web-and-fetch-pages-32bg</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/local-models-can-now-search-the-web-and-fetch-pages-32bg</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/local-models-can-now-search-the-web-and-fetch-pages/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec Workbench puts local-model behavior and its security controls in&lt;br&gt;
the shared core, but until now neither local adapter could search the web&lt;br&gt;
or fetch a source page. Two new shared core tools have been added:&lt;br&gt;
&lt;code&gt;search_web&lt;/code&gt; and &lt;code&gt;fetch_webpage&lt;/code&gt;. They let local-model agents consult a&lt;br&gt;
SearXNG endpoint and retrieve HTML pages as Markdown-backed JSON artifacts&lt;br&gt;
that can be analyzed or passed to another agent.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the tools do
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;search_web&lt;/code&gt; sends a query to a SearXNG JSON endpoint and returns the&lt;br&gt;
results as structured data. &lt;code&gt;fetch_webpage&lt;/code&gt; retrieves a public HTTP(S) URL,&lt;br&gt;
converts the article or main content to Markdown, writes every data table&lt;br&gt;
as a Markdown table, and returns provenance, page metadata and timestamps&lt;br&gt;
in JSON.&lt;/p&gt;

&lt;p&gt;Both tools are available to both local-model adapters: &lt;code&gt;local-llm&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;local-llm-acp&lt;/code&gt;. Remote agents are unchanged. The tools do not speak to&lt;br&gt;
any vendor API; they speak only to the SearXNG instance you configure.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuration and bounds
&lt;/h2&gt;

&lt;p&gt;The single SearXNG instance is configured with the &lt;code&gt;OPENSPEC_UI_SEARXNG_URL&lt;/code&gt;&lt;br&gt;
environment variable. When search has not been configured, the tools return&lt;br&gt;
an explicit error rather than failing silently or hanging.&lt;/p&gt;

&lt;p&gt;Page fetching is restricted to bounded public HTTP(S) HTML. Pages larger&lt;br&gt;
than the configured bound are read up to the bound and the response says&lt;br&gt;
so. Remote content is treated as untrusted data throughout the pipeline.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this matters for local models
&lt;/h2&gt;

&lt;p&gt;A local model is only as useful as the information it can bring to bear.&lt;br&gt;
Before these tools, a local agent could reason about what was already in&lt;br&gt;
its context window, but it could not look up a current fact or read a&lt;br&gt;
source page. Now it can search for context and fetch the page that&lt;br&gt;
contains it, all without sending data to a remote API.&lt;/p&gt;

&lt;p&gt;The tools return Markdown-backed JSON artifacts, not raw HTML. This means&lt;br&gt;
the local model receives clean, structured content that fits into its&lt;br&gt;
context window more efficiently, and that data tables are preserved as&lt;br&gt;
Markdown tables rather than lost in markup noise.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this deliberately does not do
&lt;/h2&gt;

&lt;p&gt;These tools are not a general-purpose web browser. They do not execute&lt;br&gt;
JavaScript, they do not render CSS, and they do not interact with forms&lt;br&gt;
or buttons. They search and fetch, convert to Markdown, and return. If a&lt;br&gt;
page requires JavaScript to render its content, the fetch will return&lt;br&gt;
whatever the server sends without execution.&lt;/p&gt;

&lt;p&gt;They also do not change the command or event protocol. No new transport&lt;br&gt;
channels are opened, no new agent types are introduced. The tools are&lt;br&gt;
exposed through the existing local-model adapters, and the environment&lt;br&gt;
configuration is read by the shared core runner.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&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;OPENSPEC_UI_SEARXNG_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:8080 openspec-ui-cli run &lt;span class="nt"&gt;--change&lt;/span&gt; my-change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>ai</category>
      <category>localllm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A compliance monitor that does not busywork</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Thu, 15 Oct 2026 04:25:40 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/a-compliance-monitor-that-does-not-busywork-5f6b</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/a-compliance-monitor-that-does-not-busywork-5f6b</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/a-compliance-monitor-that-does-not-busywork/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec Workbench initializes repositories and runs agents on changes. It&lt;br&gt;
did not, until now, check that its own runtime files are ignored, or that&lt;br&gt;
its repository rules are consistent. A compliance monitor has been added to&lt;br&gt;
the shared core, and it does three things that matter: it keeps a versioned&lt;br&gt;
catalogue of rules, it runs only when the host reports idle, and it reports&lt;br&gt;
findings without moving or deleting anything.&lt;/p&gt;
&lt;h2&gt;
  
  
  The catalogue, versioned and evidence-linked
&lt;/h2&gt;

&lt;p&gt;Every rule in the catalogue carries a stable identifier, a description of&lt;br&gt;
what it checks, and metadata about the check's cost and whether it supports&lt;br&gt;
automatic repair. Each finding is linked back to the rule that produced it&lt;br&gt;
and to the evidence that triggered it, so you can trace a finding to the&lt;br&gt;
exact check and the exact file or state that caused it.&lt;/p&gt;

&lt;p&gt;The catalogue is versioned. When a rule changes, the new version is applied&lt;br&gt;
to future checks, and old findings remain linked to the version that&lt;br&gt;
produced them. This matters when a rule's threshold is tightened or&lt;br&gt;
relaxed: old findings do not retroactively become violations or pass.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it checks, on initialization
&lt;/h2&gt;

&lt;p&gt;On explicit repository initialization, the monitor verifies that Workbench&lt;br&gt;
runtime state is ignored by Git. It adds only the missing managed ignore&lt;br&gt;
rule, preserving the rest of &lt;code&gt;.gitignore&lt;/code&gt; exactly as you wrote it. Nothing&lt;br&gt;
is overwritten.&lt;/p&gt;

&lt;p&gt;It also checks Workbench runtime files in the main checkout and in&lt;br&gt;
worktrees. It identifies untracked or visible state and explains the&lt;br&gt;
expected root for each state kind without moving or deleting it. A run&lt;br&gt;
journal in &lt;code&gt;.openspec-ui/&lt;/code&gt; is expected to be untracked; a lease file in the&lt;br&gt;
main checkout is expected to be ignored. The monitor says which is which,&lt;br&gt;
and why.&lt;/p&gt;
&lt;h2&gt;
  
  
  Grouping, deduplication and shadow detection
&lt;/h2&gt;

&lt;p&gt;When the same change id appears in multiple worktrees, the monitor groups&lt;br&gt;
them as one logical change and reports the count. It flags a change id&lt;br&gt;
present in both active and archive trees, an archived change left in a&lt;br&gt;
stale worktree, or a shadow copy in main beside its owning worktree. The&lt;br&gt;
Pipeline card count is reconciled with the Changes list, and active changes&lt;br&gt;
missing from the OpenSpec README index are flagged with a suggested link,&lt;br&gt;
not an automatic edit.&lt;/p&gt;
&lt;h2&gt;
  
  
  Per-rule reactions and a periodic schedule
&lt;/h2&gt;

&lt;p&gt;Each rule accepts one of three response settings: &lt;code&gt;autorepair&lt;/code&gt;, &lt;code&gt;advice&lt;/code&gt;&lt;br&gt;
or &lt;code&gt;none&lt;/code&gt;. &lt;code&gt;autorepair&lt;/code&gt; applies a fix without asking; &lt;code&gt;advice&lt;/code&gt; produces a&lt;br&gt;
finding with a suggested action; &lt;code&gt;none&lt;/code&gt; records the finding silently. A&lt;br&gt;
global periodic-monitor switch enables or disables the entire schedule.&lt;/p&gt;

&lt;p&gt;The monitor runs one bounded batch only after the host reports idle and no&lt;br&gt;
mutating Workbench run is active. It measures each check's elapsed time,&lt;br&gt;
schedules by due date and conservative cost, and spreads expensive checks&lt;br&gt;
across later idle windows. Nothing runs as a separate OS daemon.&lt;/p&gt;
&lt;h2&gt;
  
  
  Process ledger and orphan detection
&lt;/h2&gt;

&lt;p&gt;A companion capability, the process ledger, records a synchronous snapshot&lt;br&gt;
for every process spawned by Workbench: PID, OS start time, executable,&lt;br&gt;
hostname, cwd, change, and a secret-free command-line hash. Records are&lt;br&gt;
marked exited on normal close and kept durable across host restarts, keyed&lt;br&gt;
by canonical repository identity and hostname.&lt;/p&gt;

&lt;p&gt;Before any kill, the snapshot is re-verified against the operating system.&lt;br&gt;
Orphaned and overdue processes are surfaced as read-only Compliance&lt;br&gt;
findings. An explicit, confirmable kill lives in the existing recovery&lt;br&gt;
surface, never in Compliance itself. The two capabilities share the same&lt;br&gt;
catalogue but never cross their boundaries: Compliance explains, recovery&lt;br&gt;
acts.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this deliberately does not do
&lt;/h2&gt;

&lt;p&gt;The monitor does not start a background daemon, does not modify source code,&lt;br&gt;
does not run on every file change, and does not replace a linter or a&lt;br&gt;
formatter. It checks Workbench's own runtime hygiene and the consistency of&lt;br&gt;
its repository state.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec-ui-cli compliance check &lt;span class="nt"&gt;--cwd&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>devtools</category>
      <category>compliance</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Pay for the plan, not the typing</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:35:43 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/pay-for-the-plan-not-the-typing-20lc</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/pay-for-the-plan-not-the-typing-20lc</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/pay-for-the-plan-not-the-typing/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stepAgents&lt;/code&gt; has let you name a different agent for &lt;code&gt;propose&lt;/code&gt;, &lt;code&gt;review&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;&lt;br&gt;
and &lt;code&gt;verify&lt;/code&gt; since 0.42. What is new is a reason to actually do it: the four&lt;br&gt;
stages do not cost the same thing to run well, and they do not need to.&lt;/p&gt;

&lt;p&gt;Deciding what a change should do and reading whether the result is right both&lt;br&gt;
call for judgement. Writing the diff that gets there is comparatively&lt;br&gt;
mechanical - which is not an insult to the work, it is why a cheap, fast model&lt;br&gt;
does it about as well as an expensive one. So the obvious split is: a capable&lt;br&gt;
agent on &lt;code&gt;propose&lt;/code&gt; and &lt;code&gt;review&lt;/code&gt;, and a cheap one on &lt;code&gt;apply&lt;/code&gt;. This is that&lt;br&gt;
split, what it costs at today's prices, and - the part worth reading past the&lt;br&gt;
headline number - exactly what this project's own telemetry can and cannot&lt;br&gt;
prove about the savings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffh7c5kytrwteonkpo6ip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffh7c5kytrwteonkpo6ip.png" alt="One chain, four stages, three agents, three ways of reporting what they spent - two convert to money you can cap, one reports nothing at all" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The four stages take different things seriously
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;harness.json&lt;/code&gt; entry accepts &lt;code&gt;agent&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;effort&lt;/code&gt;, &lt;code&gt;budget&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;customAgent&lt;/code&gt; - and only &lt;code&gt;propose&lt;/code&gt;, &lt;code&gt;review&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; accept a&lt;br&gt;
&lt;code&gt;stepAgents&lt;/code&gt; entry at all; &lt;code&gt;archive&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; are mechanical, with nothing to&lt;br&gt;
configure. A &lt;code&gt;model&lt;/code&gt; is only accepted for the four adapters whose registry&lt;br&gt;
entry declares a &lt;code&gt;--model&lt;/code&gt; flag: &lt;code&gt;claude-cli&lt;/code&gt;, &lt;code&gt;copilot-cli&lt;/code&gt;, &lt;code&gt;claude-cli-acp&lt;/code&gt;&lt;br&gt;
and &lt;code&gt;copilot-cli-acp&lt;/code&gt;. &lt;code&gt;budget&lt;/code&gt; is &lt;code&gt;maxCostUsd&lt;/code&gt; or &lt;code&gt;maxAiCredits&lt;/code&gt;, whichever&lt;br&gt;
unit the chosen agent's own capabilities accept - the other is rejected.&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;"stepAgents"&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;"propose"&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;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-cli-acp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-5-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&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;"maxCostUsd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&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;"review"&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;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-cli-acp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-5-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&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;"maxCostUsd"&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="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;"apply"&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;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deepseek-cli-acp"&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;"verify"&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;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"copilot-cli-acp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&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;"maxAiCredits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&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;span class="nl"&gt;"timeout"&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;"maxStageSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2700&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;&lt;code&gt;propose&lt;/code&gt; and &lt;code&gt;review&lt;/code&gt; above pin &lt;code&gt;claude-opus-5-5&lt;/code&gt; at &lt;code&gt;high&lt;/code&gt; effort, with a&lt;br&gt;
dollar ceiling each. &lt;code&gt;claude-fable-5-1&lt;/code&gt; is the other reasonable choice for&lt;br&gt;
either - cheaper per token, still a model built for judgement rather than&lt;br&gt;
throughput; which of the two is worth its price on your own changes is&lt;br&gt;
something to measure, not assume. &lt;code&gt;apply&lt;/code&gt; carries no &lt;code&gt;model&lt;/code&gt;, no &lt;code&gt;effort&lt;/code&gt; and&lt;br&gt;
no &lt;code&gt;budget&lt;/code&gt; at all, and that is not an oversight: &lt;code&gt;deepseek-cli-acp&lt;/code&gt; accepts&lt;br&gt;
none of the three. Its model is a session option this project's driver never&lt;br&gt;
sends, so DeepSeek's own default runs; nothing here can raise or lower how&lt;br&gt;
hard it thinks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the harness can prove, and what it flatly cannot see
&lt;/h2&gt;

&lt;p&gt;This is the part a "save money" article usually skips, and it is the most&lt;br&gt;
useful thing this one has to say.&lt;/p&gt;

&lt;p&gt;Two of the three agents in this split convert cleanly to a ceiling you can&lt;br&gt;
actually set. &lt;code&gt;claude-cli-acp&lt;/code&gt; reports a cost figure directly - measured in&lt;br&gt;
this repository on 2026-09-05, one stage: 60 input, 8,262 output and&lt;br&gt;
1,693,507 cache tokens for &lt;strong&gt;$1.57&lt;/strong&gt;, and &lt;code&gt;budget.maxCostUsd&lt;/code&gt; compares&lt;br&gt;
against exactly that number. &lt;code&gt;copilot-cli-acp&lt;/code&gt; reports no dollar figure, but&lt;br&gt;
it reports &lt;strong&gt;AI Credits&lt;/strong&gt;, which are not a proxy this project invented - they&lt;br&gt;
are the unit Copilot itself bills in, 1 credit = $0.01 - so&lt;br&gt;
&lt;code&gt;budget.maxAiCredits&lt;/code&gt; is a real ceiling in real money, just denominated&lt;br&gt;
differently. Two agents, two currencies, both enforceable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deepseek-cli-acp&lt;/code&gt; is the one genuine blind spot. Measured 2026-09-23, its&lt;br&gt;
one usage figure is a context gauge - tokens used against the model's&lt;br&gt;
window - never a cost, a credit count or even a token split. &lt;code&gt;budget&lt;/code&gt; has no&lt;br&gt;
mechanism to set on it, in either unit, because there is nothing to compare&lt;br&gt;
a ceiling against.&lt;/p&gt;

&lt;p&gt;So the honest description of the &lt;code&gt;harness.json&lt;/code&gt; above is: three of the four&lt;br&gt;
stages carry a leash denominated in real money - two in dollars, one in AI&lt;br&gt;
Credits - and the cheap one does not. &lt;code&gt;timeout.maxStageSeconds&lt;/code&gt; still bounds&lt;br&gt;
how &lt;em&gt;long&lt;/em&gt; &lt;code&gt;apply&lt;/code&gt; can run, because elapsed time needs no report from the&lt;br&gt;
agent - but nothing in this product will tell you, or stop you, if a&lt;br&gt;
&lt;code&gt;deepseek-cli-acp&lt;/code&gt; stage is burning through a prepaid balance faster than&lt;br&gt;
expected. That balance lives entirely on DeepSeek's side. Watch it there.&lt;/p&gt;

&lt;p&gt;Two more limits worth knowing before you lean on the cheap stage. Neither&lt;br&gt;
&lt;code&gt;copilot --acp&lt;/code&gt; nor &lt;code&gt;dsh&lt;/code&gt; (DeepSeek's CLI) asks for permission before writing&lt;br&gt;
a file or running a command, despite the Agent Client Protocol supporting the&lt;br&gt;
mechanism - both were observed completing writes and shell commands here&lt;br&gt;
without ever raising &lt;code&gt;session/request_permission&lt;/code&gt;. Cheaper is not the same as&lt;br&gt;
better-supervised; the review gate between &lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; is what is&lt;br&gt;
actually watching this stage, not the permission channel. And &lt;code&gt;dsh&lt;/code&gt; needs&lt;br&gt;
Node 22.18+ on the 22 line, or 24.2+ - below that floor it exits with code 0&lt;br&gt;
before answering, silently. If your repository pins an older Node for its own&lt;br&gt;
tooling the way this one pins 22.11 with Volta, that is the first place to&lt;br&gt;
look when an &lt;code&gt;apply&lt;/code&gt; stage using DeepSeek produces nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this costs, at today's prices
&lt;/h2&gt;

&lt;p&gt;These are vendor list prices, read 2026-09-24, not a figure this project&lt;br&gt;
measured. Nothing here converts one into the other, and the harness has no&lt;br&gt;
opinion about which subscription you carry - that lives entirely outside it,&lt;br&gt;
in each CLI's own login.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Monthly&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Max 5x alone&lt;/td&gt;
&lt;td&gt;$100&lt;/td&gt;
&lt;td&gt;every stage on &lt;code&gt;claude-cli&lt;/code&gt;/&lt;code&gt;claude-cli-acp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Pro + Copilot Pro + DeepSeek API credit&lt;/td&gt;
&lt;td&gt;$20 + $10 + about $5 in tokens ≈ &lt;strong&gt;$35&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;propose&lt;/code&gt;/&lt;code&gt;review&lt;/code&gt; on Claude, &lt;code&gt;apply&lt;/code&gt; on DeepSeek, &lt;code&gt;verify&lt;/code&gt; on Copilot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude Pro is $20/month billed monthly, $17 annually; Claude Max starts at&lt;br&gt;
$100/month for the 5x tier. Copilot Pro is $10/month, including $10 of AI&lt;br&gt;
Credits at 1 credit = $0.01 - which is also where &lt;code&gt;budget.maxAiCredits&lt;/code&gt;'s&lt;br&gt;
minimum of 30 comes from: thirty cents is the smallest ceiling the product&lt;br&gt;
will accept, because Copilot itself will not meter anything finer. DeepSeek&lt;br&gt;
has no subscription tier at all: &lt;code&gt;deepseek-flash&lt;/code&gt; (what this project's own&lt;br&gt;
measurement on 2026-09-22 recorded as &lt;code&gt;DeepSeek-V4-Flash&lt;/code&gt; - names move&lt;br&gt;
faster than documentation; check what &lt;code&gt;dsh&lt;/code&gt; reports before you trust either)&lt;br&gt;
prices output at $0.60 per million tokens off-peak, $1.20 during DeepSeek's&lt;br&gt;
own weekday peak windows. Five dollars of prepaid credit is, at off-peak&lt;br&gt;
rates, upward of eight million output tokens - many multiples of what one&lt;br&gt;
&lt;code&gt;apply&lt;/code&gt; stage moves by the only measurement available here (1.7 million&lt;br&gt;
tokens, all kinds, on a &lt;code&gt;claude-cli-acp&lt;/code&gt; stage). Read that as "a lot", not as&lt;br&gt;
a number of stages: different models tokenize and work differently, and this&lt;br&gt;
project has no &lt;code&gt;apply&lt;/code&gt;-stage measurement on DeepSeek to compare it against&lt;br&gt;
directly.&lt;/p&gt;

&lt;p&gt;The arithmetic says $65 a month, and it survives the correction: GitHub's own&lt;br&gt;
current price for Copilot Pro is $10, not the $20 an older plan once charged,&lt;br&gt;
and the saving lands in the same place regardless, because the difference&lt;br&gt;
comes out of the Claude Max to Claude Pro step either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this article is not
&lt;/h2&gt;

&lt;p&gt;Not a benchmark. Nothing here measured whether &lt;code&gt;deepseek-cli-acp&lt;/code&gt; produces&lt;br&gt;
work as good as &lt;code&gt;claude-cli-acp&lt;/code&gt; on the same &lt;code&gt;apply&lt;/code&gt; stage - that is a&lt;br&gt;
real, answerable question, and it is not this one. Not a recommendation for&lt;br&gt;
your specific changes: an architecture decision or a security-sensitive&lt;br&gt;
&lt;code&gt;apply&lt;/code&gt; stage may be exactly the case where the expensive agent belongs on&lt;br&gt;
every stage, ceilings included. And not a claim that Codex or Gemini would&lt;br&gt;
be a worse or better third leg of this - both remain agents this project has&lt;br&gt;
never run against a real binary, on any stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The full field reference for &lt;code&gt;stepAgents&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;effort&lt;/code&gt; and &lt;code&gt;budget&lt;/code&gt; is&lt;br&gt;
in &lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/HARNESS.md" rel="noopener noreferrer"&gt;HARNESS.md&lt;/a&gt;.&lt;br&gt;
Which agents report what, in which unit, is in&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/LIMITS.md" rel="noopener noreferrer"&gt;LIMITS.md&lt;/a&gt;.&lt;br&gt;
The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each claim comes from
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;stepAgents&lt;/code&gt;, its four fields, which stages accept it, and which agents
accept a &lt;code&gt;model&lt;/code&gt;: &lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/HARNESS.md" rel="noopener noreferrer"&gt;HARNESS.md&lt;/a&gt;,
"&lt;code&gt;stepAgents&lt;/code&gt;".&lt;/li&gt;
&lt;li&gt;The agent capability table (&lt;code&gt;model&lt;/code&gt;, &lt;code&gt;effort&lt;/code&gt;, spending-cap unit, run
against a real binary): HARNESS.md, "Agents, models, effort, and spending
caps".&lt;/li&gt;
&lt;li&gt;Which agents report usage, in which unit, and the $1.57 and
1,701,829-token measurements: &lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/LIMITS.md" rel="noopener noreferrer"&gt;LIMITS.md&lt;/a&gt;,
"Which agents report usage".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deepseek-cli-acp&lt;/code&gt;'s context gauge, its measurement date, and that it
reports no cost, no credits and no token split: LIMITS.md, the same
section, and the archived change
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/openspec/changes/archive/2026-09-23-a-run-can-outgrow-its-context/proposal.md" rel="noopener noreferrer"&gt;&lt;code&gt;a-run-can-outgrow-its-context&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Neither &lt;code&gt;copilot --acp&lt;/code&gt; nor &lt;code&gt;dsh&lt;/code&gt; asking for permission, and the Node
22.18+/24.2+ floor: HARNESS.md, "ACP effort and budget capabilities", and
the article
&lt;a href="https://openspec-ui.dev/articles/a-new-agent-and-nothing-else-moved/" rel="noopener noreferrer"&gt;A new agent, and nothing else moved&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Claude Pro and Max prices: &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;claude.com/pricing&lt;/a&gt;,
read 2026-09-24.&lt;/li&gt;
&lt;li&gt;Copilot Pro's price and its AI Credits: &lt;a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/" rel="noopener noreferrer"&gt;GitHub's own announcement&lt;/a&gt;,
read 2026-09-24.&lt;/li&gt;
&lt;li&gt;DeepSeek's per-token prices and peak windows:
&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek's own pricing page&lt;/a&gt;,
read 2026-09-24.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openspec</category>
      <category>ai</category>
      <category>deepseek</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Two things nobody was watching</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Tue, 06 Oct 2026 02:34:25 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/two-things-nobody-was-watching-5b2e</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/two-things-nobody-was-watching-5b2e</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/two-things-nobody-was-watching/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec Workbench's whole pitch is watching an agent while it works. Two&lt;br&gt;
small fixes this week are about the product not watching itself closely&lt;br&gt;
enough - one where cleanup quietly stopped, one where a number was arriving&lt;br&gt;
the whole time and nothing was reading it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The leftover that survived being asked twice
&lt;/h2&gt;

&lt;p&gt;A working directory is removed in two steps: git forgets it, then whatever&lt;br&gt;
git left is deleted. Where the second step meets a file a live process&lt;br&gt;
still has open, the deletion fails, something is left behind, and nobody&lt;br&gt;
ever looks again - every later pass walks the directories git lists, and&lt;br&gt;
git has already forgotten this one.&lt;/p&gt;

&lt;p&gt;That is not a hypothetical. A shell from an already-archived change,&lt;br&gt;
&lt;code&gt;a-change-knows-its-stage&lt;/code&gt;, sat under the worktree root from one day to the&lt;br&gt;
next: 54 megabytes, holding nothing but a downloaded copy of VS Code under&lt;br&gt;
&lt;code&gt;packages/extension/.vscode-test&lt;/code&gt;, which the extension's own test run had&lt;br&gt;
been holding open at the exact moment something tried to remove it. Asked&lt;br&gt;
again a day later, it went without complaint - one retry was all it ever&lt;br&gt;
needed.&lt;/p&gt;

&lt;p&gt;Two things had kept that retry from happening. The rule for "is this ours&lt;br&gt;
to delete" was emptiness: a shell holding no file at any depth was cleared,&lt;br&gt;
and anything else was left alone, on the reasoning that a directory with a&lt;br&gt;
file in it belongs to someone. But a half-finished removal leaves exactly&lt;br&gt;
the opposite - the files that could not be deleted are the ones still&lt;br&gt;
there, so the rule excused the one case it existed for. And almost nothing&lt;br&gt;
called the cleanup in the first place: it ran from a button in the&lt;br&gt;
standalone app, and the periodic sweep that both hosts already run for&lt;br&gt;
everything else never called it at all.&lt;/p&gt;

&lt;p&gt;The fix changes what "ours" means: a shell named after a change this&lt;br&gt;
repository knows, with no &lt;code&gt;.git&lt;/code&gt; of its own, is cleared whatever is inside&lt;br&gt;
it - what is in a directory says nothing about whose it is; its name and&lt;br&gt;
the absence of a checkout do. The periodic sweep now clears these itself,&lt;br&gt;
on every pass, and says what it removed and what it is still waiting on. A&lt;br&gt;
shell it cannot clear yet is asked again next time, not forgotten.&lt;/p&gt;

&lt;p&gt;Checked live against the real worktree root this repository uses: 320&lt;br&gt;
known change names, two live working directories, one shell from an&lt;br&gt;
archived change planted holding a file under &lt;code&gt;.vscode-test&lt;/code&gt;. Read correctly&lt;br&gt;
as not empty and ours, removed, with neither live working directory&lt;br&gt;
touched.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that was arriving the whole time
&lt;/h2&gt;

&lt;p&gt;DeepSeek's CLI reports no cost, no credits, no token split - the answer to&lt;br&gt;
"how do we put a spending ceiling on it" turned out to be "you can't; only&lt;br&gt;
a time limit binds it at all." But it does send one figure over the Agent&lt;br&gt;
Client Protocol: &lt;code&gt;usage_update&lt;/code&gt;, carrying how many of the model's window&lt;br&gt;
tokens the session is using right now. That figure was already arriving.&lt;br&gt;
Nothing was reading it.&lt;/p&gt;

&lt;p&gt;It is a strange number to build a ceiling on, and the reasoning for&lt;br&gt;
leaving it alone still holds: it falls after a compaction, so counting it&lt;br&gt;
as spend would under-count exactly the long runs that compact. But it&lt;br&gt;
says something a spending figure never could, and says it while a stage is&lt;br&gt;
still running rather than after: a session that has filled most of its&lt;br&gt;
window makes every further turn carry the whole conversation again -&lt;br&gt;
slower, more expensive, and worse at the task than the same work started&lt;br&gt;
fresh.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;budget.maxContextShare&lt;/code&gt; is a share of the window - &lt;code&gt;0.8&lt;/code&gt; for eighty&lt;br&gt;
percent - and reaching it now ends the run as cancelled, not failed, the&lt;br&gt;
same way a time limit already does: a ceiling doing its job is not a&lt;br&gt;
defect. A value outside &lt;code&gt;(0, 1]&lt;/code&gt; is refused up front, so someone who means&lt;br&gt;
eighty percent and types &lt;code&gt;80&lt;/code&gt; is told rather than handed a ceiling that&lt;br&gt;
could never fire.&lt;/p&gt;

&lt;p&gt;Checked live: a chain running &lt;code&gt;deepseek-cli-acp&lt;/code&gt; with the ceiling set&lt;br&gt;
absurdly low. On this project's own pinned Node version, &lt;code&gt;dsh&lt;/code&gt; refused to&lt;br&gt;
start at all - the Node-version problem &lt;a href="https://openspec-ui.dev/articles/a-new-agent-and-nothing-else-moved/" rel="noopener noreferrer"&gt;an earlier article&lt;/a&gt;&lt;br&gt;
already found, showing up again in a different feature. On Node 24.18, the&lt;br&gt;
run's first reading arrived, and the stage stopped mid-run with a reason&lt;br&gt;
naming exactly what happened: "stopped at the context ceiling:&lt;br&gt;
&lt;code&gt;budget.maxContextShare&lt;/code&gt; is 0.0%, and the agent reported 8,277 of&lt;br&gt;
1,000,000 tokens in its context (0.8%)."&lt;/p&gt;

&lt;p&gt;The product also now knows, and says, which agents this can even work on.&lt;br&gt;
&lt;code&gt;contextGauge&lt;/code&gt; is a separate question from whether an agent reports&lt;br&gt;
spending: &lt;code&gt;"none"&lt;/code&gt; where that is certain, &lt;code&gt;"sends"&lt;/code&gt; for &lt;code&gt;deepseek-cli-acp&lt;/code&gt;&lt;br&gt;
from this measurement, &lt;code&gt;"unknown"&lt;/code&gt; for every other ACP agent nobody here&lt;br&gt;
has watched yet. A stage this ceiling can bind is no longer listed as&lt;br&gt;
unbounded - and one it cannot bind still says so honestly, rather than&lt;br&gt;
implying a safety net that was never there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern, twice
&lt;/h2&gt;

&lt;p&gt;Neither of these was a code path that ran and did the wrong thing. Both&lt;br&gt;
were something already true - a file still open, a number already&lt;br&gt;
arriving - that nothing had been asked to look at. The fix, in both cases,&lt;br&gt;
was smaller than finding the gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each claim comes from
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The leftover shell, the rule that excused it, and the fix: the archived
change
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/openspec/changes/archive/2026-09-23-the-sweep-comes-back-for-what-it-left/proposal.md" rel="noopener noreferrer"&gt;&lt;code&gt;the-sweep-comes-back-for-what-it-left&lt;/code&gt;&lt;/a&gt;,
and its own &lt;code&gt;tasks.md&lt;/code&gt; for the live numbers (320 known change names, the
54 MB find).&lt;/li&gt;
&lt;li&gt;The context ceiling, the live cancellation message, and the Node-version
refusal: the archived change
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/openspec/changes/archive/2026-09-23-a-run-can-outgrow-its-context/proposal.md" rel="noopener noreferrer"&gt;&lt;code&gt;a-run-can-outgrow-its-context&lt;/code&gt;&lt;/a&gt;
and its &lt;code&gt;tasks.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxContextShare&lt;/code&gt;, why it is not counted as spend, and &lt;code&gt;contextGauge&lt;/code&gt;:
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/LIMITS.md" rel="noopener noreferrer"&gt;LIMITS.md&lt;/a&gt;,
"caps how full the context gets" and the &lt;code&gt;contextGauge&lt;/code&gt; section.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openspec</category>
      <category>devtools</category>
      <category>deepseek</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A new agent, and nothing else moved</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Thu, 01 Oct 2026 02:40:01 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/a-new-agent-and-nothing-else-moved-5hca</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/a-new-agent-and-nothing-else-moved-5hca</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/a-new-agent-and-nothing-else-moved/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The last article argued that a shared core is what keeps a VS Code extension&lt;br&gt;
and a web app from drifting apart. Two days after it went out, a new agent&lt;br&gt;
arrived, and it is as good a test of that argument as any: DeepSeek, over the&lt;br&gt;
Agent Client Protocol, requested by the owner and added the same day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdb2v8r4h7d1rfs00plsm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdb2v8r4h7d1rfs00plsm.png" alt="One new file joins the registry of ten agents; the core is the only place that changed; the server and the extension are not touched" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;One new file, &lt;code&gt;deepseek-acp.ts&lt;/code&gt;, implementing the same &lt;code&gt;AgentAdapter&lt;/code&gt;&lt;br&gt;
interface every other agent implements. It runs &lt;code&gt;dsh --profile acp&lt;/code&gt;, the&lt;br&gt;
DeepSeek CLI's own Agent Client Protocol mode, through the driver the other&lt;br&gt;
ACP-flavored adapters already share. It is added to the registry, allowed by&lt;br&gt;
the default allowlist for exactly those arguments, and detected on &lt;code&gt;PATH&lt;/code&gt; like&lt;br&gt;
every other agent. Neither host's code changed. Both the standalone&lt;br&gt;
application and the VS Code extension picked it up because both read the same&lt;br&gt;
registry.&lt;/p&gt;

&lt;p&gt;DeepSeek is the tenth agent, and the fifth that speaks ACP, beside Claude,&lt;br&gt;
Copilot, Codex and Gemini.&lt;/p&gt;

&lt;h2&gt;
  
  
  What checking it live actually found
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;initialize&lt;/code&gt; answers as &lt;code&gt;deepseek-harness-acp&lt;/code&gt;, with no auth method to run:&lt;br&gt;
the key lives in the person's own &lt;code&gt;dsh&lt;/code&gt; profile, never in this project.&lt;br&gt;
&lt;code&gt;session/new&lt;/code&gt; offers DeepSeek-V4-Flash as the default model and&lt;br&gt;
DeepSeek-V4-Pro as an option, though nothing here sends that option yet, so&lt;br&gt;
V4-Flash is what runs.&lt;/p&gt;

&lt;p&gt;Two things worth a person's attention, both found by running it rather than&lt;br&gt;
by reading its documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Node 22.11, &lt;code&gt;dsh&lt;/code&gt; exits with code 0 before answering, saying nothing at&lt;br&gt;
all.&lt;/strong&gt; This repository pins Node 22.11 with Volta for its own tooling, and&lt;br&gt;
Volta puts it first on the &lt;code&gt;PATH&lt;/code&gt; of anything it starts, including an agent&lt;br&gt;
CLI. The same run on Node 24.18 completed. "ACP connection closed" is not a&lt;br&gt;
sentence a person can act on, so a run that ends this way is now told which&lt;br&gt;
Node it met and that a newer one is needed on the &lt;code&gt;PATH&lt;/code&gt; a host starts agents&lt;br&gt;
with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It reports no usage at all.&lt;/strong&gt; The live run sent no &lt;code&gt;usage_update&lt;/code&gt; and no&lt;br&gt;
usage figure on its answer, so a spending ceiling over &lt;code&gt;deepseek-cli-acp&lt;/code&gt;&lt;br&gt;
counts nothing and cannot fire, the same as over five of the other nine&lt;br&gt;
agents. Only a time ceiling has any force over it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was asked, and what was left for later
&lt;/h2&gt;

&lt;p&gt;The owner has used DeepSeek enough to know it does best with instructions&lt;br&gt;
taken literally, one at a time, rather than left to infer how strictly to&lt;br&gt;
read what follows. So every prompt to it now opens with a short preamble:&lt;br&gt;
follow the steps in order, do exactly what each one asks, name a step as it&lt;br&gt;
finishes, and stop and say why rather than improvise if a step cannot be done&lt;br&gt;
as written.&lt;/p&gt;

&lt;p&gt;What it does not do yet: choose a model. DeepSeek's model is an Agent Client&lt;br&gt;
Protocol session option (&lt;code&gt;session/set_config_option&lt;/code&gt;), and nothing in this&lt;br&gt;
project's shared driver sends session options. Whichever model &lt;code&gt;dsh&lt;/code&gt; defaults&lt;br&gt;
to is the one that runs. Fixing the Node a host starts agents with is also out&lt;br&gt;
of reach from here: the product cannot choose what is on someone's &lt;code&gt;PATH&lt;/code&gt;; it&lt;br&gt;
can only say which one it met.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is the article, and not just a changelog line
&lt;/h2&gt;

&lt;p&gt;Adding a tenth agent is not, by itself, news. What is worth a reader's time is&lt;br&gt;
that adding it stayed inside the one package the architecture says it should:&lt;br&gt;
&lt;code&gt;core&lt;/code&gt;, and nowhere else. An implement run against this same change wrote its&lt;br&gt;
own file and ticked its own task in 22 seconds, over the very mechanism the&lt;br&gt;
article describes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI. The architecture&lt;br&gt;
this rests on is in&lt;br&gt;
&lt;a href="https://openspec-ui.dev/articles/one-core-two-hosts/" rel="noopener noreferrer"&gt;One core, two hosts&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each claim comes from
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The adapter, the live &lt;code&gt;initialize&lt;/code&gt;/&lt;code&gt;session/new&lt;/code&gt; answers, the Node 22.11
exit and its explanation, and the preamble:
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/packages/core/src/agents/deepseek-acp.ts" rel="noopener noreferrer"&gt;&lt;code&gt;packages/core/src/agents/deepseek-acp.ts&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The decision, the live findings and what was left out: the archived change,
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/openspec/changes/archive/2026-09-22-deepseek-joins-as-an-acp-agent/proposal.md" rel="noopener noreferrer"&gt;&lt;code&gt;deepseek-joins-as-an-acp-agent&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The agent table and the ten agents:
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/README.md" rel="noopener noreferrer"&gt;README.md, "Agent Selection"&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;That it reports no usage, and what that means for a spending ceiling:
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI/blob/main/HARNESS.md" rel="noopener noreferrer"&gt;HARNESS.md&lt;/a&gt;,
the agent capability table and "needs a Node newer than 22.11".&lt;/li&gt;
&lt;li&gt;The 22-second implement run: &lt;code&gt;HARNESS.md&lt;/code&gt;'s own row for &lt;code&gt;deepseek-cli-acp&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openspec</category>
      <category>ai</category>
      <category>deepseek</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A team works through git</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Sun, 27 Sep 2026 02:34:12 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/a-team-works-through-git-32cg</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/a-team-works-through-git-32cg</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/a-team-works-through-git/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every pitch for team features reaches for the same two things first: a&lt;br&gt;
server, and a database. OpenSpec Workbench now answers three questions a&lt;br&gt;
team actually asks - whose change is this, why did it go back a step, how&lt;br&gt;
long did it spend in each step - and it does it with neither.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F25q6v9o09ka1gasbyoyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F25q6v9o09ka1gasbyoyw.png" alt="The Pipeline arranged by stage: Proposed, Planned, In progress, In review, Landed, Archived, each a column, with a toggle between this arrangement and the order changes declare" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The three questions
&lt;/h2&gt;

&lt;p&gt;The repository already had a team, even if the product did not know it:&lt;br&gt;
the owner, the agents that run changes, and a second agent that writes&lt;br&gt;
these articles. Three questions kept coming back, and nothing answered&lt;br&gt;
them. Whose change is this, and who is doing it right now. Why did it go&lt;br&gt;
back a step, and who sent it back. How long did it spend in each step,&lt;br&gt;
counting every time it visited.&lt;/p&gt;

&lt;p&gt;The answer, decided on 2026-09-22 and written down as ADR 0037: team work,&lt;br&gt;
with no server and no database, git and the forge as the only shared&lt;br&gt;
truth.&lt;/p&gt;
&lt;h2&gt;
  
  
  People are files, and joining is a pull request
&lt;/h2&gt;

&lt;p&gt;Each person on a team gets one file, &lt;code&gt;openspec/people/&amp;lt;handle&amp;gt;.json&lt;/code&gt;: a&lt;br&gt;
handle, a display name, one public key per machine they sign on, and&lt;br&gt;
optionally the git e-mail addresses that are theirs. Joining writes this&lt;br&gt;
file. Nothing is committed on its own - joining is the pull request that&lt;br&gt;
carries the file, the same way any other change reaches the repository.&lt;/p&gt;

&lt;p&gt;A key is never removed, only retired with a date. What it signed before&lt;br&gt;
keeps verifying for as long as the history is kept, which is for good. The&lt;br&gt;
merge gate refuses a pull request that takes a person or a key out,&lt;br&gt;
replaces a key, or edits a retirement already recorded - the same kind of&lt;br&gt;
refusal that already guards a change's specs.&lt;/p&gt;
&lt;h2&gt;
  
  
  History is signed events, one file per event
&lt;/h2&gt;

&lt;p&gt;A change has an Owner, who answers for it, and an Implementer, who does&lt;br&gt;
the work, by hand or through agents. An agent is never either one: it acts&lt;br&gt;
for a person and signs with that person's key, and every record of what it&lt;br&gt;
did names it as an agent, not as the person.&lt;/p&gt;

&lt;p&gt;Who holds a change is played forward from its history, kept as one signed&lt;br&gt;
file per event in &lt;code&gt;openspec/changes/&amp;lt;id&amp;gt;/history/&lt;/code&gt;. The events are a&lt;br&gt;
closed list: &lt;code&gt;owner-set&lt;/code&gt;, &lt;code&gt;implementer-set&lt;/code&gt;, and &lt;code&gt;sent-back&lt;/code&gt;, which carries&lt;br&gt;
the stage it returns to, the reason, and every task item it reopens with&lt;br&gt;
why. One file per event, rather than one shared file for the whole team,&lt;br&gt;
is a small engineering decision with a real payoff: two branches never&lt;br&gt;
edit the same file, so a merge on this never conflicts.&lt;/p&gt;

&lt;p&gt;The reason in a &lt;code&gt;sent-back&lt;/code&gt; event is shown as plain text, never run - the&lt;br&gt;
same rule the rest of this product holds for everything a repository's own&lt;br&gt;
contents might say.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stages are derived, never declared
&lt;/h2&gt;

&lt;p&gt;This is the same argument the project has made about a change's state&lt;br&gt;
since its first months, applied one layer up. A change's stage - Proposed,&lt;br&gt;
Planned, In progress, In review, Landed, Archived - is never set by a&lt;br&gt;
person or a button. Each is proved by a dated fact: the commit that adds&lt;br&gt;
&lt;code&gt;proposal.md&lt;/code&gt;, the commit that adds &lt;code&gt;tasks.md&lt;/code&gt;, a closed task line or a&lt;br&gt;
run, a pull request opening, its merge, the archive commit. A &lt;code&gt;sent-back&lt;/code&gt;&lt;br&gt;
event moves a change back to a named stage, and after that only a fact&lt;br&gt;
newer than the event moves it on again - so a change can visit a stage more&lt;br&gt;
than once, and every visit is kept.&lt;/p&gt;

&lt;p&gt;Getting the times right meant asking three different forges the same&lt;br&gt;
question. The pull request's creation and merge times now come from&lt;br&gt;
whichever one &lt;code&gt;origin&lt;/code&gt; is on: GitHub, through &lt;code&gt;gh&lt;/code&gt; or its own API, GitLab,&lt;br&gt;
or Gitea - built on the same &lt;code&gt;Forge&lt;/code&gt; interface this project described&lt;br&gt;
&lt;a href="https://openspec-ui.dev/articles/one-interface-three-forges/" rel="noopener noreferrer"&gt;two articles ago&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The board
&lt;/h2&gt;

&lt;p&gt;The Pipeline already drew every active change as a card. It now offers a&lt;br&gt;
second arrangement of the same cards: &lt;strong&gt;By step&lt;/strong&gt;, the order changes&lt;br&gt;
declare, and &lt;strong&gt;By stage&lt;/strong&gt;, a column per stage from Proposed to Archived.&lt;br&gt;
There are no lines between cards on the board - a stage says where a&lt;br&gt;
change is, not what it waits for - and every card says who owns it and who&lt;br&gt;
implements it, in either arrangement. Both hosts show the same board,&lt;br&gt;
because both read the same core function, and the choice between them is&lt;br&gt;
kept for the next visit.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this deliberately does not do yet
&lt;/h2&gt;

&lt;p&gt;The ADR is explicit about what it leaves for later, and only if people ask&lt;br&gt;
for it: a server or a database (ruled out on purpose, not postponed), board&lt;br&gt;
columns of the team's own choosing, transition policies such as&lt;br&gt;
work-in-progress limits or required approvals, reports beyond time in&lt;br&gt;
stage, one-way sync to an outside board, a plugin API, and licensing. A&lt;br&gt;
paid plugin, if one ever comes, would build on the facts this records and&lt;br&gt;
change none of them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI.&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 start &lt;span class="nt"&gt;--workspace&lt;/span&gt; @openspec-ui/cli &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="nt"&gt;--handle&lt;/span&gt; ada &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Ada Lovelace"&lt;/span&gt; &lt;span class="nt"&gt;--cwd&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>openspec</category>
      <category>git</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>One interface, three forges</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Sat, 26 Sep 2026 02:29:20 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/one-interface-three-forges-2k8f</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/one-interface-three-forges-2k8f</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/one-interface-three-forges/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Until this week, OpenSpec Workbench knew one way to open a pull request,&lt;br&gt;
check it, and merge it: &lt;code&gt;gh&lt;/code&gt;, GitHub's own CLI. If your repository lived on&lt;br&gt;
GitLab or Gitea, or if &lt;code&gt;gh&lt;/code&gt; simply was not installed, the archive stage said&lt;br&gt;
so and stopped. That is fixed now, on all three, and the way it got fixed is&lt;br&gt;
the more interesting part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9zc7sb0wfp8wrkidvtd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9zc7sb0wfp8wrkidvtd.png" alt="Your repository's origin decides which forge answers: GitHub, GitLab or another host probed for Gitea, all behind one Forge interface" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The interface, not the tool
&lt;/h2&gt;

&lt;p&gt;The fix is not "support GitLab too" bolted onto the GitHub path. It is one&lt;br&gt;
&lt;code&gt;Forge&lt;/code&gt; interface - list a pull request by branch, open one, ask for a merge,&lt;br&gt;
read its checks - that the &lt;code&gt;git&lt;/code&gt; stage and the archive pass call without&lt;br&gt;
knowing which host answers. Three implementations sit behind it: GitHub's&lt;br&gt;
own REST and GraphQL, GitLab's REST API, and Gitea's. Which one runs is&lt;br&gt;
decided once, by reading the repository's own &lt;code&gt;origin&lt;/code&gt;: github.com is&lt;br&gt;
GitHub, gitlab.com is GitLab, and anything else is probed - Gitea's&lt;br&gt;
&lt;code&gt;/api/v1/version&lt;/code&gt; first, then GitLab's &lt;code&gt;/api/v4/version&lt;/code&gt; - so a self-hosted&lt;br&gt;
instance is found without being told what it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The token decides the way, even for GitHub
&lt;/h2&gt;

&lt;p&gt;GitHub is the one host that had two paths from the start: &lt;code&gt;gh&lt;/code&gt;, or now its&lt;br&gt;
own API when &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; or &lt;code&gt;GH_TOKEN&lt;/code&gt; (the same variable &lt;code&gt;gh&lt;/code&gt; itself&lt;br&gt;
reads) is set in the environment. Without one, the product goes through&lt;br&gt;
&lt;code&gt;gh&lt;/code&gt; exactly as before - this did not become a breaking change for anyone&lt;br&gt;
already running it. Where neither a token nor &lt;code&gt;gh&lt;/code&gt; is available, the&lt;br&gt;
reading says so plainly: "gh is not installed, and GITHUB_TOKEN is not&lt;br&gt;
set", rather than failing on a command that was never going to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What running it against real hosts found
&lt;/h2&gt;

&lt;p&gt;Reading a REST API's documentation and calling it in anger are two&lt;br&gt;
different exercises, and this is where the honest part of the story is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gitea&lt;/strong&gt; was tested live on 1.26.4, in a throwaway repository deleted&lt;br&gt;
after each run. Two behaviours needed handling that no amount of reading&lt;br&gt;
would have surfaced: a repository with a single branch answers a pull&lt;br&gt;
request listing with 404 rather than an empty list, and a merged pull&lt;br&gt;
request's head can report a label naming a branch that no longer exists.&lt;br&gt;
Handled once, both stay handled everywhere Gitea is the forge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab&lt;/strong&gt; was tested live on gitlab.com, in a throwaway private project.&lt;br&gt;
The first token given for it was fine-grained without quite enough scope,&lt;br&gt;
and GitLab said so plainly (&lt;code&gt;insufficient_granular_scope&lt;/code&gt;) rather than&lt;br&gt;
failing silently. Once a token with the right scope was in place, the&lt;br&gt;
first merge attempt was refused with a 422, "Branch cannot be merged" -&lt;br&gt;
not because it could not be merged, but because GitLab had not yet finished&lt;br&gt;
deciding whether it could. The forge now waits and asks again before&lt;br&gt;
believing a 422. On both hosts, once that was in place, the full path ran&lt;br&gt;
end to end with the real archive command: it found the finished change,&lt;br&gt;
opened a pull request, watched it merge by itself, and the repository held&lt;br&gt;
the archived change afterward, spec applied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub's own API&lt;/strong&gt; was tested live too, on 2026-09-22, with &lt;code&gt;gh&lt;/code&gt;'s own&lt;br&gt;
token handed to the process as &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; and &lt;code&gt;gh&lt;/code&gt; itself kept off the&lt;br&gt;
&lt;code&gt;PATH&lt;/code&gt; - so there was no fallback to quietly catch a mistake. Against this&lt;br&gt;
repository it read 147 pull requests and a real check run as a pass,&lt;br&gt;
read-only. Against a fresh throwaway repository, the first automatic merge&lt;br&gt;
was refused outright: "Auto merge is not allowed for this repository",&lt;br&gt;
GitHub's own setting on a brand-new repo. The archive pass left the pull&lt;br&gt;
request open and said why, exactly as the project's own rule requires&lt;br&gt;
rather than pretending the merge had happened. Turning the setting on let&lt;br&gt;
the same request merge by squash. A second pull request, opened to test&lt;br&gt;
what the &lt;code&gt;git&lt;/code&gt; stage's own gateway does with no checks configured at all,&lt;br&gt;
correctly read "no check result was available" and declined to merge -&lt;br&gt;
absence of information is not the same as a green light.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not cover
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub Enterprise Server&lt;/strong&gt; is not detected as its own case. A host that&lt;br&gt;
answers neither Gitea's nor GitLab's version probe is assumed to be GitHub&lt;br&gt;
and handled through &lt;code&gt;gh&lt;/code&gt;, which already knows how to reach a GitHub host of&lt;br&gt;
its own. &lt;strong&gt;The allowlist that gates what the &lt;code&gt;git&lt;/code&gt; stage may push, create or&lt;br&gt;
merge&lt;/strong&gt; still speaks in &lt;code&gt;gh&lt;/code&gt;'s own terms - the remote and the branches it&lt;br&gt;
names - regardless of which forge actually does the work underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI. The core this&lt;br&gt;
rests on is described in&lt;br&gt;
&lt;a href="https://openspec-ui.dev/articles/one-core-two-hosts/" rel="noopener noreferrer"&gt;One core, two hosts&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openspec</category>
      <category>git</category>
      <category>gitlab</category>
      <category>gitea</category>
    </item>
    <item>
      <title>One core, two hosts</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:33:56 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/one-core-two-hosts-3dai</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/one-core-two-hosts-3dai</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/one-core-two-hosts/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec Workbench ships in two forms: a VS Code extension and a standalone&lt;br&gt;
web application that runs on your machine. They show the same OpenSpec and Git&lt;br&gt;
data and run the same workflows. The first architectural decision was how to&lt;br&gt;
stop them from becoming two different products, because two deliveries of "the&lt;br&gt;
same" behaviour diverge, and they do it quietly.&lt;/p&gt;

&lt;p&gt;The answer was to write the behaviour once and let each host be a thin adapter&lt;br&gt;
around it. This is what that looks like, what was turned down, and where "thin"&lt;br&gt;
turned out to cost something.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5re2a01v96xq26a2dmld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5re2a01v96xq26a2dmld.png" alt="The shared core with the server, the extension and the command line as adapters around it, and the shared UI between the two hosts" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision
&lt;/h2&gt;

&lt;p&gt;Five packages, one of them holding all of the behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;core&lt;/code&gt; owns execution, OpenSpec and Git integration, security, persistence
and the derived state of a change.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;webui&lt;/code&gt; holds the React components, written so they do not know how they are
reached.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt; and &lt;code&gt;extension&lt;/code&gt; are the two hosts, and &lt;code&gt;cli&lt;/code&gt; is a third,
non-interactive one for CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The protocol began as five commands (&lt;code&gt;plan&lt;/code&gt;, &lt;code&gt;implement&lt;/code&gt;, &lt;code&gt;review&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;status&lt;/code&gt; and &lt;code&gt;cancel&lt;/code&gt;) and seven events (&lt;code&gt;started&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt;, &lt;code&gt;stderr&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;progress&lt;/code&gt;, &lt;code&gt;completed&lt;/code&gt;, &lt;code&gt;failed&lt;/code&gt; and &lt;code&gt;cancelled&lt;/code&gt;), and it has grown since,&lt;br&gt;
with events such as &lt;code&gt;checkpoint&lt;/code&gt; and &lt;code&gt;permissionRequest&lt;/code&gt;. It is defined in&lt;br&gt;
&lt;code&gt;core&lt;/code&gt; and nowhere else. A host serialises it and does not reimplement any of the&lt;br&gt;
execution behind it.&lt;/p&gt;

&lt;p&gt;Security is in the core too, from the start and not as a later addition. Every&lt;br&gt;
run is restricted to the workspace, commands and their arguments are&lt;br&gt;
allowlisted, executions are audited, and the contents of the repository are&lt;br&gt;
treated as data, never as instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was rejected
&lt;/h2&gt;

&lt;p&gt;Three alternatives were written down and refused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running the standalone server inside the extension.&lt;/strong&gt; It would have made both&lt;br&gt;
hosts share one transport, but the extension host already runs Node and needs no&lt;br&gt;
HTTP for ordinary work. Dynamic ports, window collisions, authentication and&lt;br&gt;
process clean-up would have burdened the most common workflow. So the extension&lt;br&gt;
imports the core directly and talks to its webview over a message bridge, and&lt;br&gt;
the local server stays an optional mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing execution separately in each host.&lt;/strong&gt; Streaming, cancellation,&lt;br&gt;
errors and security would have drifted apart. Both hosts adapt the same&lt;br&gt;
protocol instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storing a change's state in &lt;code&gt;.openspec.yaml&lt;/code&gt;.&lt;/strong&gt; That would have extended a&lt;br&gt;
format that belongs to OpenSpec. Draft, in progress, implemented and archived&lt;br&gt;
are inferred from where a change sits and from its &lt;code&gt;tasks.md&lt;/code&gt;, by a heuristic&lt;br&gt;
in the core, so the tool never forks the format it depends on.&lt;/p&gt;

&lt;p&gt;The delivery model was also reviewed independently after the first proposal,&lt;br&gt;
and that review changed the recommendation for the extension's transport while&lt;br&gt;
keeping the shared core.&lt;/p&gt;

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

&lt;p&gt;A defect is fixed once, in the core, which depends on neither HTTP nor the VS&lt;br&gt;
Code API. The run status record that the Pipeline draws on a card is the same&lt;br&gt;
record &lt;code&gt;openspec-ui-cli status&lt;/code&gt; prints, whichever host started the run. A new&lt;br&gt;
agent is one adapter in the core's registry, and both hosts see it.&lt;/p&gt;

&lt;p&gt;One choice in that registry is worth naming. The plain agent adapters treat a&lt;br&gt;
CLI's output as opaque text on purpose, so that a change in a CLI's output&lt;br&gt;
format between versions cannot break the event stream. The price is that they&lt;br&gt;
cannot see or gate a single action mid-run, which is what the adapters that&lt;br&gt;
speak the Agent Client Protocol were added for, beside the plain ones and not in&lt;br&gt;
place of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where "thin" was not free
&lt;/h2&gt;

&lt;p&gt;A thin adapter is still an adapter, and each host has to route each command.&lt;/p&gt;

&lt;p&gt;When an agent asked for permission in the middle of a chain, the chain had no&lt;br&gt;
way to receive the answer. The core routed &lt;code&gt;cancel&lt;/code&gt; to the stage in flight but&lt;br&gt;
not &lt;code&gt;resolvePermission&lt;/code&gt;. The fix was in the core, and both hosts gained the matching branch. The run&lt;br&gt;
did not fail. It waited.&lt;/p&gt;

&lt;p&gt;So the rule is narrower than "hosts contain no logic". Hosts contain no&lt;br&gt;
behaviour, but each of them still has to carry every command to the core, and a&lt;br&gt;
command that one host forgets to carry is invisible until somebody uses it.&lt;br&gt;
The project requires contract tests between the shared UI and the server before&lt;br&gt;
a change is archived, for that reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
where the repository and packages keep the name OpenSpec-UI. A short tour of&lt;br&gt;
what it does is in&lt;br&gt;
&lt;a href="https://openspec-ui.dev/articles/supervise-agents-on-openspec-changes/" rel="noopener noreferrer"&gt;Supervise agents on OpenSpec changes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openspec</category>
      <category>architecture</category>
      <category>vscode</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Supervise the agents that build your OpenSpec changes</title>
      <dc:creator>Alexander Ivanov</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:28:47 +0000</pubDate>
      <link>https://dev.to/someone_somewhere_05cad9e/supervise-the-agents-that-build-your-openspec-changes-5fb9</link>
      <guid>https://dev.to/someone_somewhere_05cad9e/supervise-the-agents-that-build-your-openspec-changes-5fb9</guid>
      <description>&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://openspec-ui.dev/articles/supervise-agents-on-openspec-changes/" rel="noopener noreferrer"&gt;openspec-ui.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec gives an agent something to build from: a proposal, a design, a list&lt;br&gt;
of tasks, and the spec deltas the change will leave behind. What it does not&lt;br&gt;
give you is a way to see the agent while it works.&lt;/p&gt;

&lt;p&gt;Several tools draw OpenSpec changes as boards and lists, and they do it well.&lt;br&gt;
Read on 2026-09-20 in their own words, none of the three I compared starts an&lt;br&gt;
agent. OpenSpec Workbench does: it runs an agent CLI on a change, says what&lt;br&gt;
the run is doing while it does it, and lets a person stop it. This is a short&lt;br&gt;
tour of how, and of what it will not promise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhb9mmcjtehhbujvpzo1z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhb9mmcjtehhbujvpzo1z.gif" alt="A run started from a change's card, stopping at a checkpoint, and a stop asked for with a reason" width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A change becomes a card
&lt;/h2&gt;

&lt;p&gt;The Pipeline draws every active change in the order the changes declare, with&lt;br&gt;
what a live run last said and what can start alongside what. A card is where&lt;br&gt;
you act: it starts a run, answers a checkpoint, and asks a run to stop, with a&lt;br&gt;
reason. The same Pipeline is in the standalone web application and in the VS&lt;br&gt;
Code extension, and both read the same shared core.&lt;/p&gt;

&lt;p&gt;A run is a chain of stages: propose, review, apply, verify, archive, git. You&lt;br&gt;
choose the agent for each stage, and where the chain pauses for you.&lt;/p&gt;
&lt;h2&gt;
  
  
  What a run says it is doing
&lt;/h2&gt;

&lt;p&gt;Every run writes a status record: whose it is, where it runs, what it last&lt;br&gt;
said it was doing and how long ago. The Pipeline draws it on the card, and&lt;br&gt;
&lt;code&gt;openspec-ui-cli status&lt;/code&gt; prints it for every run of the repository, whichever&lt;br&gt;
host started it.&lt;/p&gt;

&lt;p&gt;It never says a run is stuck. A silent agent and a hung one look identical,&lt;br&gt;
and telling them apart is a person's judgement, so the tool leaves that call&lt;br&gt;
to you and gives you the last thing the run said and its age.&lt;/p&gt;

&lt;p&gt;Changes can run side by side. Each one can have its own git worktree, guarded&lt;br&gt;
by a lease, and &lt;code&gt;openspec-ui-cli ready&lt;/code&gt; says which ready changes can start&lt;br&gt;
alongside which, and why the others cannot.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stop it where its work is sound
&lt;/h2&gt;

&lt;p&gt;A card's Stop asks for a reason. The request is signed with the machine's key.&lt;br&gt;
The run reads it at its next renewal and acts only if it is verified and&lt;br&gt;
fresh, and it stops where its work is sound. From 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;openspec-ui-cli stop &amp;lt;instanceId&amp;gt; &lt;span class="nt"&gt;--reason&lt;/span&gt; &lt;span class="s2"&gt;"wrong branch"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--after &amp;lt;task&amp;gt;&lt;/code&gt; lets the run finish a named task first and stops at the next&lt;br&gt;
sound point after it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Your agent, per stage
&lt;/h2&gt;

&lt;p&gt;The picker drives the agent CLIs you already have: Claude CLI, GitHub Copilot&lt;br&gt;
CLI, Codex CLI, Gemini CLI, DeepSeek CLI, and a local model behind an&lt;br&gt;
OpenAI-compatible endpoint. The CLI-based ones also come in a variant that&lt;br&gt;
speaks the Agent Client Protocol, which carries structured progress instead of&lt;br&gt;
scraped text. The tool never handles an API key: each CLI keeps its own login.&lt;/p&gt;

&lt;p&gt;Here is what has actually been run. Claude CLI and Copilot CLI have been run&lt;br&gt;
against the real binaries, and so has DeepSeek CLI over ACP, on Node 22.18+ or&lt;br&gt;
24.2+. Codex and Gemini never have, raw or over ACP: their adapters are&lt;br&gt;
written to the vendors' documented interfaces and tested against a mocked&lt;br&gt;
peer, and if either misbehaves for you, that is the likeliest reason and a&lt;br&gt;
report would be useful. Two ACP caveats are worth knowing before you choose&lt;br&gt;
one: the Claude adapter never asks for permission, and &lt;code&gt;copilot --acp&lt;/code&gt;&lt;br&gt;
completed file writes and shell commands here without asking either.&lt;/p&gt;
&lt;h2&gt;
  
  
  Checkpoints, ceilings and a record
&lt;/h2&gt;

&lt;p&gt;Where a person decides is configuration, and the runner enforces it: an&lt;br&gt;
autonomy level, checkpoints between stages, a review gate that only a change's&lt;br&gt;
own settings can relax, and tasks marked as human-only or delegated to a named&lt;br&gt;
agent. Each stage has its own spending cap in the unit its agent honours, each&lt;br&gt;
chain has a ceiling, and every run lands in an audit log. The project also&lt;br&gt;
states plainly which limits do not exist, in a document of their own.&lt;/p&gt;

&lt;p&gt;The Workbench ships 17 change templates, for things like a Vite migration, a&lt;br&gt;
production Dockerfile, an authentication middleware or a testing baseline, so&lt;br&gt;
a new change can start from a proposal that already has the right shape.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;In VS Code, install it from the Marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; openspec-ui.openspec-ui-vscode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run the standalone application from a clone of the repository:&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;npm run build &lt;span class="nt"&gt;--workspace&lt;/span&gt; @openspec-ui/server
npm run start &lt;span class="nt"&gt;--workspace&lt;/span&gt; @openspec-ui/server &lt;span class="nt"&gt;--&lt;/span&gt; &amp;lt;workspaceRoot&amp;gt; 4317
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints a URL with a one-time token; open that exact URL. Everything runs on&lt;br&gt;
your machine, against your own agent CLIs.&lt;/p&gt;

&lt;p&gt;The repository, the packages and the extension are still called OpenSpec-UI;&lt;br&gt;
the product's name is OpenSpec Workbench. The code and the issues are at&lt;br&gt;
&lt;a href="https://github.com/VeryComplexAndLongName/OpenSpec-UI" rel="noopener noreferrer"&gt;github.com/VeryComplexAndLongName/OpenSpec-UI&lt;/a&gt;,&lt;br&gt;
and I would like to hear from anyone who tries it, above all from people who&lt;br&gt;
run Codex or Gemini, which I have not been able to.&lt;/p&gt;

</description>
      <category>openspec</category>
      <category>ai</category>
      <category>vscode</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
