Skip to content

devia 0.9.0 — an undefined decision is not an implicit permission - #11

Merged
schneiderjoseph merged 3 commits into
mainfrom
canvas/decision-register
Sep 15, 2026
Merged

schneiderjoseph merged 3 commits into
mainfrom
canvas/decision-register

Conversation

@schneiderjoseph

@schneiderjoseph schneiderjoseph commented Sep 15, 2026

Copy link
Copy Markdown
Owner

0.8.0 could tell an agent what this project had already decided. It had nothing to say about what
it had not decided, and that is where agents do the most damage — not by breaking a rule, but
by answering a question nobody asked them.

information missing              information missing
       ↓                                ↓
reasonable assumption      →       PENDING — recorded, owned, in every context
       ↓                                ↓
implementation                human decision OR bounded delegation
       ↓                                ↓
a policy nobody chose              implementation

The purple-gradient landing page is the visible case. The expensive ones are quiet: a framework
major inherited from whatever scaffolded the repository, an indexing policy inherited from a
template, a rounding rule that was load-bearing by the time anyone noticed. In all of them the
same failure — a missing answer was read as a free choice.

The decision register

.devia/decisions.yaml, seeded by devia init from the project profile. The two older
registries are reactive: a gap is a question somebody tripped over, a debt line is a rule somebody
noticed the code breaking. Neither says anything before the work starts.

decisions.yaml   the questions this kind of project always has   — known unknowns
11_GAPS.md       a question somebody hit while working           — unknown unknowns
12_DEBT.md       decided, not built

Four statuses, not three, because absence of information and absence of need are different facts.
not_required is a ruling — "this product ships no photography" — and folding it into pending
would leave a permanent false alarm on a project that has already answered.

Means What an agent may do
decided A human ruled. Implement it.
pending Nobody has ruled. Build around it. Never answer it.
delegated The agent may choose, inside bounded_by. Choose — inside those bounds only.
not_required Deliberately not needed here. Do not add one.

A delegation with no bounds is not delegation; it is absence wearing the word "explicit", and
devia validate fails it. There is no verb that deletes a slot: drop records that a question is
deliberately not needed, with a reason and a diff somebody can read.

It travels with the work

A pending decision the task touches is admitted to the blocking tier of devia context,
before the budget is consulted, and is never evicted — in strict mode it shrinks to its own name
rather than disappearing.

$ devia context "style the marketing hero" --files app/marketing/page.tsx

## Blocking — these stop the change
- decision design.direction · pending
  The visual direction this product commits to — owed by design lead.
  Do not encode an answer: record the consequence or ask (DEC-001).
- decision brand.colors · pending
- decision content.imagery · pending

The agent is told not to invent a brand at the moment it would have.

Three checks against the repository, not against good intentions

brand.logo       decided → assets/brand/logo.svg   the file is not there       FAIL  DEC-005
stack.framework  decided → 16.x, package next      the manifest says ^15.2.0   FAIL  DEC-003
design.direction pending, blocks app/marketing     app/marketing exists   P0   FAIL  DEC-001

Only the third blocks, and only because the project itself declared what that decision blocks. A
gate that failed on every open question would be switched off inside a week, and devia would have
traded a real stop for a warning nobody reads.

DEC-STACK compares the decision with the manifest, never with a registry. devia has no
network for this, and "latest" rots in a file the day after it is written. Whether 16 is still the
newest is npm outdated's question; whether we decided 16 and shipped 15 is devia's, and it is
the one that is actually a defect. Staying on an old major on purpose is recorded, not punished.

Discovery is a surface, not a task at the end

Fourteen new rules: seven DEC-* on how decisions are made, seven DISC-* on how a project is
found. 08_DISCOVERY.md appears for the web-app and docs profiles only — a required file a
project has no use for is a file it fills with TODO(devia), and placeholders are how a memory
stops being read.

Search and machine access share one set of mechanisms and differ only in policy, so they are one
file with two sections. DISC-006 is the counterweight: a discovery mechanism is never adopted on
the strength of its name, an unratified convention is recorded as a convention with what actually
honours it, and is never described to anyone as protection. DISC-004 (P0) is the one that
blocks: a staging host is kept out of the index by authentication, not by a robots directive —
that is a request, and a public list of what exists.

