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 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 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.
426
448
 
427
- A corp entry in the tracked manifest is deliberately alias-only — no path, no remote — which is
428
- what keeps employer names out of a file that gets pushed. It also means the entry resolves to
429
- 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
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 employer directory names are
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 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
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 corp` was silently ignored, registering employer repos as personal.
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 corp`
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 employer host and its internal
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 corp host
477
- declared in the local manifest while the profile is not corp — the manifest already knows which
478
- 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
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-corp entries —
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`); corp entries are opaque aliases resolved only through the local file, and every resolution failure is disclosed, never silently skipped.
676
- - 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.
677
- - 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.
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/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
 
@@ -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 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.
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 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. |
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 employer's name, an internal hostname, an account
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 ^AcmeCorp|BetaCorp$
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 `corp`; the **wall** is the placement
422
- 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
423
425
  AGENTS.md does, and `etymd fleet` keeps it true. Decision record:
424
- [`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
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-corp entry — see below
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
- // Corp entries: opaque alias, private, NO path — real dirs live only in the local file.
462
- { "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
+ },
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": "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
472
480
  "root": "~/projects", // optional per-machine root override
473
- "dirs": { "c-one": "~/projects/real-corp-dir" },
474
- "labels": { "c-one": "real-corp-dir" },
475
- "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"],
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-corp entry** (`public-repo` | `public-bound` | `private`).
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. Corp entries omit it — `profile: "corp"` already
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 **corp
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). Corp findings stay dismissible: their ledger lives at
505
- `<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.
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.** Corp contract files found inside a corp worktree, unregistered checkouts
518
- 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
519
527
  repo, private **needles** — the identifiers the local file holds (labels, dir names, hosts) —
520
- 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
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/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
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/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
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
- [`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
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-Y6RZRED3.js';
4
- import { VERSION } from './chunk-DBWDMIYO.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,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- export { run } from './chunk-CEO3BXQB.js';
3
- import './chunk-2RNQ6OLV.js';
4
- import './chunk-IWG77WV3.js';
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-Y6RZRED3.js';
8
- import './chunk-DBWDMIYO.js';
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-Y6RZRED3.js';
4
- import './chunk-DBWDMIYO.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';