Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: A compliance monitor that does not busywork
published: false
description: OpenSpec Workbench now ships a versioned catalogue of rules, checks that run only when the host is idle, and findings that explain what is wrong without moving a single file.
tags: openspec, devtools, compliance, opensource
cover_image: https://raw.githubusercontent.com/VeryComplexAndLongName/OpenSpec-UI/main/docs/articles/site/a-compliance-monitor-that-does-not-busywork/cover.jpg
canonical_url: https://openspec-ui.dev/articles/a-compliance-monitor-that-does-not-busywork/
---

*First published on [openspec-ui.dev](https://openspec-ui.dev/articles/a-compliance-monitor-that-does-not-busywork/).*

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

## The catalogue, versioned and evidence-linked

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

The catalogue is versioned. When a rule changes, the new version is applied
to future checks, and old findings remain linked to the version that
produced them. This matters when a rule's threshold is tightened or
relaxed: old findings do not retroactively become violations or pass.

## What it checks, on initialization

On explicit repository initialization, the monitor verifies that Workbench
runtime state is ignored by Git. It adds only the missing managed ignore
rule, preserving the rest of `.gitignore` exactly as you wrote it. Nothing
is overwritten.

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

## Grouping, deduplication and shadow detection

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

## Per-rule reactions and a periodic schedule

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

The monitor runs one bounded batch only after the host reports idle and no
mutating Workbench run is active. It measures each check's elapsed time,
schedules by due date and conservative cost, and spreads expensive checks
across later idle windows. Nothing runs as a separate OS daemon.

## Process ledger and orphan detection

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

Before any kill, the snapshot is re-verified against the operating system.
Orphaned and overdue processes are surfaced as read-only Compliance
findings. An explicit, confirmable kill lives in the existing recovery
surface, never in Compliance itself. The two capabilities share the same
catalogue but never cross their boundaries: Compliance explains, recovery
acts.

## What this deliberately does not do

The monitor does not start a background daemon, does not modify source code,
does not run on every file change, and does not replace a linter or a
formatter. It checks Workbench's own runtime hygiene and the consistency of
its repository state.

## Try it

The code is at
[github.com/VeryComplexAndLongName/OpenSpec-UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI),
where the repository and packages keep the name OpenSpec-UI.

```bash
openspec-ui-cli compliance check --cwd .
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: Local models can now search the web and fetch pages
published: false
description: Two new shared core tools, search_web and fetch_webpage, let local-model agents consult a SearXNG endpoint and retrieve HTML pages as Markdown. No remote agents are changed.
tags: openspec, ai, local-llm, opensource
cover_image: https://raw.githubusercontent.com/VeryComplexAndLongName/OpenSpec-UI/main/docs/articles/site/local-models-can-now-search-the-web-and-fetch-pages/cover.jpg
canonical_url: https://openspec-ui.dev/articles/local-models-can-now-search-the-web-and-fetch-pages/
---

*First published on [openspec-ui.dev](https://openspec-ui.dev/articles/local-models-can-now-search-the-web-and-fetch-pages/).*

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

## What the tools do

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

Both tools are available to both local-model adapters: `local-llm` and
`local-llm-acp`. Remote agents are unchanged. The tools do not speak to
any vendor API; they speak only to the SearXNG instance you configure.

## Configuration and bounds

The single SearXNG instance is configured with the `OPENSPEC_UI_SEARXNG_URL`
environment variable. When search has not been configured, the tools return
an explicit error rather than failing silently or hanging.

Page fetching is restricted to bounded public HTTP(S) HTML. Pages larger
than the configured bound are read up to the bound and the response says
so. Remote content is treated as untrusted data throughout the pipeline.

## Why this matters for local models

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

The tools return Markdown-backed JSON artifacts, not raw HTML. This means
the local model receives clean, structured content that fits into its
context window more efficiently, and that data tables are preserved as
Markdown tables rather than lost in markup noise.

## What this deliberately does not do

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

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

## Try it

The code is at
[github.com/VeryComplexAndLongName/OpenSpec-UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI),
where the repository and packages keep the name OpenSpec-UI.

```bash
OPENSPEC_UI_SEARXNG_URL=http://localhost:8080 openspec-ui-cli run --change my-change
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: The agent asks the operator, and the chain waits
published: false
description: An agent can now raise a question for the operator, the chain stops and waits, answers are kept in decisions.md, and the product's own agent asks in its turn from the start.
tags: openspec, ai, agents, opensource
cover_image: https://raw.githubusercontent.com/VeryComplexAndLongName/OpenSpec-UI/main/docs/articles/site/the-agent-asks-the-operator-and-the-chain-waits/cover.jpg
canonical_url: https://openspec-ui.dev/articles/the-agent-asks-the-operator-and-the-chain-waits/
---

*First published on [openspec-ui.dev](https://openspec-ui.dev/articles/the-agent-asks-the-operator-and-the-chain-waits/).*

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

## The marker, the event and the command

Every agent stage's instruction asks for `Question for the operator: <…>`
lines for decisions the agent needs. The product reads them as markers.
`local-llm-acp` gets an `ask_operator` tool that blocks its turn until
answered.

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

## Where answers live

`openspec/changes/<change>/decisions.md` holds every question and answer.
The audit log records both. An agent run on a change with an open question
is refused.

Questions are answered from the card, the AI panel, the Human-Only Inbox
and `openspec-ui-cli answer`. The card shows open questions with answer
fields; the panel does the same. The Inbox surfaces questions alongside
its usual checkpoint answers. From a terminal:

```bash
openspec-ui-cli answer <changeId> --question "Which branch strategy?" --answer "gitflow"
```

## What this changes about autonomy

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

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

## Try it

The code is at
[github.com/VeryComplexAndLongName/OpenSpec-UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI),
where the repository and packages keep the name OpenSpec-UI.

```bash
openspec-ui-cli answer <changeId> --question <questionId> --answer <answer>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: Every message has an identifier, from one register
published: false
description: What the product says to a person is now written in one register, every message carries an OSW-XXX code, and a generated page lets you look up the identifier you saw.
tags: openspec, devtools, typescript, opensource
cover_image: https://raw.githubusercontent.com/VeryComplexAndLongName/OpenSpec-UI/main/docs/articles/site/every-message-has-an-identifier-from-one-register/cover.jpg
canonical_url: https://openspec-ui.dev/articles/every-message-has-an-identifier-from-one-register/
---

*First published on [openspec-ui.dev](https://openspec-ui.dev/articles/every-message-has-an-identifier-from-one-register/).*

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

## The register, and what it holds

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

The `CLI`, `RUN`, `QST` and `PRM` messages are said from the register:
the CLI's argument errors, the chain's refusals, limits and endings, the
operator's questions, and the permission requests nobody can answer. Their
words do not change; the CLI's lines lose `openspec-ui-cli:` and gain the
form, `error OSW-CLI-003: --cwd requires a value`.

## A page you can search for

`docs/messages.md` is generated from the register: a page a person
searches for the identifier they saw. If you see `error OSW-RUN-042` in
your terminal or panel, you can look up what it means, why it was said,
and what to do about it, without guessing from the words alone.

## Codes on events and in JSON

`progress`, `failed` and `cancelled` events carry `code` beside their
words, and so does the CLI's JSON. The CLI, the output channel and the
panels put the identifier before the words; the panels show it as a label
that links to its entry.

## Tests that hold the line

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

## Try it

The code is at
[github.com/VeryComplexAndLongName/OpenSpec-UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI),
where the repository and packages keep the name OpenSpec-UI.

```bash
openspec-ui-cli run change --cwd . 2>&1 | grep 'OSW-'
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: A change is committed where it is made
published: false
description: Agent workflow rules now say when to commit and push. A new change is committed and pushed as it is made. A card of a change's own worktree offers Commit Change, and the git stage commits what the stages left.
tags: openspec, git, opensource, productivity
cover_image: https://raw.githubusercontent.com/VeryComplexAndLongName/OpenSpec-UI/main/docs/articles/site/a-change-is-committed-where-it-is-made/cover.jpg
canonical_url: https://openspec-ui.dev/articles/a-change-is-committed-where-it-is-made/
---

*First published on [openspec-ui.dev](https://openspec-ui.dev/articles/a-change-is-committed-where-it-is-made/).*

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

The owner decided the same day: when a change is made, everything needed
is done, so the change can be worked at once as one's own.

## The rules, now with when

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

Create Change, in both hosts, commits the new change on its branch and
pushes it at once. A refused push leaves the change made and says why
(`OSW-GIT-203`).

The `git` stage commits what the stages left before it pushes
(`OSW-GIT-103` where that commit fails).

## The survey and the card

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

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

A card of a change's own worktree offers its actions in the arrangement
by step as on the board, and **Run Change...**: the owner found on
2026-10-10 that such a change could be acted on but never run — a card's
Start was refused for a change this checkout does not have. It runs in
the change's worktree, in both hosts.

## Messages in the new GIT group

Messages in the register's new GIT group (`OSW-GIT-001`..`203`) name
every failure, refusal and success in the commit and push path. ADR 0043
is amended to reflect the new rules.

## Try it

The code is at
[github.com/VeryComplexAndLongName/OpenSpec-UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI),
where the repository and packages keep the name OpenSpec-UI.

```bash
openspec-ui-cli commit change --cwd .
```
Loading
Loading