etymd 0.14.0 → 0.16.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 +52 -16
- package/README.md +114 -47
- package/dist/{approve-QDOH2E7V.js → approve-K5FFTPA4.js} +2 -2
- package/dist/{audit-42OHF7ON.js → audit-HBNXNGD6.js} +5 -4
- package/dist/{brief-ZUQK3CFL.js → brief-DOQ2DQEG.js} +2 -2
- package/dist/{fleet-F3XDEDCV.js → chunk-ANXGT7V3.js} +431 -466
- package/dist/{chunk-HWC6PKJM.js → chunk-EJM47PIF.js} +1 -1
- package/dist/{chunk-PAMWS665.js → chunk-FKDT4RBP.js} +2 -1
- package/dist/{chunk-UFNETE6P.js → chunk-OKJ3RGJB.js} +1 -1
- package/dist/{chunk-LT7JMVV3.js → chunk-UV3YIBDW.js} +43 -730
- package/dist/{chunk-623W6KVC.js → chunk-UYCGP5BS.js} +1 -1
- package/dist/chunk-Y4E6ERCM.js +783 -0
- package/dist/cli.js +78 -22
- package/dist/{doctor-CE5SD4JW.js → doctor-X6XAGKV7.js} +5 -4
- package/dist/fleet-UEFLKIRI.js +489 -0
- package/dist/{gates-F4YPKWJR.js → gates-NVWWKMT2.js} +3 -3
- package/dist/{generate-KEX75XNG.js → generate-YEARCSHW.js} +1 -1
- package/dist/index.d.ts +301 -20
- package/dist/index.js +958 -233
- package/dist/{init-AHA7VRRI.js → init-Z5O2WVRJ.js} +3 -3
- package/dist/premise-4QRU7LRI.js +368 -0
- package/dist/propose-IKE4CUIM.js +252 -0
- package/dist/scan-64Z6SNYF.js +5 -0
- package/dist/{scan-TVX3FKK4.js → scan-H3NF5IV5.js} +2 -2
- package/package.json +5 -5
- package/dist/scan-QRTH6HAP.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# etymd
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dc77a1f: `contract.milestones` and `etymd fleet board` — a project declares its plan in `MILESTONES.md`
|
|
8
|
+
(`# Milestones`, then `| id | milestone | goal | status | next | effort | depends-on |`); the sweep
|
|
9
|
+
files a gap when a declared file is absent or off-shape; `fleet add` registers the file when
|
|
10
|
+
present; `fleet board --initiatives <file> --out <file>` renders every project's rows plus a ranked
|
|
11
|
+
initiatives table and totals, deterministic, guarded entries excluded, exit 1 on holes.
|
|
12
|
+
- b3fb81a: The fleet manifest's second profile is now `guarded` (it had another name before), with every derived name
|
|
13
|
+
following: `guardedHosts` in the local file, `--profile guarded`, the `guarded/<name>/` persistence
|
|
14
|
+
zone beside the manifest, and the guarded-worktree wall checks. No alias for the old value: a
|
|
15
|
+
manifest still carrying it fails `fleet check` and names the entry. Prose and examples describe the
|
|
16
|
+
feature as a second, guarded workspace whose entries stay alias-only and machine-pinned.
|
|
17
|
+
- 79af085: `etymd propose` — score the sweep's improvement findings and recurring classes against a
|
|
18
|
+
fleet-authored rubric file (`criterion: <weight>` labeled lines; criteria: severity, economy,
|
|
19
|
+
confidence, breadth — an unknown criterion is refused quoting the line) and emit deterministic,
|
|
20
|
+
read-only `proposal/1` records carrying score, the fired rubric lines, and an implications block
|
|
21
|
+
(projects, files, gates, reversibility) extracted from finding evidence. Guarded entries are
|
|
22
|
+
excluded from the output entire. `--manifest` (fresh read-only sweep) or `--from <fleet.json>`
|
|
23
|
+
(stored sweep), `--json`. Decision record: docs/decisions/012.
|
|
24
|
+
|
|
25
|
+
## 0.15.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- 6f40a7d: `etymd premise "<task>"` — the task you hand an agent is an instruction too. Every path, script,
|
|
30
|
+
well-known doc and decision id the task names is verified against the repo (a missing thing the
|
|
31
|
+
task is _about_ ranks as risk), and a brief hands the agent the premises only it can verify.
|
|
32
|
+
`--file <path>` (`-` reads stdin), `--json` (schema `premise/1`), `--no-brief`, `--fail-on <tier>`.
|
|
33
|
+
Zero trace in a repo that never opted in; no ledger. Prose is read with stricter rules than a code
|
|
34
|
+
span (a script needs the `run` form, a directory claim needs a first segment that exists, a host
|
|
35
|
+
name is a URL) and every class left as prose is disclosed. The command/path/doc-reference/
|
|
36
|
+
decision-reference truth checks are now one shared implementation used by `instruction-truth` and
|
|
37
|
+
`premise` alike — ids, tiers and disclosures of existing findings are unchanged.
|
|
38
|
+
|
|
3
39
|
## 0.14.0
|
|
4
40
|
|
|
5
41
|
Knowledge pack **v8 → v12**: every generated hook and script is regenerated by `etymd gates`.
|
|
@@ -408,18 +444,18 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
408
444
|
|
|
409
445
|
### Minor Changes
|
|
410
446
|
|
|
411
|
-
- e265713: `fleet add --profile
|
|
447
|
+
- e265713: `fleet add --profile guarded` now records the alias-to-directory mapping too, not just the entry.
|
|
412
448
|
|
|
413
|
-
A
|
|
414
|
-
what keeps
|
|
415
|
-
nothing on its own. Registering wrote only that half, so every
|
|
449
|
+
A guarded entry in the tracked manifest is deliberately alias-only — no path, no remote — which is
|
|
450
|
+
what keeps guarded-side names out of a file that gets pushed. It also means the entry resolves to
|
|
451
|
+
nothing on its own. Registering wrote only that half, so every guarded registration ended as a
|
|
416
452
|
dangling entry that `fleet check` reported immediately and the user had to fix by hand, in the
|
|
417
453
|
one file the tool otherwise never asks anyone to hand-edit.
|
|
418
454
|
|
|
419
455
|
The mapping is written `~`-relative, so the local manifest stays portable between machines, and
|
|
420
456
|
merged into the existing document so hand-maintained entries survive.
|
|
421
457
|
|
|
422
|
-
Two refusals guard it, because this file is the one place real
|
|
458
|
+
Two refusals guard it, because this file is the one place real guarded-side directory names are
|
|
423
459
|
written down. If the local manifest is not gitignored, registration refuses rather than creating
|
|
424
460
|
a file git would track — a leak the tool creates is worse than a registration it declines to
|
|
425
461
|
finish. If the file exists but is not valid JSON, it refuses rather than overwriting mappings
|
|
@@ -438,7 +474,7 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
438
474
|
It is also the field that turned a mis-profiled entry into a real disclosure. A raw remote URL
|
|
439
475
|
carries the host and the internal group path; `path` carries a bare directory name. Removing a
|
|
440
476
|
field no consumer reads retires that class outright, with no host-matching heuristic, and unlike
|
|
441
|
-
the
|
|
477
|
+
the guarded-host guard it keeps working on a machine that has no local manifest to read guarded hosts
|
|
442
478
|
from. The remote stays derivable from the checkout at any time, which is where it came from.
|
|
443
479
|
|
|
444
480
|
Existing entries that already carry a remote are left alone — nothing reads them, and rewriting
|
|
@@ -448,20 +484,20 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
448
484
|
|
|
449
485
|
### Patch Changes
|
|
450
486
|
|
|
451
|
-
- 88492b5: Fix: `fleet add --profile
|
|
487
|
+
- 88492b5: Fix: `fleet add --profile guarded` was silently ignored, registering guarded repos as personal.
|
|
452
488
|
|
|
453
489
|
`fleet` declares its own `--profile` (the sweep filter), and commander hands a parent-declared
|
|
454
|
-
option the value even when it is typed after the subcommand. So `fleet add <dir> --profile
|
|
490
|
+
option the value even when it is typed after the subcommand. So `fleet add <dir> --profile guarded`
|
|
455
491
|
left the subcommand's own option undefined, the placement fell back to personal, and the
|
|
456
|
-
personal branch records `path` and the RAW `remote` — writing the
|
|
492
|
+
personal branch records `path` and the RAW `remote` — writing the guarded host and its internal
|
|
457
493
|
group structure into a manifest that is tracked and pushed. That is precisely the disclosure the
|
|
458
494
|
alias convention exists to prevent. The same shadowing class was already fixed for `--manifest`
|
|
459
495
|
and `--json`; `--profile` was missed.
|
|
460
496
|
|
|
461
497
|
Two changes, because the flag alone is not enough. The CLI now reads the merged option view, so
|
|
462
|
-
the flag works. And `fleet add` refuses outright when the target's remote matches a
|
|
463
|
-
declared in the local manifest while the profile is not
|
|
464
|
-
hosts are the
|
|
498
|
+
the flag works. And `fleet add` refuses outright when the target's remote matches a guarded host
|
|
499
|
+
declared in the local manifest while the profile is not guarded — the manifest already knows which
|
|
500
|
+
hosts are the guarded side's and the remote has just been read, so the tool has every fact needed to
|
|
465
501
|
prevent this without anyone remembering a flag. It refuses rather than auto-correcting:
|
|
466
502
|
placement is the user's decision, but it may not be made by omission.
|
|
467
503
|
|
|
@@ -598,7 +634,7 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
598
634
|
value it explains changes, because a stale reason misleads exactly where it meant to inform.
|
|
599
635
|
|
|
600
636
|
**`etymd fleet add`** registers a project, prompting for what no scan can derive and refusing to
|
|
601
|
-
write an incomplete entry. Fleet manifests gain a mandatory `trust` level on non-
|
|
637
|
+
write an incomplete entry. Fleet manifests gain a mandatory `trust` level on non-guarded entries —
|
|
602
638
|
absence is a finding, never a silent default — a manifest-level `orientation.root` replacing
|
|
603
639
|
per-entry links, and a gate-drift check that reports a repository missing a gate its siblings
|
|
604
640
|
install.
|
|
@@ -658,9 +694,9 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
658
694
|
|
|
659
695
|
- New `state-freshness` truth lens: state/decisions artifacts dated by git committer dates only (never mtime); staleness is relative, so a dormant repo's old state is current; state char budget against the ~10k session-hook truncation; marker-gated decisions format checks (`Scope:`, duplicate/out-of-order `D-NNN` ids, past `Revisit:` dates as due review debt); ADR conventions (`docs/adr/`, `docs/decisions/`, `NNNN-*.md`) recognized natively.
|
|
660
696
|
- New `etymd fleet` command family. The sweep runs a read-only audit per registered repo (`--manifest` required unless the cwd holds `registry.json` — no env var, no global pointer) and renders one line per project with a delta against `last.fleet.json`; detail only for new or risk findings. `fleet check` validates the manifest pair alone (dangling mappings, duplicate names, privacy leaks, machine paths). `fleet dismiss`/`fleet accept` resolve a project's finding from any cwd.
|
|
661
|
-
- The manifest loader (`src/core/fleet.ts`) resolves both the fleet registry pair (`registry.json` + gitignored `registry.local.json`) and the legacy corpus pair (`sources.json` + `sources.local.json`);
|
|
662
|
-
- Persistence invariants, pinned by tests: the sweep never creates `.etymd` anywhere; `--persist-ledgers` only persists into personal repos that already opted in;
|
|
663
|
-
- Fleet-scope wall findings (lens id `fleet-manifest`):
|
|
697
|
+
- The manifest loader (`src/core/fleet.ts`) resolves both the fleet registry pair (`registry.json` + gitignored `registry.local.json`) and the legacy corpus pair (`sources.json` + `sources.local.json`); guarded entries are opaque aliases resolved only through the local file, and every resolution failure is disclosed, never silently skipped.
|
|
698
|
+
- Persistence invariants, pinned by tests: the sweep never creates `.etymd` anywhere; `--persist-ledgers` only persists into personal repos that already opted in; guarded worktrees take zero writes under every flag combination — guarded findings persist (and stay dismissible) at `<manifestDir>/guarded/<name>/.etymd/`; zero guarded-resolved content under the manifest repo's tracked paths.
|
|
699
|
+
- Fleet-scope wall findings (lens id `fleet-manifest`): guarded contract files inside a guarded worktree, unregistered guarded-remote checkouts under the fleet root, tracked `/Users/` paths in the manifest's own repo, private needles inside `trust: "public-repo"` entries, guarded-host commit emails on personal entries.
|
|
664
700
|
- Fork-aware freshness: entries with `upstream` are dated on fork-authored commits only (`HEAD --not --remotes=<upstream>`), with a disclosed fallback when the remote is absent.
|
|
665
701
|
|
|
666
702
|
## 0.1.0 — 2026-07-31
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Etymd
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
|
-
<img src="https://raw.githubusercontent.com/
|
|
4
|
+
<img src="https://raw.githubusercontent.com/fleetorders/etymd/main/media/etymd-logo.png" width="520" alt="Etymd — a papyrus of written instructions, each line checked against the repository it describes">
|
|
5
5
|
<p>
|
|
6
6
|
<a href="https://www.npmjs.com/package/etymd"><img src="https://img.shields.io/npm/v/etymd.svg?label=npm&color=cb3837" alt="npm version"></a>
|
|
7
|
-
<a href="https://github.com/
|
|
8
|
-
<a href="https://github.com/
|
|
7
|
+
<a href="https://github.com/fleetorders/etymd/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/fleetorders/etymd/ci.yml?branch=main&label=CI" alt="CI"></a>
|
|
8
|
+
<a href="https://github.com/fleetorders/etymd/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license"></a>
|
|
9
9
|
</p>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
@@ -15,7 +15,8 @@ You wrote rules for your AI months ago. Since then a script got renamed, a folde
|
|
|
15
15
|
changed — and the AI still trusts every word. The file never complains when it goes stale; it just
|
|
16
16
|
keeps instructing, confidently, and you live with the results.
|
|
17
17
|
|
|
18
|
-
Etymd reads those instruction files and
|
|
18
|
+
Etymd reads those instruction files — and, when you ask, the task you are about to hand an agent —
|
|
19
|
+
and checks every claim against your actual project.
|
|
19
20
|
|
|
20
21
|
One command, run in your project's folder (needs Node ≥ 18.17, nothing else): `npx etymd audit`.
|
|
21
22
|
Here it is on a small demo project whose AGENTS.md still tells the AI to run `npm run start` and
|
|
@@ -110,6 +111,7 @@ cd your-project
|
|
|
110
111
|
npx etymd audit # verify every instruction claim against the repo
|
|
111
112
|
npx etymd init # opt in to drift: approve the baseline (+ scaffold AGENTS.md only if you have none)
|
|
112
113
|
npx etymd audit --fail-on risk # the CI gate
|
|
114
|
+
npx etymd premise "fix the flaky test in src/legacy/foo.test.ts" # is this the right task?
|
|
113
115
|
```
|
|
114
116
|
|
|
115
117
|
`audit` needs no setup — without `init` you get the full findings report and lose only drift
|
|
@@ -132,6 +134,14 @@ gate into a pipeline, see [In CI](#in-ci).
|
|
|
132
134
|
- **Drift vs baseline** — documented commands/artifacts/layout that existed at approval and are
|
|
133
135
|
now gone.
|
|
134
136
|
|
|
137
|
+
**`premise`** (via `etymd premise`) — the task itself is an instruction. Before an agent acts on
|
|
138
|
+
it, every path, script, well-known doc and decision id the task names is checked with the same
|
|
139
|
+
rules instruction files get. A path the task is _about_ and that does not exist ranks as **risk**:
|
|
140
|
+
the task would solve the wrong problem precisely. What cannot be read from files — that the named
|
|
141
|
+
things are the ones meant, that the mechanism the task assumes actually runs, that the state it
|
|
142
|
+
assumes holds — is handed to the agent in a brief, never guessed at. Nothing is remembered between
|
|
143
|
+
runs.
|
|
144
|
+
|
|
135
145
|
**`gate-integrity`** — a CI config is a claim too: checks enforced only in CI (failures surface a
|
|
136
146
|
slow pipeline after the agent finished — `etymd gates` generates the local mirror), checks only in
|
|
137
147
|
skippable local hooks, latent gaps (coverage collected but nothing gates on it; commitlint
|
|
@@ -157,28 +167,31 @@ append race is a defect in the file's own convention, not a format opinion.
|
|
|
157
167
|
|
|
158
168
|
**`fleet-manifest`** (via `etymd fleet`) — one truth guard across every repo you registered:
|
|
159
169
|
per-repo audits plus checks on the fleet manifest itself and on the placement wall between
|
|
160
|
-
personal and
|
|
170
|
+
personal and guarded repos. See [the fleet manifest](#the-fleet-manifest-experimental) below.
|
|
161
171
|
|
|
162
172
|
## Commands
|
|
163
173
|
|
|
164
|
-
| Command | What it does
|
|
165
|
-
| -------------------------------- |
|
|
166
|
-
| `etymd audit` | Verify every claim; ranked findings (risk → gap → polish) + ledger diff. `--lens`, `--truth`, `--json`, `--no-ledger`, `--fail-on <tier>`.
|
|
167
|
-
| `etymd init` | Onboard: approve the committed baseline; scaffold a minimal AGENTS.md **only if missing**. Never overwrites.
|
|
168
|
-
| `etymd doctor` | Alias for `audit --truth`.
|
|
169
|
-
| `etymd context` | The economy view: per-file always-loaded footprint + extraction candidates.
|
|
170
|
-
| `etymd gates` | Install local git-hook gates (pre-commit / commit-msg / pre-push, plus a publish screen where something ships) built from your own check scripts — and from the repo's shell surface, where it has one.
|
|
171
|
-
| `etymd screen` | Content screen: find text that must never be published. Four scopes — `--staged`, `--message`, `--tree`, `--dir`. Bring your own patterns; etymd ships none.
|
|
172
|
-
| `etymd scan` | The deterministic reckoning behind everything. `--json`.
|
|
173
|
-
| `etymd brief` | A grounded briefing your in-repo agent completes to author the semantic layer.
|
|
174
|
-
| `etymd
|
|
175
|
-
| `etymd
|
|
176
|
-
| `etymd
|
|
177
|
-
| `etymd
|
|
178
|
-
| `etymd
|
|
179
|
-
| `etymd fleet
|
|
180
|
-
| `etymd fleet
|
|
181
|
-
| `etymd fleet
|
|
174
|
+
| Command | What it does |
|
|
175
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
176
|
+
| `etymd audit` | Verify every claim; ranked findings (risk → gap → polish) + ledger diff. `--lens`, `--truth`, `--json`, `--no-ledger`, `--fail-on <tier>`. |
|
|
177
|
+
| `etymd init` | Onboard: approve the committed baseline; scaffold a minimal AGENTS.md **only if missing**. Never overwrites. |
|
|
178
|
+
| `etymd doctor` | Alias for `audit --truth`. |
|
|
179
|
+
| `etymd context` | The economy view: per-file always-loaded footprint + extraction candidates. |
|
|
180
|
+
| `etymd gates` | Install local git-hook gates (pre-commit / commit-msg / pre-push, plus a publish screen where something ships) built from your own check scripts — and from the repo's shell surface, where it has one. |
|
|
181
|
+
| `etymd screen` | Content screen: find text that must never be published. Four scopes — `--staged`, `--message`, `--tree`, `--dir`. Bring your own patterns; etymd ships none. |
|
|
182
|
+
| `etymd scan` | The deterministic reckoning behind everything. `--json`. |
|
|
183
|
+
| `etymd brief` | A grounded briefing your in-repo agent completes to author the semantic layer. |
|
|
184
|
+
| `etymd premise` | `premise "<task>"` — is this the right task? What it names, verified against the repo; a brief for what only the agent can verify. `--file` (`-` = stdin), `--json`, `--no-brief`, `--fail-on <tier>`. No ledger. |
|
|
185
|
+
| `etymd approve` | Refresh the committed baseline non-interactively after intentional structural changes. |
|
|
186
|
+
| `etymd ledger` | The findings memory: every tracked finding with status and history. |
|
|
187
|
+
| `etymd dismiss` | `dismiss <id> --reason <text>` — a dismissed finding never resurfaces without regressing. |
|
|
188
|
+
| `etymd accept` | `accept <id>` — record a finding as accepted reality; visible in the ledger, out of the report. |
|
|
189
|
+
| `etymd fleet` | Sweep every project in a fleet manifest: read-only per-repo audits + manifest/wall checks. `--manifest`, `--only`, `--profile`, `--truth`, `--persist-ledgers`, `--json`, `--fail-on`. |
|
|
190
|
+
| `etymd fleet check` | Validate the manifest pair alone (no lenses): dangling mappings, duplicate names, privacy leaks, undeclared trust, machine paths. Non-zero exit on any finding. |
|
|
191
|
+
| `etymd fleet add` | `add <dir>` — register a project: scans it, asks for what no scan can derive, and refuses to write an entry missing a mandatory field. `--name`, `--kind`, `--profile`, `--trust`, `-y`. |
|
|
192
|
+
| `etymd fleet board` | Render the fleet board: every project's `MILESTONES.md` (contract key `milestones`, shape-checked by the sweep) plus a ranked initiatives table on one page. `--initiatives <file>`, `--out <file>`, `--json`. |
|
|
193
|
+
| `etymd propose` | Score the sweep's improvement findings + recurring classes against a fleet-authored rubric — stable `proposal/1` records, read-only, deterministic, guarded entries excluded. `--rubric <file>` (required), `--manifest <file>` or `--from <fleet.json>`, `--json`. |
|
|
194
|
+
| `etymd fleet dismiss` / `accept` | `<name> <id>` — resolve a project's finding from any cwd; guarded findings persist beside the manifest, never in the guarded worktree. |
|
|
182
195
|
|
|
183
196
|
`--cwd <dir>` targets another directory. Read-only probing of any repo leaves **zero trace**
|
|
184
197
|
(`audit --no-ledger` writes nothing).
|
|
@@ -313,7 +326,7 @@ the generated pre-push now carries a `shellcheck` step. Three properties are del
|
|
|
313
326
|
### The content screen (`etymd screen`)
|
|
314
327
|
|
|
315
328
|
A separate question from "are the instructions true?": **does this repo carry text that must
|
|
316
|
-
never be published?** Absolute home paths, an
|
|
329
|
+
never be published?** Absolute home paths, an organisation's name, an internal hostname, an account
|
|
317
330
|
identifier — permanent the moment they are committed, because publishing exposes all history,
|
|
318
331
|
not the current tree.
|
|
319
332
|
|
|
@@ -347,7 +360,7 @@ inline marker would not survive into the artifact. Those live in `.etymd-screen-
|
|
|
347
360
|
repo root — one labeled line per field, so the pattern is never delimited:
|
|
348
361
|
|
|
349
362
|
```
|
|
350
|
-
pattern ^
|
|
363
|
+
pattern ^AcmeInc|BetaInc$
|
|
351
364
|
reason fixture proving the detector fires on either name
|
|
352
365
|
date 2026-08-15
|
|
353
366
|
author someone
|
|
@@ -407,10 +420,10 @@ steps:
|
|
|
407
420
|
`etymd fleet` extends the one objective across every repository you work in — your fleet of
|
|
408
421
|
**repositories**, not a fleet of agents. The manifest, `registry.json`, is itself an
|
|
409
422
|
agent-context file: claims about your fleet (what exists, where, under which **profile** — the
|
|
410
|
-
side of the wall an entry belongs to, `personal` or `
|
|
411
|
-
boundary between personal and
|
|
423
|
+
side of the wall an entry belongs to, `personal` or `guarded`; the **wall** is the placement
|
|
424
|
+
boundary between personal and guarded content that the sweep polices). It rots like any
|
|
412
425
|
AGENTS.md does, and `etymd fleet` keeps it true. Decision record:
|
|
413
|
-
[`docs/decisions/004-fleet-truth-guard.md`](https://github.com/
|
|
426
|
+
[`docs/decisions/004-fleet-truth-guard.md`](https://github.com/fleetorders/etymd/blob/main/docs/decisions/004-fleet-truth-guard.md). Both the
|
|
414
427
|
registry schema and the fleet `--json` schema are **experimental through 0.2.x**.
|
|
415
428
|
|
|
416
429
|
Two files beside each other — the split is the privacy model:
|
|
@@ -435,7 +448,7 @@ Two files beside each other — the split is the privacy model:
|
|
|
435
448
|
"kind": "docs",
|
|
436
449
|
"profile": "personal",
|
|
437
450
|
"path": "notes",
|
|
438
|
-
"trust": "private", // mandatory on every non-
|
|
451
|
+
"trust": "private", // mandatory on every non-guarded entry — see below
|
|
439
452
|
"staleAfterDays": 45, // per-entry freshness window
|
|
440
453
|
"contract": { "state": "STATUS.md" }, // native conventions register, never migrate
|
|
441
454
|
},
|
|
@@ -447,8 +460,14 @@ Two files beside each other — the split is the privacy model:
|
|
|
447
460
|
"upstream": "origin", // freshness measured on fork-authored commits only
|
|
448
461
|
"trust": "public-repo", // hygiene needles apply (see below)
|
|
449
462
|
},
|
|
450
|
-
//
|
|
451
|
-
{
|
|
463
|
+
// Guarded entries: opaque alias, private, NO path — real dirs live only in the local file.
|
|
464
|
+
{
|
|
465
|
+
"name": "c-one",
|
|
466
|
+
"kind": "repo",
|
|
467
|
+
"profile": "guarded",
|
|
468
|
+
"private": true,
|
|
469
|
+
"staleAfterDays": 45,
|
|
470
|
+
},
|
|
452
471
|
],
|
|
453
472
|
}
|
|
454
473
|
```
|
|
@@ -457,23 +476,23 @@ Two files beside each other — the split is the privacy model:
|
|
|
457
476
|
|
|
458
477
|
```jsonc
|
|
459
478
|
{
|
|
460
|
-
"machineProfile": "
|
|
479
|
+
"machineProfile": "guarded", // which profile this machine resolves; "personal" resolves guarded entries disclosed-absent
|
|
461
480
|
"root": "~/projects", // optional per-machine root override
|
|
462
|
-
"dirs": { "c-one": "~/projects/real-
|
|
463
|
-
"labels": { "c-one": "real-
|
|
464
|
-
"
|
|
481
|
+
"dirs": { "c-one": "~/projects/real-guarded-dir" },
|
|
482
|
+
"labels": { "c-one": "real-guarded-dir" },
|
|
483
|
+
"guardedHosts": ["git.example-guarded.com"],
|
|
465
484
|
}
|
|
466
485
|
```
|
|
467
486
|
|
|
468
487
|
Two fields the scan can never derive, so the manifest must declare them:
|
|
469
488
|
|
|
470
|
-
- **`trust` — mandatory on every non-
|
|
489
|
+
- **`trust` — mandatory on every non-guarded entry** (`public-repo` | `public-bound` | `private`).
|
|
471
490
|
It is a _safety predicate_, not a label: it decides whether content screening applies, so an
|
|
472
491
|
absent value is reported (`fleet check` flags it), never read as a silent `private`.
|
|
473
492
|
`public-bound` means private today, plausibly public later — screened exactly as hard as
|
|
474
493
|
public, because publishing exposes _all_ history: the scrub has to precede the first commit,
|
|
475
494
|
not the visibility flip. A value outside the vocabulary is flagged rather than coerced, so a
|
|
476
|
-
typo can never quietly disable screening.
|
|
495
|
+
typo can never quietly disable screening. Guarded entries omit it — `profile: "guarded"` already
|
|
477
496
|
implies the answer.
|
|
478
497
|
- **`orientation.root` — optional, declared once.** Names the one entry every other entry is
|
|
479
498
|
guided by. Declared at the manifest level rather than repeated per entry, because a per-entry
|
|
@@ -488,10 +507,10 @@ CI) must pass every mandatory value as a flag — there is deliberately no defau
|
|
|
488
507
|
How the sweep behaves:
|
|
489
508
|
|
|
490
509
|
- **Read-only by default, everywhere.** The sweep never creates `.etymd` anywhere.
|
|
491
|
-
`--persist-ledgers` persists only into personal repos that already opted in, and a **
|
|
510
|
+
`--persist-ledgers` persists only into personal repos that already opted in, and a **guarded
|
|
492
511
|
worktree is never written** — regardless of flags, even if a stray `.etymd` exists inside it
|
|
493
|
-
(pinned by test).
|
|
494
|
-
`<manifest-dir>/
|
|
512
|
+
(pinned by test). Guarded findings stay dismissible: their ledger lives at
|
|
513
|
+
`<manifest-dir>/guarded/<name>/.etymd/`, beside the manifest.
|
|
495
514
|
- **Deltas.** Each sweep compares against `last.fleet.json` stored beside the manifest and
|
|
496
515
|
renders `Δ +new −resolved` per project. Add `*.fleet.json` to the manifest repo's
|
|
497
516
|
`.gitignore` — sweep output is local-only and never tracked.
|
|
@@ -503,17 +522,64 @@ How the sweep behaves:
|
|
|
503
522
|
that instruction files are legitimately absent in that project — the sweep drops its
|
|
504
523
|
missing-contract finding instead of re-reporting a decision every run. Absence disclosed on
|
|
505
524
|
purpose is a state, not a gap.
|
|
506
|
-
- **Wall checks.**
|
|
507
|
-
under the fleet root whose remotes match `
|
|
525
|
+
- **Wall checks.** Guarded contract files found inside a guarded worktree, unregistered checkouts
|
|
526
|
+
under the fleet root whose remotes match `guardedHosts`, tracked `/Users/` paths in the manifest
|
|
508
527
|
repo, private **needles** — the identifiers the local file holds (labels, dir names, hosts) —
|
|
509
|
-
inside `trust: "public-repo"` entries, and
|
|
528
|
+
inside `trust: "public-repo"` entries, and guarded-host commit emails on personal entries — each
|
|
510
529
|
a risk finding; each check that cannot run is disclosed.
|
|
511
530
|
- **No global pointer.** `--manifest` is required unless the cwd holds `registry.json` — there
|
|
512
531
|
is deliberately no env var and no home-directory pointer.
|
|
532
|
+
- **Milestones and the fleet board.** A project declares its plan in one file — `MILESTONES.md`,
|
|
533
|
+
registered as `"contract": { "milestones": "MILESTONES.md" }` (`fleet add` registers it when the
|
|
534
|
+
file is present; `"none"` declares a project deliberately carries no plan). The file has a fixed
|
|
535
|
+
shape so the fleet can read every plan without an agent: a `# Milestones` heading, then a table
|
|
536
|
+
`| id | milestone | goal | status | next | effort | depends-on |` — `id` is `M<n>`, `goal` is 1, 2
|
|
537
|
+
or 3 (your fleet's own ordered goals, declared outside this tool), `status` is planned | active |
|
|
538
|
+
blocked | done, `next` is the one concrete next step, `effort` the S | M | L remaining,
|
|
539
|
+
`depends-on` a list of ids or `—`. Prose after the table is free. The sweep files a gap when a
|
|
540
|
+
declared file is absent or off-shape. `etymd fleet board --initiatives <file> --out <file>`
|
|
541
|
+
renders every project's rows, a ranked initiatives table (`| rank | id | initiative | goal |
|
|
542
|
+
status | next | effort | projects | depends-on |`, the one hand-edited fleet-level surface), and
|
|
543
|
+
totals; guarded entries never appear on it. Day-precision stamp, deterministic output, exit code 1
|
|
544
|
+
when any project is missing or invalid — a board with holes still renders, and says so.
|
|
513
545
|
|
|
514
546
|
Formatter interop for the `.etymd` state the sweep resolves: same rule as everywhere — see
|
|
515
547
|
[the files Etymd keeps](#the-files-etymd-keeps).
|
|
516
548
|
|
|
549
|
+
### Rubric-scored proposals (`etymd propose`)
|
|
550
|
+
|
|
551
|
+
The sweep already gives every improvement finding an action, an effort and a confidence, and
|
|
552
|
+
names the classes open in two or more projects. `etymd propose` adds the scoring step — against
|
|
553
|
+
a rubric **your fleet authors**, because what is worth doing is your call, not the tool's:
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
severity: 2 # risk=3 · gap=2 · polish=1
|
|
557
|
+
economy: 3 # S=3 · M=2 · L=1
|
|
558
|
+
confidence: 1 # high=3 · medium=2 · low=1
|
|
559
|
+
breadth: 4 # projects carrying it, capped at 3
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
One criterion per line (`#` comments and blanks ignored). Those four criteria are the whole
|
|
563
|
+
vocabulary — each is computed from finding facts, so a score is arithmetic, not an opinion; a
|
|
564
|
+
line naming anything else is refused quoting the line. `score` is Σ weight × value; a line
|
|
565
|
+
**fires** (listed in `matched`) when the subject reads at/above the criterion's midpoint.
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
etymd propose --manifest registry.json --rubric opportunity.rubric --json
|
|
569
|
+
# or, without re-sweeping: --from <fleet.json> (a stored `etymd fleet --json` output)
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
Subjects are every `kind: improvement` finding from personal projects plus every recurring
|
|
573
|
+
class, recomputed over personal projects only — **guarded entries are excluded from the output
|
|
574
|
+
entire, by name**. A class is scored conservatively (worst tier, dearest effort, weakest
|
|
575
|
+
confidence). Each `proposal/1` record carries id, class, projects, action, effort, confidence,
|
|
576
|
+
score, the fired rubric lines, and an `implications` block (projects, files, gates,
|
|
577
|
+
reversibility) extracted from the findings' evidence — files are path-shaped evidence tokens,
|
|
578
|
+
and `undetermined` reversibility says so rather than guessing. Read-only and deterministic: no
|
|
579
|
+
timestamps, nothing written, identical input → identical bytes, so a filed proposal can be
|
|
580
|
+
re-derived and compared. Decision record:
|
|
581
|
+
[`docs/decisions/012-propose-rubric-scored-proposals.md`](https://github.com/fleetorders/etymd/blob/main/docs/decisions/012-propose-rubric-scored-proposals.md).
|
|
582
|
+
|
|
517
583
|
## Programmatic use
|
|
518
584
|
|
|
519
585
|
```ts
|
|
@@ -526,7 +592,7 @@ console.log(audit.findings) // one schema: claim · evidence · why · action ·
|
|
|
526
592
|
## The corpus (how this is validated)
|
|
527
593
|
|
|
528
594
|
Etymd is developed against a corpus of real sibling repos rather than fixtures alone —
|
|
529
|
-
[`sources.json`](https://github.com/
|
|
595
|
+
[`sources.json`](https://github.com/fleetorders/etymd/blob/main/sources.json) lists them by shape. Every heuristic here exists because a real
|
|
530
596
|
repo proved the previous one wrong, and each skip class in the truth lens is a false positive that
|
|
531
597
|
a corpus run caught.
|
|
532
598
|
|
|
@@ -544,11 +610,12 @@ Without it — on a fresh clone or in CI — those suites skip cleanly and the r
|
|
|
544
610
|
|
|
545
611
|
## Decision record & roadmap
|
|
546
612
|
|
|
547
|
-
[`docs/decisions/`](https://github.com/
|
|
613
|
+
[`docs/decisions/`](https://github.com/fleetorders/etymd/tree/main/docs/decisions) — 001 founding · 002 foundation re-lock · **003 the truth-guard
|
|
548
614
|
pivot** (the current identity; includes the state-of-the-field investigation it rests on) ·
|
|
549
615
|
**004 fleet mode** (the truth guard across your repositories) · 005 declared rules (design only) ·
|
|
550
|
-
**006 local gate provenance** (what the tool may read, and what it may rewrite)
|
|
551
|
-
|
|
616
|
+
**006 local gate provenance** (what the tool may read, and what it may rewrite) · 011 milestones
|
|
617
|
+
& the fleet board · 012 `etymd propose` (rubric-scored proposals).
|
|
618
|
+
[`ROADMAP.md`](https://github.com/fleetorders/etymd/blob/main/ROADMAP.md) — what's now / next / later, and the accepted heuristic
|
|
552
619
|
trade-offs.
|
|
553
620
|
|
|
554
621
|
## License
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { section, theme, print, renderBaselineDrift, glyph } from './chunk-HI7NWPRA.js';
|
|
3
|
-
import { scanProject } from './chunk-
|
|
4
|
-
import { VERSION } from './chunk-
|
|
3
|
+
import { scanProject } from './chunk-EJM47PIF.js';
|
|
4
|
+
import { VERSION } from './chunk-UYCGP5BS.js';
|
|
5
5
|
import { readBaseline, summarizeBaselineDrift, isDriftEmpty, writeBaseline, deriveProfile } from './chunk-P6ATKV2R.js';
|
|
6
6
|
import { PACK_VERSION } from './chunk-YQZDYDAK.js';
|
|
7
7
|
import './chunk-4VPBP6K6.js';
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { run } from './chunk-
|
|
3
|
-
import './chunk-
|
|
2
|
+
export { run } from './chunk-FKDT4RBP.js';
|
|
3
|
+
import './chunk-UV3YIBDW.js';
|
|
4
|
+
import './chunk-Y4E6ERCM.js';
|
|
4
5
|
import './chunk-3E2IPCRY.js';
|
|
5
6
|
import './chunk-HI7NWPRA.js';
|
|
6
|
-
import './chunk-
|
|
7
|
-
import './chunk-
|
|
7
|
+
import './chunk-EJM47PIF.js';
|
|
8
|
+
import './chunk-UYCGP5BS.js';
|
|
8
9
|
import './chunk-P6ATKV2R.js';
|
|
9
10
|
import './chunk-YQZDYDAK.js';
|
|
10
11
|
import './chunk-4VPBP6K6.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { print, theme } from './chunk-HI7NWPRA.js';
|
|
3
|
-
import { scanProject } from './chunk-
|
|
4
|
-
import './chunk-
|
|
3
|
+
import { scanProject } from './chunk-EJM47PIF.js';
|
|
4
|
+
import './chunk-UYCGP5BS.js';
|
|
5
5
|
import { ETYMD_DIR } from './chunk-P6ATKV2R.js';
|
|
6
6
|
import './chunk-YQZDYDAK.js';
|
|
7
7
|
import './chunk-4VPBP6K6.js';
|