Skip to content

Commit d0e48e6

Browse files
committed
docs: record that manifests are located from the repository root only
A review of PR #28 found behaviour none of the documents stated: deps probes the repository root for known filenames, so a monorepo sub-package lock is not measured — and audit does not notice, because the version declaration added in that PR compares the root manifest and nothing else. Measured rather than read from the code: a fixture whose root package-lock.json pins root-a and whose packages/app/package-lock.json pins sub-b and sub-c reports only root-a from both `audit` and `deps --repo`, with an empty code list, while `euthyna deps --lockfile <sub>` reports sub-b and sub-c. That makes the remedy part of the contract: measure a sub-package by pointing --lockfile at it, or say it was not measured. Not a code change — reporting the root lock while silently ignoring the rest of the workspace would let one manifest stand in for a whole repository's dependency surface. Also corrected two stale statements in AGENTS.md: the suite count (235 → 246, measured today) and the description of what cli.test.js now pins.
1 parent cdece0f commit d0e48e6

5 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎.agents/skills/euthyna-en/references/dependency-audit.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ Available discovery sources (use whichever is installed — **audit the source b
4848
**The target directory must have one of** `package.json` / `pyproject.toml` / `requirements*.txt` / `go.mod`.
4949
If none exists, **say so and stop** — do not improvise an audit for an ecosystem the collector does not parse.
5050

51+
**Manifests are located from the repository root only.** A multi-package repository's per-package locks are not measured by
52+
`euthyna audit` or `euthyna deps --repo` (🧪 measured 2026-10-10: the root `package-lock.json` pins `root-a` while
53+
`packages/app/package-lock.json` pins `sub-b` and `sub-c`, and the output reports only `root-a`, **with no declaration at
54+
all**). Measuring a sub-package means running `euthyna deps --lockfile <path>` per package; if that is not done, say the
55+
sub-packages were not measured — do not treat the root manifest's facts as facts about the whole monorepo.
56+
5157
**Exactly resolvable**: `package-lock.json`, `npm-shrinkwrap.json`, `uv.lock`, `go.mod` for Go 1.17+.
5258

5359
**Not resolvable**: `yarn.lock`, `pnpm-lock.yaml`, `poetry.lock`.

‎.agents/skills/euthyna/references/dependency-audit.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
**目标目录必须有** `package.json` / `pyproject.toml` / `requirements*.txt` / `go.mod` 之一。
4949
没有就**直接说没有并停止**,不要为一个采集器不解析的生态即兴审计。
5050

51+
**清单只按仓库根查找。**多包仓库里子包各自的 lock 不会被 `euthyna audit` 或 `euthyna deps --repo` 测到
52+
(🧪 实测 2026-10-10:根 `package-lock.json` 锁 `root-a`,子包 `packages/app/package-lock.json` 锁 `sub-b`/`sub-c`,
53+
输出只有 `root-a`,**并且没有任何声明**)。要管子包就逐包跑 `euthyna deps --lockfile <路径>`;
54+
不跑就明说没测——别把根清单当成整个 monorepo 的事实。
55+
5156
**能精确解析版本的**:`package-lock.json`、`npm-shrinkwrap.json`、`uv.lock`、Go 1.17+ 的 `go.mod`。
5257

5358
**不能的**:`yarn.lock`、`pnpm-lock.yaml`、`poetry.lock`。

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,8 @@ is faithful — that is a reading job, and a mechanical stand-in would only manu
564564

565565
| Path | What it is |
566566
|---|---|
567-
| `bin/` + `src/` | **Fact producers** (zero-dependency Node CLI). `src/contract.js` is the contract in code; `src/facts/history.js`, `src/facts/coverage.js` and `src/facts/deps.js` are the three measurements (`history` has `--origins` to chase the first introducer and classifies by message + deleted-line + diff, **on by default since `euthyna audit`**; `coverage.js` reads c8/V8 JSON, coverage.py JSON (format 3) **and Go's text `-coverprofile`** (the latter needs `--source <module root>`: the profile carries no function names, so the producer parses top-level `func` ranges from the gofmt'd source; a file absent from the profile is notEvaluated, never "never executed"); `deps.js` reads package-lock.json / Cargo.lock / go.mod). `euthyna audit` (0.4.0, `src/cli.js`) runs history + the full dependency surface in one command — the single entry point the CI recipe and the skill both describe. `src/gate.js` is the six-gate report validator behind `euthyna gate <报告> [--verify] [--allow-exec]` — it parses the skill's 裁定格式, downgrades findings that lack evidence/reproduce or contradict their verdict, and executes nothing but `git` unless the caller consents to interpreters |
568-
| `test/` | 235 tests via `node --test` (234 pass + 1 deliberate skip on non-Windows, measured 2026-10-09), no third-party framework. **The history tests build real git repositories rather than mocking**; the coverage tests carry fixtures shaped like c8, coverage.py and Go profile output (`coverage.test.js` + `coverage-go.test.js`); the deps tests carry fixture lockfiles (npm v1/v2/v3, Cargo.lock, go.mod); the contract tests carry the shell-quoting and render-boundary regressions from PR #1; `git.test.js` carries the error-path quoting and stderr-sanitization regressions from issue #2; `gate.test.js` pins the six-gate validator (including that `--verify` does not run an interpreter command without `--allow-exec`); `cli.test.js` pins the `audit` command and its exit codes; `skill.test.js` pins the written discipline (the six gates, the three verdicts, the `euthyna gate` reference) so weakening the skill turns CI red; `plugin.test.js`, `claude-plugin.test.js` and `hermes-plugin.test.js` pin the three manifest surfaces; `manifest-version.test.js` pins `package.json` against the three plugin manifests (0.5.0 shipped them a release behind and nothing compared them); `i18n.test.js` pins language rendering and report parsing (zh/en); `skill-mirror.test.js` compares the two skill editions against each other — file set, heading sequence (ATX and Setext), fenced blocks, table shapes, and the `euthyna <subcommand>` / `--flag` identifier sets — so an unmirrored discipline change goes red instead of relying on memory, `docs-pair.test.js` holds `docs/` to the same rule (every document paired, and each pair equal on structure and identifiers — not on prose fidelity), and `markdown-structure.test.js` pins the shared scanner itself with seven crafted-document cases, because a legal Markdown form it cannot read makes any comparison pass on that form (neither guard compares word counts, which a translation legitimately needs room for) |
567+
| `bin/` + `src/` | **Fact producers** (zero-dependency Node CLI). `src/contract.js` is the contract in code; `src/facts/history.js`, `src/facts/coverage.js` and `src/facts/deps.js` are the three measurements (`history` has `--origins` to chase the first introducer and classifies by message + deleted-line + diff, **on by default since `euthyna audit`**; `coverage.js` reads c8/V8 JSON, coverage.py JSON (format 3) **and Go's text `-coverprofile`** (the latter needs `--source <module root>`: the profile carries no function names, so the producer parses top-level `func` ranges from the gofmt'd source; a file absent from the profile is notEvaluated, never "never executed"); `deps.js` reads package-lock.json / Cargo.lock / go.mod, **located from the repository root only** — a monorepo sub-package lock is measured only by `deps --lockfile <path>`, and `audit` does not notice its absence, which `docs/fact-contract.md` §6.3 records with the numbers from the probe). `audit` compares the manifest it measured with the one at `--head` and records `manifest-not-head` when the content differs or the file is absent there. `euthyna audit` (0.4.0, `src/cli.js`) runs history + the full dependency surface in one command — the single entry point the CI recipe and the skill both describe. `src/gate.js` is the six-gate report validator behind `euthyna gate <报告> [--verify] [--allow-exec]` — it parses the skill's 裁定格式, downgrades findings that lack evidence/reproduce or contradict their verdict, and executes nothing but `git` unless the caller consents to interpreters |
568+
| `test/` | 246 tests via `node --test` (245 pass + 1 deliberate skip on non-Windows, measured 2026-10-10), no third-party framework. **The history tests build real git repositories rather than mocking**; the coverage tests carry fixtures shaped like c8, coverage.py and Go profile output (`coverage.test.js` + `coverage-go.test.js`); the deps tests carry fixture lockfiles (npm v1/v2/v3, Cargo.lock, go.mod); the contract tests carry the shell-quoting and render-boundary regressions from PR #1; `git.test.js` carries the error-path quoting and stderr-sanitization regressions from issue #2; `gate.test.js` pins the six-gate validator (including that `--verify` does not run an interpreter command without `--allow-exec`); `cli.test.js` pins the `audit` command, its exit codes, and the manifest-version declaration — content against `--head`, so a PR merge commit whose lockfile is byte-identical to `--head`'s declares nothing while a gitignored lockfile declares itself; the decision itself is an exported pure function so the fail-closed branch (`manifest-unverified`) is reachable from a test; `skill.test.js` pins the written discipline (the six gates, the three verdicts, the `euthyna gate` reference) so weakening the skill turns CI red; `plugin.test.js`, `claude-plugin.test.js` and `hermes-plugin.test.js` pin the three manifest surfaces; `manifest-version.test.js` pins `package.json` against the three plugin manifests (0.5.0 shipped them a release behind and nothing compared them); `i18n.test.js` pins language rendering and report parsing (zh/en); `skill-mirror.test.js` compares the two skill editions against each other — file set, heading sequence (ATX and Setext), fenced blocks, table shapes, and the `euthyna <subcommand>` / `--flag` identifier sets — so an unmirrored discipline change goes red instead of relying on memory, `docs-pair.test.js` holds `docs/` to the same rule (every document paired, and each pair equal on structure and identifiers — not on prose fidelity), and `markdown-structure.test.js` pins the shared scanner itself with seven crafted-document cases, because a legal Markdown form it cannot read makes any comparison pass on that form (neither guard compares word counts, which a translation legitimately needs room for) |
569569
| `.agents/skills/euthyna/` | **The skill.** Doubles as source and as a project skill root (rank 200), so it is live in this workspace without a restart |
570570
| `bench/` | **The recall benchmark.** `exploits.js` establishes ground truth by execution (18 cases, 8 near-neighbour pairs); `prepare-blind.js` produces answer-free copies with per-round shuffled ids; `adjudicate.js` closes the loop end to end (blind tree → one adjudicator process per case → `collect-verdicts.js` machine validation → `score.js`), with a deterministic `golden` mode (ground truth written into reports — plumbing self-check only, never a real round) for CI; `collect-verdicts.js` machine-validates reports and extracts verdicts without the orchestrator reading report bodies; `score.js` computes the confusion matrix and the pair view; `perf.js` is the `history` scaling baseline. The verified-platform matrix (bsdtar vs GNU tar, p6 skip) lives in `bench/README.md`. Results: `bench/RESULTS.md` (round 1), `bench/RESULTS-round2.md` (round 2: three independent runs per case, zero flips), `bench/RESULTS-round3.md` (round 3: 18 cases, 54 adjudications, two fixture defects caught by adjudicators), `bench/RESULTS-round4.md` (round 4: 54/54, p6b v3 confirmed by first blind adjudication, cross-model run executed — zero flips across two model families); protocols in `bench/README.md`, design pre-registrations in `bench/DESIGN-round3.md` and `bench/DESIGN-round4.md` |
571571
| `docs/positioning.md` + `-zh` | Competitive analysis across the DSH catalog, including three claims that were tested and refuted |

‎docs/fact-contract-zh.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ tsserver × c8 的**行号对齐未做集成验证**(fixture 里的调用点
357357
自动检测到的其他格式(`pnpm-lock.yaml`、`yarn.lock`、`poetry.lock` 等)一律报
358358
**notEvaluated** 并点名文件名——绝不猜。
359359

360+
**清单只按仓库根查找。**🧪 实测 2026-10-10:一个根 `package-lock.json` 锁 `root-a`、子包
361+
`packages/app/package-lock.json` 锁 `sub-b`/`sub-c` 的仓库里,`audit` 与 `deps --repo` 都只报出 `root-a`,
362+
且**没有任何声明**(版本一致性那条只比较根清单)。子包的锁定版本要么逐包跑
363+
`euthyna deps --lockfile <路径>`(实测报出 `sub-b`、`sub-c`),要么就明说没测——**不要把根清单的结果
364+
当作整个 monorepo 的事实。**
365+
360366
**拒绝说什么和说什么同样重要。** 只报版本,其他一概不报:没有"有漏洞"、没有严重性、没有 CVE 映射。
361367
版本 → CVE 的映射归判定层(`assertNoVerdictFields` 在机制上强制这一点)。
362368
一个报出"版本 4.17.21 受 CVE-XXXX 影响"的产出器等于替判定层下了结论,而那个结论不可复算。

‎docs/fact-contract.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,13 @@ record of what is actually resolved.
358358
Anything else detected at auto-detect (`pnpm-lock.yaml`, `yarn.lock`, `poetry.lock`, …) is reported as
359359
*not evaluated* with the filenames named — never guessed at.
360360

361+
**Manifests are located from the repository root only.** 🧪 Measured 2026-10-10 in a repository whose root
362+
`package-lock.json` pins `root-a` while a sub-package's `packages/app/package-lock.json` pins `sub-b` and
363+
`sub-c`: both `audit` and `deps --repo` reported only `root-a`, with **no declaration at all** (the
364+
manifest-version check compares the root manifest and nothing else). A sub-package's pinned versions require
365+
running `euthyna deps --lockfile <path>` per package (measured: it reports `sub-b` and `sub-c`), or an explicit
366+
statement that they were not measured — **do not read the root manifest's facts as facts about the whole monorepo.**
367+
361368
**What it refuses to say is as important as what it says.** It emits versions, and nothing else:
362369
no "vulnerable", no severity, no CVE mapping. The version-to-CVE mapping is the adjudication layer's job
363370
(the `assertNoVerdictFields` guard enforces this mechanically). A producer that emitted "version 4.17.21 is

0 commit comments

Comments
 (0)