data-olympus 0.3.4__tar.gz → 0.3.5__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 (81) hide show
  1. {data_olympus-0.3.4 → data_olympus-0.3.5}/CHANGELOG.md +28 -0
  2. {data_olympus-0.3.4 → data_olympus-0.3.5}/PKG-INFO +1 -1
  3. {data_olympus-0.3.4 → data_olympus-0.3.5}/pyproject.toml +1 -1
  4. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/auth.py +26 -0
  5. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/tools_write.py +12 -4
  6. {data_olympus-0.3.4 → data_olympus-0.3.5}/.gitignore +0 -0
  7. {data_olympus-0.3.4 → data_olympus-0.3.5}/LICENSE +0 -0
  8. {data_olympus-0.3.4 → data_olympus-0.3.5}/NOTICE +0 -0
  9. {data_olympus-0.3.4 → data_olympus-0.3.5}/README.md +0 -0
  10. {data_olympus-0.3.4 → data_olympus-0.3.5}/SPEC.md +0 -0
  11. {data_olympus-0.3.4 → data_olympus-0.3.5}/benchmarks/README.md +0 -0
  12. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/_kb_detect_workspace.sh +0 -0
  13. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/_kb_enforce.py +0 -0
  14. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/_kb_fallback.py +0 -0
  15. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/kb +0 -0
  16. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/kb-enforce-hook +0 -0
  17. {data_olympus-0.3.4 → data_olympus-0.3.5}/bin/opencode/data-olympus-gate.ts +0 -0
  18. {data_olympus-0.3.4 → data_olympus-0.3.5}/deploy/k8s/README.md +0 -0
  19. {data_olympus-0.3.4 → data_olympus-0.3.5}/deploy/k8s/read-replica/README.md +0 -0
  20. {data_olympus-0.3.4 → data_olympus-0.3.5}/docs/releases/README.md +0 -0
  21. {data_olympus-0.3.4 → data_olympus-0.3.5}/example-bundle/projects/acme-app/README.md +0 -0
  22. {data_olympus-0.3.4 → data_olympus-0.3.5}/example-bundle/projects/acme-app/components/api/README.md +0 -0
  23. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/__init__.py +0 -0
  24. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/audit_log.py +0 -0
  25. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/audit_trailers.py +0 -0
  26. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cli/__init__.py +0 -0
  27. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cli/import_cmd.py +0 -0
  28. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cli/indexgen.py +0 -0
  29. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cli/main.py +0 -0
  30. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cli/report_cmd.py +0 -0
  31. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/config.py +0 -0
  32. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/cooccurrence.py +0 -0
  33. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/dedup.py +0 -0
  34. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/durable.py +0 -0
  35. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/embeddings.py +0 -0
  36. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/enforce_policy.py +0 -0
  37. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/format/__init__.py +0 -0
  38. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/format/document.py +0 -0
  39. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/format/frontmatter.py +0 -0
  40. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/format/lint.py +0 -0
  41. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/format/validate.py +0 -0
  42. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/git_ops.py +0 -0
  43. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/health.py +0 -0
  44. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/__init__.py +0 -0
  45. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/adr.py +0 -0
  46. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/flat.py +0 -0
  47. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/model.py +0 -0
  48. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/okf.py +0 -0
  49. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/run.py +0 -0
  50. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/importer/stamp.py +0 -0
  51. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/index.py +0 -0
  52. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/markdown_parse.py +0 -0
  53. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/models.py +0 -0
  54. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/onboarding.py +0 -0
  55. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/onboarding_inflight.py +0 -0
  56. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/onboarding_playbook.py +0 -0
  57. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/pending.py +0 -0
  58. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/principals.py +0 -0
  59. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/prompts.py +0 -0
  60. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/push_queue.py +0 -0
  61. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/query_expansion.py +0 -0
  62. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/rate_limit.py +0 -0
  63. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/refresh.py +0 -0
  64. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/report.py +0 -0
  65. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/rest_api.py +0 -0
  66. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/safe_id.py +0 -0
  67. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/search_gate.py +0 -0
  68. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/search_shortcut.py +0 -0
  69. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/server.py +0 -0
  70. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/session_metrics.py +0 -0
  71. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/setup_wizard.py +0 -0
  72. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/thin_pointer.py +0 -0
  73. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/tools_audit.py +0 -0
  74. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/tools_enforce.py +0 -0
  75. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/tools_onboarding.py +0 -0
  76. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/tools_read.py +0 -0
  77. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/trigram.py +0 -0
  78. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/viewer/__init__.py +0 -0
  79. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/viewer/generator.py +0 -0
  80. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/worktrees.py +0 -0
  81. {data_olympus-0.3.4 → data_olympus-0.3.5}/src/data_olympus/write_gate.py +0 -0
@@ -12,6 +12,34 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.3.5] - 2026-07-06
16
+
17
+ ### Fixed
18
+
19
+ - **Ambiguous rejection reason for writes outside the deployment's indexed
20
+ prefixes.** `kb_propose_edit` (and `kb_propose_memory`) rejected a
21
+ well-formed, non-malicious path outside `KB_INDEXED_PREFIXES` with the same
22
+ opaque reason string (`traversal_or_excluded` / `not_md_or_excluded`) used
23
+ for an actual traversal or control-character attempt. A syntactically fine
24
+ path outside the configured prefixes is an ordinary deployment-configuration
25
+ fact, not a structural or security rejection - conflating the two sent an
26
+ operator hunting for a nonexistent traversal bug during a company-knowledge
27
+ KB audit session (`operator/laptop.md`, readable and indexed for search,
28
+ came back rejected on write with no indication it was simply outside the
29
+ deployment's writable-prefix allowlist). `rejected_path_not_indexable`
30
+ responses (and audit events) now carry a specific reason:
31
+ `structurally_invalid` (empty/control-chars/absolute/traversal/excluded
32
+ segment), `not_markdown`, or `not_in_indexed_prefixes`.
33
+ - Investigated the same session's second report (a `rejected_stale_base`
34
+ resolve appearing to "leak" its path lock) and confirmed it is **not a
35
+ bug**: `restore_resolve` deliberately keeps the path lock held after a
36
+ gate rejection so the operator's still-pending proposal can't be clobbered
37
+ by a concurrent write to the same path (documented behavior, hardened
38
+ across multiple rounds of review). `reject`ing the stale entry (which does
39
+ release the lock) and re-proposing fresh, exactly what that session did, is
40
+ the correct and only intended recovery for a pending entry with a
41
+ permanently-wrong base marker - there was nothing to fix.
42
+
15
43
  ## [0.3.4] - 2026-07-05
16
44
 
17
45
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-olympus
3
- Version: 0.3.4
3
+ Version: 0.3.5
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "data-olympus"
3
- version = "0.3.4"
3
+ version = "0.3.5"
4
4
  description = "Governance-grade knowledge-base format (OKF-compatible) plus CLI and single-writer MCP server"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -95,6 +95,32 @@ def is_writable_path(target_path: str) -> bool:
95
95
  return any(canonical.startswith(p) for p in indexed_prefixes())
96
96
 
97
97
 
98
+ def path_rejection_reason(raw_target_path: str) -> str:
99
+ """Why a path fails :func:`is_writable_path`, for a caller that already knows
100
+ it does. Three distinct causes were previously collapsed into one opaque
101
+ reason string (``not_md_or_excluded`` / ``traversal_or_excluded``), which
102
+ reads identically whether the path was a malicious traversal attempt or a
103
+ perfectly legitimate path outside the deployment's configured
104
+ ``KB_INDEXED_PREFIXES`` - the latter is an ordinary, expected outcome for any
105
+ deployment whose repo layout has top-level directories beyond the generic
106
+ default (see ``indexed_prefixes()``), not a security event, and conflating
107
+ the two sent at least one operator hunting for a traversal bug that was
108
+ actually a missing ``KB_INDEXED_PREFIXES`` entry.
109
+
110
+ Returns one of: ``structurally_invalid`` (empty/control-chars/absolute/
111
+ traversal/excluded-segment - :func:`normalize_target_path` rejected it),
112
+ ``not_markdown``, or ``not_in_indexed_prefixes`` (a syntactically fine path
113
+ outside every configured prefix - a deployment-configuration gap, not a
114
+ structural or security rejection).
115
+ """
116
+ canonical = normalize_target_path(raw_target_path)
117
+ if canonical is None:
118
+ return "structurally_invalid"
119
+ if not canonical.endswith(".md"):
120
+ return "not_markdown"
121
+ return "not_in_indexed_prefixes"
122
+
123
+
98
124
  def safe_join_under_root(root: str, target_path: str) -> str | None:
99
125
  """Join ``target_path`` under ``root`` and return the absolute path only when
100
126
  it resolves to exactly that lexical location inside ``root``; else return None.
@@ -15,6 +15,7 @@ from data_olympus.auth import (
15
15
  PathBlocklist,
16
16
  is_writable_path,
17
17
  normalize_target_path,
18
+ path_rejection_reason,
18
19
  safe_join_under_root,
19
20
  )
20
21
  from data_olympus.format.frontmatter import parse_frontmatter
@@ -486,11 +487,12 @@ def kb_propose_memory_fn(
486
487
 
487
488
  # 1. Structural rule (cheap).
488
489
  if not is_writable_path(target_path):
490
+ reason = path_rejection_reason(target_path)
489
491
  _emit_audit(audit_log, **{**audit_base, "status": "rejected_path_not_indexable",
490
- "reason": "not_md_or_excluded"})
492
+ "reason": reason})
491
493
  return ProposeResponse(
492
494
  status="rejected_path_not_indexable",
493
- reason="not_md_or_excluded",
495
+ reason=reason,
494
496
  target_path=target_path,
495
497
  )
496
498
 
@@ -657,9 +659,15 @@ def kb_propose_edit_fn(
657
659
  }
658
660
  canonical = normalize_target_path(target_path)
659
661
  if canonical is None or not is_writable_path(canonical):
660
- _emit_audit(audit_log, **{**audit_base, "status": "rejected_path_not_indexable"})
662
+ # audit_base["reason"] otherwise carries the caller's edit rationale;
663
+ # for this terminal rejection (nothing is written) it is overwritten
664
+ # with the machine rejection cause instead, since that is what an
665
+ # operator reading the audit log needs to see here.
666
+ rejection_reason = path_rejection_reason(target_path)
667
+ _emit_audit(audit_log, **{**audit_base, "status": "rejected_path_not_indexable",
668
+ "reason": rejection_reason})
661
669
  return ProposeResponse(status="rejected_path_not_indexable",
662
- reason="traversal_or_excluded",
670
+ reason=rejection_reason,
663
671
  target_path=target_path)
664
672
  # From here on operate ONLY on the canonical path: classification, blocklist,
665
673
  # the pending record, the filesystem join, and ``git add`` must all agree with
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes