Skip to content

feat: add profiled activity reconciliation and compact review - #328

Merged
flowcool merged 17 commits into
mainfrom
spec/profiled-activity-reconciliation
Oct 28, 2026
Merged

flowcool merged 17 commits into
mainfrom
spec/profiled-activity-reconciliation

Conversation

@flowcool

@flowcool flowcool commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Behavior

Activity review now supports explicit bounded reconciliation profiles. off remains the default. Matching uses complete one-to-one candidate graphs, local calendar dates, exact gross limits and source-derived fees. Unique legacy references can migrate under the configured account mapping.

fresh source + destination evidence
  -> manual yes or bounded profile authority
  -> one shared PUT budget
  -> durable journal + exact readback

Schema 3 provides one compact decision file and a private bound state file. Pair rejection affects only its source-target association. Historical maintenance keeps separate operation authority and exact selected-subset replay. Group diagnosis never authorizes aggregate corrections.

Release impact

Major. The review-file and new journal formats change recovery compatibility. Back up the operator file, .state.yaml companion and journal together. Schemas 1 and 2 migrate with cleared consent. Old unresolved journal entries remain readable. Keep a compatible reader until pending requests settle. Software rollback does not reverse financial writes.

Profiles are opt-in. Stable publication and production require separate GO. Exact-image empty/golden lab qualification remains pending. Shared import/index helpers changed: downstream propagation needs separate review; DEGIRO remains pinned.

Verification

  • bash scripts/check.sh: PASS: Ruff, strict mypy (15 modules), and all 2,967 offline tests.
  • Synthetic regression cases cover thresholds, DST, numerical residue, migration, state generations, rejection, journal recovery, no resend, one-write budgeting, grouped outcomes and maintenance selections.
  • A same-group dividend rejection test failed before the correction and passes after it. A journal-recovery result-count test also failed before its correction and passes after it.
  • openspec validate --all --strict: PASS after archive: 6 specifications, 0 failures. All eleven tasks are complete in openspec/changes/archive/2026-10-28-profiled-activity-reconciliation/.
  • git diff --check: PASS.

AI assistance

Codex implemented the approved OpenSpec tasks. The author checked the complete diff, preservation and account boundaries, exact decimal evidence, offline tests and strict typing. Planning received two earlier Claude reviews.

Pullfrog review

Pending review of the pushed implementation head. The operator requested a pause after this push to add CodeRabbit review. This PR remains open. Merge and release preparation await resumption. The repository Pullfrog and required-CI gates still apply before merge.

Review evidence: pending.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions github-actions Bot added the feature New capability label Oct 28, 2026
Task 1: 26 profile and financial boundary tests pass. Strict mypy passes for all 15 runtime modules. Automatic dispatch remains for task 8.
Task 2: four numeric residue scenarios pass under Python 3.14.6; all 30 profile tests and strict mypy pass. Reuses validated event conversion, preserves exact wire values.
Task 3: 29 matching and existing candidate scenarios pass, including both ambiguity directions, exact precedence, DST, invalid TZ and dividend boundaries. Strict mypy passes. Profile metadata does not bind manual consent.
Task 4: five migration/source-scope scenarios pass. Comment-only readback rebuilds a separate blank financial correction. Independent destinations stay independent; current collisions refuse migration. Existing app rebuilds all selection indexes after process_review.
Task 5: five conformity scenarios and 59 affected dividend/review scenarios pass. Differences survive blocking; conflicts name counterparts. Distinct-gross legacy dividend pairs migrate separately; signed tax keeps converter semantics. Strict mypy passes.
Task 6: 29 compact/shared/persistence tests pass; strict mypy and Ruff pass. Schemas 1/2 clear consent once, immutable legacy journal aliases survive, journal v1/v2 readers stay compatible. State-first publication retains previous generation; offline rebuild retains surviving bytes, clears consent and records profile cooldown. Maintenance full file passed 304 tests before added recovery cases; targeted adapter tests remain green.
Task 7: six rejection scenarios pass, including changed-evidence cooldown, missing endpoints, per-event trade/dividend exclusion and summed fills. All 336 affected filter/index/proximity tests pass. Strict mypy and Ruff pass.
…udget