devia says there is a newer devia; it never installs one

A tool whose whole argument is that an agent must not decide for the project cannot quietly
upgrade itself. devia update answers three questions in order — is there a newer version, what
does it bring, do you want it — and stops at the third.

devia update — 0.9.0
  INFO  devia 1.1.0 est disponible. Vous êtes en 0.9.0.

  Ce qu'apporte la version 1.1.0 :
  ...

  Mettez à jour quand vous le décidez :
    npm install -D @schneiderjoseph/devia@1.1.0
    npx devia update --yes

  devia n'installe rien de lui-même — cette commande vous appartient.

The summary is in the reader's language — en, fr, es, de, it, pt, falling back to
English key by key. Only the lines addressed to a person are translated; the standard, the
rules and every memory file stay in English, because they are read by agents, cited by identifier,
and a translated obligation is a second wording of the same rule.

devia can describe a version it does not have because every release publishes its own summary in
its devia.release manifest field, which npm view serves from the registry. The alternative was
summarising a release devia had never seen, which is inventing (AGT-004). That text is remote,
so it is treated as data: control characters stripped, strings and bullet counts capped
(AI-001).

  • Nothing installed without --yes, and --yes runs the command it just printed
  • The lookup is handed to the user's own npm — their registry, proxy and credentials — because
    devia ships no HTTP client and no runtime dependency, exactly as contribute hands publishing
    to gh
  • The package name is all that is sent. Nothing about the repository leaves it; that promise
    belongs to contribute and is untouched
  • One lookup a day, inside update and doctor only. init is excluded on purpose: a devia you
    just installed is the newest one by construction
  • Off with DEVIA_NO_UPDATE_CHECK=1, "update": { "check": false }, or CI
  • Never on --json, and never able to change a command's exit code

What the review caught

The first push was green on CI and wrong in seven places; the live run then found four more. CI
was green because nothing tested what the release promised. Every one of these is now a test.

Found Why it mattered
08_DISCOVERY.md missing failed an 0.8.0 memory This release promised literally the opposite
An impact map naming decisions.yaml produced seven identical failures Noise that hides a real problem
devia read omitted the register A page called "the memory" without what the project ruled on
A pasted multi-line reason could make the register unparseable Silently unreadable
An arbitrary slot key was written without complaint An invalid address in the router
decide pending checked consistency on the filtered slice Went quiet exactly when the view was narrowed
padEnd on an already-coloured string Right without colour, wrong in a terminal
npm view returns a bare scalar when only one field resolves The lookup was dead, and reported "registry not reached"
npm.cmd cannot be spawned without a shell (CVE-2024-27980) The Windows lookup had never once succeeded
The cache dropped checkedAt on its second write Every answer permanently stale; doctor re-looked-up every run
"newest published version" said while 0.8.0 was the newest published Ahead of the registry is not current, and a diagnosis does not get to say that

The last four hid behind one sentence — "could not reach the registry" — that looked like somebody
else's problem, and was believed for a whole round of manual testing. The 186 tests that passed
had all seeded a cache by hand, so the function that fills it was never executed. There is now a
test that drives the real lookup and tolerates it being unavailable, plus the three shapes npm
actually produces.

Two never/always lines earned: a failure message is not a diagnosis, and a code path that
talks to the outside world gets one test that talks to it
.

Compatibility

Nothing that passed under 0.8.0 starts failing.

Situation 0.9.0
No decisions.yaml devia validate warns; all five DEC-* gates SKIP with the reason
A register with every slot pending DEC-PENDING warns at P2. Nothing blocks
A pending slot with no blocks: Never blocks
A CLI, library or service profile No 08_DISCOVERY.md, and no discovery rules routed in

devia init adds the register and 08_DISCOVERY.md without touching a file that holds decisions.

Standard 0.2.0 → 0.3.0 (rule domains decision and discovery). Memory schema 1 → 2.

Verified

192 tests pass. Standard validates: 155 rules, 47 P0, 67 automatable, 25 domains, 626 links in
393 files, index current. devia check P0 clear (18 pass, 1 warn OPS-001 lint, 5 skip).
devia validate 20 pass. Benchmark 352 runs — critical-rule recall 100%, routing accuracy 100%,
strict budget compliance 176/176, advisory over target 75/176 and in all 75 selected equals the
mandatory floor. npm pack carries devia.release intact in all six languages.

Not verified: that the npm registry serves the devia.release field back is unprovable until
0.9.0 is published. npm demonstrably serves other non-standard manifest fields, and the field
survives packing, but the proof is post-publication. First check after publish:

npm view @schneiderjoseph/devia@latest devia.release --json

If it comes back empty, the notice degrades to version numbers plus the changelog link rather than
breaking. Cost per correct decision is not measured, and the benchmark output says so.

0.8.0 could say what a project had already decided. It had nothing to say about
what it had not, and that is where agents do the most damage — not by breaking a
rule, but by answering a question nobody asked them. A missing answer was read as
a free choice, and the project acquired a brand, a framework major and a robots
policy that nobody chose.

The decision register, .devia/decisions.yaml, enumerates the questions a project
of this kind always owes an answer to, seeded from its profile, every slot
pending until somebody rules. Four statuses, not three: not_required is a ruling
("this product ships no photography"), and folding it into pending would leave a
permanent false alarm on a project that has already answered.

It travels with the work. A pending decision the task touches is admitted to the
blocking tier of devia context before the budget is consulted, and is never
evicted — in strict mode it shrinks to its own name rather than disappearing. The
agent is told not to invent a brand at the moment it would have.

Three checks read the repository rather than good intentions: a decided asset
whose file is not there, a pinned package whose major the manifest contradicts,
and a pending decision whose subject already exists. Only the third blocks, and
only because the project itself declared what that decision blocks — a gate that
failed on every open question would be switched off inside a week.

DEC-STACK compares the decision with the manifest, never with a registry. devia
has no network, and "latest" rots the day after it is written. Whether 16 is
still the newest is npm outdated's question; whether we decided 16 and shipped 15
is devia's, and it is the one that is actually a defect.

Fourteen new rules: seven DEC-* for how decisions are made, seven DISC-* for how
a project is found. 08_DISCOVERY.md appears for web-app and docs only — a
required file a project has no use for is a file it fills with placeholders, and
placeholders are how a memory stops being read. Search and machine access share
one set of mechanisms and differ only in policy, so they are one file with two
sections. DISC-006 is the counterweight: a discovery mechanism is never adopted
on the strength of its name, and an unratified convention is never described to
anyone as protection.

Nothing that passed under 0.8.0 starts failing. A memory with no register is
warned about, not failed, and all five DEC-* gates report SKIP with the reason;
devia init adds the register without touching a file that holds decisions.

Standard 0.2.0 to 0.3.0. Memory schema 1 to 2.
A tool whose whole argument is that an agent must not decide for the project
cannot quietly upgrade itself. So `devia update` answers three questions in
order — is there a newer version, what does it bring, do you want it — and stops
at the third. `--yes` runs the command it has just printed, and nothing else ever
runs it.

The summary is in the reader's language: en, fr, es, de, it, pt, falling back to
English key by key. Only the lines addressed to a person are translated. The
standard, the rules and every memory file stay in English, because they are read
by agents and cited by identifier, and a translated obligation is a second
wording of the same rule.

devia can describe a version it does not have because every release publishes its
own summary in its `devia.release` manifest field, which `npm view` serves from
the registry. The alternative was to summarise a release devia had never seen,
which is inventing (AGT-004). That text is remote, so it is data: control
characters stripped, strings and bullet counts capped (AI-001).

The lookup is handed to the user's own npm — their registry, proxy and
credentials — because devia ships no HTTP client and no runtime dependency,
exactly as contribute hands publishing to gh. The package name is all that is
sent; nothing about the repository leaves it, and that promise stays
contribute's. One lookup a day, and only inside update, init and doctor: every
other command reads the cached answer, shows three lines, and never waits on a
registry. Off with DEVIA_NO_UPDATE_CHECK=1, `"update": { "check": false }`, or CI.
Never on --json, and never able to change a command's exit code.

