Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
49e99a2
chore: prepare v0.20.0 release candidate
stephenschoettler Jul 23, 2026
425cade
merge: origin/main (v0.20.0 release candidate) into wave-1
100yenadmin Jul 25, 2026
424a6b2
test: batch large inflight maintenance fixture (#446)
stephenschoettler Jul 26, 2026
5f7c64f
fix: retry WAL conversion on lock contention at connection setup (#361)
ai-ag2026 Jul 27, 2026
8cf25bf
test: batch large vector metadata fixture (#447)
stephenschoettler Jul 27, 2026
c7e50e2
Merge branch 'main' into upstream-wave-1
100yenadmin Jul 28, 2026
0173e2c
test: join the encoder loader thread before leaving the reset fixture
Jul 25, 2026
58d0c0d
merge bench/w3b-on-wave1 (R2 consolidated train) into upstream-wave-1
100yenadmin Jul 29, 2026
e89b135
merge round-4 response (2e5ebed) into 436 prep
100yenadmin Jul 29, 2026
ca31964
docs: CHANGELOG round-count refresh (four review rounds, 35→11→6→8)
100yenadmin Jul 29, 2026
b0ac31a
merge round-5 response (4b684c9) into 436 prep
100yenadmin Jul 29, 2026
79d04cb
merge round-6 response (060a0df) into 436 prep
100yenadmin Jul 29, 2026
ccc76ee
merge round-7 final batch (93a3ade) into 436 consolidation — in-train…
100yenadmin Jul 29, 2026
a53276c
upstream review follow-up 1: five validated mechanical fixes
100yenadmin Jul 29, 2026
ae0f068
fix(adaptive_retrieval): mark_failed cleanup is best-effort — the onl…
100yenadmin Jul 29, 2026
3fbc365
fix: defer temporal rollup maintenance from session start
Tosko4 Jul 25, 2026
ff653fb
fix: serialize manual temporal rollup rebuilds
Tosko4 Jul 25, 2026
2ae3a66
fix: acquire rollup lease before opening store
Tosko4 Jul 25, 2026
6eec6c5
fix: recover rollup maintenance after cleanup failures
Tosko4 Jul 31, 2026
9d6ab8a
test: drain rollup maintenance before stale assertion
Tosko4 Jul 31, 2026
77aafbf
fix: release completed rollup maintenance keys
Tosko4 Jul 31, 2026
854e869
Merge pull request #440 from Tosko4/fix/async-rollup-maintenance
stephenschoettler Aug 1, 2026
8674109
Merge upstream main (async-rollup maintenance train, #440) into upstr…
100yenadmin Aug 1, 2026
d93cc83
fix(436-b3/idx6): bind query views to retrieval start
100yenadmin Aug 1, 2026
4daa890
fix(436-b3/idx11): distinguish dated repeated events
100yenadmin Aug 1, 2026
1f02e28
fix(436-b3/idx16): trust stored occurrence anchors
100yenadmin Aug 1, 2026
a441ad8
fix(436-b3/idx19): ground explicit assertion subjects
100yenadmin Aug 1, 2026
481607a
fix(436-b3/idx24): route how-long-ago intervals
100yenadmin Aug 1, 2026
96ea568
fix(436-b3/idx26): bind distinct keys to counted entities
100yenadmin Aug 1, 2026
da08af4
fix(436-b3/idx29): reject out-of-range observation epochs
100yenadmin Aug 1, 2026
a2f519a
fix(436-b3/idx31): preserve enumerated sum operands
100yenadmin Aug 1, 2026
fa00ec9
Repair five confirmed PR 436 findings
stephenschoettler Aug 1, 2026
a74a90b
fix(436-b4/idx6): bind named sum selectors
100yenadmin Aug 2, 2026
e9963d0
fix(436-b4/idx7): keep rolling windows exact
100yenadmin Aug 2, 2026
738c6a7
fix(436-b4/idx8): ground extracted assertion values
100yenadmin Aug 2, 2026
40f81bf
fix(436-b4/idx9): bind counted events to actions
100yenadmin Aug 2, 2026
2523ac9
fix: close seven evidence reasoning gaps
stephenschoettler Aug 2, 2026
ace70f1
fix: close four PR 436 Codex findings
stephenschoettler Aug 2, 2026
b6288eb
Merge pull request #436 from 100yenadmin/upstream-wave-1
stephenschoettler Aug 3, 2026
15eefe9
chore: prepare v0.21.0-rc1 release candidate
stephenschoettler Aug 3, 2026
4553a91
Merge pull request #491 from stephenschoettler/release/v0.21.0-rc1-prep
stephenschoettler Aug 4, 2026
059b5db
docs: correct backup and pre-answer provider claims
stephenschoettler Aug 4, 2026
6b7dbb1
Merge pull request #493 from stephenschoettler/docs/pr491-backup-reca…
stephenschoettler Aug 4, 2026
a071fff
fix: preserve Unicode boundaries in token chunks
stephenschoettler Aug 4, 2026
e1977c6
Merge pull request #492 from stephenschoettler/fix/cjk-safe-tiktoken-…
stephenschoettler Aug 5, 2026
8591f50
chore: prepare v0.21.0-rc2 metadata candidate
stephenschoettler Aug 5, 2026
10cbb78
Merge pull request #511 from stephenschoettler/release/v0.21.0-rc2
stephenschoettler Aug 6, 2026
e607076
merge upstream/main (v0.21.0-rc2) into fork main
Aug 7, 2026
e422efa
fix merge resolution: ground_evidence needs BOTH session_dates and en…
Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ body:
attributes:
label: Version and branch
description: hermes-lcm version, branch, commit, or release tag.
placeholder: v0.20.0, main, or commit SHA
placeholder: v0.21.0-rc2, main, or commit SHA
validations:
required: true

Expand Down
25 changes: 25 additions & 0 deletions .github/release-notes/v0.21.0-rc1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# hermes-lcm v0.21.0-rc1

Lossless Context Management for Hermes: an RC for the merged experience-memory, exact-evidence, citable-retrieval, and large-store reliability wave.

## Highlights

- Adds the trajectory/experience-memory subsystem plus opt-in assertion, query-view, pre-answer evidence, and adaptive-retrieval paths; stock installs retain the previous behavior until operators enable those paths.
- Keeps the core SQLite schema at version 5. New feature stores use additive named migrations in the same profile database; existing v0.20 databases need no manual migration or embedding backfill.
- Hardens large-store and startup behavior with bounded vector/metadata work, WAL-conversion lock retry, and eventual background temporal-rollup maintenance.
- Carries reference-strict citable delivery, direct source identity for summary recall hits, and configurable query embedding spend limits. Committed benchmark results apply only to their documented harness and corpus.

## Changes

- #436 Consolidate trajectory/experience memory, exact evidence, citable retrieval, privacy, scaling, and release-validation work.
- #361 Retry WAL conversion on lock contention during connection setup.
- #440 Defer temporal-rollup maintenance from the session-start critical path.
- #446 Batch embedding-backfill fixture setup for release-scale validation.
- #447 Batch large vector-metadata fixture setup for release-scale validation.

## Contributors

- @stephenschoettler
- @100yenadmin
- @ai-ag2026
- @Tosko4
16 changes: 16 additions & 0 deletions .github/release-notes/v0.21.0-rc2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# hermes-lcm v0.21.0-rc2

Lossless Context Management for Hermes: a release candidate limited to the merged CJK-safe trajectory-state token chunking correction.

## Highlights

- Preserves UTF-8 character boundaries when the optional `tiktoken` path splits oversized trajectory-state documents.
- Keeps each decoded chunk within the requested token budget and fails explicitly when that budget cannot contain one complete Unicode character.

## Changes

- #492 corrects fixed-width token-ID slicing that could split a multibyte character and introduce U+FFFD replacement characters when chunks were decoded independently.

## Contributors

- @stephenschoettler
48 changes: 9 additions & 39 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,19 @@ jobs:
with:
fetch-depth: 0

- name: Generate changelog
id: changelog
env:
GH_TOKEN: ${{ github.token }}
- name: Validate curated release notes
run: |
CURRENT_TAG="${{ github.ref_name }}"
PREV_TAG=$(git tag --sort=-v:refname | grep -Fxv "$CURRENT_TAG" | head -1)

if [ -z "$PREV_TAG" ]; then
RANGE="$CURRENT_TAG"
else
RANGE="$PREV_TAG..$CURRENT_TAG"
fi

echo "body<<EOF" >> "$GITHUB_OUTPUT"

if [ -z "$PREV_TAG" ]; then
echo "Initial release." >> "$GITHUB_OUTPUT"
else
git log --pretty=format:"- %s (%h)" "$RANGE" >> "$GITHUB_OUTPUT"
NOTES_FILE=".github/release-notes/${GITHUB_REF_NAME}.md"
if [ ! -s "$NOTES_FILE" ]; then
echo "Missing curated release notes: $NOTES_FILE" >&2
exit 1
fi

echo "" >> "$GITHUB_OUTPUT"
echo "## Contributors" >> "$GITHUB_OUTPUT"
if [ -n "$PREV_TAG" ]; then
CONTRIBUTORS=$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/compare/${PREV_TAG}...${CURRENT_TAG}" \
| jq -r '[.commits[].author.login // empty] | unique | .[] | "- @\(.)"')
if [ -n "$CONTRIBUTORS" ]; then
printf '%s\n' "$CONTRIBUTORS" >> "$GITHUB_OUTPUT"
else
echo "- No GitHub usernames found" >> "$GITHUB_OUTPUT"
fi
else
echo "- No GitHub usernames found" >> "$GITHUB_OUTPUT"
fi

echo "EOF" >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
body: ${{ steps.changelog.outputs.body }}
body_path: .github/release-notes/${{ github.ref_name }}.md
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
make_latest: ${{ contains(github.ref_name, '-') && 'false' || 'true' }}
generate_release_notes: false
63 changes: 56 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,62 @@ This repo also publishes GitHub Releases. This file is the repo-root release sur

## Unreleased

## v0.20.0 - 2026-07-29

Release focus: benchmark-driven retrieval scaling, evidence provenance, and bounded query embedding spend.

- Removed the large-corpus recall ceilings by scanning the full summary and chunk corpora in bounded batches and sanitizing raw natural-language FTS queries before fallback. (#169)
- Made the query-path embedding spend guard configurable with a generous default while preserving the exempt backfill contract. (stephenschoettler/hermes-lcm#434)
- Preserved a direct source `store_id` on summary recall hits so strict evidence renderers can validate their source identity. (#164)
No additional changes yet.

## v0.21.0-rc2 - 2026-08-05

### Changed

- #492 corrects the optional `tiktoken` trajectory-state chunking path to
preserve UTF-8 character boundaries while keeping each decoded chunk within
its token budget. If the budget cannot contain one complete Unicode
character, the path fails explicitly instead of emitting replacement
characters.

## v0.21.0-rc1 - 2026-08-03

### Highlights

- Add the trajectory/experience-memory subsystem and the opt-in assertion,
evidence, query-view, and adaptive-retrieval surfaces delivered by the
consolidated wave-1 merge (#436).
- Keep the core SQLite schema at version 5. New feature stores use additive,
named migrations in the same profile database, while disabled/default-off
installs do not create optional assertion, query-view, or embedding tables.
- Improve large-store and startup behavior with bounded vector/metadata work,
lock-contention retry during WAL conversion, and deferred temporal-rollup
maintenance (#361, #440, #446, #447).

### Changed

- #436 adds the consolidated trajectory/experience-memory, retrieval,
exact-evidence, citable-delivery, privacy, scale, and release-validation wave.
Its committed benchmark results are directional evidence for the documented
harness and corpus, not universal provider or workload guarantees.
- #361 retries WAL conversion when connection setup meets lock contention.
- #440 moves temporal-rollup maintenance off the session-start critical path;
bounded background work is eventual and `lcm_recent` retains its fallback.
- #446 and #447 batch large fixture setup for embedding/vector metadata release
coverage without changing runtime behavior.

### Upgrade notes

- Back up `lcm.db`, update the plugin checkout, restart Hermes, send one normal
message, then verify `plugin_version: 0.21.0-rc1` and the expected database
path with `lcm_status`. The core schema remains version 5.
- No manual core migration or embedding backfill is required from v0.20.0.
- Query/evidence tool schemas are exposed after upgrade, but assertion
extraction, assertion storage, query-view storage, pre-answer evidence, and
adaptive retrieval remain opt-in. Review provider/privacy boundaries before
enabling model- or embedding-backed paths.

## v0.20.0 - 2026-07-23

Release focus: Lossless-Claw parity plus the merged cross-session recall and temporal retrieval stack.

- Completed the five selected Lossless-Claw parity behaviors: recoverable active-replay stubs for large externalized tool results; token-bounded fresh tails that preserve the newest message and complete tool-call/result groups; dry-run-first historical tool-output backfill with guarded rollback; bounded active-session externalized-payload search with strict ownership and recoverability checks; and bounded atomic threshold full sweeps with one final active-context publication. (#380, #381, #382, #413)
- Shipped the merged #413 recall and temporal surface: `lcm_recall`, `lcm_recent`, and `lcm_load_session`; semantic and hybrid retrieval over summaries and message chunks; temporal rollups with bounded fallback; optional proactive recall; and the corresponding benchmark and reproduction documentation.
- Release boundary: stock installs keep large-output externalization, active-replay stubbing, embeddings, temporal rollups, proactive recall, and threshold full sweeps disabled by default. Payload search requires explicit `content_scope`; historical backfill remains an operator-invoked, dry-run-first command. Committed benchmark results are directional evidence under their documented model and harness, not a universal provider-parity claim. This release does not include the later work tracked in #423, #434, or #436.

## v0.19.0 - 2026-07-07

Expand Down
13 changes: 13 additions & 0 deletions FINDINGS-VERDICTS-UP1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# FINDINGS-VERDICTS-UP1
- Source head: `ccc76ee4f30fa6203d50e73fb0eeb0eb8983b2aa`; branch `fix/upstream-followup-1`.
Comment on lines +1 to +2

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

Add a blank line after the heading.

Markdownlint reports MD022 because the list starts immediately after the heading. Add one empty line after Line 1.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 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 `@FINDINGS-VERDICTS-UP1.md` around lines 1 - 2, Add a blank line between the
FINDINGS-VERDICTS-UP1 heading and the Source head list item to satisfy Markdown
heading-spacing requirements.

Source: Linters/SAST tools

- 3649814070 VALIDATED/FIXED: finite coverage now counts distinct grounded `exact_ref` values.
- 3672539167 VALIDATED/FIXED: hit CAS checks generation/rowcount and re-reads readiness before returning `hit`.
- 3672539173 VALIDATED/FIXED: persisted slot refs are intersected with the selected evidence set.
- 3668416705 VALIDATED/FIXED: existing trajectory schema version is rejected before schema/FTS repair DDL.
- 3672539191 VALIDATED/FIXED: deadline-expired paths skip `COUNT(*)`; in-memory totals remain meaningful.
- Focused regressions: 9 passed; see `laneUP1-logs/focused-up1.xml`.
- Ruff: whole repository clean; see `laneUP1-logs/ruff-up1.txt`.
- Full suite: 2709 passed, 35 failed, 1 skipped, 12 xfailed; 0 new and 0 missing failure names vs baseline.
- Full-suite evidence: `laneUP1-logs/full-suite-up1.xml` and `full-suite-up1-comparison.txt`.
- Surface verdict: no validation evidence contradicted the NON-V1-delivery triage classification.
- No push, merge, release, deploy, or runtime mutation performed.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,17 @@ Expected signals:

- plugin list includes `hermes-lcm`
- selected context engine is `lcm`
- tool list includes `lcm_grep`, `lcm_load_session`, `lcm_describe`,
`lcm_expand`, `lcm_expand_query`, `lcm_status`, `lcm_inspect`, and
`lcm_doctor`
- tool list includes `lcm_grep`, `lcm_recall`, `lcm_recent`,
`lcm_load_session`, `lcm_describe`, `lcm_expand`, `lcm_expand_query`,
`lcm_status`, `lcm_inspect`, and `lcm_doctor`
Comment on lines +198 to +200

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

List all 15 tools in the verification checklist.

The checklist names only 10 tools. It omits lcm_query_state, lcm_compute, lcm_compile_evidence, lcm_evidence_pack, and lcm_retrieve. The sample output and Line 221 claim 15 tools, so the verification instructions are inconsistent. Match docs/operator-guide.md Lines 126-130.

Also applies to: 221-223

🤖 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 `@README.md` around lines 198 - 200, Update the README verification checklist’s
tool list to include all 15 tools, adding lcm_query_state, lcm_compute,
lcm_compile_evidence, lcm_evidence_pack, and lcm_retrieve to the existing
entries. Ensure the checklist and the referenced sample-output count remain
consistent with the complete list, matching docs/operator-guide.md.

- the normal available-skills index includes `hermes-lcm`; current hosts can
also resolve the explicit plugin-qualified skill `hermes-lcm:hermes-lcm`

Typical output:

```text
Plugins (1):
✓ hermes-lcm v0.20.0 (8 tools)
✓ hermes-lcm v0.21.0-rc2 (15 tools)

Provider Plugins:
Context Engine: lcm
Expand All @@ -218,7 +218,7 @@ best-effort git identity:

If startup logs say LCM tools are available through `context-engine schemas` or
mention the `Path B fallback`, that is expected on older Hermes hosts such as
Hermes Agent v0.16. The seven `lcm_*` tools remain available through the
Hermes Agent v0.16. All 15 `lcm_*` tools remain available through the
context-engine path; standalone plugin-registry registration is not required
there.

Expand All @@ -244,6 +244,17 @@ If you installed a symlink from a separate checkout:

Restart Hermes after updating.

For the `v0.21.0-rc2` line, take a normal backup of `lcm.db` before updating,
then update the checkout and restart Hermes. No manual core migration or
backfill is required: the core schema remains version 5. New assertion,
query-view, and adaptive-retrieval state is additive, created only after the
corresponding opt-in is enabled, and stored in the same profile database under
named feature markers. The five new query/evidence tool schemas are visible in
the tool list on stock installs, but automatic extraction, pre-answer evidence,
assertion storage, query-view storage, and adaptive retrieval remain off. See
[the operator upgrade and opt-in notes](docs/operator-guide.md#upgrade-from-v0200-to-v0210-rc2)
before enabling them.

## Commands and tools

### Agent tools
Expand Down
5 changes: 5 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ def _pre_llm_context(active_engine, recall_policy: str, payload: dict) -> dict:
contract = compile_answer_contract(question, question_date)
if contract.status != "planned":
return {"context": recall_policy}
baseline_was_internal = not isinstance(
payload.get("baseline_refs"), (list, tuple)
)
baseline_refs = _answer_ready_baseline(active_engine, question, payload)
result = compile_preanswer_evidence(
question,
Expand All @@ -196,6 +199,7 @@ def _pre_llm_context(active_engine, recall_policy: str, payload: dict) -> dict:
"lcm_recall", recall_args
),
enabled=True,
render_baseline_context=baseline_was_internal,
)
try:
active_engine._last_preanswer_evidence_trace = result
Expand Down Expand Up @@ -258,6 +262,7 @@ def _pre_llm_context(active_engine, recall_policy: str, payload: dict) -> dict:
question,
baseline_refs=compiler_refs,
question_date=question_date,
engine=active_engine,
)
if prepared["status"] == "selector_required":
proposal, selector_usage = call_selective_auxiliary_selector(
Expand Down
12 changes: 11 additions & 1 deletion adaptive_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import uuid

from .query_view_store import (
CorpusSnapshot,
QueryViewBuildInProgressError,
QueryViewIdentity,
QueryViewStore,
Expand Down Expand Up @@ -333,6 +334,7 @@ class RetrievalState:
view_delta_events: tuple[dict[str, Any], ...] = ()
view_delta_truncated: bool = False
cached_trace: dict[str, Any] | None = None
view_corpus_snapshot: CorpusSnapshot | None = None


def _parse_requirements(raw: Any) -> tuple[EvidenceRequirement, ...]:
Expand Down Expand Up @@ -902,6 +904,11 @@ def start(
created_at=now,
updated_at=now,
slot_refs={item.slot_id: [] for item in parsed_requirements},
view_corpus_snapshot=(
self._query_views.corpus_snapshot()
if self._query_views is not None
else None
),
)
if self._query_views is not None:
lookup = self._query_views.lookup(parsed_identity, record_hit=False)
Expand Down Expand Up @@ -1188,7 +1195,10 @@ def _persist_view(
return {"status": "skipped", "reason": "computation did not validate"}
token = None
try:
token = self._query_views.claim_build(state.identity)
token = self._query_views.claim_build(
state.identity,
corpus_snapshot=state.view_corpus_snapshot,
)
dependencies = [
self._query_views.snapshot_dependency(
item.store_id,
Expand Down
26 changes: 24 additions & 2 deletions answer_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,28 @@ def _named_operands(text: str, operation: str) -> tuple[str, ...]:
)
if len(values) == 2:
return values
if operation == "sum":
enumeration = re.search(
r"\b(?:spend|spent)\s+(?:on\s+)?(.{1,240}?)(?:\?|$)|"
r"\b(?:across|of|for|on)\s+(.{1,240}?)(?:\?|$)",
normalized,
re.IGNORECASE,
)
if enumeration:
tail = next(group for group in enumeration.groups() if group is not None)
parts = [part.strip() for part in tail.split(",")]
if len(parts) >= 3:
parts[-1] = re.sub(
r"^(?:and|plus)\s+", "", parts[-1], flags=re.IGNORECASE
)
values = tuple(
value
for raw in parts
if (value := _clean_operand(raw)) is not None
)
if len(values) == len(parts) and len(values) <= 8:
return values
return ()
pair = re.search(
r"\b(?:spend|spent|across|between|from|of|for|doing|on)\s+(.{1,80}?)\s+"
r"(?:and|plus)\s+(?:doing|on|the)?\s*(.{1,80}?)(?:\?|$)",
Expand Down Expand Up @@ -450,9 +472,9 @@ def _time_window(text: str, canonical_as_of: str | None) -> TemporalWindow | Non
raw = rolling.group(1)
amount = int(raw) if raw.isdigit() else _WORD_NUMBERS[raw]
if rolling.group(2) == "day":
start = anchor - timedelta(days=amount)
start = anchor - timedelta(days=amount - 1)
elif rolling.group(2) == "week":
start = anchor - timedelta(days=amount * 7)
start = anchor - timedelta(days=amount * 7 - 1)
else:
month_index = anchor.year * 12 + anchor.month - 1 - amount
year, month_zero = divmod(month_index, 12)
Expand Down
Loading