daftari 1.30.0 → 1.31.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 +106 -0
- package/README.md +139 -4
- package/dist/access/rbac.d.ts +1 -0
- package/dist/access/rbac.d.ts.map +1 -1
- package/dist/access/rbac.js +7 -0
- package/dist/access/rbac.js.map +1 -1
- package/dist/audit/checks/broken_refs.d.ts +1 -1
- package/dist/audit/checks/broken_refs.d.ts.map +1 -1
- package/dist/audit/checks/broken_refs.js +77 -22
- package/dist/audit/checks/broken_refs.js.map +1 -1
- package/dist/audit/collect.d.ts +1 -0
- package/dist/audit/collect.d.ts.map +1 -1
- package/dist/audit/collect.js +22 -2
- package/dist/audit/collect.js.map +1 -1
- package/dist/audit/index.d.ts.map +1 -1
- package/dist/audit/index.js +9 -3
- package/dist/audit/index.js.map +1 -1
- package/dist/audit/links.d.ts.map +1 -1
- package/dist/audit/links.js +9 -3
- package/dist/audit/links.js.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/report.js +2 -0
- package/dist/audit/report.js.map +1 -1
- package/dist/audit/types.d.ts +4 -1
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js.map +1 -1
- package/dist/backfill/derive.d.ts.map +1 -1
- package/dist/backfill/derive.js +1 -0
- package/dist/backfill/derive.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/court/precedent.d.ts.map +1 -1
- package/dist/court/precedent.js +6 -1
- package/dist/court/precedent.js.map +1 -1
- package/dist/curation/consumes.d.ts +24 -0
- package/dist/curation/consumes.d.ts.map +1 -0
- package/dist/curation/consumes.js +122 -0
- package/dist/curation/consumes.js.map +1 -0
- package/dist/curation/edge-staleness.d.ts +47 -0
- package/dist/curation/edge-staleness.d.ts.map +1 -0
- package/dist/curation/edge-staleness.js +258 -0
- package/dist/curation/edge-staleness.js.map +1 -0
- package/dist/curation/edges.d.ts.map +1 -1
- package/dist/curation/edges.js +164 -10
- package/dist/curation/edges.js.map +1 -1
- package/dist/curation/lint.d.ts +4 -1
- package/dist/curation/lint.d.ts.map +1 -1
- package/dist/curation/lint.js +61 -6
- package/dist/curation/lint.js.map +1 -1
- package/dist/curation/provenance.d.ts +2 -0
- package/dist/curation/provenance.d.ts.map +1 -1
- package/dist/curation/provenance.js +2 -0
- package/dist/curation/provenance.js.map +1 -1
- package/dist/curation/read-log.d.ts +19 -0
- package/dist/curation/read-log.d.ts.map +1 -0
- package/dist/curation/read-log.js +120 -0
- package/dist/curation/read-log.js.map +1 -0
- package/dist/curation/review-throughput.d.ts +24 -0
- package/dist/curation/review-throughput.d.ts.map +1 -0
- package/dist/curation/review-throughput.js +102 -0
- package/dist/curation/review-throughput.js.map +1 -0
- package/dist/curation/staged-actions.d.ts +11 -1
- package/dist/curation/staged-actions.d.ts.map +1 -1
- package/dist/curation/staged-actions.js +102 -24
- package/dist/curation/staged-actions.js.map +1 -1
- package/dist/curation/structural.d.ts +16 -0
- package/dist/curation/structural.d.ts.map +1 -0
- package/dist/curation/structural.js +64 -0
- package/dist/curation/structural.js.map +1 -0
- package/dist/curation/tension-blast.d.ts +4 -0
- package/dist/curation/tension-blast.d.ts.map +1 -1
- package/dist/curation/tension-blast.js +9 -0
- package/dist/curation/tension-blast.js.map +1 -1
- package/dist/curation/tension.d.ts +5 -2
- package/dist/curation/tension.d.ts.map +1 -1
- package/dist/curation/tension.js +68 -25
- package/dist/curation/tension.js.map +1 -1
- package/dist/curation/tier0.d.ts +27 -0
- package/dist/curation/tier0.d.ts.map +1 -0
- package/dist/curation/tier0.js +196 -0
- package/dist/curation/tier0.js.map +1 -0
- package/dist/curation/tier1.d.ts +30 -0
- package/dist/curation/tier1.d.ts.map +1 -0
- package/dist/curation/tier1.js +142 -0
- package/dist/curation/tier1.js.map +1 -0
- package/dist/curation/tier2.d.ts +43 -0
- package/dist/curation/tier2.d.ts.map +1 -0
- package/dist/curation/tier2.js +182 -0
- package/dist/curation/tier2.js.map +1 -0
- package/dist/curation/vault-docs.d.ts +5 -1
- package/dist/curation/vault-docs.d.ts.map +1 -1
- package/dist/curation/vault-docs.js +14 -0
- package/dist/curation/vault-docs.js.map +1 -1
- package/dist/eval/generate.js +22 -5
- package/dist/eval/generate.js.map +1 -1
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +173 -25
- package/dist/eval/index.js.map +1 -1
- package/dist/eval/llm.js +1 -1
- package/dist/eval/llm.js.map +1 -1
- package/dist/eval/prune.d.ts +24 -0
- package/dist/eval/prune.d.ts.map +1 -0
- package/dist/eval/prune.js +83 -0
- package/dist/eval/prune.js.map +1 -0
- package/dist/eval/storage.d.ts +3 -0
- package/dist/eval/storage.d.ts.map +1 -1
- package/dist/eval/storage.js +14 -0
- package/dist/eval/storage.js.map +1 -1
- package/dist/frontmatter/schema.d.ts.map +1 -1
- package/dist/frontmatter/schema.js +19 -1
- package/dist/frontmatter/schema.js.map +1 -1
- package/dist/frontmatter/types.d.ts +4 -1
- package/dist/frontmatter/types.d.ts.map +1 -1
- package/dist/frontmatter/types.js +8 -0
- package/dist/frontmatter/types.js.map +1 -1
- package/dist/import/index.d.ts.map +1 -1
- package/dist/import/index.js +44 -9
- package/dist/import/index.js.map +1 -1
- package/dist/import/langgraph-store.d.ts +53 -0
- package/dist/import/langgraph-store.d.ts.map +1 -0
- package/dist/import/langgraph-store.js +292 -0
- package/dist/import/langgraph-store.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -18
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/lock.d.ts +9 -1
- package/dist/lifecycle/lock.d.ts.map +1 -1
- package/dist/lifecycle/lock.js +34 -9
- package/dist/lifecycle/lock.js.map +1 -1
- package/dist/okf/export.d.ts +12 -0
- package/dist/okf/export.d.ts.map +1 -0
- package/dist/okf/export.js +115 -0
- package/dist/okf/export.js.map +1 -0
- package/dist/okf/import.d.ts +24 -0
- package/dist/okf/import.d.ts.map +1 -0
- package/dist/okf/import.js +107 -0
- package/dist/okf/import.js.map +1 -0
- package/dist/okf/index.d.ts +2 -0
- package/dist/okf/index.d.ts.map +1 -0
- package/dist/okf/index.js +147 -0
- package/dist/okf/index.js.map +1 -0
- package/dist/okf/map.d.ts +16 -0
- package/dist/okf/map.d.ts.map +1 -0
- package/dist/okf/map.js +149 -0
- package/dist/okf/map.js.map +1 -0
- package/dist/okf/types.d.ts +9 -0
- package/dist/okf/types.d.ts.map +1 -0
- package/dist/okf/types.js +48 -0
- package/dist/okf/types.js.map +1 -0
- package/dist/search/coverage.d.ts.map +1 -1
- package/dist/search/coverage.js +1 -1
- package/dist/search/coverage.js.map +1 -1
- package/dist/search/hybrid.d.ts +19 -0
- package/dist/search/hybrid.d.ts.map +1 -1
- package/dist/search/hybrid.js +58 -10
- package/dist/search/hybrid.js.map +1 -1
- package/dist/search/reindex.d.ts +1 -0
- package/dist/search/reindex.d.ts.map +1 -1
- package/dist/search/reindex.js +41 -11
- package/dist/search/reindex.js.map +1 -1
- package/dist/search/watcher.d.ts +2 -0
- package/dist/search/watcher.d.ts.map +1 -1
- package/dist/search/watcher.js +21 -10
- package/dist/search/watcher.js.map +1 -1
- package/dist/serve/index.d.ts +36 -0
- package/dist/serve/index.d.ts.map +1 -0
- package/dist/serve/index.js +507 -0
- package/dist/serve/index.js.map +1 -0
- package/dist/server.d.ts +10 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +89 -14
- package/dist/server.js.map +1 -1
- package/dist/sleep/index.d.ts +2 -0
- package/dist/sleep/index.d.ts.map +1 -1
- package/dist/sleep/index.js +212 -21
- package/dist/sleep/index.js.map +1 -1
- package/dist/sleep/report.d.ts +3 -0
- package/dist/sleep/report.d.ts.map +1 -1
- package/dist/sleep/report.js +52 -0
- package/dist/sleep/report.js.map +1 -1
- package/dist/sleep/tension-scan-state.d.ts +19 -0
- package/dist/sleep/tension-scan-state.d.ts.map +1 -0
- package/dist/sleep/tension-scan-state.js +0 -0
- package/dist/sleep/tension-scan-state.js.map +1 -0
- package/dist/sleep/tension-scan.d.ts +44 -0
- package/dist/sleep/tension-scan.d.ts.map +1 -0
- package/dist/sleep/tension-scan.js +0 -0
- package/dist/sleep/tension-scan.js.map +1 -0
- package/dist/storage/backend.d.ts +12 -0
- package/dist/storage/backend.d.ts.map +1 -0
- package/dist/storage/backend.js +231 -0
- package/dist/storage/backend.js.map +1 -0
- package/dist/storage/backends/fs.d.ts +4 -0
- package/dist/storage/backends/fs.d.ts.map +1 -0
- package/dist/storage/backends/fs.js +99 -0
- package/dist/storage/backends/fs.js.map +1 -0
- package/dist/storage/fs-walk.d.ts +5 -0
- package/dist/storage/fs-walk.d.ts.map +1 -0
- package/dist/storage/fs-walk.js +28 -0
- package/dist/storage/fs-walk.js.map +1 -0
- package/dist/storage/index-db.d.ts +16 -0
- package/dist/storage/index-db.d.ts.map +1 -1
- package/dist/storage/index-db.js +94 -5
- package/dist/storage/index-db.js.map +1 -1
- package/dist/storage/local.d.ts +1 -0
- package/dist/storage/local.d.ts.map +1 -1
- package/dist/storage/local.js +12 -0
- package/dist/storage/local.js.map +1 -1
- package/dist/storage/sync.d.ts +25 -0
- package/dist/storage/sync.d.ts.map +1 -0
- package/dist/storage/sync.js +308 -0
- package/dist/storage/sync.js.map +1 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +136 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/themes/clustering.d.ts +6 -11
- package/dist/themes/clustering.d.ts.map +1 -1
- package/dist/themes/clustering.js +76 -84
- package/dist/themes/clustering.js.map +1 -1
- package/dist/tools/consumes.d.ts +14 -0
- package/dist/tools/consumes.d.ts.map +1 -0
- package/dist/tools/consumes.js +99 -0
- package/dist/tools/consumes.js.map +1 -0
- package/dist/tools/curation.d.ts +2 -0
- package/dist/tools/curation.d.ts.map +1 -1
- package/dist/tools/curation.js +53 -15
- package/dist/tools/curation.js.map +1 -1
- package/dist/tools/edge-staleness.d.ts +28 -0
- package/dist/tools/edge-staleness.d.ts.map +1 -0
- package/dist/tools/edge-staleness.js +205 -0
- package/dist/tools/edge-staleness.js.map +1 -0
- package/dist/tools/edges.d.ts.map +1 -1
- package/dist/tools/edges.js +27 -21
- package/dist/tools/edges.js.map +1 -1
- package/dist/tools/read.d.ts +15 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +142 -29
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/receipt.d.ts.map +1 -1
- package/dist/tools/receipt.js +16 -7
- package/dist/tools/receipt.js.map +1 -1
- package/dist/tools/search.d.ts +1 -0
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +147 -5
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/staged-actions.d.ts +3 -0
- package/dist/tools/staged-actions.d.ts.map +1 -1
- package/dist/tools/staged-actions.js +227 -22
- package/dist/tools/staged-actions.js.map +1 -1
- package/dist/tools/themes.d.ts +8 -0
- package/dist/tools/themes.d.ts.map +1 -1
- package/dist/tools/themes.js +208 -122
- package/dist/tools/themes.js.map +1 -1
- package/dist/tools/tier1.d.ts +14 -0
- package/dist/tools/tier1.d.ts.map +1 -0
- package/dist/tools/tier1.js +169 -0
- package/dist/tools/tier1.js.map +1 -0
- package/dist/tools/tier2.d.ts +27 -0
- package/dist/tools/tier2.d.ts.map +1 -0
- package/dist/tools/tier2.js +344 -0
- package/dist/tools/tier2.js.map +1 -0
- package/dist/tools/write.d.ts +9 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +492 -6
- package/dist/tools/write.js.map +1 -1
- package/dist/utils/config.d.ts +52 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +372 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/run-id.d.ts +3 -0
- package/dist/utils/run-id.d.ts.map +1 -0
- package/dist/utils/run-id.js +19 -0
- package/dist/utils/run-id.js.map +1 -0
- package/dist/utils/vault-gitignore.d.ts +1 -1
- package/dist/utils/vault-gitignore.d.ts.map +1 -1
- package/dist/utils/vault-gitignore.js +4 -1
- package/dist/utils/vault-gitignore.js.map +1 -1
- package/package.json +21 -1
- package/templates/config.yaml +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,111 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.31.0] - 2026-07-22
|
|
11
|
+
|
|
12
|
+
The self-hosted deployment release: one always-on `daftari serve` instance
|
|
13
|
+
over Streamable HTTP with per-session identity (static tokens and/or OAuth
|
|
14
|
+
2.1), plus durable object-storage backing (S3/Azure/GCS-interop) behind the
|
|
15
|
+
canonical local git working copy. Design record:
|
|
16
|
+
`docs/superpowers/specs/2026-07-20-self-hosted-server-mode-design.md`.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`daftari serve` — server mode over Streamable HTTP (#5).** One always-on
|
|
21
|
+
instance, many MCP clients, per-session RBAC identity resolved at session
|
|
22
|
+
open (zero tool changes — every existence-disclosure invariant applies per
|
|
23
|
+
connection). Static bearer tokens declared in config with values in env
|
|
24
|
+
vars. Fail-loud posture throughout: a non-loopback bind refuses without
|
|
25
|
+
auth AND an explicit `server.transport_security: external` declaration
|
|
26
|
+
(daftari never terminates TLS); with auth configured a bad credential is a
|
|
27
|
+
401 at session open, never a guest downgrade; loopback binds carry a
|
|
28
|
+
DNS-rebinding guard (Host/Origin validated before routing). The process
|
|
29
|
+
lock learns modes: a stray stdio invocation refuses against a live server,
|
|
30
|
+
a new serve refuses against any live holder, and deliberate replacement is
|
|
31
|
+
`daftari serve --takeover` (stdio-vs-stdio keeps its classic takeover;
|
|
32
|
+
stale locks are still overwritten silently).
|
|
33
|
+
- **OAuth 2.1 resource-server auth for serve (#7).** Bearer JWTs verified
|
|
34
|
+
against your IdP's JWKS (issuer + audience + signature + expiry via
|
|
35
|
+
`jose`), with the subject claim mapped through a config-declared subjects
|
|
36
|
+
table. A valid token with an unmapped subject is 403 — authenticated, not
|
|
37
|
+
authorized — never a guest, never a default role. Composes with static
|
|
38
|
+
tokens (agents on tokens, humans through the IdP). Hardened: https-only
|
|
39
|
+
issuer/JWKS URLs (loopback http for test IdPs only), constant-time static
|
|
40
|
+
token matching, `Object.prototype`-collision-proof subject lookup.
|
|
41
|
+
- **Pluggable storage backends as sync targets (#6).** The local git working
|
|
42
|
+
copy stays canonical; a backend is a dumb `get/put/list/delete` target.
|
|
43
|
+
Backends: `fs` (local/mounted directory), `s3` (S3, MinIO, R2, GCS via its
|
|
44
|
+
S3-interop endpoint), `azure` (Blob/ADLS Gen2) — cloud SDKs are optional
|
|
45
|
+
peer dependencies loaded on demand, credentials come only from the SDKs'
|
|
46
|
+
environment chains, custom endpoints must be https. `daftari sync` pushes
|
|
47
|
+
incrementally against a remote hash manifest (tree + `.git` + durable
|
|
48
|
+
`.daftari` journals; the rebuildable index/locks never sync) and
|
|
49
|
+
`daftari sync --restore` rebuilds a vault into an empty directory with
|
|
50
|
+
per-object hash verification and a reindex. `daftari serve` can push on a
|
|
51
|
+
cadence (`storage.sync_interval_minutes`). Security posture: `.git/config`
|
|
52
|
+
and `.git/hooks` are never synced or restored (git executes what they
|
|
53
|
+
declare — a backup channel must not deliver code), and restore re-enforces
|
|
54
|
+
every exclusion against the untrusted manifest.
|
|
55
|
+
- **Agent-as-judge rerank pool on `vault_search` (#3).** Opt-in
|
|
56
|
+
`rerank_candidates`: the server prepares the fused candidate pool and the
|
|
57
|
+
protocol; the calling agent is the judge.
|
|
58
|
+
- **Per-document theme distributions (#58).** True multi-theme membership via
|
|
59
|
+
chunk-level clustering.
|
|
60
|
+
- **Configurable tool-exposure tiers (#103, #104).** `tools.tier`
|
|
61
|
+
(`core`/`standard`/`full`) plus `include`/`exclude` lists in config.
|
|
62
|
+
- **Resumable reindex (#54).** Per-batch embedding commits so an interrupted
|
|
63
|
+
reindex resumes instead of restarting.
|
|
64
|
+
- **`daftari eval prune` (#100)** — `results/` and `scores/` housekeeping —
|
|
65
|
+
and eval v1 polish (#102): honest exit codes, skipped-run reporting, resume
|
|
66
|
+
validation, tier-aware triviality, intra-call dedupe, `--max-nodes`.
|
|
67
|
+
- **Advisory domain-boundary enforcement (#4)** and inline structural decay
|
|
68
|
+
checks (orphans, deprecated-still-linked) in the curation engine.
|
|
69
|
+
- **Advisory supersede nudge** when overwriting an existing doc, and a YAML
|
|
70
|
+
config error hint for comment lines that lost their `#`.
|
|
71
|
+
- **`daftari okf export|import` — Open Knowledge Format bridge.** OKF is Google
|
|
72
|
+
Cloud's vendor-neutral spec (v0.1) for the LLM-wiki pattern — a directory of
|
|
73
|
+
markdown files with YAML frontmatter — which is exactly Daftari's storage
|
|
74
|
+
model. `okf export` renders a vault as a portable OKF bundle: each doc becomes
|
|
75
|
+
an OKF concept doc carrying the core `type` / `title` / `description` /
|
|
76
|
+
`resource` / `tags` / `timestamp` fields plus a verbatim `daftari` sidecar for
|
|
77
|
+
lossless round-trip, alongside generated `index.md` and `log.md` reserved
|
|
78
|
+
files; the source vault is never mutated. `okf import` adopts an OKF bundle
|
|
79
|
+
into a vault — a bundle from `okf export` round-trips exactly via its sidecar,
|
|
80
|
+
while a foreign bundle is mapped conservatively (docs land as `draft` in the
|
|
81
|
+
`accumulation` domain, the source OKF `type` preserved in `okf_type`); writes
|
|
82
|
+
auto-commit and the index is rebuilt. `--dry-run` previews the import plan.
|
|
83
|
+
|
|
84
|
+
- **The compilation pipeline (#232–#236, #141, #217).** Edge provenance
|
|
85
|
+
classes with type-directed change dispatch (tier 1), structure-first tier-0
|
|
86
|
+
lint checks with a ratify gate, a semantic-review queue with typed verdicts
|
|
87
|
+
(tier 2), the compiled `consumes` dependency graph (run-correlated reads ×
|
|
88
|
+
writes, #233), edge staleness classes and the broken-read rate (#234),
|
|
89
|
+
proposal deltas (write action type, `run_id` provenance, inter-proposal
|
|
90
|
+
tension, a propose-only role, #235), write-protection tiers
|
|
91
|
+
(source/compiled/manual with a demote-then-write escape hatch, #141), a
|
|
92
|
+
review-throughput lint aggregate, and edge-graph existence disclosure
|
|
93
|
+
(blast B′, edges/lint omission) extending the 2026-07-14 RBAC spec (#217).
|
|
94
|
+
- **`sleep` tension-scan dream-type (#228)** closing the day-0 detection gap,
|
|
95
|
+
a langgraph-store adapter with the dedup-is-not-epistemics demo (#227),
|
|
96
|
+
the daftari.dev landing page (#226), and MCP Registry metadata (#222).
|
|
97
|
+
- **Dev maturity (#238–#242):** e2e gate on the built server, tag-driven npm
|
|
98
|
+
release via OIDC trusted publishing, automated code + security review on
|
|
99
|
+
PRs, an `@claude` mention responder, and a nightly maintenance loop.
|
|
100
|
+
|
|
101
|
+
### Fixed
|
|
102
|
+
|
|
103
|
+
- Canonical-path tension/provenance gates in `vault_status`, `vault_receipt`,
|
|
104
|
+
and precedent lookups (#216), and tier-1 anchors that are unreadable leak
|
|
105
|
+
neither provenance metadata nor existence (#252).
|
|
106
|
+
- `daftari audit` no longer flags on-disk assets and unaudited siblings as
|
|
107
|
+
missing (#255); eval exit codes and resume validation honesty (#102).
|
|
108
|
+
|
|
109
|
+
### Performance
|
|
110
|
+
|
|
111
|
+
- SQL-authoritative edge reads with write-through and stat-marker self-heal
|
|
112
|
+
(#236), FTS5 `snippet()` for lexical excerpts on the chunk path, and the
|
|
113
|
+
file watcher filters non-markdown events at watch time (#107).
|
|
114
|
+
|
|
10
115
|
## [1.30.0] - 2026-07-13
|
|
11
116
|
|
|
12
117
|
### Added
|
|
@@ -1377,6 +1482,7 @@ vault to AI agents, exposing 13 tools over stdio.
|
|
|
1377
1482
|
example documents, git history, search index); `daftari --vault` serves it.
|
|
1378
1483
|
- 160 tests covering all 13 tools and their supporting modules.
|
|
1379
1484
|
|
|
1485
|
+
[1.31.0]: https://github.com/mavaali/daftari/releases/tag/v1.31.0
|
|
1380
1486
|
[1.5.1]: https://github.com/mavaali/daftari/releases/tag/v1.5.1
|
|
1381
1487
|
[1.4.0]: https://github.com/mavaali/daftari/releases/tag/v1.4.0
|
|
1382
1488
|
[1.1.1]: https://github.com/mavaali/daftari/releases/tag/v1.1.1
|
package/README.md
CHANGED
|
@@ -116,6 +116,31 @@ The curation engine is advisory: `vault_lint` reports problems and
|
|
|
116
116
|
`vault_tension_log` records contradictions. Neither auto-fixes anything. Every
|
|
117
117
|
change is a deliberate, attributable act.
|
|
118
118
|
|
|
119
|
+
### Tool tiers
|
|
120
|
+
|
|
121
|
+
Every advertised tool costs the agent context tokens and a decision branch.
|
|
122
|
+
The `tools` block in `.daftari/config.yaml` picks how much of the registry
|
|
123
|
+
`tools/list` advertises:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
tools:
|
|
127
|
+
tier: standard # core | standard | full (default: full)
|
|
128
|
+
include: # add tools the tier omits
|
|
129
|
+
- vault_tension_log
|
|
130
|
+
exclude: # remove tools the tier includes — exclude wins
|
|
131
|
+
- vault_status
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`core` is the search-before-derive loop (`vault_search`, `vault_read`,
|
|
135
|
+
`vault_write`, `vault_index`, `vault_lint`, `vault_status`); `standard` adds
|
|
136
|
+
the full document lifecycle (append/promote/deprecate/supersede/merge,
|
|
137
|
+
confidence and tier setters, propose/ratify) plus diagnostics; `full` is
|
|
138
|
+
everything. `--tools <tier>` overrides the tier for one invocation. Filtering
|
|
139
|
+
only changes what is *advertised* — calls to any registered tool still work,
|
|
140
|
+
so an agent holding a cached tool name keeps working across a tier change.
|
|
141
|
+
Unknown names in `include`/`exclude` warn at startup and are ignored, so a
|
|
142
|
+
config written for a newer daftari still loads.
|
|
143
|
+
|
|
119
144
|
**Evaluate (opt-in, requires an Anthropic API key):** `daftari eval` — scores how
|
|
120
145
|
well an LLM can use the curation surface to answer multi-hop questions about the
|
|
121
146
|
vault. See the [design spec](docs/superpowers/specs/2026-05-31-cortex-quality-metric-design.md)
|
|
@@ -243,6 +268,110 @@ daftari backfill --plan
|
|
|
243
268
|
daftari backfill --apply --scope specs
|
|
244
269
|
```
|
|
245
270
|
|
|
271
|
+
## Open Knowledge Format (OKF)
|
|
272
|
+
|
|
273
|
+
[OKF](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing/)
|
|
274
|
+
is Google Cloud's vendor-neutral spec (v0.1) for the LLM-wiki pattern: a
|
|
275
|
+
directory of markdown files with YAML frontmatter that any producer can emit and
|
|
276
|
+
any consumer can read without translation. A Daftari vault *is* that pattern, so
|
|
277
|
+
`daftari okf` bridges the two directions.
|
|
278
|
+
|
|
279
|
+
**Export** renders the vault as a portable OKF bundle — every doc becomes an OKF
|
|
280
|
+
concept doc (the core `type` / `title` / `description` / `resource` / `tags` /
|
|
281
|
+
`timestamp` fields, plus a verbatim `daftari` sidecar for lossless round-trip),
|
|
282
|
+
with generated `index.md` (progressive-disclosure listing) and `log.md`
|
|
283
|
+
(chronological history). The source vault is never mutated.
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
daftari okf export ./my-vault --out ./okf-bundle
|
|
287
|
+
daftari okf export ./my-vault --out ./okf-bundle --collection pricing
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Import** adopts an OKF bundle into a vault. A bundle produced by `okf export`
|
|
291
|
+
round-trips exactly via its sidecar; a foreign bundle is mapped conservatively
|
|
292
|
+
(docs land as `draft` in the `accumulation` domain, the original OKF `type` is
|
|
293
|
+
preserved in an `okf_type` field). Writes auto-commit and the index is rebuilt.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
daftari okf import ./okf-bundle --into ./my-vault --dry-run
|
|
297
|
+
daftari okf import ./okf-bundle --into ./my-vault
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Server mode (self-hosted)
|
|
301
|
+
|
|
302
|
+
By default daftari is a per-user stdio process. `daftari serve` starts the
|
|
303
|
+
same vault as **one always-on instance over Streamable HTTP** — for a team, or
|
|
304
|
+
for clients that can't spawn a local process (claude.ai web/mobile, Cowork):
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
daftari serve --vault ./my-vault # http://127.0.0.1:8787/mcp
|
|
308
|
+
daftari serve --vault ./my-vault --bind 0.0.0.0 --port 9000
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Identity is **per MCP session**, resolved when the session opens, so every
|
|
312
|
+
RBAC and existence-disclosure rule applies per connection with zero tool
|
|
313
|
+
changes. Two composable auth schemes, both declared in config:
|
|
314
|
+
|
|
315
|
+
```yaml
|
|
316
|
+
server:
|
|
317
|
+
transport_security: external # required off-loopback: TLS terminates upstream
|
|
318
|
+
auth:
|
|
319
|
+
tokens: # static bearer tokens (agents, ETL)
|
|
320
|
+
- env: DAFTARI_TOKEN_ETL # the VALUE lives in this env var, never in config
|
|
321
|
+
user: agent:etl
|
|
322
|
+
role: admin
|
|
323
|
+
oauth: # OAuth 2.1 resource server (humans via your IdP)
|
|
324
|
+
issuer: https://idp.example.com
|
|
325
|
+
audience: daftari
|
|
326
|
+
jwks_uri: https://idp.example.com/.well-known/jwks.json
|
|
327
|
+
subjects:
|
|
328
|
+
"alice@example.com": { user: human:alice, role: analyst }
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The posture is fail-loud, never silent-downgrade: a non-loopback bind refuses
|
|
332
|
+
to start without auth **and** the explicit `transport_security: external`
|
|
333
|
+
acknowledgment (daftari never terminates TLS itself — put Caddy/nginx/a load
|
|
334
|
+
balancer in front); with any auth configured, a bad or missing credential is
|
|
335
|
+
a 401 at session open and a valid JWT whose subject isn't mapped is a 403 —
|
|
336
|
+
neither ever becomes the guest. Guest sessions exist only in the no-auth
|
|
337
|
+
loopback configuration. A running server also refuses to be silently killed:
|
|
338
|
+
stray stdio invocations against the same vault refuse to start, and replacing
|
|
339
|
+
a live holder requires a deliberate `daftari serve --takeover`.
|
|
340
|
+
|
|
341
|
+
## Storage backing
|
|
342
|
+
|
|
343
|
+
A self-hosted vault can push to durable object storage (#6). The local git
|
|
344
|
+
working copy stays canonical — reads, writes, auto-commits, locks, and the
|
|
345
|
+
index all stay exactly as above — and the backing is a dumb sync target:
|
|
346
|
+
|
|
347
|
+
```yaml
|
|
348
|
+
# .daftari/config.yaml
|
|
349
|
+
storage:
|
|
350
|
+
backend: s3 # fs | s3 | azure
|
|
351
|
+
bucket: team-vault
|
|
352
|
+
region: us-east-1
|
|
353
|
+
# endpoint: https://… # MinIO / R2 / GCS S3-interop endpoints
|
|
354
|
+
# sync_interval_minutes: 15 # daftari serve pushes on this cadence
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
daftari sync --vault ./my-vault # incremental push
|
|
359
|
+
daftari sync --vault ./my-vault --dry-run # show the diff only
|
|
360
|
+
daftari sync --vault ./empty-dir --restore --backend s3 --bucket team-vault
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The push covers the markdown tree, the `.git` directory, and durable
|
|
364
|
+
`.daftari` journals; the rebuildable SQLite index and lock files never sync.
|
|
365
|
+
Neither do `.git/config` and `.git/hooks` — git *executes* what those
|
|
366
|
+
declare, and a backup channel must not deliver code (`git clone` refuses to
|
|
367
|
+
transmit them for the same reason), so re-add remotes and local git config
|
|
368
|
+
by hand after a restore.
|
|
369
|
+
Cloud backends load their SDKs (`@aws-sdk/client-s3`, `@azure/storage-blob`)
|
|
370
|
+
as optional dependencies — install the one you use; credentials come from the
|
|
371
|
+
SDK's standard environment chain, never from vault config. GCS is reached via
|
|
372
|
+
its S3-interoperability endpoint. Restore refuses non-empty directories and
|
|
373
|
+
reindexes when done.
|
|
374
|
+
|
|
246
375
|
## How it compares
|
|
247
376
|
|
|
248
377
|
| |AGENTS.md |RAG |Daftari |
|
|
@@ -257,12 +386,18 @@ daftari backfill --apply --scope specs
|
|
|
257
386
|
|
|
258
387
|
Deliberately deferred to keep the surface tight:
|
|
259
388
|
|
|
260
|
-
- **Cloud-hosted multi-tenant
|
|
261
|
-
|
|
389
|
+
- **Cloud-hosted multi-tenant SaaS** (self-hosted server mode HAS shipped —
|
|
390
|
+
see [Server mode](#server-mode-self-hosted) and
|
|
391
|
+
[Storage backing](#storage-backing); the project itself hosts nothing)
|
|
262
392
|
- **Conflict resolution beyond file-level locks** (CRDTs, semantic merge)
|
|
263
393
|
- **Background curation agent** running lint on a cadence
|
|
264
|
-
- **
|
|
265
|
-
|
|
394
|
+
- **Enforced domain separation** (v1 documents the convention; v2 enforces it —
|
|
395
|
+
advisory boundary warnings shipped in the meantime: `vault_lint`'s
|
|
396
|
+
`domainLeaks` check and write-time `domain_warnings`)
|
|
397
|
+
|
|
398
|
+
(LLM reranking, deferred here originally, has since shipped as the opt-in
|
|
399
|
+
agent-as-judge `rerank_candidates` on `vault_search`: the server prepares the
|
|
400
|
+
fused candidate pool and the protocol; the calling agent is the judge.)
|
|
266
401
|
|
|
267
402
|
Each is a clean increment on a surface that already works.
|
|
268
403
|
|
package/dist/access/rbac.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function canRead(role: RoleConfig | null, collection: string): bo
|
|
|
12
12
|
export declare function canWrite(role: RoleConfig | null, collection: string): boolean;
|
|
13
13
|
export declare function canPromote(role: RoleConfig | null): boolean;
|
|
14
14
|
export declare function canRatify(role: RoleConfig | null): boolean;
|
|
15
|
+
export declare function isProposeOnly(role: RoleConfig | null): boolean;
|
|
15
16
|
export declare function hasAnyRead(role: RoleConfig | null): boolean;
|
|
16
17
|
export declare function filterByReadPermission<T extends {
|
|
17
18
|
collection: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEpE,eAAO,MAAM,UAAU,UAAU,CAAC;AAClC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAI5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAKD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,aAAa,CAEf;AAID,wBAAgB,WAAW,CAAC,IAAI,SAAU,GAAG,aAAa,CAEzD;AAOD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5E;AAGD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7E;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE1D;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAID,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EACrE,IAAI,EAAE,UAAU,GAAG,IAAI,EACvB,KAAK,EAAE,CAAC,EAAE,GACT,CAAC,EAAE,CAEL"}
|
|
1
|
+
{"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEpE,eAAO,MAAM,UAAU,UAAU,CAAC;AAClC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAI5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAKD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,aAAa,CAEf;AAID,wBAAgB,WAAW,CAAC,IAAI,SAAU,GAAG,aAAa,CAEzD;AAOD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5E;AAGD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7E;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE1D;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE9D;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAID,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EACrE,IAAI,EAAE,UAAU,GAAG,IAAI,EACvB,KAAK,EAAE,CAAC,EAAE,GACT,CAAC,EAAE,CAEL"}
|
package/dist/access/rbac.js
CHANGED
|
@@ -42,6 +42,13 @@ export function canPromote(role) {
|
|
|
42
42
|
export function canRatify(role) {
|
|
43
43
|
return role?.ratify ?? false;
|
|
44
44
|
}
|
|
45
|
+
// True if the role is propose-only (#235): its writes must land as staged
|
|
46
|
+
// `write` proposals, never as direct mutations. vault_write coerces; every
|
|
47
|
+
// other write tool denies. The write grant still scopes WHICH collections the
|
|
48
|
+
// role may propose into.
|
|
49
|
+
export function isProposeOnly(role) {
|
|
50
|
+
return role?.proposeOnly ?? false;
|
|
51
|
+
}
|
|
45
52
|
// True if the role has read access to at least one collection. Curation tools
|
|
46
53
|
// (lint, tension log, provenance) are open to anyone with any read grant.
|
|
47
54
|
export function hasAnyRead(role) {
|
package/dist/access/rbac.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAI1E,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAClC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAU5B,+EAA+E;AAC/E,6EAA6E;AAC7E,2BAA2B;AAC3B,MAAM,UAAU,aAAa,CAC3B,MAAqB,EACrB,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,OAAO;IACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,UAAkB;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC9D,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,OAAO,CAAC,IAAuB,EAAE,UAAkB;IACjE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,IAAuB,EAAE,UAAkB;IAClE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,OAAO,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;AAC/B,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,MAAM,UAAU,sBAAsB,CACpC,IAAuB,EACvB,KAAU;IAEV,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC"}
|
|
1
|
+
{"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAI1E,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAClC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAU5B,+EAA+E;AAC/E,6EAA6E;AAC7E,2BAA2B;AAC3B,MAAM,UAAU,aAAa,CAC3B,MAAqB,EACrB,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,OAAO;IACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,UAAkB;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC9D,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,OAAO,CAAC,IAAuB,EAAE,UAAkB;IACjE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,IAAuB,EAAE,UAAkB;IAClE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,OAAO,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;AAC/B,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,yBAAyB;AACzB,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,OAAO,IAAI,EAAE,WAAW,IAAI,KAAK,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,MAAM,UAAU,sBAAsB,CACpC,IAAuB,EACvB,KAAU;IAEV,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BrokenRefFinding, LinkEdge, RepoSnapshot } from "../types.js";
|
|
2
|
-
export declare function checkBrokenRefs(snapshots: RepoSnapshot[], edges: LinkEdge[]): BrokenRefFinding[];
|
|
2
|
+
export declare function checkBrokenRefs(snapshots: RepoSnapshot[], edges: LinkEdge[], existsWithin?: (rootAbs: string, targetAbs: string) => boolean): BrokenRefFinding[];
|
|
3
3
|
//# sourceMappingURL=broken_refs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broken_refs.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"broken_refs.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS5E,wBAAgB,eAAe,CAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,KAAK,EAAE,QAAQ,EAAE,EAIjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,GAC7D,gBAAgB,EAAE,CAiGpB"}
|
|
@@ -1,32 +1,72 @@
|
|
|
1
1
|
// src/audit/checks/broken_refs.ts
|
|
2
2
|
// Pure function: given snapshots and classified edges, return every broken
|
|
3
|
-
// reference as a BrokenRefFinding. No throws. No classes.
|
|
3
|
+
// reference as a BrokenRefFinding. No throws. No classes. Disk access enters
|
|
4
|
+
// ONLY through the injected `existsWithin` oracle — the orchestrator passes
|
|
5
|
+
// collect.ts's symlink-safe implementation; tests pass a fake; omitting it
|
|
6
|
+
// reproduces the index-only behavior. The oracle receives the CONTAINMENT
|
|
7
|
+
// ROOT alongside the target because existence and confinement must be
|
|
8
|
+
// decided together: a lexical check here plus a bare exists() there would
|
|
9
|
+
// let a committed symlink (repo/escape -> /) walk the probe back out.
|
|
4
10
|
//
|
|
5
11
|
// Resolution logic:
|
|
6
12
|
// 1. Look up target repo by name; if not found → missing_file.
|
|
7
13
|
// 2. Look up targetPath in that repo's docs Map.
|
|
8
14
|
// 3. If not found and targetPath lacks a ".md" extension, try targetPath +
|
|
9
15
|
// ".md" (spec §7 bare-path fallback).
|
|
10
|
-
// 4.
|
|
11
|
-
//
|
|
16
|
+
// 4. Still not found, edge escaped every configured repo (outOfScope):
|
|
17
|
+
// existing on disk → out_of_scope_target (the audit can't vouch for it,
|
|
18
|
+
// but the link is not broken — #133); absent → missing_file.
|
|
19
|
+
// 5. Still not found, in-scope: existing on disk → NOT a finding — the doc
|
|
20
|
+
// index only holds glob-matched markdown, and references to real assets
|
|
21
|
+
// (.png, .pdf, .dagitty, …) are legitimate (#132). Anchors into such
|
|
22
|
+
// files are unverifiable and deliberately not flagged.
|
|
23
|
+
// 6. Otherwise → missing_file.
|
|
24
|
+
// 7. If found and targetAnchor is set: check DocSnapshot.headings. If absent
|
|
12
25
|
// → missing_anchor.
|
|
13
|
-
|
|
26
|
+
import { isAbsolute, relative as nodeRelative, resolve as nodeResolve } from "node:path";
|
|
27
|
+
// True iff `abs` sits at or under `root` (lexical containment; both sides are
|
|
28
|
+
// already-resolved absolute paths).
|
|
29
|
+
function within(root, abs) {
|
|
30
|
+
const rel = nodeRelative(root, abs);
|
|
31
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
32
|
+
}
|
|
33
|
+
export function checkBrokenRefs(snapshots, edges,
|
|
34
|
+
// Must return true only when targetAbs exists on disk AND its REAL
|
|
35
|
+
// (symlink-resolved) location sits under rootAbs. See
|
|
36
|
+
// collect.ts#symlinkSafeExistsWithin for the production implementation.
|
|
37
|
+
existsWithin) {
|
|
14
38
|
// Index snapshots by repo name for O(1) lookup.
|
|
15
39
|
const byRepo = new Map();
|
|
16
40
|
for (const snap of snapshots) {
|
|
17
41
|
byRepo.set(snap.config.name, snap);
|
|
18
42
|
}
|
|
43
|
+
// The existence oracle is CONFINED (security review on #255): probing an
|
|
44
|
+
// arbitrary resolvedAbs would let anyone who can author a doc turn the
|
|
45
|
+
// audit report into a host-filesystem existence probe
|
|
46
|
+
// ([x](../../../../etc/shadow) → out_of_scope_target vs missing_file).
|
|
47
|
+
// #133's actual case is UNAUDITED SIBLINGS, so out-of-scope probes are
|
|
48
|
+
// allowed only under the parent directories of the configured repo roots;
|
|
49
|
+
// anything further out is reported missing_file without ever touching
|
|
50
|
+
// disk — exactly the pre-oracle behavior. A repo mounted directly under
|
|
51
|
+
// the filesystem root gets NO sibling scope: its "parent" would be the
|
|
52
|
+
// entire filesystem and the confinement would collapse to none, so
|
|
53
|
+
// out-of-scope refs from such a repo stay unprobed.
|
|
54
|
+
const siblingScopes = snapshots.flatMap((s) => {
|
|
55
|
+
const parent = nodeResolve(s.config.path, "..");
|
|
56
|
+
return nodeResolve(parent, "..") === parent ? [] : [parent];
|
|
57
|
+
});
|
|
19
58
|
const findings = [];
|
|
20
59
|
for (const edge of edges) {
|
|
60
|
+
const finding = (kind, path) => ({
|
|
61
|
+
kind,
|
|
62
|
+
source: { repo: edge.sourceRepo, path: edge.sourcePath },
|
|
63
|
+
target: { repo: edge.targetRepo, path, anchor: edge.targetAnchor },
|
|
64
|
+
rawHref: edge.rawHref,
|
|
65
|
+
});
|
|
21
66
|
const targetSnap = byRepo.get(edge.targetRepo);
|
|
22
67
|
if (!targetSnap) {
|
|
23
68
|
// Target repo itself is unknown — treat as missing file.
|
|
24
|
-
findings.push(
|
|
25
|
-
kind: "missing_file",
|
|
26
|
-
source: { repo: edge.sourceRepo, path: edge.sourcePath },
|
|
27
|
-
target: { repo: edge.targetRepo, path: edge.targetPath, anchor: edge.targetAnchor },
|
|
28
|
-
rawHref: edge.rawHref,
|
|
29
|
-
});
|
|
69
|
+
findings.push(finding("missing_file", edge.targetPath));
|
|
30
70
|
continue;
|
|
31
71
|
}
|
|
32
72
|
// Resolve the target doc: exact match, then .md fallback.
|
|
@@ -41,24 +81,39 @@ export function checkBrokenRefs(snapshots, edges) {
|
|
|
41
81
|
}
|
|
42
82
|
}
|
|
43
83
|
if (resolvedPath === null) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
84
|
+
if (edge.outOfScope) {
|
|
85
|
+
// The href escaped every audited repo (#133). A file that exists out
|
|
86
|
+
// there is not a broken link — but it stays VISIBLE with its own
|
|
87
|
+
// kind, never silently passed: the audit cannot vouch for a target
|
|
88
|
+
// it does not scan. The probe runs only inside the sibling scopes
|
|
89
|
+
// (see above); a target beyond them is missing_file, unprobed.
|
|
90
|
+
const resolvedAbs = edge.resolvedAbs;
|
|
91
|
+
// Lexical pre-filter picks the candidate scope without touching
|
|
92
|
+
// disk; the oracle then re-verifies containment on REAL paths.
|
|
93
|
+
const scope = resolvedAbs !== undefined ? siblingScopes.find((s) => within(s, resolvedAbs)) : undefined;
|
|
94
|
+
findings.push(finding(resolvedAbs !== undefined && scope !== undefined && existsWithin?.(scope, resolvedAbs)
|
|
95
|
+
? "out_of_scope_target"
|
|
96
|
+
: "missing_file", edge.targetPath));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// In-scope miss: the docs map only holds glob-matched markdown, but a
|
|
100
|
+
// reference to a real on-disk asset is legitimate (#132) — one
|
|
101
|
+
// existence probe, no finding. Anchors into assets are unverifiable.
|
|
102
|
+
// The probe is confined to the target repo root: a URL-derived
|
|
103
|
+
// targetPath can carry ../ segments, and escaping paths must not be
|
|
104
|
+
// probed (same oracle concern as above).
|
|
105
|
+
const abs = nodeResolve(targetSnap.config.path, edge.targetPath);
|
|
106
|
+
if (within(targetSnap.config.path, abs) && existsWithin?.(targetSnap.config.path, abs)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
findings.push(finding("missing_file", edge.targetPath));
|
|
50
110
|
continue;
|
|
51
111
|
}
|
|
52
112
|
// File found. Check anchor if present.
|
|
53
113
|
if (edge.targetAnchor !== null) {
|
|
54
114
|
const doc = targetSnap.docs.get(resolvedPath);
|
|
55
115
|
if (doc && !doc.headings.has(edge.targetAnchor)) {
|
|
56
|
-
findings.push(
|
|
57
|
-
kind: "missing_anchor",
|
|
58
|
-
source: { repo: edge.sourceRepo, path: edge.sourcePath },
|
|
59
|
-
target: { repo: edge.targetRepo, path: resolvedPath, anchor: edge.targetAnchor },
|
|
60
|
-
rawHref: edge.rawHref,
|
|
61
|
-
});
|
|
116
|
+
findings.push(finding("missing_anchor", resolvedPath));
|
|
62
117
|
}
|
|
63
118
|
}
|
|
64
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broken_refs.js","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,2EAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"broken_refs.js","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,0EAA0E;AAC1E,sEAAsE;AACtE,0EAA0E;AAC1E,sEAAsE;AACtE,EAAE;AACF,oBAAoB;AACpB,iEAAiE;AACjE,mDAAmD;AACnD,6EAA6E;AAC7E,2CAA2C;AAC3C,yEAAyE;AACzE,6EAA6E;AAC7E,kEAAkE;AAClE,6EAA6E;AAC7E,6EAA6E;AAC7E,0EAA0E;AAC1E,4DAA4D;AAC5D,iCAAiC;AACjC,+EAA+E;AAC/E,yBAAyB;AAEzB,OAAO,EAAE,UAAU,EAAE,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AAGzF,8EAA8E;AAC9E,oCAAoC;AACpC,SAAS,MAAM,CAAC,IAAY,EAAE,GAAW;IACvC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACpC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,SAAyB,EACzB,KAAiB;AACjB,mEAAmE;AACnE,sDAAsD;AACtD,wEAAwE;AACxE,YAA8D;IAE9D,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,yEAAyE;IACzE,uEAAuE;IACvE,sDAAsD;IACtD,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,sEAAsE;IACtE,wEAAwE;IACxE,uEAAuE;IACvE,mEAAmE;IACnE,oDAAoD;IACpD,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,OAAO,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,CAAC,IAA8B,EAAE,IAAY,EAAoB,EAAE,CAAC,CAAC;YACnF,IAAI;YACJ,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;YACxD,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE;YAClE,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,yDAAyD;YACzD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QAED,0DAA0D;QAC1D,IAAI,YAAY,GAAkB,IAAI,CAAC;QACvC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC;QACjC,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,KAAK,CAAC;YACvC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,YAAY,GAAG,MAAM,CAAC;YACxB,CAAC;QACH,CAAC;QAED,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,kEAAkE;gBAClE,+DAA+D;gBAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;gBACrC,gEAAgE;gBAChE,+DAA+D;gBAC/D,MAAM,KAAK,GACT,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC5F,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,WAAW,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,YAAY,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC;oBACpF,CAAC,CAAC,qBAAqB;oBACvB,CAAC,CAAC,cAAc,EAClB,IAAI,CAAC,UAAU,CAChB,CACF,CAAC;gBACF,SAAS;YACX,CAAC;YACD,sEAAsE;YACtE,+DAA+D;YAC/D,qEAAqE;YACrE,+DAA+D;YAC/D,oEAAoE;YACpE,yCAAyC;YACzC,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACjE,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACvF,SAAS;YACX,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QAED,uCAAuC;QACvC,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC9C,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBAChD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/audit/collect.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Result } from "../frontmatter/types.js";
|
|
2
2
|
import type { AuditConfig, AuditError, RepoSnapshot } from "./types.js";
|
|
3
|
+
export declare function symlinkSafeExistsWithin(rootAbs: string, targetAbs: string): boolean;
|
|
3
4
|
export declare function collectRepos(config: AuditConfig): Promise<Result<RepoSnapshot[], AuditError>>;
|
|
4
5
|
//# sourceMappingURL=collect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;AAYjG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CASnF;AAoKD,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC,CAa7C"}
|
package/dist/audit/collect.js
CHANGED
|
@@ -3,14 +3,34 @@
|
|
|
3
3
|
// frontmatter, extract headings (GitHub-slugged) and links, then batch
|
|
4
4
|
// git log to populate mtimes; on any git failure, fall back to fs mtime.
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
import { statSync } from "node:fs";
|
|
6
|
+
import { realpathSync, statSync } from "node:fs";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
8
|
-
import { resolve as nodeResolve } from "node:path";
|
|
8
|
+
import { isAbsolute, relative as nodeRelative, resolve as nodeResolve } from "node:path";
|
|
9
9
|
import { glob } from "glob";
|
|
10
10
|
import matter from "gray-matter";
|
|
11
11
|
import { err, ok } from "../frontmatter/types.js";
|
|
12
12
|
import { extractLinksFromBody } from "./links.js";
|
|
13
13
|
import { runtimeError } from "./types.js";
|
|
14
|
+
// The disk oracle for checkBrokenRefs (#132/#133): true iff targetAbs
|
|
15
|
+
// exists AND its REAL location sits under rootAbs. realpathSync resolves
|
|
16
|
+
// every component, so a symlink committed inside an audited repo
|
|
17
|
+
// (escape -> /) cannot route the probe outside the containment root — a
|
|
18
|
+
// lexical check plus a bare existsSync would (security review on #255).
|
|
19
|
+
// rootAbs is expected to be already-real: repo roots are realpathSync'd at
|
|
20
|
+
// config load, and the parent prefix of a real path is itself real.
|
|
21
|
+
// A nonexistent target makes realpathSync throw ENOENT → false, which is
|
|
22
|
+
// exactly the "missing" answer.
|
|
23
|
+
export function symlinkSafeExistsWithin(rootAbs, targetAbs) {
|
|
24
|
+
let real;
|
|
25
|
+
try {
|
|
26
|
+
real = realpathSync(targetAbs);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const rel = nodeRelative(rootAbs, real);
|
|
32
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
33
|
+
}
|
|
14
34
|
function slugify(heading) {
|
|
15
35
|
// GitHub slug: lowercase, strip non-alphanumeric (keep `-_`), whitespace -> `-`.
|
|
16
36
|
return heading
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,sEAAsE;AACtE,uEAAuE;AACvE,yEAAyE;AAEzE,OAAO,EAA4B,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAqBvE,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAqED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAqBvE,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB;AAqED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoI9F"}
|
package/dist/audit/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { createAnthropicClient } from "../eval/llm.js";
|
|
|
11
11
|
import { checkBrokenRefs } from "./checks/broken_refs.js";
|
|
12
12
|
import { checkDescribesRefs } from "./checks/describes_refs.js";
|
|
13
13
|
import { checkStaleness } from "./checks/staleness.js";
|
|
14
|
-
import { collectRepos } from "./collect.js";
|
|
14
|
+
import { collectRepos, symlinkSafeExistsWithin } from "./collect.js";
|
|
15
15
|
import { parseAuditConfig } from "./config.js";
|
|
16
16
|
import { classifyDescribesEdges } from "./describes.js";
|
|
17
17
|
import { computeExitCode } from "./exit.js";
|
|
@@ -135,7 +135,10 @@ export async function runAudit(argv, overrides = {}) {
|
|
|
135
135
|
}
|
|
136
136
|
const snapshots = collected.value;
|
|
137
137
|
const edges = classifyEdges(snapshots);
|
|
138
|
-
|
|
138
|
+
// The disk oracle lets the pure check distinguish real breaks from
|
|
139
|
+
// references to on-disk assets (#132) and unaudited siblings (#133) —
|
|
140
|
+
// symlink-safe, so a committed link cannot route the probe off-scope.
|
|
141
|
+
const brokenRefs = checkBrokenRefs(snapshots, edges, symlinkSafeExistsWithin);
|
|
139
142
|
const staleness = checkStaleness(snapshots, edges, config.staleness.thresholdDays, new Date());
|
|
140
143
|
const describesEdges = classifyDescribesEdges(snapshots);
|
|
141
144
|
const describesRefs = checkDescribesRefs(snapshots, describesEdges);
|
|
@@ -162,7 +165,10 @@ export async function runAudit(argv, overrides = {}) {
|
|
|
162
165
|
docsScanned: snapshots
|
|
163
166
|
.filter((s) => s.config.type !== "code")
|
|
164
167
|
.reduce((n, s) => n + s.docs.size, 0),
|
|
165
|
-
|
|
168
|
+
// Out-of-scope targets are visibility, not breakage (#133): their own
|
|
169
|
+
// total, excluded from brokenRefs so the failOn gate never trips on them.
|
|
170
|
+
brokenRefs: brokenRefs.filter((f) => f.kind !== "out_of_scope_target").length,
|
|
171
|
+
outOfScopeTargets: brokenRefs.filter((f) => f.kind === "out_of_scope_target").length,
|
|
166
172
|
directlyStale: staleness.filter((f) => f.kind === "direct").length,
|
|
167
173
|
transitivelyStale: staleness.filter((f) => f.kind === "transitive").length,
|
|
168
174
|
brokenDescribes: describesRefs.length,
|