Verification of the register, and what it found

- 08_DISCOVERY.md missing failed a memory created by 0.8.0, which is exactly the
  claim the release made and the never/always line warns about. It warns now, and
  names devia init as the remedy
- An impact map naming decisions.yaml produced seven identical failures. Aggregated
  to one warning per missing file; a target devia has never heard of still fails,
  because that is a typo
- devia read omitted the register. A page called the memory that omits what the
  project has ruled on omits the part an agent is least allowed to guess
- A pasted multi-line reason could make the register unparseable; a slot key that
  is not an address was written without complaint. Both refused now
- `decide pending` ran its consistency checks against the filtered slice, so
  narrowing the view silenced them
- npm is npm.cmd on Windows, and execFile will not run a .cmd without a shell

186 tests, the standard validates, P0 gates clear. No test reaches a registry.
The registry was fine. `npm view` had been answering all along, and the answer
was being thrown away.

`npm view pkg version devia --json` returns an object when both fields resolve,
and a bare scalar when only one does — which is every release published before
the release field existed, which is to say the normal case on the day this ships.
The parser knew one shape and read the other as failure.

Underneath that, `npm.cmd` cannot be spawned without a shell at all since the fix
for CVE-2024-27980: Node throws EINVAL. So the Windows lookup had never once
succeeded. A shell with an args array then warns on stderr (DEP0190), so the
command goes through as one string, with every argument a constant and the only
outside value — the version to install — gated by isVersionLike first.

Both failures printed the same sentence, and that sentence looked like somebody
else's problem. Reported as "registry not reached" and believed, for a whole
round of manual testing.

Two more the live run showed:

- The cache was written twice and the second write dropped checkedAt whenever it
  ran against a cache that did not exist yet, so every answer was permanently
  stale and doctor re-looked-up on every run. One object now, written once
- "devia 0.9.0 is the newest published version" was said while 0.8.0 was the
  newest published version. Ahead of the registry is not the same as current, and
  saying otherwise to somebody on an unreleased build is a small lie a diagnosis
  does not get to tell. `say.ahead` in all six languages

init no longer spends a lookup. A devia you have just installed is the newest one
by construction, and a registry round trip on the tool's first impression buys
nothing — it cost two seconds and made the test suite depend on a network.

Why the suite missed all of it: every update test seeded a cache by hand, so the
function that fills the cache was never executed. There is now one test that
drives the real lookup and tolerates it being unavailable, plus the three shapes
npm actually produces, the cache's timestamp, and the ahead message. Two
never/always lines earned: a failure message is not a diagnosis, and a code path
that talks to the outside world gets one test that talks to it.

The trailers below describe this tree rather than the commit that carries the
version bump, because that commit's numbers were superseded twice by this same
branch, and a trailer that was true and is now false is worse than one attached
to the tree it describes.

Version: package 0.9.0, standard 0.3.0 (rule domains decision and discovery,
DEC-001..007 and DISC-001..007; memory schema 2).

Verified: 192 tests pass; validate clean (155 rules, 47 P0, 67 automatable, 25
domains, 626 links in 393 files, index current); check P0 clear (18 pass, 1 warn
OPS-001 lint, 5 skip); memory validate 20 pass. Benchmark 352 runs -- critical-
rule recall 100%, routing accuracy 100%, strict budget compliance 176/176,
advisory over target 75/176 and in all 75 selected equals the mandatory floor.
npm pack carries devia.release intact in all six languages, and a live `npm view`
returns the published 0.8.0 through the same path the notice uses. That the
registry serves this particular field back is unprovable until 0.9.0 is published;
if it does not, the notice degrades to version numbers plus the changelog link.
Cost per correct decision is not measured and the output says so.
@schneiderjoseph schneiderjoseph changed the title An undefined decision is not an implicit permission devia 0.9.0 — an undefined decision is not an implicit permission Sep 15, 2026
@schneiderjoseph
schneiderjoseph merged commit e4c1e11 into main Sep 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant