Skip to content

feat(timeline): kb.timeline — entity chronological trajectory (#313)#354

Closed
devmixa702 wants to merge 15 commits into
vouchdev:mainfrom
devmixa702:feat/timeline
Closed

feat(timeline): kb.timeline — entity chronological trajectory (#313)#354
devmixa702 wants to merge 15 commits into
vouchdev:mainfrom
devmixa702:feat/timeline

Conversation

@devmixa702

@devmixa702 devmixa702 commented Jul 3, 2026

Copy link
Copy Markdown

parallels #352 (dhgoal's original kb.timeline); opened from a fork so it can
carry a small review follow-up on top of the feature. credit for the feature
itself to @dhgoal.

kb.read_entity returns an entity and its claims as a flat set; kb.neighbors
expands graph adjacency at a point in time. neither answers "what did the kb
learn about this entity, in what order?" — a trajectory. this orders an
entity's approved claims and relations along a time axis, oldest-first.

  • ordereffective uses artifact created_at (when the fact entered the
    kb); decided recovers approval time from the append-only audit log.
  • filters — --since / --until / --types / --limit; --json for the
    machine shape.
  • superseded/archived claims still appear, flagged by current status; relations
    carry status = null; pending proposals never appear.
  • pure read — no write path is reachable from it. registered at all four
    surfaces (mcp/jsonl/capabilities/cli), and attaches _meta.vouch_salience
    when a session_id is passed.

on top of the feature, one review follow-up: metrics._APPROVE_RE is promoted
to a public metrics.APPROVE_RE, so timeline reuses it intentionally rather
than reaching into another module's private symbol — a rename in metrics would
otherwise silently break the decided-order axis with no compile-time signal.

jsdevninja and others added 14 commits July 1, 2026 23:55
scaffold typed page and entity proposals from the page-kind registry so
authors get correctly-shaped drafts through the normal review gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
catch invalid yaml in --field and interactive prompts, gate citation-required kinds before filing, unify dry-run json id key, and move new command tests into test_cli.py.

Co-authored-by: Cursor <cursoragent@cursor.com>
the old openclaw.plugin.json spoke a manifest dialect current openclaw
no longer parses: installs of the linked repo failed before load (the
loader hard-requires id + configSchema, routes any dir containing an
openclaw.plugin.json away from the bundle path, and needs a
package.json openclaw.extensions pointer to find the entry module).
every old-dialect field the manifest relied on — mcpServers, contracts,
family, shared_deps, openclaw.* — is silently ignored by the current
parser.

rewrite the packaging for the current contract, verified against a real
openclaw 2026.6.11 install:

* openclaw.plugin.json: id + json-schema configSchema + kind:
  context-engine + version (synced with pyproject) + skills as
  SKILL.md directories under adapters/openclaw/skills/ (mirrored from
  the claude-code commands; a sync test keeps them identical). the
  kb.* mcp server moves to deployment config (openclaw mcp add).
* package.json (new, loader-facing only): openclaw.extensions entry
  pointer + openclaw.compat.pluginApi floor.
* engine id renamed vouch-context -> vouch to equal the plugin id:
  the installer auto-binds plugins.slots.contextEngine to the plugin
  id while resolveContextEngine looks the slot value up by engine id,
  so distinct ids quarantined the engine and silently fell back to
  openclaw's legacy engine.
* vouch openclaw-rpc: json-serialize datetimes in wire payloads.
  contextPack.generated_at crashed json.dumps on every assemble that
  found a kb — found by running a live agent turn against the linked
  plugin, where openclaw quarantined the engine for the process.
* tests: manifest sync tests rewritten for the new dialect (id
  alignment, three-way version sync, publishable skill dirs, dead
  dialect fields rejected); new tier-2 e2e
  (tests/test_openclaw_plugin_load_real.py, skipped without the
  openclaw cli) links the repo into an isolated profile and asserts
  import, engine registration, slot auto-bind, skill publication, and
  a clean plugins doctor; new stdio round-trip regression test for the
  datetime fix.

verified live: plugins install --link of this repo, inspect --runtime
(imported, loaded, contextEngineIds ["vouch"]), four skills ready,
plugins doctor clean, and a full embedded agent turn where assemble()
ran against a real kb with zero context-engine quarantines.
93f5836 made storage.list_sources() skip artifacts it cannot parse so one corrupt file no longer crashes bulk listings, and pinned utf-8 so a non-utf-8 locale no longer mis-decodes healthy files into mojibake at read time. the skip leaves a gap for files that really are corrupt on disk: a hand edit or an external writer under a mismatched locale can land a raw control character pyyaml rejects, and such a meta then vanishes from the lint sweep with only a log warning while any claim citing the source is misreported as broken_citation.

lint now loads source metas per-file like _load_claims_for_lint does for claims: a meta that fails to parse becomes an unreadable_source error finding with a repair hint, and the source id (its directory name) still counts as present so citation checks point at the actual problem instead of a phantom missing source.
# Conflicts:
#	CHANGELOG.md
#	openclaw.plugin.json
…emplate-refs

Docs/remove gittensor template refs
…ifest

fix(openclaw): repackage plugin for the 2026.6 loader dialect
fix(health): surface unreadable source metas as lint findings
new remember-across-sessions tutorial told through prompts typed into
the claude code window, with real captured transcripts: monday's
session fixes a staging bug and proposes what it learned over mcp; the
sessionstart hook injects the approved claims into tuesday's fresh
session, which answers a pr-review question in 2 turns citing the
incident and its regression test, while the same question without
vouch is slower and generic.

includes a measured a/b benchmark on the fix task (4 verified headless
runs per arm, claude-sonnet-5): 17% faster, 32% fewer turns, 31% fewer
output tokens, 18% cheaper with the ~260-token recall digest injected.
adds the tutorial to the index.
feat(cli): add vouch new scaffold command
1. Add 8 new CLI commands for reading and listing approved artifacts
   (read-{claim,page,entity,relation}, list-{claims,pages,entities,relations})

2. Document self-approval prevention in getting-started guide with
   config option for local testing (approver_role: trusted-agent)

3. Parameterize jsonl-quickstart test to support dynamic method count

4. Complete decision-log example with 5 approved decisions and full KB

Fixes discovered during end-to-end installation testing.
the relation model uses source/relation/target but the list-relations
command was incorrectly trying to access from_id/relation_type/to_id.
this caused mypy errors that failed the ci type-check step.
…ifest

fix: add CLI read/list, docs, fix test, complete example
…ev#313)

read_entity returns an entity's claims as a flat set; neighbors expands
graph adjacency. neither answers "what did the kb learn about this
entity, in what order?". kb.timeline orders an entity's approved claims
and relations along a time axis, oldest first.

- order=effective uses the artifact's own created_at (accrual time);
  order=decided recovers approval time from the audit log's approve
  events (object_ids[1] -> created_at), falling back to created_at for
  artifacts written outside the proposal path.
- since/until/types/limit filters; limit keeps the most recent n while
  preserving chronological order.
- superseded/archived claims still appear, flagged by current status;
  relations carry status=null; pending proposals never appear.

pure read: no propose_*, no approve, no write path is reachable — a
viewport over already-reviewed artifacts. all ordering logic lives in a
new timeline.py, not storage.py. registered at all four surfaces
(mcp/jsonl/capabilities/cli); attaches _meta.vouch_salience when a
session_id is passed, per the per-tool convention.
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface adapters agent host adapters and install manifests openclaw openclaw integration mcp mcp, jsonl, and http surfaces tests tests and fixtures size: XL 1000 or more changed non-doc lines labels Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a read-only kb.timeline feature, expands vouch new plus approved-artifact read/list commands, realigns OpenClaw packaging and engine IDs, fixes RPC serialization and lint source-metadata validation, and adds example fixtures and tutorial documentation.

Changes

kb.timeline feature

Layer / File(s) Summary
Timeline domain module
src/vouch/timeline.py
Builds entity timelines with effective/decided ordering, filtering, windowing, and summary/status fields.
Server wiring
src/vouch/capabilities.py, src/vouch/jsonl_server.py, src/vouch/server.py, examples/jsonl-quickstart/run.sh
Registers kb.timeline across capabilities, JSONL, and MCP surfaces, and updates the quickstart assertion.
Timeline CLI command
src/vouch/cli.py, CHANGELOG.md
Adds vouch timeline <entity> with ordering, filters, and JSON/table output.
Timeline tests
tests/test_timeline.py
Covers ordering, filtering, limits, error handling, registration, and CLI output.

Approved-artifact reads and vouch new scaffolding

Layer / File(s) Summary
Read/list approved commands
src/vouch/cli.py, README.md, docs/getting-started.md
Adds read/list commands for approved claims, pages, entities, and relations, and documents them.
vouch new helpers
src/vouch/cli.py
Updates meta parsing and adds page/entity resolution, prompting, and draft rendering helpers.
vouch new command
src/vouch/cli.py, CHANGELOG.md
Implements vouch new <kind> with dry-run, proposal creation, and citation handling.
vouch new tests
tests/test_cli.py
Covers proposal creation, dry-run, routing, citations, invalid fields, and review-queue behavior.

OpenClaw plugin packaging alignment

Layer / File(s) Summary
Manifest and engine IDs
openclaw.plugin.json, package.json, adapters/openclaw/vouch-context-engine.mjs, src/vouch/openclaw/context_engine.py
Aligns the plugin manifest, package entry point, and engine identifiers with the current loader dialect.
Packaging docs and skills
CLAUDE.md, README.md, adapters/openclaw/README.md, adapters/openclaw/skills/*/SKILL.md, CHANGELOG.md
Updates packaging guidance and adds four OpenClaw skill specs.
RPC serialization fix
src/vouch/openclaw/rpc.py, tests/test_openclaw_context_engine.py
Adds datetime/path JSON serialization for stdio responses and verifies it with a regression test.
Real loader e2e tests
tests/test_openclaw_plugin_load_real.py
Verifies real OpenClaw install, inspection, binding, skills, and doctor output.
Manifest contract tests
tests/test_openclaw_plugin_manifest.py
Validates manifest/package alignment, skill publishability, and dead-field removal.

Lint source-metadata validation fix

Layer / File(s) Summary
Source metadata lint check
src/vouch/health.py, tests/test_health.py
Scans source meta files during lint and reports unreadable source metadata.

Example decision-log fixture directory

Layer / File(s) Summary
Decision-log example fixtures
examples/decision-log/vouch/*
Adds a full example KB fixture tree with config, audit log, claims, pages, sources, and schema version.

Tutorial and getting-started documentation

Layer / File(s) Summary
Cross-session memory tutorial
docs/tutorials/remember-across-sessions.md, docs/tutorials/README.md, docs/getting-started.md
Adds a new tutorial and updates the getting-started approval guidance.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related issues

Possibly related PRs

  • vouchdev/vouch#44: Both extend the shared src/vouch/capabilities.py METHODS registry.
  • vouchdev/vouch#221: Both modify OpenClaw plugin integration artifacts, especially openclaw.plugin.json.
  • vouchdev/vouch#333: Both implement and test vouch new <kind> scaffolding.

Suggested labels: mcp, storage, tests, size: XL

Suggested reviewers: plind-junior

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately highlights the main change: the new kb.timeline entity trajectory feature.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

timeline recovered approval time from the audit log by importing metrics'
private `_APPROVE_RE`. promote it to a public `APPROVE_RE` so the cross-module
use is intentional rather than reaching into another module's private symbol —
a rename in metrics would otherwise silently break the decided-order axis, with
every artifact falling back to created_at and no compile-time signal.

`_CREATE_RE` / `_REJECT_RE` stay private; only the approve regex is shared.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/vouch/health.py (1)

124-165: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Solid implementation; matches upstream Source/storage contracts.

Logic correctly surfaces corrupt meta.yaml as unreadable_source while still counting the id as "present" for citation checks (avoiding a misleading broken_citation), consistent with the Source model and sources/<sha>/meta.yaml on-disk layout used elsewhere in storage.py.

One optional nit: this function structurally mirrors _load_claims_for_lint (iterate → parse → validate → catch → append Finding). Not urgent, since the iteration mechanics differ (glob vs. iterdir + explicit meta check), but a shared helper could reduce duplication if a third similar scanner (e.g. entities/relations) is added later.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/health.py` around lines 124 - 165, The implementation is correct,
and no functional change is required; this mirrors `_load_claims_for_lint`’s
iterate/parse/validate/catch/appended Finding pattern in
`_collect_sources_for_lint` and fits the `Source`/`meta.yaml` contract. If you
touch this area later, consider extracting the shared scan-and-validate flow
into a helper used by `_load_claims_for_lint` and `_collect_sources_for_lint` to
reduce duplication, especially if additional scanners are added.
src/vouch/cli.py (1)

1395-1416: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate citation_reminder derivation.

The dry-run guard re-derives requires_citations and not (claims or sources), which is exactly citation_reminder computed a few lines above. Reuse the variable for clarity.

♻️ Proposed fix
     if dry_run:
-        if not missing and not (requires_citations and not (claims or sources)):
+        if not missing and not citation_reminder:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/cli.py` around lines 1395 - 1416, The dry-run branch in cli.py is
recalculating the citation reminder condition instead of reusing the existing
citation_reminder variable. Update the logic in the page draft flow around
page_draft and the dry_run check to reference citation_reminder directly so the
condition is defined once and stays consistent.
src/vouch/timeline.py (2)

49-63: 🚀 Performance & Scalability | 🔵 Trivial

Full audit-log scan on every order=decided call.

_decided_map re-reads the entire audit.log.jsonl on every invocation of build_timeline(order="decided"). Fine today, but worth keeping in mind as a scaling limit for KBs with long-lived, heavily-audited histories (repeated kb.timeline calls become O(events) each).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/timeline.py` around lines 49 - 63, The _decided_map helper in
timeline.py rescans the full audit stream every time
build_timeline(order="decided") runs, which makes repeated timeline calls scale
poorly. Update _decided_map (and any caller path in build_timeline) to avoid
re-reading all events on each invocation by caching the decided mapping per
KBStore/kb_dir or otherwise deriving it from a maintained index, while keeping
the fallback behavior for artifacts without an approve event.

22-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Export a public approval-regex helper
timeline.py imports metrics._APPROVE_RE, but metrics.py only defines it as a private module constant. Expose a public alias/helper there and use that instead so _decided_map doesn’t couple to an internal name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/timeline.py` around lines 22 - 23, The approval-regex is being
imported in timeline.py through the private metrics._APPROVE_RE constant, which
creates an internal dependency. In metrics.py, add a public alias or helper for
the approval regex and update _decided_map in timeline.py to import and use that
सार्वजनिक symbol instead of the underscored one. Keep the existing regex
behavior the same, but route access through the new public name so the timeline
code no longer depends on a private module constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tutorials/README.md`:
- Around line 30-40: Update the tutorial README intro to stay consistent with
the “one session” framing by explicitly marking this walkthrough as a
two-session exception or softening the claim in the opening copy. Locate the
affected tutorial blurb around the “Carry what one Claude Code session learned
into the next” entry and adjust the surrounding language so it clearly signals
that this example spans two sessions while preserving the rest of the page’s
messaging.

In `@docs/tutorials/remember-across-sessions.md`:
- Around line 13-17: The tutorial text overstates that the 30-second review is
the only vouch command ever typed, which conflicts with the setup steps that use
vouch init and vouch install-mcp. Update the wording in the
remember-across-sessions section to qualify the claim as applying after setup or
once installed, so the description matches the commands shown in the tutorial
and avoids contradicting itself.

In `@src/vouch/jsonl_server.py`:
- Around line 185-204: The `handle_request` exception mapping is treating
`ArtifactNotFoundError` from `kb.timeline` as `missing_param` because it
subclasses `KeyError`. Update the `except` ordering in
`src/vouch/jsonl_server.py::handle_request` so the more specific unknown-entity
path from `_h_timeline`/`build_timeline` is caught before the generic `KeyError`
branch and returned as `invalid_request`.

In `@src/vouch/timeline.py`:
- Around line 93-109: In the timeline filtering logic, `types` can be a plain
string and currently gets treated like an iterable of characters in the `want =
{t.strip() ...}` path. Update the input validation in the function that builds
`want` so `types` must be a list/sequence of strings, and raise `TimelineError`
with a clear message when a string or other invalid type is passed. Keep the
rest of the `store.get_entity`, `_as_utc`, and `ORDER_DECIDED` flow unchanged.

In `@tests/test_timeline.py`:
- Around line 189-195: The kb.timeline test currently calls the handler directly
and misses the JSONL request envelope shape that handle_request returns. Update
test_jsonl_handler_runs to exercise the JSONL path via the request wrapper used
by vouch.jsonl_server.handle_request, and assert the full {id, ok, result}
success envelope (or {id, ok: false, error} on failure) instead of only checking
HANDLERS["kb.timeline"] output. Keep the existing kb.timeline and HANDLERS
symbols as the locator for the handler under test.

---

Nitpick comments:
In `@src/vouch/cli.py`:
- Around line 1395-1416: The dry-run branch in cli.py is recalculating the
citation reminder condition instead of reusing the existing citation_reminder
variable. Update the logic in the page draft flow around page_draft and the
dry_run check to reference citation_reminder directly so the condition is
defined once and stays consistent.

In `@src/vouch/health.py`:
- Around line 124-165: The implementation is correct, and no functional change
is required; this mirrors `_load_claims_for_lint`’s
iterate/parse/validate/catch/appended Finding pattern in
`_collect_sources_for_lint` and fits the `Source`/`meta.yaml` contract. If you
touch this area later, consider extracting the shared scan-and-validate flow
into a helper used by `_load_claims_for_lint` and `_collect_sources_for_lint` to
reduce duplication, especially if additional scanners are added.

In `@src/vouch/timeline.py`:
- Around line 49-63: The _decided_map helper in timeline.py rescans the full
audit stream every time build_timeline(order="decided") runs, which makes
repeated timeline calls scale poorly. Update _decided_map (and any caller path
in build_timeline) to avoid re-reading all events on each invocation by caching
the decided mapping per KBStore/kb_dir or otherwise deriving it from a
maintained index, while keeping the fallback behavior for artifacts without an
approve event.
- Around line 22-23: The approval-regex is being imported in timeline.py through
the private metrics._APPROVE_RE constant, which creates an internal dependency.
In metrics.py, add a public alias or helper for the approval regex and update
_decided_map in timeline.py to import and use that सार्वजनिक symbol instead of
the underscored one. Keep the existing regex behavior the same, but route access
through the new public name so the timeline code no longer depends on a private
module constant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c6b376fd-2d08-440d-8a71-69b00cf8ee23

📥 Commits

Reviewing files that changed from the base of the PR and between d80b1e2 and 5ec569f.

📒 Files selected for processing (53)
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • adapters/openclaw/README.md
  • adapters/openclaw/skills/vouch-propose-from-pr/SKILL.md
  • adapters/openclaw/skills/vouch-recall/SKILL.md
  • adapters/openclaw/skills/vouch-resolve-issue/SKILL.md
  • adapters/openclaw/skills/vouch-status/SKILL.md
  • adapters/openclaw/vouch-context-engine.mjs
  • docs/getting-started.md
  • docs/tutorials/README.md
  • docs/tutorials/remember-across-sessions.md
  • examples/decision-log/vouch/.gitignore
  • examples/decision-log/vouch/audit.log.jsonl
  • examples/decision-log/vouch/claims/billing-data-requires-acid-guarantees-postgresql-is-the-prim.yaml
  • examples/decision-log/vouch/claims/critical-system-incidents-must-have-a-15-minute-response-sla.yaml
  • examples/decision-log/vouch/claims/free-tier-100-req-superseded.yaml
  • examples/decision-log/vouch/claims/free-tier-500-req.yaml
  • examples/decision-log/vouch/claims/free-tier-gets-100-requests-per-day-as-baseline.yaml
  • examples/decision-log/vouch/claims/incident-response-15min-sla.yaml
  • examples/decision-log/vouch/claims/use-postgres-for-billing.yaml
  • examples/decision-log/vouch/claims/use-postgresql-15-for-enhanced-replication-and-monitoring-fe.yaml
  • examples/decision-log/vouch/claims/vouch-starter-reviewed-knowledge.yaml
  • examples/decision-log/vouch/config.yaml
  • examples/decision-log/vouch/decided/20260703-091452-063145f6.yaml
  • examples/decision-log/vouch/decided/20260703-091452-4ffb9687.yaml
  • examples/decision-log/vouch/decided/20260703-091452-976471f3.yaml
  • examples/decision-log/vouch/decided/20260703-091453-4ca258b5.yaml
  • examples/decision-log/vouch/pages/edit-in-obsidian.md
  • examples/decision-log/vouch/schema_version
  • examples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/content
  • examples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/meta.yaml
  • examples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/content
  • examples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/meta.yaml
  • examples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/content
  • examples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/meta.yaml
  • examples/jsonl-quickstart/run.sh
  • openclaw.plugin.json
  • package.json
  • src/vouch/capabilities.py
  • src/vouch/cli.py
  • src/vouch/health.py
  • src/vouch/jsonl_server.py
  • src/vouch/openclaw/context_engine.py
  • src/vouch/openclaw/rpc.py
  • src/vouch/server.py
  • src/vouch/timeline.py
  • tests/test_cli.py
  • tests/test_health.py
  • tests/test_openclaw_context_engine.py
  • tests/test_openclaw_plugin_load_real.py
  • tests/test_openclaw_plugin_manifest.py
  • tests/test_timeline.py
💤 Files with no reviewable changes (4)
  • examples/decision-log/vouch/claims/use-postgres-for-billing.yaml
  • examples/decision-log/vouch/claims/free-tier-500-req.yaml
  • examples/decision-log/vouch/claims/free-tier-100-req-superseded.yaml
  • examples/decision-log/vouch/claims/incident-response-15min-sla.yaml

Comment thread docs/tutorials/README.md
Comment on lines +30 to +40
- [**Carry what one Claude Code session learned into the next**](remember-across-sessions.md)
— the two-session story, told entirely through prompts typed into the Claude
Code window (real captured transcripts). Monday you type a bug report;
Claude fixes it and proposes what it learned to vouch on its own (MCP +
hooks — you never type a vouch command except the 30-second approve).
Tuesday you type a PR-review question into a brand-new session: the
SessionStart hook has already injected the approved claims, and Claude
answers in 2 turns and 15 seconds citing the incident and the regression
test — while the same question without vouch is 33% slower and generic.
Includes a measured A/B benchmark (~17% faster, ~32% fewer turns, ~18%
cheaper on the same bug-fix task). About 15 minutes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

call out the two-session exception.

this entry is explicitly a two-session walkthrough, but the intro above still says tutorials get from nothing to a working result "in one session." either relax that framing or label this one as the exception so the page stays consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tutorials/README.md` around lines 30 - 40, Update the tutorial README
intro to stay consistent with the “one session” framing by explicitly marking
this walkthrough as a two-session exception or softening the claim in the
opening copy. Locate the affected tutorial blurb around the “Carry what one
Claude Code session learned into the next” entry and adjust the surrounding
language so it clearly signals that this example spans two sessions while
preserving the rest of the page’s messaging.

Comment on lines +13 to +17
You never drive vouch yourself. The agent proposes knowledge over MCP
while it works, hooks auto-capture the session, and every new session
gets the approved claims injected before your first message. The only
vouch command you ever type is the 30-second review — which is the point:
nothing becomes "what the team knows" without a human approving it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

qualify the no-vouch-command claim.

this says the only vouch command you ever type is the 30-second review, but the setup immediately below still has vouch init and vouch install-mcp. narrow the claim to "after setup" or "once installed" so the tutorial does not contradict itself.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/tutorials/remember-across-sessions.md` around lines 13 - 17, The
tutorial text overstates that the 30-second review is the only vouch command
ever typed, which conflicts with the setup steps that use vouch init and vouch
install-mcp. Update the wording in the remember-across-sessions section to
qualify the claim as applying after setup or once installed, so the description
matches the commands shown in the tutorial and avoids contradicting itself.

Comment thread src/vouch/jsonl_server.py
Comment on lines +185 to +204
def _h_timeline(p: dict) -> dict:
from .metrics import parse_since
from .timeline import build_timeline

store = _store()
cfg = _load_cfg(store)
session_id = p.get("session_id")
limit = p.get("limit")
result = build_timeline(
store,
p["entity_id"],
since=parse_since(p.get("since")),
until=parse_since(p.get("until")),
order=p.get("order", "effective"),
types=p.get("types"),
limit=int(limit) if limit is not None else None,
)
return salience_mod.attach_salience(result, store, session_id, cfg)


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Files of interest\n'
git ls-files 'src/vouch/jsonl_server.py' 'src/vouch/storage.py' 'src/vouch/*' | sed -n '1,120p'

printf '\n## Relevant symbols in src/vouch/jsonl_server.py\n'
ast-grep outline src/vouch/jsonl_server.py --view expanded | sed -n '1,220p'

printf '\n## Relevant symbols in src/vouch/storage.py\n'
ast-grep outline src/vouch/storage.py --view expanded | sed -n '1,220p'

printf '\n## Find handle_request and ArtifactNotFoundError definitions/usages\n'
rg -n "def handle_request|class ArtifactNotFoundError|except KeyError|except \\(ValueError, ProposalError, ArtifactNotFoundError\\)|kb.timeline|timeline" src/vouch -n -A 4 -B 4

Repository: vouchdev/vouch

Length of output: 25538


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## build_timeline exception path\n'
sed -n '74,170p' src/vouch/timeline.py | cat -n

printf '\n## jsonl_server handle_request exception ordering\n'
sed -n '757,784p' src/vouch/jsonl_server.py | cat -n

printf '\n## any tests covering jsonl handle_request or kb.timeline errors\n'
rg -n "kb.timeline|handle_request\\(|missing_param|invalid_request|ArtifactNotFoundError" tests src/vouch -g '!src/vouch/web/**' -A 3 -B 3

Repository: vouchdev/vouch

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## timeline tests around JSONL coverage\n'
sed -n '180,220p' tests/test_timeline.py | cat -n

printf '\n## jsonl server tests around error mapping\n'
sed -n '1,120p' tests/test_jsonl_server.py | cat -n

Repository: vouchdev/vouch

Length of output: 7201


Map missing entity_id to invalid_request in kb.timeline.
ArtifactNotFoundError from build_timeline() subclasses KeyError, so the JSONL envelope currently reports an unknown entity as missing_param. Reorder the except blocks in src/vouch/jsonl_server.py::handle_request so this path surfaces as invalid_request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/jsonl_server.py` around lines 185 - 204, The `handle_request`
exception mapping is treating `ArtifactNotFoundError` from `kb.timeline` as
`missing_param` because it subclasses `KeyError`. Update the `except` ordering
in `src/vouch/jsonl_server.py::handle_request` so the more specific
unknown-entity path from `_h_timeline`/`build_timeline` is caught before the
generic `KeyError` branch and returned as `invalid_request`.

Comment thread src/vouch/timeline.py
Comment on lines +93 to +109
if order not in ORDERS:
raise TimelineError(f"order must be one of {ORDERS}, got {order!r}")
if limit is not None and limit < 0:
raise TimelineError("limit must be >= 0")
since = _as_utc(since)
until = _as_utc(until)

entity = store.get_entity(entity_id) # raises ArtifactNotFoundError

want = {t.strip() for t in types if t.strip()} if types else None
decided = _decided_map(store) if order == ORDER_DECIDED else {}

def when_for(artifact_id: str, created: datetime | None) -> datetime | None:
eff = _as_utc(created)
if order == ORDER_DECIDED:
return decided.get(artifact_id) or eff
return eff

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

types silently mis-filters when passed a string instead of a list.

If types arrives as a plain string (plausible from JSONL/MCP callers, since nothing enforces the list type at runtime), {t.strip() for t in types if t.strip()} iterates over individual characters rather than raising a clear error, producing a confusing empty/garbage filter instead of a validation error.

🐛 Proposed fix
     want = {t.strip() for t in types if t.strip()} if types else None
+    if types is not None and isinstance(types, str):
+        raise TimelineError("types must be a list of strings, not a single string")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/timeline.py` around lines 93 - 109, In the timeline filtering
logic, `types` can be a plain string and currently gets treated like an iterable
of characters in the `want = {t.strip() ...}` path. Update the input validation
in the function that builds `want` so `types` must be a list/sequence of
strings, and raise `TimelineError` with a clear message when a string or other
invalid type is passed. Keep the rest of the `store.get_entity`, `_as_utc`, and
`ORDER_DECIDED` flow unchanged.

Comment thread tests/test_timeline.py
Comment on lines +189 to +195
def test_jsonl_handler_runs(store: KBStore, monkeypatch) -> None:
_seed(store)
monkeypatch.chdir(store.root)
from vouch.jsonl_server import HANDLERS

out = HANDLERS["kb.timeline"]({"entity_id": "acme", "order": "effective"})
assert [e["id"] for e in out["entries"]] == ["c1", "c2", "r1", "c3"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'handle_request' tests/test_jsonl_server.py -A3 -B3

Repository: vouchdev/vouch

Length of output: 5850


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the timeline test and related JSONL server tests/handlers.
sed -n '160,220p' tests/test_timeline.py
printf '\n---\n'
sed -n '1,260p' tests/test_jsonl_server.py
printf '\n---\n'
sed -n '1,220p' vouch/jsonl_server.py

Repository: vouchdev/vouch

Length of output: 11665


🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -a 'jsonl_server.py' .
printf '\n---\n'
rg -n 'kb\.timeline|def handle_request|HANDLERS' -S .

Repository: vouchdev/vouch

Length of output: 13196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the JSONL server dispatch/envelope logic and the tests that might cover it generically.
sed -n '680,790p' src/vouch/jsonl_server.py
printf '\n---\n'
sed -n '1,200p' tests/test_capabilities.py
printf '\n---\n'
sed -n '1,220p' tests/test_provenance.py
printf '\n---\n'
sed -n '1,180p' tests/test_synthesize.py

Repository: vouchdev/vouch

Length of output: 16814


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '680,790p' src/vouch/jsonl_server.py
printf '\n---\n'
sed -n '1,200p' tests/test_capabilities.py
printf '\n---\n'
sed -n '1,220p' tests/test_provenance.py
printf '\n---\n'
sed -n '1,180p' tests/test_synthesize.py

Repository: vouchdev/vouch

Length of output: 16814


Add a JSONL envelope assertion for kb.timeline. test_jsonl_handler_runs only calls HANDLERS["kb.timeline"] directly, so it skips the {id, ok, result} / {id, ok: false, error} shape that handle_request returns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_timeline.py` around lines 189 - 195, The kb.timeline test
currently calls the handler directly and misses the JSONL request envelope shape
that handle_request returns. Update test_jsonl_handler_runs to exercise the
JSONL path via the request wrapper used by vouch.jsonl_server.handle_request,
and assert the full {id, ok, result} success envelope (or {id, ok: false, error}
on failure) instead of only checking HANDLERS["kb.timeline"] output. Keep the
existing kb.timeline and HANDLERS symbols as the locator for the handler under
test.

Source: Coding guidelines

@devmixa702 devmixa702 changed the title feat/timeline feat(timeline): kb.timeline — entity chronological trajectory (#313) Jul 3, 2026
@devmixa702

Copy link
Copy Markdown
Author

closing — this duplicates #352 (dhgoal's original kb.timeline). opened by mistake from a fork; the feature belongs to #352.

@devmixa702 devmixa702 closed this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adapters agent host adapters and install manifests cli command line interface docs documentation, specs, examples, and repo guidance mcp mcp, jsonl, and http surfaces openclaw openclaw integration size: XL 1000 or more changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants