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 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 corp` now records the alias-to-directory mapping too, not just the entry.
447
+ - e265713: `fleet add --profile guarded` now records the alias-to-directory mapping too, not just the entry.
412
448
 
413
- A corp entry in the tracked manifest is deliberately alias-only — no path, no remote — which is
414
- what keeps employer names out of a file that gets pushed. It also means the entry resolves to
415
- nothing on its own. Registering wrote only that half, so every corp registration ended as a
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 employer directory names are
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 corp-host guard it keeps working on a machine that has no local manifest to read corp hosts
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 corp` was silently ignored, registering employer repos as personal.
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 corp`
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 employer host and its internal
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 corp host
463
- declared in the local manifest while the profile is not corp — the manifest already knows which
464
- hosts are the employer's and the remote has just been read, so the tool has every fact needed to
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-corp entries —
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`); corp entries are opaque aliases resolved only through the local file, and every resolution failure is disclosed, never silently skipped.
662
- - Persistence invariants, pinned by tests: the sweep never creates `.etymd` anywhere; `--persist-ledgers` only persists into personal repos that already opted in; corp worktrees take zero writes under every flag combination — corp findings persist (and stay dismissible) at `<manifestDir>/corp/<name>/.etymd/`; zero corp-resolved content under the manifest repo's tracked paths.
663
- - Fleet-scope wall findings (lens id `fleet-manifest`): corp contract files inside a corp worktree, unregistered corp-remote checkouts under the fleet root, tracked `/Users/` paths in the manifest's own repo, private needles inside `trust: "public-repo"` entries, corp-host commit emails on personal entries.
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/triartleet/etymd/main/media/etymd-logo.png" width="520" alt="Etymd — a papyrus of written instructions, each line checked against the repository it describes">
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/triartleet/etymd/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/triartleet/etymd/ci.yml?branch=main&label=CI" alt="CI"></a>
8
- <a href="https://github.com/triartleet/etymd/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license"></a>
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 checks every claim in them against your actual project.
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 employer repos. See [the fleet manifest](#the-fleet-manifest-experimental) below.
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 approve` | Refresh the committed baseline non-interactively after intentional structural changes. |
175
- | `etymd ledger` | The findings memory: every tracked finding with status and history. |
176
- | `etymd dismiss` | `dismiss <id> --reason <text>` — a dismissed finding never resurfaces without regressing. |
177
- | `etymd accept` | `accept <id>` — record a finding as accepted reality; visible in the ledger, out of the report. |
178
- | `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`. |
179
- | `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. |
180
- | `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`. |
181
- | `etymd fleet dismiss` / `accept` | `<name> <id>` — resolve a project's finding from any cwd; corp findings persist beside the manifest, never in the corp worktree. |
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 employer's name, an internal hostname, an account
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 ^AcmeCorp|BetaCorp$
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 `corp`; the **wall** is the placement
411
- boundary between personal and employer content that the sweep polices). It rots like any
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/triartleet/etymd/blob/main/docs/decisions/004-fleet-truth-guard.md). Both the
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-corp entry — see below
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
- // Corp entries: opaque alias, private, NO path — real dirs live only in the local file.
451
- { "name": "c-one", "kind": "repo", "profile": "corp", "private": true, "staleAfterDays": 45 },
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": "corp", // which profile this machine resolves; "personal" resolves corp entries disclosed-absent
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-corp-dir" },
463
- "labels": { "c-one": "real-corp-dir" },
464
- "corpHosts": ["git.example-corp.com"],
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-corp entry** (`public-repo` | `public-bound` | `private`).
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. Corp entries omit it — `profile: "corp"` already
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 **corp
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). Corp findings stay dismissible: their ledger lives at
494
- `<manifest-dir>/corp/<name>/.etymd/`, beside the manifest.
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.** Corp contract files found inside a corp worktree, unregistered checkouts
507
- under the fleet root whose remotes match `corpHosts`, tracked `/Users/` paths in the manifest
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 corp-host commit emails on personal entries — each
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/triartleet/etymd/blob/main/sources.json) lists them by shape. Every heuristic here exists because a real
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/triartleet/etymd/tree/main/docs/decisions) — 001 founding · 002 foundation re-lock · **003 the truth-guard
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
- [`ROADMAP.md`](https://github.com/triartleet/etymd/blob/main/ROADMAP.md) — what's now / next / later, and the accepted heuristic
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-HWC6PKJM.js';
4
- import { VERSION } from './chunk-623W6KVC.js';
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-PAMWS665.js';
3
- import './chunk-LT7JMVV3.js';
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-HWC6PKJM.js';
7
- import './chunk-623W6KVC.js';
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-HWC6PKJM.js';
4
- import './chunk-623W6KVC.js';
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';