Task 8: 37 profile/approval/simulation tests pass, plus three full-runtime budget/restart tests. Fresh profile authority is journaled separately from consent; exact readback and account fences control completion. One shipped PUT budget, oldest eligible revisions and rebuild/rejection cooldowns are retained. Strict typing and lint pass.
Task 9: 15 grouping/profile outcome scenarios and 12 runtime budget/operator-action scenarios pass. Four fills versus two targets count each endpoint once; no aggregate correction. Preview, deferred, rejected, blocked and confirmed outcomes remain distinct; external summaries expose fixed counts only. Strict mypy and Ruff pass.
…y-reconciliation

# Conflicts:
#	docs/configuration.md
@flowcool flowcool added the breaking-change Backward-incompatible change; appears first in release notes label Oct 28, 2026
@flowcool
flowcool marked this pull request as ready for review October 28, 2026 13:01

@pullfrog pullfrog 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.

ℹ️ No critical issues — one minor suggestion inline.

Reviewed changes — the profiled reconciliation implementation (runtime, maintenance CLI, specs, ADRs and docs). I read the code paths end to end and skimmed the tests; I did not run the test suite (the review runner has no .venv).

  • Reconciliation rules — new activity_reconciliation.py with local-date eligibility, exact Decimal gross limits, residue evidence and group diagnosis.
  • Compact review (schema 3) — operator view plus private .state.yaml with generation recovery; schemas 1/2 migrate with consent cleared; journal v2 records authority and eligibility.
  • Shared PUT budget — profile-authorized and manual corrections go through selected_proposals, the same QUALIFIED_EXECUTION limit, fresh re-qualification before each dispatch and the existing journal/readback path. simulated now also covers the forced TZ preview, and every write guard still sits behind it.
  • Pair rejection — rejected source/target pairs are excluded from trade and dividend proximity matching only for that pair.
  • Maintenance CLI — schema 3 historical staging, rebuild-review, state hash bound in the manifest.

ℹ️ Profiles add an automatic update path for existing activities

Under conservative/balanced/permissive, the connector now rewrites an existing Ghostfolio activity without a per-item yes. AGENTS.md, ADR 0003/0009 and the archived OpenSpec change all record this as intended and bounded (opt-in, one correction per cycle, journal plus readback), so I treat it as approved scope rather than a defect. It is the one place where this PR departs from the "never update automatically" default, so the operator GO for the exact-image lab runs should explicitly cover it.

ℹ️ Downstream propagation

index_existing_activities now appends the activity id to manual_buys/manual_sells/dividend_dates entries, and _dividend_date_matches slices it off again. The PR body already flags DEGIRO propagation. The index_existing_activities docstring in ghostfolio.py (around lines 233–236) still describes the old two- and four-element shapes.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-sonnet-5-5 | 𝕏

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — the single commit since the prior Pullfrog review (2041e50). It touches tests, one docstring and removes dead code; no runtime behavior changes. I did not run the test suite (the review runner has no .venv).

  • Removed dead financial_eligible — the unused function and its dividend_quantity_proven flag are gone from activity_reconciliation.py, so the quantity rule lives only in automatic_pairs.
  • Tests exercise the production rule — the quantity, denomination (GBp/GBP/EUR) and sub-precision gross boundary tests now call automatic_pairs, which authorizes writes.
  • Docstring updated — index_existing_activities documents the activity id element in manual_sells, manual_buys and dividend_dates entries.

The prior inline thread on financial_eligible is addressed and already resolved.

Pullfrog  | Fix it ➔ | View workflow run | Using claude-sonnet-5-5 | 𝕏

@flowcool
flowcool merged commit 976cc4f into main Oct 28, 2026
11 checks passed
@flowcool
flowcool deleted the spec/profiled-activity-reconciliation branch October 28, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Backward-incompatible change; appears first in release notes feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant