data-olympus 0.3.4__tar.gz → 0.4.0__tar.gz

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.
Files changed (91) hide show
  1. {data_olympus-0.3.4 → data_olympus-0.4.0}/CHANGELOG.md +423 -1
  2. {data_olympus-0.3.4 → data_olympus-0.4.0}/PKG-INFO +9 -3
  3. {data_olympus-0.3.4 → data_olympus-0.4.0}/README.md +6 -2
  4. {data_olympus-0.3.4 → data_olympus-0.4.0}/SPEC.md +68 -12
  5. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/kb +77 -6
  6. data_olympus-0.4.0/bin/kb-session-recap-hook +107 -0
  7. {data_olympus-0.3.4 → data_olympus-0.4.0}/pyproject.toml +8 -1
  8. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/auth.py +26 -0
  9. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cli/indexgen.py +7 -1
  10. data_olympus-0.4.0/src/data_olympus/cli/init_cmd.py +44 -0
  11. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cli/main.py +4 -0
  12. data_olympus-0.4.0/src/data_olympus/cli/validity_report_cmd.py +104 -0
  13. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/config.py +25 -0
  14. data_olympus-0.4.0/src/data_olympus/format/lint.py +367 -0
  15. data_olympus-0.4.0/src/data_olympus/format/validate.py +392 -0
  16. data_olympus-0.4.0/src/data_olympus/governed_lane.py +383 -0
  17. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/health.py +16 -0
  18. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/index.py +621 -39
  19. data_olympus-0.4.0/src/data_olympus/maintenance.py +523 -0
  20. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/markdown_parse.py +87 -0
  21. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/models.py +188 -3
  22. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/pending.py +21 -0
  23. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/principals.py +7 -4
  24. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/refresh.py +27 -0
  25. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/rest_api.py +49 -8
  26. data_olympus-0.4.0/src/data_olympus/scaffold.py +389 -0
  27. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/server.py +110 -14
  28. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/tools_enforce.py +55 -2
  29. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/tools_onboarding.py +191 -9
  30. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/tools_read.py +74 -1
  31. data_olympus-0.4.0/src/data_olympus/tools_recap.py +65 -0
  32. data_olympus-0.4.0/src/data_olympus/tools_write.py +1540 -0
  33. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/write_gate.py +317 -5
  34. data_olympus-0.3.4/src/data_olympus/format/lint.py +0 -92
  35. data_olympus-0.3.4/src/data_olympus/format/validate.py +0 -71
  36. data_olympus-0.3.4/src/data_olympus/tools_write.py +0 -878
  37. {data_olympus-0.3.4 → data_olympus-0.4.0}/.gitignore +0 -0
  38. {data_olympus-0.3.4 → data_olympus-0.4.0}/LICENSE +0 -0
  39. {data_olympus-0.3.4 → data_olympus-0.4.0}/NOTICE +0 -0
  40. {data_olympus-0.3.4 → data_olympus-0.4.0}/benchmarks/README.md +0 -0
  41. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/_kb_detect_workspace.sh +0 -0
  42. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/_kb_enforce.py +0 -0
  43. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/_kb_fallback.py +0 -0
  44. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/kb-enforce-hook +0 -0
  45. {data_olympus-0.3.4 → data_olympus-0.4.0}/bin/opencode/data-olympus-gate.ts +0 -0
  46. {data_olympus-0.3.4 → data_olympus-0.4.0}/deploy/k8s/README.md +0 -0
  47. {data_olympus-0.3.4 → data_olympus-0.4.0}/deploy/k8s/read-replica/README.md +0 -0
  48. {data_olympus-0.3.4 → data_olympus-0.4.0}/docs/releases/README.md +0 -0
  49. {data_olympus-0.3.4 → data_olympus-0.4.0}/example-bundle/projects/acme-app/README.md +0 -0
  50. {data_olympus-0.3.4 → data_olympus-0.4.0}/example-bundle/projects/acme-app/components/api/README.md +0 -0
  51. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/__init__.py +0 -0
  52. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/audit_log.py +0 -0
  53. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/audit_trailers.py +0 -0
  54. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cli/__init__.py +0 -0
  55. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cli/import_cmd.py +0 -0
  56. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cli/report_cmd.py +0 -0
  57. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/cooccurrence.py +0 -0
  58. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/dedup.py +0 -0
  59. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/durable.py +0 -0
  60. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/embeddings.py +0 -0
  61. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/enforce_policy.py +0 -0
  62. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/format/__init__.py +0 -0
  63. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/format/document.py +0 -0
  64. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/format/frontmatter.py +0 -0
  65. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/git_ops.py +0 -0
  66. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/__init__.py +0 -0
  67. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/adr.py +0 -0
  68. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/flat.py +0 -0
  69. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/model.py +0 -0
  70. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/okf.py +0 -0
  71. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/run.py +0 -0
  72. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/importer/stamp.py +0 -0
  73. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/onboarding.py +0 -0
  74. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/onboarding_inflight.py +0 -0
  75. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/onboarding_playbook.py +0 -0
  76. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/prompts.py +0 -0
  77. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/push_queue.py +0 -0
  78. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/query_expansion.py +0 -0
  79. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/rate_limit.py +0 -0
  80. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/report.py +0 -0
  81. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/safe_id.py +0 -0
  82. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/search_gate.py +0 -0
  83. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/search_shortcut.py +0 -0
  84. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/session_metrics.py +0 -0
  85. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/setup_wizard.py +0 -0
  86. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/thin_pointer.py +0 -0
  87. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/tools_audit.py +0 -0
  88. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/trigram.py +0 -0
  89. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/viewer/__init__.py +0 -0
  90. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/viewer/generator.py +0 -0
  91. {data_olympus-0.3.4 → data_olympus-0.4.0}/src/data_olympus/worktrees.py +0 -0
