etymd 0.15.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 +38 -16
- package/README.md +103 -47
- package/dist/{approve-MA4Z3TBT.js → approve-K5FFTPA4.js} +2 -2
- package/dist/{audit-YSALDC2L.js → audit-HBNXNGD6.js} +5 -5
- package/dist/{brief-DPZYSAMC.js → brief-DOQ2DQEG.js} +2 -2
- package/dist/{fleet-4FYZ3GBK.js → chunk-ANXGT7V3.js} +431 -467
- package/dist/{chunk-Y6RZRED3.js → chunk-EJM47PIF.js} +1 -1
- package/dist/{chunk-CEO3BXQB.js → chunk-FKDT4RBP.js} +2 -2
- package/dist/{chunk-UFNETE6P.js → chunk-OKJ3RGJB.js} +1 -1
- package/dist/{chunk-2RNQ6OLV.js → chunk-UV3YIBDW.js} +2 -2
- package/dist/{chunk-DBWDMIYO.js → chunk-UYCGP5BS.js} +1 -1
- package/dist/{chunk-IWG77WV3.js → chunk-Y4E6ERCM.js} +31 -6
- package/dist/cli.js +57 -22
- package/dist/{doctor-Y3DWDEBT.js → doctor-X6XAGKV7.js} +5 -5
- package/dist/fleet-UEFLKIRI.js +489 -0
- package/dist/{gates-IBQ7HCAN.js → gates-NVWWKMT2.js} +3 -3
- package/dist/{generate-KEX75XNG.js → generate-YEARCSHW.js} +1 -1
- package/dist/index.d.ts +138 -20
- package/dist/index.js +485 -92
- package/dist/{init-H57H2JBE.js → init-Z5O2WVRJ.js} +3 -3
- package/dist/{premise-VMC3UUIB.js → premise-4QRU7LRI.js} +59 -24
- package/dist/propose-IKE4CUIM.js +252 -0
- package/dist/scan-64Z6SNYF.js +5 -0
- package/dist/{scan-XBOHDXAH.js → scan-H3NF5IV5.js} +2 -2
- package/package.json +4 -4
- package/dist/scan-W4US23MU.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
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
|
+
|
|
3
25
|
## 0.15.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
|
@@ -422,18 +444,18 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
422
444
|
|
|
423
445
|
### Minor Changes
|
|
424
446
|
|
|
425
|
-
- e265713: `fleet add --profile
|
|
447
|
+
- e265713: `fleet add --profile guarded` now records the alias-to-directory mapping too, not just the entry.
|
|
426
448
|
|
|
427
|
-
A
|
|
428
|
-
what keeps
|
|
429
|
-
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
|
|
430
452
|
dangling entry that `fleet check` reported immediately and the user had to fix by hand, in the
|
|
431
453
|
one file the tool otherwise never asks anyone to hand-edit.
|
|
432
454
|
|
|
433
455
|
The mapping is written `~`-relative, so the local manifest stays portable between machines, and
|
|
434
456
|
merged into the existing document so hand-maintained entries survive.
|
|
435
457
|
|
|
436
|
-
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
|
|
437
459
|
written down. If the local manifest is not gitignored, registration refuses rather than creating
|
|
438
460
|
a file git would track — a leak the tool creates is worse than a registration it declines to
|
|
439
461
|
finish. If the file exists but is not valid JSON, it refuses rather than overwriting mappings
|
|
@@ -452,7 +474,7 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
452
474
|
It is also the field that turned a mis-profiled entry into a real disclosure. A raw remote URL
|
|
453
475
|
carries the host and the internal group path; `path` carries a bare directory name. Removing a
|
|
454
476
|
field no consumer reads retires that class outright, with no host-matching heuristic, and unlike
|
|
455
|
-
the
|
|
477
|
+
the guarded-host guard it keeps working on a machine that has no local manifest to read guarded hosts
|
|
456
478
|
from. The remote stays derivable from the checkout at any time, which is where it came from.
|
|
457
479
|
|
|
458
480
|
Existing entries that already carry a remote are left alone — nothing reads them, and rewriting
|
|
@@ -462,20 +484,20 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
462
484
|
|
|
463
485
|
### Patch Changes
|
|
464
486
|
|
|
465
|
-
- 88492b5: Fix: `fleet add --profile
|
|
487
|
+
- 88492b5: Fix: `fleet add --profile guarded` was silently ignored, registering guarded repos as personal.
|
|
466
488
|
|
|
467
489
|
`fleet` declares its own `--profile` (the sweep filter), and commander hands a parent-declared
|
|
468
|
-
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`
|
|
469
491
|
left the subcommand's own option undefined, the placement fell back to personal, and the
|
|
470
|
-
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
|
|
471
493
|
group structure into a manifest that is tracked and pushed. That is precisely the disclosure the
|
|
472
494
|
alias convention exists to prevent. The same shadowing class was already fixed for `--manifest`
|
|
473
495
|
and `--json`; `--profile` was missed.
|
|
474
496
|
|
|
475
497
|
Two changes, because the flag alone is not enough. The CLI now reads the merged option view, so
|
|
476
|
-
the flag works. And `fleet add` refuses outright when the target's remote matches a
|
|
477
|
-
declared in the local manifest while the profile is not
|
|
478
|
-
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
|
|
479
501
|
prevent this without anyone remembering a flag. It refuses rather than auto-correcting:
|
|
480
502
|
placement is the user's decision, but it may not be made by omission.
|
|
481
503
|
|
|
@@ -612,7 +634,7 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
612
634
|
value it explains changes, because a stale reason misleads exactly where it meant to inform.
|
|
613
635
|
|
|
614
636
|
**`etymd fleet add`** registers a project, prompting for what no scan can derive and refusing to
|
|
615
|
-
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 —
|
|
616
638
|
absence is a finding, never a silent default — a manifest-level `orientation.root` replacing
|
|
617
639
|
per-entry links, and a gate-drift check that reports a repository missing a gate its siblings
|
|
618
640
|
install.
|
|
@@ -672,9 +694,9 @@ so a pack number cited in a baseline always means exactly one pack.
|
|
|
672
694
|
|
|
673
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.
|
|
674
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.
|
|
675
|
-
- 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`);
|
|
676
|
-
- Persistence invariants, pinned by tests: the sweep never creates `.etymd` anywhere; `--persist-ledgers` only persists into personal repos that already opted in;
|
|
677
|
-
- 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.
|
|
678
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.
|
|
679
701
|
|
|
680
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
|
|
|
@@ -167,29 +167,31 @@ append race is a defect in the file's own convention, not a format opinion.
|
|
|
167
167
|
|
|
168
168
|
**`fleet-manifest`** (via `etymd fleet`) — one truth guard across every repo you registered:
|
|
169
169
|
per-repo audits plus checks on the fleet manifest itself and on the placement wall between
|
|
170
|
-
personal and
|
|
170
|
+
personal and guarded repos. See [the fleet manifest](#the-fleet-manifest-experimental) below.
|
|
171
171
|
|
|
172
172
|
## Commands
|
|
173
173
|
|
|
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
|
|
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. |
|
|
193
195
|
|
|
194
196
|
`--cwd <dir>` targets another directory. Read-only probing of any repo leaves **zero trace**
|
|
195
197
|
(`audit --no-ledger` writes nothing).
|
|
@@ -324,7 +326,7 @@ the generated pre-push now carries a `shellcheck` step. Three properties are del
|
|
|
324
326
|
### The content screen (`etymd screen`)
|
|
325
327
|
|
|
326
328
|
A separate question from "are the instructions true?": **does this repo carry text that must
|
|
327
|
-
never be published?** Absolute home paths, an
|
|
329
|
+
never be published?** Absolute home paths, an organisation's name, an internal hostname, an account
|
|
328
330
|
identifier — permanent the moment they are committed, because publishing exposes all history,
|
|
329
331
|
not the current tree.
|
|
330
332
|
|
|
@@ -358,7 +360,7 @@ inline marker would not survive into the artifact. Those live in `.etymd-screen-
|
|
|
358
360
|
repo root — one labeled line per field, so the pattern is never delimited:
|
|
359
361
|
|
|
360
362
|
```
|
|
361
|
-
pattern ^
|
|
363
|
+
pattern ^AcmeInc|BetaInc$
|
|
362
364
|
reason fixture proving the detector fires on either name
|
|
363
365
|
date 2026-08-15
|
|
364
366
|
author someone
|
|
@@ -418,10 +420,10 @@ steps:
|
|
|
418
420
|
`etymd fleet` extends the one objective across every repository you work in — your fleet of
|
|
419
421
|
**repositories**, not a fleet of agents. The manifest, `registry.json`, is itself an
|
|
420
422
|
agent-context file: claims about your fleet (what exists, where, under which **profile** — the
|
|
421
|
-
side of the wall an entry belongs to, `personal` or `
|
|
422
|
-
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
|
|
423
425
|
AGENTS.md does, and `etymd fleet` keeps it true. Decision record:
|
|
424
|
-
[`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
|
|
425
427
|
registry schema and the fleet `--json` schema are **experimental through 0.2.x**.
|
|
426
428
|
|
|
427
429
|
Two files beside each other — the split is the privacy model:
|
|
@@ -446,7 +448,7 @@ Two files beside each other — the split is the privacy model:
|
|
|
446
448
|
"kind": "docs",
|
|
447
449
|
"profile": "personal",
|
|
448
450
|
"path": "notes",
|
|
449
|
-
"trust": "private", // mandatory on every non-
|
|
451
|
+
"trust": "private", // mandatory on every non-guarded entry — see below
|
|
450
452
|
"staleAfterDays": 45, // per-entry freshness window
|
|
451
453
|
"contract": { "state": "STATUS.md" }, // native conventions register, never migrate
|
|
452
454
|
},
|
|
@@ -458,8 +460,14 @@ Two files beside each other — the split is the privacy model:
|
|
|
458
460
|
"upstream": "origin", // freshness measured on fork-authored commits only
|
|
459
461
|
"trust": "public-repo", // hygiene needles apply (see below)
|
|
460
462
|
},
|
|
461
|
-
//
|
|
462
|
-
{
|
|
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
|
+
},
|
|
463
471
|
],
|
|
464
472
|
}
|
|
465
473
|
```
|
|
@@ -468,23 +476,23 @@ Two files beside each other — the split is the privacy model:
|
|
|
468
476
|
|
|
469
477
|
```jsonc
|
|
470
478
|
{
|
|
471
|
-
"machineProfile": "
|
|
479
|
+
"machineProfile": "guarded", // which profile this machine resolves; "personal" resolves guarded entries disclosed-absent
|
|
472
480
|
"root": "~/projects", // optional per-machine root override
|
|
473
|
-
"dirs": { "c-one": "~/projects/real-
|
|
474
|
-
"labels": { "c-one": "real-
|
|
475
|
-
"
|
|
481
|
+
"dirs": { "c-one": "~/projects/real-guarded-dir" },
|
|
482
|
+
"labels": { "c-one": "real-guarded-dir" },
|
|
483
|
+
"guardedHosts": ["git.example-guarded.com"],
|
|
476
484
|
}
|
|
477
485
|
```
|
|
478
486
|
|
|
479
487
|
Two fields the scan can never derive, so the manifest must declare them:
|
|
480
488
|
|
|
481
|
-
- **`trust` — mandatory on every non-
|
|
489
|
+
- **`trust` — mandatory on every non-guarded entry** (`public-repo` | `public-bound` | `private`).
|
|
482
490
|
It is a _safety predicate_, not a label: it decides whether content screening applies, so an
|
|
483
491
|
absent value is reported (`fleet check` flags it), never read as a silent `private`.
|
|
484
492
|
`public-bound` means private today, plausibly public later — screened exactly as hard as
|
|
485
493
|
public, because publishing exposes _all_ history: the scrub has to precede the first commit,
|
|
486
494
|
not the visibility flip. A value outside the vocabulary is flagged rather than coerced, so a
|
|
487
|
-
typo can never quietly disable screening.
|
|
495
|
+
typo can never quietly disable screening. Guarded entries omit it — `profile: "guarded"` already
|
|
488
496
|
implies the answer.
|
|
489
497
|
- **`orientation.root` — optional, declared once.** Names the one entry every other entry is
|
|
490
498
|
guided by. Declared at the manifest level rather than repeated per entry, because a per-entry
|
|
@@ -499,10 +507,10 @@ CI) must pass every mandatory value as a flag — there is deliberately no defau
|
|
|
499
507
|
How the sweep behaves:
|
|
500
508
|
|
|
501
509
|
- **Read-only by default, everywhere.** The sweep never creates `.etymd` anywhere.
|
|
502
|
-
`--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
|
|
503
511
|
worktree is never written** — regardless of flags, even if a stray `.etymd` exists inside it
|
|
504
|
-
(pinned by test).
|
|
505
|
-
`<manifest-dir>/
|
|
512
|
+
(pinned by test). Guarded findings stay dismissible: their ledger lives at
|
|
513
|
+
`<manifest-dir>/guarded/<name>/.etymd/`, beside the manifest.
|
|
506
514
|
- **Deltas.** Each sweep compares against `last.fleet.json` stored beside the manifest and
|
|
507
515
|
renders `Δ +new −resolved` per project. Add `*.fleet.json` to the manifest repo's
|
|
508
516
|
`.gitignore` — sweep output is local-only and never tracked.
|
|
@@ -514,17 +522,64 @@ How the sweep behaves:
|
|
|
514
522
|
that instruction files are legitimately absent in that project — the sweep drops its
|
|
515
523
|
missing-contract finding instead of re-reporting a decision every run. Absence disclosed on
|
|
516
524
|
purpose is a state, not a gap.
|
|
517
|
-
- **Wall checks.**
|
|
518
|
-
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
|
|
519
527
|
repo, private **needles** — the identifiers the local file holds (labels, dir names, hosts) —
|
|
520
|
-
inside `trust: "public-repo"` entries, and
|
|
528
|
+
inside `trust: "public-repo"` entries, and guarded-host commit emails on personal entries — each
|
|
521
529
|
a risk finding; each check that cannot run is disclosed.
|
|
522
530
|
- **No global pointer.** `--manifest` is required unless the cwd holds `registry.json` — there
|
|
523
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.
|
|
524
545
|
|
|
525
546
|
Formatter interop for the `.etymd` state the sweep resolves: same rule as everywhere — see
|
|
526
547
|
[the files Etymd keeps](#the-files-etymd-keeps).
|
|
527
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
|
+
|
|
528
583
|
## Programmatic use
|
|
529
584
|
|
|
530
585
|
```ts
|
|
@@ -537,7 +592,7 @@ console.log(audit.findings) // one schema: claim · evidence · why · action ·
|
|
|
537
592
|
## The corpus (how this is validated)
|
|
538
593
|
|
|
539
594
|
Etymd is developed against a corpus of real sibling repos rather than fixtures alone —
|
|
540
|
-
[`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
|
|
541
596
|
repo proved the previous one wrong, and each skip class in the truth lens is a false positive that
|
|
542
597
|
a corpus run caught.
|
|
543
598
|
|
|
@@ -555,11 +610,12 @@ Without it — on a fresh clone or in CI — those suites skip cleanly and the r
|
|
|
555
610
|
|
|
556
611
|
## Decision record & roadmap
|
|
557
612
|
|
|
558
|
-
[`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
|
|
559
614
|
pivot** (the current identity; includes the state-of-the-field investigation it rests on) ·
|
|
560
615
|
**004 fleet mode** (the truth guard across your repositories) · 005 declared rules (design only) ·
|
|
561
|
-
**006 local gate provenance** (what the tool may read, and what it may rewrite)
|
|
562
|
-
|
|
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
|
|
563
619
|
trade-offs.
|
|
564
620
|
|
|
565
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,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { run } from './chunk-
|
|
3
|
-
import './chunk-
|
|
4
|
-
import './chunk-
|
|
2
|
+
export { run } from './chunk-FKDT4RBP.js';
|
|
3
|
+
import './chunk-UV3YIBDW.js';
|
|
4
|
+
import './chunk-Y4E6ERCM.js';
|
|
5
5
|
import './chunk-3E2IPCRY.js';
|
|
6
6
|
import './chunk-HI7NWPRA.js';
|
|
7
|
-
import './chunk-
|
|
8
|
-
import './chunk-
|
|
7
|
+
import './chunk-EJM47PIF.js';
|
|
8
|
+
import './chunk-UYCGP5BS.js';
|
|
9
9
|
import './chunk-P6ATKV2R.js';
|
|
10
10
|
import './chunk-YQZDYDAK.js';
|
|
11
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';
|