@schneiderjoseph/devia 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/MIGRATION.md +3 -2
- package/README.md +1 -1
- package/package.json +1 -1
- package/skills/devia/SKILL.md +3 -2
- package/src/commands/check.mjs +66 -21
- package/src/commands/doctor.mjs +4 -5
- package/src/commands/init.mjs +33 -11
- package/templates/agents/AGENTS.md +2 -2
- package/templates/agents/CLAUDE.md +2 -1
- package/templates/project/14_INDEX.md +12 -8
- package/templates/project/AGENTS.md +3 -3
- package/templates/project/README.md +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 — 2026-09-09
|
|
4
|
+
|
|
5
|
+
The standard is unchanged: `VERSION` stays at 0.1.0.
|
|
6
|
+
|
|
7
|
+
### Pinning the standard is now opt-in
|
|
8
|
+
|
|
9
|
+
`devia init` used to copy the whole standard into `.devia/standard/`. Measured on a real
|
|
10
|
+
repository: **391 pinned files against 17 of memory** — a folder whose purpose is to be read by
|
|
11
|
+
a human and an agent, in which 96% of the files were a copy nobody wrote. On a 211-file project
|
|
12
|
+
it tripled the repository, and every `devia sync` produced a 391-file diff in which a real
|
|
13
|
+
memory change was invisible.
|
|
14
|
+
|
|
15
|
+
- `devia init` writes the memory and the adapters, and pins nothing: **17 files, 57 kB**
|
|
16
|
+
- `devia init --vendor` pins the copy for those who want it up front
|
|
17
|
+
- `devia sync` pins it on demand and refreshes it afterwards — that is now its first job, not
|
|
18
|
+
only its maintenance one
|
|
19
|
+
- `doctor` reports an unpinned standard as `INFO`, not a `WARN` to clear: the default is not a
|
|
20
|
+
defect
|
|
21
|
+
- The memory templates, the agent adapters and the skill read the rules with
|
|
22
|
+
`npx devia rules --id <ID>` / `--domain <name>` instead of linking into a copy that may not
|
|
23
|
+
exist. A pinned copy is mentioned as what it is: optional
|
|
24
|
+
|
|
25
|
+
Nothing to do when upgrading. An existing `.devia/standard/` is left alone, `devia sync` keeps
|
|
26
|
+
refreshing it, and only new `devia init` runs behave differently. Recorded as G8.
|
|
27
|
+
|
|
28
|
+
### A P0 blocker comes from the priority cell, never from prose
|
|
29
|
+
|
|
30
|
+
`MEM-DEBT-P0` matched `P0` anywhere in a debt row. A P1 line reading "becomes P0 once the
|
|
31
|
+
payment module ships" failed the gate, so a project with no P0 debt was told it was blocked by
|
|
32
|
+
one. The check now reads the priority cell. Found by writing a real project's debt registry.
|
|
33
|
+
|
|
34
|
+
## 0.4.0 — 2026-09-09
|
|
35
|
+
|
|
36
|
+
The standard is unchanged: `VERSION` stays at 0.1.0, no adopter needs `devia sync`.
|
|
37
|
+
|
|
38
|
+
### A manifest is not always at the root
|
|
39
|
+
|
|
40
|
+
Running `devia init` on a real project for the first time — a Next.js app whose manifest lives
|
|
41
|
+
in `apps/web/` — exposed five gates reporting `SKIP no package.json` to a repository that has
|
|
42
|
+
one, with a lockfile, a lint script and thirteen dependencies. The letter of the rule was kept,
|
|
43
|
+
since nothing was rounded up to `PASS`; the reason given was false, which is worse. A reader
|
|
44
|
+
believes the tool looked.
|
|
45
|
+
|
|
46
|
+
- `check` reads every `package.json` in the repository, nearest the root first, and takes the
|
|
47
|
+
union of their dependencies: "does this project use X" is not a question about one directory
|
|
48
|
+
- A lockfile is looked for next to each manifest, not only at the root
|
|
49
|
+
- A migrations directory is found at any depth
|
|
50
|
+
- `SKIP` now says `no package.json anywhere in the repository`, and findings name the file they
|
|
51
|
+
came from — `no npm test script in apps/web/package.json`
|
|
52
|
+
- `init` detects the profile from the nearest manifest instead of falling back to a default, and
|
|
53
|
+
records the directories holding manifests in `code.paths`, which is what `doctor` watches for
|
|
54
|
+
staleness
|
|
55
|
+
|
|
56
|
+
On that project: six SKIPs became two, four gates turned into real findings, and the dependency
|
|
57
|
+
lockfile went from invisible to `PASS apps/web/package-lock.json`.
|
|
58
|
+
|
|
59
|
+
- G1 closed and reframed: the blind spot was never the ecosystem, it was the root assumption.
|
|
60
|
+
D9 records what is still root-only: `pyproject.toml`, `go.mod`, `Cargo.toml`
|
|
61
|
+
- G7 opened: what devia should do when a repository already carries an ad-hoc memory of its own
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Nested directories were not excluded from the scan on Windows when git was unavailable: the
|
|
66
|
+
separator class only matched `/`, so `apps/web/node_modules` was walked. Both separators now.
|
|
67
|
+
|
|
3
68
|
## 0.3.0 — 2026-09-09
|
|
4
69
|
|
|
5
70
|
The standard is unchanged: `VERSION` stays at 0.1.0, no adopter needs `devia sync`.
|
package/MIGRATION.md
CHANGED
|
@@ -58,5 +58,6 @@ Then:
|
|
|
58
58
|
(undecided) and `.devia/12_DEBT.md` (decided, not built) — the split matters.
|
|
59
59
|
4. Replace `node scripts/production-check.mjs` in CI with `npx devia check`.
|
|
60
60
|
|
|
61
|
-
Nothing in the old repos is lost: the full text of both
|
|
62
|
-
|
|
61
|
+
Nothing in the old repos is lost: the full text of both ships with this package, readable with
|
|
62
|
+
`npx devia rules`, and `npx devia sync` pins a copy under `.devia/standard/` when a project
|
|
63
|
+
wants one on disk.
|
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ npx devia doctor # adoption + staleness diagnosis
|
|
|
56
56
|
├── 14_INDEX.md # where to find what
|
|
57
57
|
├── impact-map.yaml # change type → files that must be updated
|
|
58
58
|
├── devia.json # profile, modules, maturity target, pinned version
|
|
59
|
-
└── standard/ #
|
|
59
|
+
└── standard/ # optional: `devia sync` pins a copy of the standard here
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Plus adapters so every agent gets the same contract: `AGENTS.md` (universal), `CLAUDE.md`,
|
package/package.json
CHANGED
package/skills/devia/SKILL.md
CHANGED
|
@@ -40,8 +40,9 @@ the difference between a task and a guess (`AGT-002`).
|
|
|
40
40
|
|
|
41
41
|
## Step 2 — work under the rules
|
|
42
42
|
|
|
43
|
-
The rules
|
|
44
|
-
|
|
43
|
+
The rules have stable IDs and are read with `npx devia rules --id <ID>`, or `--domain <name>`
|
|
44
|
+
for a whole area. A project that ran `devia sync` also has them on disk under
|
|
45
|
+
`.devia/standard/rules/`. The ones that stop most bad changes:
|
|
45
46
|
|
|
46
47
|
| If you are about to… | Rule |
|
|
47
48
|
|---|---|
|
package/src/commands/check.mjs
CHANGED
|
@@ -59,7 +59,9 @@ const TEXT_EXT = new Set([
|
|
|
59
59
|
* secret scan that silently scanned nothing is worse than one that over-reports.
|
|
60
60
|
*/
|
|
61
61
|
function sourceFiles(root) {
|
|
62
|
-
|
|
62
|
+
// Both separators: git reports "/", walk() reports path.sep, and a nested node_modules must be
|
|
63
|
+
// skipped in either shape.
|
|
64
|
+
const skipped = (rel) => rel.split(/[\\/]/).some((seg) => SKIP_DIRS.has(seg));
|
|
63
65
|
const wanted = (rel) => TEXT_EXT.has(path.extname(rel).toLowerCase());
|
|
64
66
|
|
|
65
67
|
const tracked = trackedFiles(root);
|
|
@@ -81,13 +83,31 @@ function anyExists(root, candidates) {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
function makeChecks(root, ctx) {
|
|
84
|
-
const pkg = readJSON(path.join(root, "package.json"));
|
|
85
|
-
const scripts = pkg?.scripts || {};
|
|
86
|
-
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
87
86
|
const config = readJSON(path.join(root, ".devia", "devia.json"));
|
|
88
87
|
const files = sourceFiles(root);
|
|
89
88
|
const rel = (p) => p.split(path.sep).join("/");
|
|
90
89
|
|
|
90
|
+
/**
|
|
91
|
+
* A manifest is not always at the repository root: `apps/web/package.json` is as real as
|
|
92
|
+
* `./package.json`. Reading only the root turned "I did not look there" into "you have no
|
|
93
|
+
* package.json", which is a wrong answer dressed as a SKIP — worse than no answer, because
|
|
94
|
+
* the reader believes the tool looked.
|
|
95
|
+
*/
|
|
96
|
+
const manifests = files
|
|
97
|
+
.filter((f) => /(^|[\\/])package\.json$/.test(f))
|
|
98
|
+
.sort((a, b) => a.split(path.sep).length - b.split(path.sep).length || a.localeCompare(b));
|
|
99
|
+
const primary = manifests[0] || null;
|
|
100
|
+
const pkg = primary ? readJSON(path.join(root, primary)) : null;
|
|
101
|
+
const pkgWhere = primary ? rel(primary) : null;
|
|
102
|
+
const scripts = pkg?.scripts || {};
|
|
103
|
+
// Dependencies are asked as "does this project use X anywhere", so every manifest counts.
|
|
104
|
+
const deps = {};
|
|
105
|
+
for (const m of manifests) {
|
|
106
|
+
const json = readJSON(path.join(root, m)) || {};
|
|
107
|
+
Object.assign(deps, json.dependencies, json.devDependencies);
|
|
108
|
+
}
|
|
109
|
+
const noManifest = `no package.json anywhere in the repository`;
|
|
110
|
+
|
|
91
111
|
const hasDep = (...names) => names.some((n) => n in deps);
|
|
92
112
|
const grepFiles = (re, limit = 40) => {
|
|
93
113
|
const hits = [];
|
|
@@ -134,9 +154,16 @@ function makeChecks(root, ctx) {
|
|
|
134
154
|
run: () => {
|
|
135
155
|
const text = read(path.join(root, ".devia", "12_DEBT.md"));
|
|
136
156
|
if (text === null) return { kind: "SKIP", detail: "no debt registry" };
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
// The priority is a cell, not a word somewhere in the row. Matching the whole line made
|
|
158
|
+
// a P1 line reading "becomes P0 once the payment module ships" fail the gate — a P0
|
|
159
|
+
// blocker invented out of prose, on a project that had none.
|
|
160
|
+
const rows = (text.match(/^\|\s*D\d+\s*\|.*$/gm) || []).filter((row) => {
|
|
161
|
+
if (/TODO\(devia\)/.test(row)) return false;
|
|
162
|
+
return row
|
|
163
|
+
.split("|")
|
|
164
|
+
.slice(1, -1)
|
|
165
|
+
.some((cell) => cell.trim().toUpperCase() === "P0");
|
|
166
|
+
});
|
|
140
167
|
return rows.length
|
|
141
168
|
? { kind: "FAIL", detail: `${rows.length} P0 debt line(s) open` }
|
|
142
169
|
: { kind: "PASS" };
|
|
@@ -305,12 +332,13 @@ function makeChecks(root, ctx) {
|
|
|
305
332
|
rule: "TST-001",
|
|
306
333
|
title: "A test command exists",
|
|
307
334
|
run: () => {
|
|
308
|
-
if (!pkg) return { kind: "SKIP", detail:
|
|
335
|
+
if (!pkg) return { kind: "SKIP", detail: noManifest };
|
|
309
336
|
const t = scripts.test;
|
|
310
|
-
|
|
337
|
+
const where = pkgWhere === "package.json" ? "" : ` in ${pkgWhere}`;
|
|
338
|
+
if (!t) return { kind: "WARN", detail: `no npm test script${where}` };
|
|
311
339
|
return /no test specified/.test(t)
|
|
312
|
-
? { kind: "FAIL", detail:
|
|
313
|
-
: { kind: "PASS" };
|
|
340
|
+
? { kind: "FAIL", detail: `test script is the npm placeholder${where}` }
|
|
341
|
+
: { kind: "PASS", detail: where.trim() };
|
|
314
342
|
},
|
|
315
343
|
},
|
|
316
344
|
{
|
|
@@ -319,9 +347,7 @@ function makeChecks(root, ctx) {
|
|
|
319
347
|
rule: "OPS-004",
|
|
320
348
|
title: "Dependency lockfile committed",
|
|
321
349
|
run: () => {
|
|
322
|
-
const
|
|
323
|
-
if (!anyExists(root, manifests)) return { kind: "SKIP", detail: "no manifest" };
|
|
324
|
-
const lock = anyExists(root, [
|
|
350
|
+
const LOCKS = [
|
|
325
351
|
"package-lock.json",
|
|
326
352
|
"pnpm-lock.yaml",
|
|
327
353
|
"yarn.lock",
|
|
@@ -331,10 +357,23 @@ function makeChecks(root, ctx) {
|
|
|
331
357
|
"go.sum",
|
|
332
358
|
"Cargo.lock",
|
|
333
359
|
"Gemfile.lock",
|
|
360
|
+
];
|
|
361
|
+
// A lockfile sits next to the manifest it locks, which is not always the root.
|
|
362
|
+
const dirs = new Set(["."]);
|
|
363
|
+
for (const m of manifests) dirs.add(path.dirname(m));
|
|
364
|
+
const rootManifest = anyExists(root, [
|
|
365
|
+
"package.json",
|
|
366
|
+
"pyproject.toml",
|
|
367
|
+
"go.mod",
|
|
368
|
+
"Cargo.toml",
|
|
369
|
+
"Gemfile",
|
|
334
370
|
]);
|
|
335
|
-
return
|
|
336
|
-
|
|
337
|
-
|
|
371
|
+
if (!rootManifest && !manifests.length) return { kind: "SKIP", detail: "no manifest" };
|
|
372
|
+
for (const dir of dirs) {
|
|
373
|
+
const found = LOCKS.find((l) => exists(path.join(root, dir, l)));
|
|
374
|
+
if (found) return { kind: "PASS", detail: rel(path.join(dir, found)) };
|
|
375
|
+
}
|
|
376
|
+
return { kind: "FAIL", detail: "no lockfile" };
|
|
338
377
|
},
|
|
339
378
|
},
|
|
340
379
|
{
|
|
@@ -355,8 +394,14 @@ function makeChecks(root, ctx) {
|
|
|
355
394
|
"alembic",
|
|
356
395
|
path.join("src", "migrations"),
|
|
357
396
|
]);
|
|
358
|
-
return dir
|
|
359
|
-
|
|
397
|
+
if (dir) return { kind: "PASS", detail: dir };
|
|
398
|
+
// Not only at the root: a migrations directory can live under any package.
|
|
399
|
+
const SEGMENTS = new Set(["migrations", "migrate", "alembic"]);
|
|
400
|
+
const nested = files
|
|
401
|
+
.map(rel)
|
|
402
|
+
.find((f) => f.split("/").slice(0, -1).some((seg) => SEGMENTS.has(seg)));
|
|
403
|
+
return nested
|
|
404
|
+
? { kind: "PASS", detail: nested.split("/").slice(0, -1).join("/") }
|
|
360
405
|
: { kind: "FAIL", detail: "database in use, no migrations directory" };
|
|
361
406
|
},
|
|
362
407
|
},
|
|
@@ -388,7 +433,7 @@ function makeChecks(root, ctx) {
|
|
|
388
433
|
run: () => {
|
|
389
434
|
if (config && config.modules && config.modules.design === false)
|
|
390
435
|
return { kind: "SKIP", detail: "design module disabled" };
|
|
391
|
-
if (!pkg) return { kind: "SKIP", detail:
|
|
436
|
+
if (!pkg) return { kind: "SKIP", detail: noManifest };
|
|
392
437
|
const found = hasDep(
|
|
393
438
|
"axe-core", "@axe-core/react", "@axe-core/playwright", "jest-axe",
|
|
394
439
|
"eslint-plugin-jsx-a11y", "pa11y", "@storybook/addon-a11y", "lighthouse"
|
|
@@ -407,7 +452,7 @@ function makeChecks(root, ctx) {
|
|
|
407
452
|
const profile = config?.project?.profile;
|
|
408
453
|
if (["cli", "library", "docs"].includes(profile))
|
|
409
454
|
return { kind: "SKIP", detail: `${profile} does not run as a watched service` };
|
|
410
|
-
if (!pkg) return { kind: "SKIP", detail:
|
|
455
|
+
if (!pkg) return { kind: "SKIP", detail: noManifest };
|
|
411
456
|
const found = hasDep("@sentry/node", "@sentry/browser", "@sentry/nextjs", "bugsnag",
|
|
412
457
|
"rollbar", "datadog-lambda-js", "dd-trace", "@opentelemetry/api");
|
|
413
458
|
return found
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -64,12 +64,11 @@ ${color.bold("devia doctor")} — adoption, drift and staleness
|
|
|
64
64
|
const vendored = walk(path.join(deviaDir, "standard")).length;
|
|
65
65
|
if (path.resolve(root) === path.resolve(packageRoot)) {
|
|
66
66
|
status("SKIP", "vendored standard", "this repository is the standard");
|
|
67
|
+
} else if (vendored) {
|
|
68
|
+
status("PASS", `standard pinned: ${vendored} files`);
|
|
67
69
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`vendored standard: ${vendored} files`,
|
|
71
|
-
vendored ? "" : "run `devia sync`"
|
|
72
|
-
);
|
|
70
|
+
// Not pinning is the default, so absence is a fact to report, never a warning to clear.
|
|
71
|
+
status("INFO", "standard not pinned", "`devia rules` reads it; `devia sync` pins a copy");
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
// Adapters
|
package/src/commands/init.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { packageRoot, exists, read, writeFile, writeJSON, walk } from "../lib/fs.mjs";
|
|
4
|
+
import { trackedFiles } from "../lib/git.mjs";
|
|
4
5
|
import { cliVersion, standardVersion } from "../lib/version.mjs";
|
|
5
6
|
import { vendorStandard } from "../lib/vendor.mjs";
|
|
6
7
|
import { color, heading, status, line } from "../lib/ui.mjs";
|
|
@@ -15,10 +16,22 @@ const PROFILES = {
|
|
|
15
16
|
docs: "Documentation or content repository",
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Every package.json in the repository, nearest to the root first. A monorepo keeps its real
|
|
21
|
+
* manifest in `apps/web/` or `packages/*`, and a profile detected from the root alone falls back
|
|
22
|
+
* to a default while the evidence sits one directory down.
|
|
23
|
+
*/
|
|
24
|
+
function findManifests(root) {
|
|
25
|
+
const tracked = trackedFiles(root);
|
|
26
|
+
if (!tracked) return exists(path.join(root, "package.json")) ? ["package.json"] : [];
|
|
27
|
+
return tracked
|
|
28
|
+
.filter((f) => /(^|\/)package\.json$/.test(f) && !f.includes("node_modules/"))
|
|
29
|
+
.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
function detectProfile(root) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const json = JSON.parse(read(pkg) || "{}");
|
|
33
|
+
for (const manifest of findManifests(root)) {
|
|
34
|
+
const json = JSON.parse(read(path.join(root, manifest)) || "{}");
|
|
22
35
|
const deps = { ...json.dependencies, ...json.devDependencies };
|
|
23
36
|
if (json.bin) return "cli";
|
|
24
37
|
if (deps.next || deps.react || deps.vue || deps.svelte || deps["@angular/core"])
|
|
@@ -46,8 +59,14 @@ function detectName(root) {
|
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
function detectCodePaths(root) {
|
|
49
|
-
const candidates = ["src", "app", "lib", "packages", "server", "api", "web", "components"];
|
|
50
|
-
|
|
62
|
+
const candidates = ["src", "app", "apps", "lib", "packages", "server", "api", "web", "components"];
|
|
63
|
+
const found = candidates.filter((c) => exists(path.join(root, c)));
|
|
64
|
+
// The directory holding a manifest is code by definition, wherever it sits.
|
|
65
|
+
for (const manifest of findManifests(root)) {
|
|
66
|
+
const dir = path.posix.dirname(manifest);
|
|
67
|
+
if (dir !== "." && !found.some((f) => dir === f || dir.startsWith(`${f}/`))) found.push(dir);
|
|
68
|
+
}
|
|
69
|
+
return found;
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
function fill(text, vars) {
|
|
@@ -65,7 +84,7 @@ ${color.bold("devia init")} — create .devia/ in this repository
|
|
|
65
84
|
--profile <name> ${Object.keys(PROFILES).join(" | ")}
|
|
66
85
|
--force overwrite existing memory files (dangerous: they hold your decisions)
|
|
67
86
|
--no-agents do not write the agent adapters
|
|
68
|
-
--
|
|
87
|
+
--vendor pin a copy of the standard into .devia/standard/ (~390 files)
|
|
69
88
|
--yes accept a detected root that is not the current directory
|
|
70
89
|
`.trim());
|
|
71
90
|
return 0;
|
|
@@ -144,17 +163,20 @@ ${color.bold("devia init")} — create .devia/ in this repository
|
|
|
144
163
|
status("SKIP", "devia.json kept", "use --force to regenerate");
|
|
145
164
|
}
|
|
146
165
|
|
|
147
|
-
// 3.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
166
|
+
// 3. Pinned standard — opt-in. Vendoring writes ~390 files a project did not author, which
|
|
167
|
+
// buries the memory it is supposed to serve: on a real repository the ratio was 17 files of
|
|
168
|
+
// memory to 391 of copy, and every `sync` produced a 391-file diff. The rules stay reachable
|
|
169
|
+
// through `devia rules`, and `devia sync` pins the copy for whoever needs it offline.
|
|
170
|
+
if (flags.vendor && flags.vendor !== "false") {
|
|
151
171
|
const files = vendorStandard(path.join(deviaDir, "standard"), {
|
|
152
172
|
by: "devia init",
|
|
153
173
|
cli,
|
|
154
174
|
standard: version,
|
|
155
175
|
date: vars.DATE,
|
|
156
176
|
});
|
|
157
|
-
status("PASS", `standard
|
|
177
|
+
status("PASS", `standard pinned: ${files} files`, `v${version}`);
|
|
178
|
+
} else {
|
|
179
|
+
status("SKIP", "standard not pinned", "`devia sync` writes .devia/standard/ when you need it");
|
|
158
180
|
}
|
|
159
181
|
|
|
160
182
|
// 4. Agent adapters
|
|
@@ -31,5 +31,5 @@ npx devia check # readiness gates
|
|
|
31
31
|
Update `.devia/` in the same change (see `.devia/impact-map.yaml`), then report what you changed
|
|
32
32
|
**and what you did not verify**.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
Rules by ID: `npx devia rules --id SEC-001`, or by domain: `npx devia rules --domain database`.
|
|
35
|
+
A pinned copy lives under `.devia/standard/` only if this project ran `devia sync`.
|
|
@@ -31,4 +31,5 @@ npx devia check
|
|
|
31
31
|
|
|
32
32
|
Report the checks that ran, the rule IDs involved, and what you did **not** verify.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Rules by ID: `npx devia rules --id SEC-001`, or by domain: `npx devia rules --domain database`.
|
|
35
|
+
A pinned copy lives under `.devia/standard/` only if this project ran `devia sync`.
|
|
@@ -38,13 +38,17 @@
|
|
|
38
38
|
|---|---|---|
|
|
39
39
|
| TODO(devia) | | |
|
|
40
40
|
|
|
41
|
-
## Standard
|
|
41
|
+
## Standard
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
The standard is read through the CLI, not copied into this repository.
|
|
44
|
+
|
|
45
|
+
| Need | Command |
|
|
44
46
|
|---|---|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
| A rule by ID | `npx devia rules --id SEC-001` |
|
|
48
|
+
| Every rule in a domain | `npx devia rules --domain database --priority P0` |
|
|
49
|
+
| Work contract | `AGENTS.md` at this repository's root |
|
|
50
|
+
| Readiness gates | `npx devia check` |
|
|
51
|
+
|
|
52
|
+
`npx devia sync` pins a version-locked copy under `standard/` when an agent must read it
|
|
53
|
+
offline, or when an audit needs the exact wording you built against. Add the paths here if you
|
|
54
|
+
do.
|
|
@@ -8,8 +8,8 @@ This file is your contract. Violating it is a failed task.
|
|
|
8
8
|
1. Read [`10_NEVER_ALWAYS.md`](10_NEVER_ALWAYS.md) — what this project has already banned
|
|
9
9
|
2. Read [`00_OVERVIEW.md`](00_OVERVIEW.md) — what this project is
|
|
10
10
|
3. Read the memory file for the surface you are about to change ([`14_INDEX.md`](14_INDEX.md))
|
|
11
|
-
4. Read the
|
|
12
|
-
|
|
11
|
+
4. Read the rules for what you are touching: `npx devia rules --domain <name>`, or
|
|
12
|
+
`npx devia rules --id <ID>` for one
|
|
13
13
|
|
|
14
14
|
Then work. Then update this memory in the same change.
|
|
15
15
|
|
|
@@ -48,4 +48,4 @@ TODO(devia): add this project's own commands — install, dev, test, lint, migra
|
|
|
48
48
|
- Not verified: ...
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
Full contract, routing table and hard stops:
|
|
51
|
+
Full contract, routing table and hard stops: the `AGENTS.md` at this repository's root.
|
|
@@ -27,8 +27,11 @@ Created by `devia init` (devia {{DEVIA_VERSION}}, {{DATE}}).
|
|
|
27
27
|
Machine files: [`devia.json`](devia.json) (profile, maturity, pinned version) and
|
|
28
28
|
[`impact-map.yaml`](impact-map.yaml) (change type → files to update).
|
|
29
29
|
|
|
30
|
-
The standard itself is
|
|
31
|
-
|
|
30
|
+
The standard itself is not copied in here. Read it with `npx devia rules --id SEC-001` or
|
|
31
|
+
`npx devia rules --domain database`, which is the same text for every agent. If you need it on
|
|
32
|
+
disk — an agent with no network, or an audit that must show the exact wording you built against
|
|
33
|
+
— `npx devia sync` pins a version-locked copy under `standard/`, and `14_INDEX.md` then points
|
|
34
|
+
at it.
|
|
32
35
|
|
|
33
36
|
## The two registries
|
|
34
37
|
|
|
@@ -43,7 +46,7 @@ for every agent, offline, no network and no package manager required.
|
|
|
43
46
|
npx devia validate # structure, registries, placeholders
|
|
44
47
|
npx devia doctor # is the memory older than the code?
|
|
45
48
|
npx devia check # readiness gates
|
|
46
|
-
npx devia sync #
|
|
49
|
+
npx devia sync # pin the standard under standard/, or refresh a pinned copy
|
|
47
50
|
```
|
|
48
51
|
|
|
49
52
|
`.devia/` is committed. It is part of the repository, not a local scratch pad.
|