@@ -12,6 +12,425 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.4.0] - 2026-07-08
16
+
17
+ ### Fixed
18
+
19
+ - **`data-olympus init` stamped a stale format version** (found by the
20
+ release-gate review): the scaffold's root `index.md` declared
21
+ `spec_version: "0.1"` while the shipped format is `0.2`. Corrected, with a
22
+ drift test tying the scaffold constant to SPEC.md's header and
23
+ `example-bundle/index.md` so future format bumps fail fast.
24
+
25
+ - **Graph-exclusion source guard now applies the memory-inbox floor** (found
26
+ by the v0.4.0 release-gate review). `graph_excluded_ids_sql` checked the
27
+ superseding source's status class and validity window but not `is_inbox`,
28
+ so a forged or legacy `memory/inbox/` document claiming `status: active`
29
+ with `supersedes: <real-rule>` could retire a real rule from
30
+ `in_force=true` retrieval, `kb_consult`, and the computed `in_force`
31
+ boolean. The source guard now requires `src.is_inbox = 0`, matching the
32
+ full single-sourced in-force predicate; regression-tested with an
33
+ inbox-source scenario.
34
+
35
+ ### Added
36
+
37
+ - **Write-path enforcement of mandatory `status` (issue #114).** `status`
38
+ has been a required frontmatter field (SPEC.md 4.2) and a `kb lint` error
39
+ since the format's `0.1` draft — this was already true of the lint layer
40
+ and is not a change of required-field semantics. The gap this closes is at
41
+ the write path: `kb_propose_edit` now rejects a postimage that creates a
42
+ **NEW** document without `status` (`rejected_invalid_document`, reason
43
+ `missing_status`), where previously the write path was more permissive
44
+ than `kb lint` and let a brand-new status-less document through.
45
+ - **Staged migration, not a hard break.** Editing an EXISTING status-less
46
+ document still requires no `status`, so a legacy corpus already carrying
47
+ status-less docs (tracked by the issue #113 maintenance ledger's
48
+ `status_present_in_all_kb_entries` flag and `missing_status` file list)
49
+ is never locked out of incremental fixes. Those documents keep being
50
+ served by `kb_search`/`kb_get` exactly as before; they are simply never
51
+ in force (already true via `IN_FORCE_STATUSES` membership) and never
52
+ surfaced by `kb_consult`.
53
+ - `kb_propose_memory` is unaffected: every server-rendered memory already
54
+ stamps `status: proposed` (issue #109).
55
+ - Reserved filenames (`index.md`/`log.md`/`template.md`) and documents
56
+ under the memory-inbox prefix are exempt, consistent with their existing
57
+ exemptions from the write-path's other schema checks.
58
+ - No SPEC.md version bump: see SPEC.md section 10 for why this is a
59
+ reference-implementation enforcement-timing change, not a format change.
60
+ - See `docs/operations.md` section 5.1 for the operator migration runbook
61
+ (lint, read the ledger's missing-status list, fix, done) and the new
62
+ integration test (`tests/test_status_mandatory_migration.py`) tying
63
+ lint-error + still-served + never-in-force + ledger flag + the
64
+ `pending_actions` CTA together as one tested contract.
65
+
66
+ - **Governed-lane write protection (issue #112) -- BREAKING DEFAULT-BEHAVIOR
67
+ CHANGE.** "Agents can propose, only humans can promote." Behind
68
+ `KB_GOVERNED_LANE_PROTECTION` (**default ON**; set to `off` to restore the
69
+ exact pre-#112 behavior). No write is ever rejected by this feature --
70
+ affected writes land as a PENDING entry instead of auto-committing:
71
+ - **Status clamp.** Any non-operator-confirmed write (the auto-commit path
72
+ of `kb_propose_memory` / `kb_propose_edit`, and `kb_bootstrap_project`
73
+ files) whose postimage sets or changes `status` INTO the in-force class
74
+ (`active`/`accepted`/`approved`, single-sourced from
75
+ `format.validate.IN_FORCE_STATUSES`) is demoted to pending with
76
+ `demotion_reason: "status_promotion"`. An operator-confirmed resolve of
77
+ that entry commits it -- the human step IS the promotion.
78
+ - **Governed-target edit demotion.** Any `kb_propose_edit` whose target
79
+ document is CURRENTLY in force (the full composed predicate: status class
80
+ AND validity window AND not-inbox AND not-graph-excluded, evaluated
81
+ against the live index) is demoted with `demotion_reason:
82
+ "governed_target"`, regardless of confidence. An expired or
83
+ superseded-out target is NOT in force and so is not protected by this
84
+ rule. The lookup FAILS CLOSED: an edit whose target's in-force state
85
+ cannot be verified (no index, or an index read failure) is demoted with
86
+ the distinct `demotion_reason: "governed_target_unverified"` rather than
87
+ auto-committed on the strength of a broken lookup. An authoritative
88
+ in-worktree backstop additionally re-judges the target's CURRENT bytes
89
+ on the refreshed commit base (inside the serialized commit section,
90
+ after the hard gates), consulting nothing from the index (a stale
91
+ graph-exclusion edge could otherwise remove protection the base's own
92
+ bytes assert), so an in-force doc that exists in git but is not yet
93
+ re-indexed is still demoted -- index lag cannot bypass the rule in
94
+ either direction.
95
+ - **Injection-pattern annotation (advisory only).** Postimages are scanned
96
+ for agent-directed injection patterns ("ignore previous instructions",
97
+ exfiltration-shaped imperatives, base64-looking blobs, "do not tell the
98
+ operator", ...); a match annotates the pending entry (`injection_suspect`,
99
+ `injection_patterns`: pattern names + line numbers, never the matched
100
+ text) but never blocks or demotes by itself.
101
+ - **Ordering:** the issue #71 secret-scanning gate (and the content-
102
+ validation / duplicate-id gate) always run BEFORE this feature's
103
+ demotion decision -- a postimage that would be rejected outright is
104
+ REJECTED, never silently demoted.
105
+ - **Feedback loop, so a demotion is never silent:** every demotion response
106
+ carries the `pending_id`, `demotion_reason`, and an `operator_prompt`
107
+ instructing the model to inform the operator; a new read-only
108
+ `kb_session_recap(source_session)` MCP tool / `GET /api/v1/session-recap`
109
+ REST route / `kb session-recap SOURCE_SESSION` CLI subcommand reports N
110
+ committed / M demoted-to-pending / K rejected for a session; `kb_consult`
111
+ surfaces a `demoted_writes` item in its `pending_actions` envelope when
112
+ the calling session has open demotions; and `bin/kb-session-recap-hook`
113
+ is a ready-to-wire SessionEnd/Stop hook script (documented for Claude
114
+ Code / Codex) that prints the recap one-liner automatically.
115
+ - **CLI review gate:** `bin/kb propose` never flows a demoted
116
+ (`demotion_reason`) or secret-flagged (`matching_pattern`) proposal into
117
+ its same-command interactive resolve (which defaults to accept without a
118
+ TTY); it prints the operator prompt and requires an explicit later
119
+ `kb resolve`. The MCP `kb_session_recap` tool requires an authenticated
120
+ principal when auth is configured, matching the REST route.
121
+ - Unaffected: the maintenance-ledger system write path (issue #113) and the
122
+ operator pending-resolve path (which IS the promotion).
123
+
124
+ - **Provenance surfacing + consult hardening (issue #109).** Revised decision:
125
+ no `authority_state`/`allowed_use` enum (rejected as a parallel vocabulary
126
+ that could drift from `status`); the real gaps are fixed at the root
127
+ instead:
128
+ - **Memory stamping.** Server-rendered memory files (`kb_propose_memory`)
129
+ now carry `type: memory` and `status: proposed` in frontmatter (neither
130
+ was stamped before), so the EXISTING status filter, status rerank, and
131
+ compact deviation-only status emission all apply to an agent-written
132
+ memory with zero new code paths. Promotion out of `proposed` happens at
133
+ operator review time, not here.
134
+ - **Memory-inbox in-force floor.** A document under the memory-inbox prefix
135
+ (`KB_MEMORY_INBOX_PREFIX`, default `memory/inbox/`) is now NEVER in force,
136
+ regardless of claimed status — covers both a legacy inbox file and forged
137
+ frontmatter on an agent-written memory. Implemented as a new `is_inbox`
138
+ column derived once at index-build time
139
+ (`format.validate.is_inbox_path`/`memory_inbox_prefix`, the single source
140
+ also consulted by the write path) and composed into the existing
141
+ single-sourced `is_in_force` predicate and SQL fragments (`index.py`
142
+ schema version bumped 10 -> 11), not a forked predicate.
143
+ - **Computed `in_force: bool`** on verbose `kb_get` and verbose `kb_search`
144
+ hits, derived from the single-sourced predicate (status class AND
145
+ validity window AND not-inbox AND, composing the issue #110 slice-2 graph
146
+ rule, not-graph-excluded). Never stored in frontmatter — it is a
147
+ serving-layer-only derivation (see SPEC.md's new "runtime envelope"
148
+ note). Compact responses emit it deviation-only (`in_force: false` only
149
+ when the doc is NOT in force; an in-force doc's compact shape is
150
+ byte-for-byte unchanged): the compact `status`/`freshness` fields reflect
151
+ raw frontmatter, so a memory-inbox doc with a forged `status: active`
152
+ would otherwise read as an ordinary current rule with no correcting
153
+ signal. The existing deviation-only `status`/`freshness` emissions stay
154
+ as they are.
155
+ - **`evidence: list[str]`** optional parameter on `kb_propose_memory` and
156
+ `kb_propose_edit` (max 10 items, 500 chars each). For a memory proposal it
157
+ is rendered into frontmatter via the same `yaml.safe_dump` path as `tags`
158
+ (so a secret-shaped evidence item is caught by the existing
159
+ full-postimage secret scan — no separate scan needed); for an edit
160
+ proposal (whose postimage is caller-supplied verbatim, with no template)
161
+ it is validated and redacted the same way `reason` already is. In both
162
+ cases it is persisted in pending meta, echoed on the propose/commit audit
163
+ events, and surfaced by `kb_list_pending`.
164
+ - **`kb_list_pending` surfaces provenance it already persisted**:
165
+ `source_session`, `reason`, and the new `evidence` were already written
166
+ into pending meta at enqueue time but never returned; they are now on the
167
+ `PendingEntry` model (`None` when absent, e.g. a memory proposal has no
168
+ `reason`).
169
+
170
+ - **Maintenance ledger: committed corpus-state audit + `pending_actions` CTA
171
+ surface (issue #113).** A frontmatter-only markdown doc (default
172
+ `tooling/maintenance-ledger.md`, `KB_MAINTENANCE_LEDGER_PATH`) is now
173
+ computed at every index build: whether `status` is present on every
174
+ indexed document (except reserved filenames), plus a capped list (50 paths
175
+ + a total count) of the ones missing it — the migration vehicle for making
176
+ `status` mandatory — and, consuming issue #107 validity data, documents
177
+ that recently expired or are expiring soon within configurable windows
178
+ (`KB_MAINTENANCE_RECENTLY_EXPIRED_DAYS` / `KB_MAINTENANCE_EXPIRING_SOON_DAYS`,
179
+ both default 30 days), each capped the same way. When the computed state
180
+ changes since the last committed copy, the ledger is committed through the
181
+ same serialized write/commit machinery every other write uses (system
182
+ agent identity `data-olympus-system`, a normal `maintenance_ledger` audit
183
+ event); a commit failure is logged/audited and never breaks index refresh
184
+ or serving. The ledger doc is excluded from its own audit and always
185
+ carries valid concept frontmatter, so it lints clean on its own merits
186
+ rather than relying on reserved-filename semantics. The same computed state
187
+ drives a new `pending_actions` field (a list of `{kind, message, count}`
188
+ items) on `kb_consult` and `kb_health` responses — deliberately never
189
+ `kb_search` — present only while the corpus is dirty; both tool
190
+ descriptions now instruct the model to surface open items to the operator
191
+ and act on them only with operator confirmation. `kb health`'s plain-text
192
+ CLI summary displays any open items. Silencing is automatic: remediate,
193
+ the next index build flips the flag, the next ledger commit records it,
194
+ and `pending_actions` disappears with no manual acking. See
195
+ `docs/operations.md` §5.
196
+
197
+ - **Validity/freshness frontmatter metadata with hard expiry semantics**
198
+ (issue #107, format `0.2`). Concept documents may now declare an optional
199
+ nested `validity` object: `valid_from`, `valid_until`, `last_verified`,
200
+ `recheck_by` (ISO date or datetime, normalized to a date at index/lint
201
+ time), and free-text `verification_source`. Indexed as columns on the
202
+ docs table (schema version bumped 9 -> 10, on top of the lifecycle-edges
203
+ bump below). See SPEC.md section 4.2.
204
+
205
+ - **`data-olympus init <dir>` bundle scaffold command** (issue #66). Creates a
206
+ new knowledge bundle: the tier directories (`--tiers`, default `universal,
207
+ tech-stacks,projects,decisions,workflows,tooling`), a root `index.md`
208
+ carrying the `spec_version`/`okf_version` frontmatter, a `template.md`
209
+ authoring scaffold, and one example concept document per SPEC-supported
210
+ `type` (`standard`, `decision`, `workflow`, `project`, `memory`,
211
+ `reference`), including a real `superseded`/`superseded_by`/`supersedes`
212
+ pair and `applies_when` trigger metadata. The generated bundle passes
213
+ `data-olympus lint` with zero errors and zero warnings and builds cleanly
214
+ under `data-olympus index`. Refuses to scaffold into a non-empty directory
215
+ (no `--force` in this slice).
216
+
217
+ - **Typed lifecycle relationships: parsed, indexed, and lint-validated
218
+ (issue #110, slice 1).** `supersedes` (scalar ID or list of IDs, normalized
219
+ to a list at parse time so both shapes the ADR importer emits lint clean),
220
+ `superseded_by` (scalar ID), and a new `contradicts` field (scalar ID or
221
+ list of IDs, normalized to a list the same way as `supersedes`;
222
+ unresolved-conflict evidence that never affects retrieval ranking) are now
223
+ parsed into `ParsedDoc` and extracted into a new `edges` table
224
+ (`source_id`, `rel`, `target_id`) at index-build time (schema version
225
+ bumped 8 -> 9). `kb lint` gained a cross-file pass (an in-memory id map
226
+ over the discovered bundle, no database) that reports: **errors** for a
227
+ malformed field shape, self-supersession, or a supersession cycle of any
228
+ length; **warnings** for a dangling target id, an asymmetric
229
+ supersedes/superseded_by pair, a path-shaped target value (concept ids are
230
+ the only stable target, never paths), `superseded_by` set on an in-force
231
+ document, `status: superseded` with no `superseded_by`, and an in-force
232
+ `contradicts` pair. `SPEC.md` section 4.2 documents the normalized shapes
233
+ and the full lint severity list; the "not validated today" note is
234
+ removed. Slice 2 (a separate change) will consume the edges table for
235
+ in-force graph exclusion, `kb_get`/`kb_search` surfacing, and a health
236
+ counter.
237
+
238
+ - **Supersession edges become executable retrieval policy (issue #110,
239
+ slice 2).** `in_force=true` now ADDITIONALLY excludes any document that is
240
+ the TARGET of a `supersedes` edge whose SOURCE document is itself in force
241
+ (the full status-class-AND-validity-window predicate, not merely
242
+ `status: superseded`): a `draft`, expired, or already-retired document can
243
+ never retire another document just by naming it in `supersedes`.
244
+ - **Behavior change: a document superseded only via an edge (its own
245
+ `status` was never flipped) is now excluded from `in_force=true`
246
+ results, even though its own status class would otherwise qualify it.**
247
+ It remains visible in a default (non-`in_force`) `kb_search`, same as an
248
+ `upcoming` document. This closes the "forgotten status flip" gap.
249
+ - A mutually-supersessive in-force cycle (already a `kb lint` ERROR at
250
+ parse time) is NOT special-cased: every member independently satisfies
251
+ the exclusion rule, so ALL members of the cycle are excluded from
252
+ `in_force=true`.
253
+ - A dangling edge (source or target id with no corresponding document)
254
+ excludes nothing and is not counted; the exclusion query joins `edges`
255
+ to `docs` on both ends, never trusting either raw id.
256
+ - **`kb_consult` now queries with `in_force=true`** (previously it ran a
257
+ plain default search), so it never returns a not-yet-in-force, retired,
258
+ or graph-excluded document as a "governing rule". The unconditional
259
+ not-expired exclusion already gave this guarantee for expired docs; this
260
+ closes the same gap for status/graph exclusion.
261
+ - New `graph_excluded_docs` health counter (`kb_health` / `/api/v1/health`,
262
+ alongside `malformed_frontmatter` / `malformed_validity`): the count of
263
+ documents currently excluded by this rule, evaluated LIVE at health-read
264
+ time against the current date from the same SQL definition
265
+ (`format.validate.graph_excluded_ids_sql`) the retrieval-time filter
266
+ uses, so the counter can never drift from the filter it reports on, even
267
+ across a date boundary where an in-force source's validity window opens
268
+ or closes between index rebuilds. Exposed on the Index as
269
+ `graph_excluded_count(today=...)` (injectable date for deterministic
270
+ tests; health reads the real wall clock, bounded by the short health
271
+ cache).
272
+ - **Retirement is explainable.** `kb_get` (regardless of in-force/graph-
273
+ exclusion status, same as it already ignores expiry) gains
274
+ `superseded_by`: the sorted UNION of the document's own frontmatter
275
+ `superseded_by` claim and any reverse `supersedes` edge naming it -- ONE
276
+ consistent computed shape covering both the honest self-declared case
277
+ and the forgotten-status-flip case. `kb_get` also gains `contradicts`
278
+ (the document's own frontmatter list) and the computed reverse
279
+ `contradicted_by`. All three are omitted from the compact response when
280
+ empty. `contradicts` is annotation only: it has NO filtering or ranking
281
+ effect anywhere in the retrieval path.
282
+ - Compact `kb_search` hits gain a deviation-only `superseded_by` (the same
283
+ computed union above), omitted when the document is not superseded;
284
+ computed and attached to every hit regardless of `in_force`, so a plain
285
+ search result still explains why a hit is historically superseded.
286
+ - `SPEC.md` section 4.2 documents the graph-exclusion rule, the health
287
+ counter, and the surfacing contract.
288
+
289
+ - **Secret-scanning gate on the write path (issue #71).** Every commit path
290
+ (`kb_propose_memory` / `kb_propose_edit` auto-commit, `kb_resolve_pending`
291
+ approve, including a resolved `edited_text`, and onboarding bootstrap) now
292
+ scans the final postimage for credential-shaped content (PEM private-key
293
+ blocks, GitHub/Slack tokens, AWS access key ids, generic `password=`/
294
+ `secret=` assignments with a non-placeholder value, and connection strings
295
+ with an inline password) before anything is written to disk. A match rejects
296
+ the write with a distinct `rejected_secret_detected` status; nothing is
297
+ committed and no pending entry is created on the auto-commit/bootstrap
298
+ paths. Only the pattern name and an approximate line number are ever
299
+ surfaced in the response, the audit event, or a log line, never the
300
+ matched secret value. Operators can extend the built-in pattern set with
301
+ `KB_SECRET_SCAN_EXTRA_PATTERNS` (comma-separated regexes; an invalid entry
302
+ is logged and skipped, never crashes the server). `kb_resolve_pending` gains
303
+ an operator-only `override_secret_scan` flag to consciously commit a
304
+ flagged postimage that is a confirmed false positive; the override is
305
+ recorded on the resulting audit event, and no auto-commit or bootstrap path
306
+ exposes it (`kb resolve --override-secret-scan` on the CLI). A low-confidence
307
+ proposal containing a secret still enters pending, so the override workflow
308
+ is not defeated, but its scan result is redacted to a pattern name in the
309
+ propose response and tagged (`secret_scan_flagged`) on the entry so
310
+ `kb pending` surfaces the warning without ever showing the matched value;
311
+ the memory filename slug itself falls back to a neutral placeholder instead
312
+ of embedding the flagged text. The scan runs before the content-validation
313
+ gate on every commit path so an invalid-document rejection can never echo a
314
+ credential value through its own error message. The gate also covers the
315
+ fields around the postimage: a credential-shaped `target_path` (filename)
316
+ is rejected on edit/bootstrap/resolve without echoing the path back, a
317
+ flagged edit `reason` is replaced with a redacted note before reaching
318
+ pending meta / push metadata / audit events, and a flagged memory tag is
319
+ stored redacted in pending meta. Extra patterns
320
+ (`KB_SECRET_SCAN_EXTRA_PATTERNS`) with the classic nested-quantifier ReDoS
321
+ shape are rejected at load time alongside invalid regexes, and every
322
+ accepted custom pattern executes through the `regex` engine (a new runtime
323
+ dependency) with a hard 1-second match timeout so a catastrophic pattern
324
+ cannot hang the single-writer write path.
325
+ - **OKF profile document (issue #108).** `docs/okf-profile.md`: a field-by-
326
+ field reference distinguishing stable governance extensions (`id`, `type`,
327
+ `status`, `tier`, `applies_when`, `supersedes`/`superseded_by`/`contradicts`,
328
+ `validity`) from runtime-only serving envelope fields (`in_force`,
329
+ `freshness`, `contradicted_by`, `pending_actions`) and from experimental
330
+ candidates tracked against open OKF ecosystem discussions (structured
331
+ `relationships:`, version-keyed validity, a confidence/reliability axis).
332
+ Documents the issue #109 decision to reject an `authority_state`/
333
+ `allowed_use` enum, and a short factual comparison against `inkxel/throughline`,
334
+ `inkxel/dotKnowledge`, and `dynamicfeed/signed-okf`. Cross-linked from
335
+ `README.md`, `WHY.md`, and `SPEC.md` section 11. Documentation only; no
336
+ behavior change.
337
+
338
+ ### Changed
339
+
340
+ - **BEHAVIOR CHANGE: `kb_consult` now hard-filters retrieval to the in-force
341
+ class (issue #109).** Previously `kb_consult` ran an UNFILTERED search, so
342
+ an unreviewed agent-written memory, a superseded/deprecated/rejected
343
+ decision, a draft/proposed document, an expired or upcoming doc, or a
344
+ memory-inbox file could all be handed back as "the" governing rule for an
345
+ intent — the single highest-value gap this change closes. `kb_consult` now
346
+ passes `in_force=true` internally, so only `active`/`accepted`/`approved`
347
+ documents within their validity window and NOT under the memory inbox are
348
+ ever returned as rules. **Operational caveat:** a document with no
349
+ `status` field at all (or a status outside `IN_FORCE_STATUSES`) now stops
350
+ appearing as a governing rule via `kb_consult`, where it previously did
351
+ (the prior unfiltered search surfaced it same as any other hit). `status`
352
+ becoming a mandatory field is tracked separately (issue #114); until a
353
+ bundle finishes that migration, a status-less doc that WAS relied upon as
354
+ an implicit rule via `kb_consult` will need `status: active` (or
355
+ `accepted`/`approved`) added before it is retrievable there again. Plain
356
+ `kb_search`/`kb_get` are unaffected (they still surface a status-less doc
357
+ by default); this change is scoped to the enforcement-facing `kb_consult`
358
+ retrieval path only.
359
+
360
+ - **BEHAVIOR CHANGE: a document past its `validity.valid_until` now leaves
361
+ the DEFAULT `kb_search` result set**, not just `in_force=true` queries.
362
+ Previously there was no `validity` concept at all, so nothing was ever
363
+ excluded on this basis; a bundle that starts dating its documents will see
364
+ expired ones silently drop out of ordinary search results (they are still
365
+ reachable via `kb_get(id)`, `include_expired=true`, or the new
366
+ `validity_state=expired` audit facet). Rationale: unlike a superseded
367
+ document, an expired one has no named successor to outrank it, so left
368
+ visible it could surface as the top hit and incorrectly govern.
369
+ - A document with a future `valid_from` ("upcoming") is NOT excluded by
370
+ default search, only by `in_force=true`; it is flagged
371
+ `freshness: "upcoming"`.
372
+ - A document past `recheck_by` ("stale") stays in force and visible;
373
+ it is flagged `freshness: "stale"` (advisory only).
374
+ - `kb_search` gains `include_expired` (default `false`) and a
375
+ `validity_state` facet (`"expired"`, `"stale"`,
376
+ `"expiring_within:N"`) for audit queries. `kb_get` by id always resolves
377
+ regardless of expiry, returning the full `validity` object plus a
378
+ computed `freshness` indicator. `kb_consult` never returns an expired
379
+ document (as of issue #109 below, via the `in_force=true` hard filter;
380
+ previously via the default search path's own expired-exclusion).
381
+ - Compact search hits gain a deviation-only `freshness` field
382
+ (`stale`/`expired`/`upcoming`), omitted when fresh or absent.
383
+ - `kb lint` gains three new WARNING-only findings (never errors, since
384
+ they are wall-clock-relative): `recheck_by` in the past; `valid_until`
385
+ in the past while `status` is in the in-force class; a malformed
386
+ `validity` value (treated as absent for indexing/search, fail open).
387
+ - `kb_health` / `data-olympus report` gain a `malformed_validity` counter
388
+ mirroring the existing `malformed_frontmatter` one.
389
+ - New `data-olympus validity-report` CLI subcommand lists expired and
390
+ soon-to-expire documents by scanning a bundle directory directly.
391
+ - `timestamp` is unaffected and remains content-change metadata only;
392
+ SPEC.md now explicitly forbids deriving staleness from `timestamp` or
393
+ `last_modified`.
394
+
395
+ ### Fixed
396
+
397
+ - **`data-olympus index` silently regenerated zero indexes for a bundle whose
398
+ absolute path passes through a skip-named ancestor** (found in companion
399
+ review of the `init` scaffold). `regenerate_indexes` matched skip-directory
400
+ names (`.git`, `.venv`, `node_modules`, ...) against the bundle's absolute
401
+ path components instead of the bundle-relative ones, so a bundle located
402
+ under e.g. a `.venv/` or `node_modules/` parent produced "wrote 0 index.md
403
+ file(s)" with no error. Skip matching is now relative to the bundle root,
404
+ matching `discover_bundle_files` in the lint pipeline.
405
+
406
+ ## [0.3.5] - 2026-07-06
407
+
408
+ ### Fixed
409
+
410
+ - **Ambiguous rejection reason for writes outside the deployment's indexed
411
+ prefixes.** `kb_propose_edit` (and `kb_propose_memory`) rejected a
412
+ well-formed, non-malicious path outside `KB_INDEXED_PREFIXES` with the same
413
+ opaque reason string (`traversal_or_excluded` / `not_md_or_excluded`) used
414
+ for an actual traversal or control-character attempt. A syntactically fine
415
+ path outside the configured prefixes is an ordinary deployment-configuration
416
+ fact, not a structural or security rejection - conflating the two sent an
417
+ operator hunting for a nonexistent traversal bug during a company-knowledge
418
+ KB audit session (`operator/laptop.md`, readable and indexed for search,
419
+ came back rejected on write with no indication it was simply outside the
420
+ deployment's writable-prefix allowlist). `rejected_path_not_indexable`
421
+ responses (and audit events) now carry a specific reason:
422
+ `structurally_invalid` (empty/control-chars/absolute/traversal/excluded
423
+ segment), `not_markdown`, or `not_in_indexed_prefixes`.
424
+ - Investigated the same session's second report (a `rejected_stale_base`
425
+ resolve appearing to "leak" its path lock) and confirmed it is **not a
426
+ bug**: `restore_resolve` deliberately keeps the path lock held after a
427
+ gate rejection so the operator's still-pending proposal can't be clobbered
428
+ by a concurrent write to the same path (documented behavior, hardened
429
+ across multiple rounds of review). `reject`ing the stale entry (which does
430
+ release the lock) and re-proposing fresh, exactly what that session did, is
431
+ the correct and only intended recovery for a pending entry with a
432
+ permanently-wrong base marker - there was nothing to fix.
433
+
15
434
  ## [0.3.4] - 2026-07-05
16
435
 
17
436
  ### Fixed
@@ -1081,7 +1500,10 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1081
1500
  - `docs/adoption.md`: bring-your-own-KB guide (author, lint, index, serve, wire an agent).
1082
1501
  - `docs/comparison.md`: how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.
1083
1502
 
1084
- [Unreleased]: https://github.com/knaisoma/data-olympus/compare/v0.3.3...HEAD
1503
+ [Unreleased]: https://github.com/knaisoma/data-olympus/compare/v0.4.0...HEAD
1504
+ [0.4.0]: https://github.com/knaisoma/data-olympus/compare/v0.3.5...v0.4.0
1505
+ [0.3.5]: https://github.com/knaisoma/data-olympus/compare/v0.3.4...v0.3.5
1506
+ [0.3.4]: https://github.com/knaisoma/data-olympus/compare/v0.3.3...v0.3.4
1085
1507
  [0.3.3]: https://github.com/knaisoma/data-olympus/compare/v0.3.2...v0.3.3
1086
1508
  [0.3.2]: https://github.com/knaisoma/data-olympus/compare/v0.3.1...v0.3.2
1087
1509
  [0.3.1]: https://github.com/knaisoma/data-olympus/compare/v0.3.0...v0.3.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-olympus
3
- Version: 0.3.4
3
+ Version: 0.4.0
4
4
  Summary: Governance-grade knowledge-base format (OKF-compatible) plus CLI and single-writer MCP server
5
5
  Project-URL: Homepage, https://github.com/knaisoma/data-olympus
6
6
  Project-URL: Repository, https://github.com/knaisoma/data-olympus
@@ -24,6 +24,7 @@ Requires-Python: >=3.13
24
24
  Requires-Dist: fastmcp<4,>=3.2.0
25
25
  Requires-Dist: pydantic>=2.9
26
26
  Requires-Dist: pyyaml>=6.0
27
+ Requires-Dist: regex>=2024.5
27
28
  Provides-Extra: bench
28
29
  Requires-Dist: numpy>=1.26; extra == 'bench'
29
30
  Requires-Dist: sentence-transformers>=3.0; extra == 'bench'
@@ -37,6 +38,7 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
37
38
  Requires-Dist: pytest>=8.3; extra == 'dev'
38
39
  Requires-Dist: ruff>=0.15.20; extra == 'dev'
39
40
  Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
41
+ Requires-Dist: types-regex>=2024.5; extra == 'dev'
40
42
  Provides-Extra: embeddings
41
43
  Requires-Dist: fastembed<0.9,>=0.8; extra == 'embeddings'
42
44
  Description-Content-Type: text/markdown
@@ -52,7 +54,7 @@ data-olympus is a governance-grade knowledge-base format and server for agent wo
52
54
 
53
55
  It governs *decisions*, not code. When an agent is about to make a choice (a library, a pattern, a migration), data-olympus surfaces the established standard or decision that should govern that choice. It is deliberately **not** a code-search, reference-finding, or "where is X used" tool: LSP, grep, and Sourcegraph already do that well. The retrieval task it targets is coding-intent to governing-rule, and it helps where current model interaction during vibe-coding is weakest: keeping the model aligned to patterns the team has already established as correct.
54
56
 
55
- **Status: pre-1.0 beta. Latest release: v0.3.0.**
57
+ **Status: pre-1.0 beta. Latest release: v0.4.0.**
56
58
 
57
59
  ## Why
58
60
 
@@ -74,11 +76,14 @@ uv venv && uv pip install -e '.[dev]'
74
76
  # Lint the example bundle
75
77
  uv run data-olympus lint example-bundle
76
78
 
79
+ # ...or scaffold your own bundle instead of copying example-bundle/
80
+ uv run data-olympus init my-kb
81
+
77
82
  # Start the MCP server against the example bundle
78
83
  ./scripts/run-local.sh
79
84
  ```
80
85
 
81
- See `docs/quickstart.md` for the full local-run walkthrough, including curl and `kb` CLI queries.
86
+ See `docs/quickstart.md` for the full local-run walkthrough, including curl and `kb` CLI queries. See `docs/adoption.md` for the full bundle-authoring guide.
82
87
 
83
88
  ## Documentation
84
89
 
@@ -88,6 +93,7 @@ See `docs/quickstart.md` for the full local-run walkthrough, including curl and
88
93
  - [`docs/serving.md`](docs/serving.md): single-replica serving model, read-only replicas, git pull loop, health/readiness/liveness split, proxy headers, audit-log rotation.
89
94
  - [`docs/operations.md`](docs/operations.md): production runbook — backup, upgrade, recovery playbooks (degraded/fetch-failed, history rewrite, frozen/demoted push entries, orphaned locks), and the health/alerting model.
90
95
  - [`docs/comparison.md`](docs/comparison.md): how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.
96
+ - [`docs/okf-profile.md`](docs/okf-profile.md): field-by-field OKF profile — which governance extensions are stable, which are runtime-only serving fields, and which are experimental candidates.
91
97
  - [`docs/enforcement.md`](docs/enforcement.md): turning the KB into a mandatory consultation gate (hooks, `kb enforce`).
92
98
  - [`benchmarks/README.md`](benchmarks/README.md): retrieval benchmark methodology and how to reproduce the numbers in `docs/comparison.md`.
93
99
  - [`SECURITY.md`](SECURITY.md): supported versions and how to report a vulnerability.
@@ -9,7 +9,7 @@ data-olympus is a governance-grade knowledge-base format and server for agent wo
9
9
 
10
10
  It governs *decisions*, not code. When an agent is about to make a choice (a library, a pattern, a migration), data-olympus surfaces the established standard or decision that should govern that choice. It is deliberately **not** a code-search, reference-finding, or "where is X used" tool: LSP, grep, and Sourcegraph already do that well. The retrieval task it targets is coding-intent to governing-rule, and it helps where current model interaction during vibe-coding is weakest: keeping the model aligned to patterns the team has already established as correct.
11
11
 
12
- **Status: pre-1.0 beta. Latest release: v0.3.0.**
12
+ **Status: pre-1.0 beta. Latest release: v0.4.0.**
13
13
 
14
14
  ## Why
15
15
 
@@ -31,11 +31,14 @@ uv venv && uv pip install -e '.[dev]'
31
31
  # Lint the example bundle
32
32
  uv run data-olympus lint example-bundle
33
33
 
34
+ # ...or scaffold your own bundle instead of copying example-bundle/
35
+ uv run data-olympus init my-kb
36
+
34
37
  # Start the MCP server against the example bundle
35
38
  ./scripts/run-local.sh
36
39
  ```
37
40
 
38
- See `docs/quickstart.md` for the full local-run walkthrough, including curl and `kb` CLI queries.
41
+ See `docs/quickstart.md` for the full local-run walkthrough, including curl and `kb` CLI queries. See `docs/adoption.md` for the full bundle-authoring guide.
39
42
 
40
43
  ## Documentation
41
44
 
@@ -45,6 +48,7 @@ See `docs/quickstart.md` for the full local-run walkthrough, including curl and
45
48
  - [`docs/serving.md`](docs/serving.md): single-replica serving model, read-only replicas, git pull loop, health/readiness/liveness split, proxy headers, audit-log rotation.
46
49
  - [`docs/operations.md`](docs/operations.md): production runbook — backup, upgrade, recovery playbooks (degraded/fetch-failed, history rewrite, frozen/demoted push entries, orphaned locks), and the health/alerting model.
47
50
  - [`docs/comparison.md`](docs/comparison.md): how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.
51
+ - [`docs/okf-profile.md`](docs/okf-profile.md): field-by-field OKF profile — which governance extensions are stable, which are runtime-only serving fields, and which are experimental candidates.
48
52
  - [`docs/enforcement.md`](docs/enforcement.md): turning the KB into a mandatory consultation gate (hooks, `kb enforce`).
49
53
  - [`benchmarks/README.md`](benchmarks/README.md): retrieval benchmark methodology and how to reproduce the numbers in `docs/comparison.md`.
50
54
  - [`SECURITY.md`](SECURITY.md): supported versions and how to report a vulnerability.