From a964fe6d96e0bfea0fd95e7f0f4189a8efdc8b25 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 21 Jul 2026 19:13:34 +0700 Subject: [PATCH] docs: remove completed implementation records --- .../260703-0408-wheelofnames-command.md | 31 ---- ...0720-1612-stock-dividend-command-naming.md | 34 ---- ...260720-1621-stock-dividend-command-plan.md | 33 ---- ...720-1636-stock-dividend-plan-validation.md | 31 ---- .../260720-1715-stock-dividend-commands.md | 67 ------- .../260721-0857-cross-platform-go-workflow.md | 47 ----- ...0923-testcontainers-mongodb-integration.md | 51 ------ .../260721-1011-telegram-command-discovery.md | 64 ------- ...0721-1446-command-parameter-conventions.md | 44 ----- .../260721-1539-stock-coin-cost-basis-pnl.md | 59 ------- ...1704-dividend-api-and-migration-cleanup.md | 87 --------- ...60721-1815-stock-dividend-event-buttons.md | 73 -------- ...se-01-implement-ratio-dividend-commands.md | 109 ------------ .../phase-02-migrate-command-statistics.md | 113 ------------ .../phase-03-verify-user-contracts.md | 95 ---------- .../plan.md | 133 -------------- ...m-260720-1715-stock-dividend-completion.md | 43 ----- ...ase-01-design-persistence-and-migration.md | 84 --------- ...2-implement-weighted-average-accounting.md | 81 --------- ...3-verify-p-l-behavior-and-compatibility.md | 86 --------- .../plan.md | 108 ------------ ...-1612-stock-dividend-command-brainstorm.md | 165 ------------------ .../pm-260721-1705-portfolio-cleanup.md | 48 ----- ...60721-1815-stock-dividend-event-buttons.md | 57 ------ 24 files changed, 1743 deletions(-) delete mode 100644 docs/journals/260703-0408-wheelofnames-command.md delete mode 100644 docs/journals/260720-1612-stock-dividend-command-naming.md delete mode 100644 docs/journals/260720-1621-stock-dividend-command-plan.md delete mode 100644 docs/journals/260720-1636-stock-dividend-plan-validation.md delete mode 100644 docs/journals/260720-1715-stock-dividend-commands.md delete mode 100644 docs/journals/260721-0857-cross-platform-go-workflow.md delete mode 100644 docs/journals/260721-0923-testcontainers-mongodb-integration.md delete mode 100644 docs/journals/260721-1011-telegram-command-discovery.md delete mode 100644 docs/journals/260721-1446-command-parameter-conventions.md delete mode 100644 docs/journals/260721-1539-stock-coin-cost-basis-pnl.md delete mode 100644 docs/journals/260721-1704-dividend-api-and-migration-cleanup.md delete mode 100644 docs/journals/260721-1815-stock-dividend-event-buttons.md delete mode 100644 plans/260720-1616-stock-dividend-commands/phase-01-implement-ratio-dividend-commands.md delete mode 100644 plans/260720-1616-stock-dividend-commands/phase-02-migrate-command-statistics.md delete mode 100644 plans/260720-1616-stock-dividend-commands/phase-03-verify-user-contracts.md delete mode 100644 plans/260720-1616-stock-dividend-commands/plan.md delete mode 100644 plans/260720-1616-stock-dividend-commands/reports/pm-260720-1715-stock-dividend-completion.md delete mode 100644 plans/260721-1508-stock-coin-cost-basis-pnl/phase-01-design-persistence-and-migration.md delete mode 100644 plans/260721-1508-stock-coin-cost-basis-pnl/phase-02-implement-weighted-average-accounting.md delete mode 100644 plans/260721-1508-stock-coin-cost-basis-pnl/phase-03-verify-p-l-behavior-and-compatibility.md delete mode 100644 plans/260721-1508-stock-coin-cost-basis-pnl/plan.md delete mode 100644 plans/reports/260720-1612-stock-dividend-command-brainstorm.md delete mode 100644 plans/reports/pm-260721-1705-portfolio-cleanup.md delete mode 100644 plans/reports/pm-260721-1815-stock-dividend-event-buttons.md diff --git a/docs/journals/260703-0408-wheelofnames-command.md b/docs/journals/260703-0408-wheelofnames-command.md deleted file mode 100644 index a6aed8d..0000000 --- a/docs/journals/260703-0408-wheelofnames-command.md +++ /dev/null @@ -1,31 +0,0 @@ -# Wheelofnames Command Journal - -## Context - -Executed plan `plans/260703-0404-wheelofnames-misc-command/plan.md`. - -## What Changed - -- Added public `/wheelofnames` command to `misc`. -- Command parses comma-separated options, trims whitespace, ignores empty entries, and returns one random valid option. -- Updated command registration tests and handler tests. -- Updated `telegram-commands.json` and README command list. -- Synced plan phases to completed. - -## Decisions - -- Plain text reply only; no HTML parse mode needed. -- Non-cryptographic randomness acceptable for casual choice selection. -- Duplicate options preserved, allowing intentional weighting. -- No stats migration needed because command is additive. - -## Validation - -- `go test ./internal/modules/misc` -- `go test ./cmd/server ./internal/modules/misc` -- `go test ./...` -- `go vet ./...` - -## Unresolved Questions - -None. diff --git a/docs/journals/260720-1612-stock-dividend-command-naming.md b/docs/journals/260720-1612-stock-dividend-command-naming.md deleted file mode 100644 index 5b4065c..0000000 --- a/docs/journals/260720-1612-stock-dividend-command-naming.md +++ /dev/null @@ -1,34 +0,0 @@ -# Stock Dividend Command Naming Journal - -## Context - -Researched official VSDC dividend notices and brainstormed clearer stock command -contracts for cash, share, and mixed dividend events. - -## What Happened - -- Approved `/stock_cash_dividend ` for cash dividends. -- Approved `/stock_share_dividend ` for share dividends. -- Approved `/stock_dividend ` for mixed - cash-and-share dividends. -- Official examples support ratios such as `4:1` and `100:10`. -- No implementation changes made yet. - -## Decisions - -- Share ratios use `owned:new`; both parts must be positive whole numbers. -- New shares use integer floor division. Fractional entitlements discarded. -- Cash input means VND paid per pre-event share. -- Mixed dividends calculate cash and new shares from the same pre-event holding, - then persist both results atomically. -- Stats history needs a one-time, idempotent migration: existing - `/stock_dividend` usage becomes `/stock_cash_dividend`, and `/stock_bonus` - usage becomes `/stock_share_dividend`. The new mixed `/stock_dividend` starts - with fresh stats. - -## Next Steps - -- Create an implementation plan covering handlers, registration, user-facing - text, command menu, atomic storage behavior, stats migrations, and tests. -- Implement the approved zero-result behavior: reject share-only payouts, but - still credit cash and report zero new shares for mixed payouts. diff --git a/docs/journals/260720-1621-stock-dividend-command-plan.md b/docs/journals/260720-1621-stock-dividend-command-plan.md deleted file mode 100644 index 9b8bf67..0000000 --- a/docs/journals/260720-1621-stock-dividend-command-plan.md +++ /dev/null @@ -1,33 +0,0 @@ -# Stock Dividend Command Plan Journal - -## Context - -Created the implementation plan at -`plans/260720-1616-stock-dividend-commands/` from the approved research and -brainstorm decisions. - -## What Happened - -- Added three pending phases: implement ratio dividend commands, migrate command - statistics, and verify user-facing contracts. -- Planned `/stock_cash_dividend` with VND/share, `/stock_share_dividend` with an - `owned:new` ratio, and combined `/stock_dividend` using both inputs. -- Preserved floor rounding, pre-event holdings, and one atomic portfolio save. -- Defined zero-share behavior: reject share-only events; combined events still - credit cash. -- Tightened migration design after review: each stats row gets a `prepared` - checkpoint containing its exact final target count. Crash retries set that - count instead of incrementing again. -- Could not persist the active-plan selection because `CK_SESSION_ID` was unset. - -## Status - -- Plan: pending -- Progress: 0/3 phases complete -- Source implementation: not started - -## Next Steps - -- Execute the three planned phases, beginning with command contracts and tests. -- Keep the stats migration before module registration so the reused - `/stock_dividend` name cannot misattribute historical usage. diff --git a/docs/journals/260720-1636-stock-dividend-plan-validation.md b/docs/journals/260720-1636-stock-dividend-plan-validation.md deleted file mode 100644 index 26c453a..0000000 --- a/docs/journals/260720-1636-stock-dividend-plan-validation.md +++ /dev/null @@ -1,31 +0,0 @@ -# Stock Dividend Plan Validation Journal - -## Context - -Validated `plans/260720-1616-stock-dividend-commands/plan.md` and its three -pending phase documents before implementation. - -## Validation Result - -- Standard verification: 30 checked, 30 verified, 0 failed, 0 unverified. -- Confirmed cash input is a positive whole VND amount per share. -- Confirmed any positive whole-number `owned:new` ratio is accepted and echoed - unreduced. -- Confirmed repeated manual calls are allowed. No event ledger or duplicate-call - restriction; caller owns correctness. -- Confirmed permanent global and per-row migration markers. Incomplete runs - resume from `prepared` checkpoints using their exact target counts. -- Propagated these decisions through all three phase documents. -- Whole-plan consistency sweep reconciled one stale float-acceptance reference - with integer-input wording and found zero contradictions. - -## Status - -- Plan validation: complete -- Plan implementation: not started -- Source changes: none - -## Next Steps - -- Execute the validated phases in order: command behavior, stats migration, then - user-contract and full-project verification. diff --git a/docs/journals/260720-1715-stock-dividend-commands.md b/docs/journals/260720-1715-stock-dividend-commands.md deleted file mode 100644 index cd7656b..0000000 --- a/docs/journals/260720-1715-stock-dividend-commands.md +++ /dev/null @@ -1,67 +0,0 @@ -# Stock Dividend Commands Journal - -## Context - -Implemented the validated stock-dividend plan: clearer manual commands, -ratio-based share entitlement, and historical stats preservation. - -## What Changed - -- Added `/stock_cash_dividend ` for positive whole-VND - cash credits. -- Added `/stock_share_dividend ` with unreduced ratio - preservation and floor-rounded whole shares. -- Changed `/stock_dividend ` to apply cash - and shares from the same pre-event holding with one portfolio save. -- Share-only zero entitlement rejects with the minimum holding; combined events - still credit cash when shares round to zero. -- Updated registration, command menu, usage text, README, and contract tests; - removed `/stock_bonus` from active commands. - -## Stats Migration - -- Migrates `stock_dividend -> stock_cash_dividend` and - `stock_bonus -> stock_share_dividend`, including anonymous and per-user rows. -- Merges existing target counts without loss. -- Permanent global and per-row markers retain migration history. -- Each row stores a `prepared` exact target count before mutation. Retries set - that count after any write-boundary failure, preventing double increments. - -## Review Findings - -- Guarded `strconv.ParseInt` range errors so its saturated return value cannot be - accepted as valid input. -- Added exact `int64` share formatting so quantities above `2^53` do not lose - digits through `float64` conversion. -- Enforced exact float-backed VND balance addition at the `2^53` boundary; - inexact sums reject without saving. -- Reviewer: 9/10, approve. Adversarial review: PASS; no disproven claims or - reachable regressions. - -## Decisions - -- Ratios remain positive integer `owned:new` values and are echoed as entered; - no normalization. -- Commands are manual adjustments. Repeated calls remain allowed, with no event - ledger or duplicate-call guard; caller owns correctness. -- Overflow, exactness, ticker, and syntax validation remain mandatory despite - the manual workflow. - -## Verification - -- Passed: `go test ./internal/modules/stock` -- Passed: `go test ./internal/modules/stats` -- Passed: `go test ./cmd/server` -- Passed: `go test ./...` -- Passed: `go vet ./...` -- Passed: `telegram-commands.json` PowerShell `ConvertFrom-Json` -- Passed: `git diff --check` (line-ending warnings only) -- Skipped: Mongo-backed tests because `MONGODB_TEST_URL` was unset. -- Skipped: `golangci-lint run` because the binary was not installed. - -## Next Considerations - -- Run Mongo-backed migration tests when a test database is available. -- Run the lint gate when `golangci-lint` is installed. -- After deployment, verify migrated counts and retained system markers before - considering migration-runtime cleanup; keep historical stats and markers. diff --git a/docs/journals/260721-0857-cross-platform-go-workflow.md b/docs/journals/260721-0857-cross-platform-go-workflow.md deleted file mode 100644 index 9867ac9..0000000 --- a/docs/journals/260721-0857-cross-platform-go-workflow.md +++ /dev/null @@ -1,47 +0,0 @@ -# Cross-Platform Go Workflow Journal - -## Context - -Removed Unix-oriented Make targets so Windows, macOS, and Linux contributors -can use the same standard Go workflow with platform-specific environment syntax. - -## What Changed - -- Deleted `Makefile` and replaced its development shortcuts with documented - `go test`, `go vet`, `go build`, `go run`, and direct Docker commands. -- Documented Telegram webhook inspection and cleanup with PowerShell - `Invoke-RestMethod` and POSIX `curl` examples. -- Deleted `telegram-commands.json`; the runtime module registry already builds - and registers the command menu on every startup, so the JSON duplicated the - authoritative Go definitions. -- Replaced Makefile linker flags with Go's embedded VCS build metadata and kept - the existing seven-character commit SHA behavior for deploy notifications. -- Updated CI and MongoDB test guidance to point contributors to the portable - README workflow. - -## Reflection - -Removing the task wrapper makes the repository less convenient for habitual -`make` users, but avoids maintaining shell-specific orchestration and duplicate -command-menu data. Standard Go, Docker, and HTTP tools keep each operation -explicit and work across supported development platforms. - -## Decisions - -- The Go module registry is the single source of truth for Telegram commands. -- Local binaries obtain their short revision from Go build information; the - deployment-provided `SOURCE_COMMIT` remains preferred at runtime. -- Platform differences are documented only where shell syntax or HTTP tooling - differs. - -## Verification - -- Passed: `go test ./...` -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Unavailable: `golangci-lint run` because the binary is not installed. - -## Next Steps - -- Run the lint gate when `golangci-lint` is available. -- Keep command registration tests aligned with future public command changes. diff --git a/docs/journals/260721-0923-testcontainers-mongodb-integration.md b/docs/journals/260721-0923-testcontainers-mongodb-integration.md deleted file mode 100644 index 1ec05a5..0000000 --- a/docs/journals/260721-0923-testcontainers-mongodb-integration.md +++ /dev/null @@ -1,51 +0,0 @@ -# Testcontainers MongoDB Integration Journal - -## Context - -MongoDB integration tests previously depended on contributors manually starting -a database and setting `MONGODB_TEST_URL`. The suite now provisions its own -MongoDB 8 instances when Docker is available. - -## What Changed - -- Added Testcontainers Go and MongoDB module v0.43.0. -- Added a shared lazy test manager that starts one `mongo:8` container per - `storage`, `lol`, and `stats` package and terminates it after that package's - suite. -- Preserved `MONGODB_TEST_URL` as an external-database override. -- Docker-unavailable environments emit an explicit warning and skip MongoDB - tests; a regression test verifies both the warning and skip behavior. -- Once the Docker provider is healthy, container startup and connection-string - failures are fatal. Cleanup failures also make an otherwise passing suite - fail. -- Updated README and CI guidance so normal Go test commands exercise - Testcontainers automatically. - -## Reflection - -Package-scoped lazy containers balance isolation and startup cost: packages do -not share database processes, while tests inside a package avoid repeatedly -starting MongoDB. Distinguishing an absent Docker daemon from a broken healthy -provider keeps local no-Docker runs usable without hiding real infrastructure -regressions. - -## Decisions - -- Pin tests and local setup guidance to MongoDB 8. -- Keep explicit external MongoDB support for constrained or pre-provisioned - environments. -- Treat lifecycle failures as test failures whenever Docker is available. - -## Verification - -- Passed: `go test ./...` -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Passed: `golangci-lint run` -- Passed: `go mod tidy` with no residual module diff. -- Three real MongoDB containers ran, one for each integration-test package, and - all three were cleaned up successfully. - -## Next Steps - -- Monitor CI duration and container startup reliability after rollout. diff --git a/docs/journals/260721-1011-telegram-command-discovery.md b/docs/journals/260721-1011-telegram-command-discovery.md deleted file mode 100644 index 910ae54..0000000 --- a/docs/journals/260721-1011-telegram-command-discovery.md +++ /dev/null @@ -1,64 +0,0 @@ -# Telegram Command Discovery Journal - -> Historical note: the later command-parameter convention simplifies -> `` to ``, keeping the literal delimiter -> visible without prose inside the placeholder. See -> `docs/command-parameter-conventions.md`. - -## Context - -Telegram's native command menu and `/help` exposed only short descriptions, -leaving users to discover parameters through failed invocations or source -documentation. - -## What Changed - -- Extended the shared command registration with `Parameters` metadata and - presentation helpers used by both discovery surfaces. -- Added metadata for all 40 public commands, including the exact stats grammar: - `[users | user | cmd ]`. -- Normalized placeholders to lowercase descriptive names, including meaningful - units or currencies; `[...]` marks optional input, `...` remaining free text, - and parentheses structured input. -- Normalized `/wheelofnames` to `` across metadata, - usage text, and tests. -- Finalized dividend placeholders as ` `, - ` `, and - ` ` for cash, share, and combined - commands. Parsing remains unchanged. -- Native menu descriptions show parameters followed by the summary, while - `/help` renders the complete invocation followed by the summary. Neither - discovery surface includes example invocations. Dynamic fields are - HTML-escaped in `/help`. -- Registration validation rejects multiline metadata and public descriptions - over Telegram's 256-character limit. -- Updated user and deployment documentation for the shared registry behavior. - -## Reflection - -Keeping syntax beside each handler registration prevents the native menu, -`/help`, and implementation from drifting independently. Both discovery -surfaces stay compact without sacrificing safe HTML rendering in `/help`. - -## Decisions - -- Existing command names, handlers, parsers, and persisted data remain - unchanged; normalization is presentation-only. -- No command was added, renamed, or deleted, so no stats migration is needed. -- Discovery surfaces intentionally omit example invocations; handler usage - errors may still include focused examples. -- The complete `/help` output remains within Telegram's 4,096-character limit. - -## Verification - -- Passed: command presentation, validation, menu, and `/help` tests for all 40 - public commands. -- Passed: `go test ./...`, including real MongoDB Testcontainers suites. -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Passed: `golangci-lint run` - -## Next Steps - -- Require parameter metadata updates alongside future public command contract - changes. diff --git a/docs/journals/260721-1446-command-parameter-conventions.md b/docs/journals/260721-1446-command-parameter-conventions.md deleted file mode 100644 index 00961a4..0000000 --- a/docs/journals/260721-1446-command-parameter-conventions.md +++ /dev/null @@ -1,44 +0,0 @@ -# Command Parameter Conventions Journal - -## Context - -Command parameter labels had grown organically, especially for comma-separated -arguments. Researched common CLI notation to establish a minimal display syntax -for Telegram's native menu, `/help`, and handler usage text. - -## What Changed - -- Added the evergreen `docs/command-parameter-conventions.md` reference with - concise rules for required, optional, variadic, alternative, and structured - parameters. -- Recorded the user-selected `` notation for required - comma-separated values. -- Updated `/random` and `/wheelofnames` metadata, usage text, and contract tests - from `` to ``. -- Linked project guidance and README command-discovery documentation to the - shared convention reference. - -## Reflection - -The compact notation communicates both one-or-more values and the literal comma -separator without turning display metadata into a schema language. Keeping the -rules in an evergreen document avoids repeating policy in project instructions -and feature docs. - -## Decisions - -- Parameter strings remain presentation-only; handlers own validation. -- `/random` and `/wheelofnames` parsing and runtime behavior remain unchanged. -- Metadata, usage errors, and tests must use the same exact notation. - -## Verification - -- Passed: focused command-presentation and misc tests. -- Passed: `go test ./...` -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Passed: `golangci-lint run` - -## Next Steps - -- Apply the evergreen conventions whenever command parameters change. diff --git a/docs/journals/260721-1539-stock-coin-cost-basis-pnl.md b/docs/journals/260721-1539-stock-coin-cost-basis-pnl.md deleted file mode 100644 index dcd5686..0000000 --- a/docs/journals/260721-1539-stock-coin-cost-basis-pnl.md +++ /dev/null @@ -1,59 +0,0 @@ -# Stock and Coin Cost Basis P&L Journal - -## Context - -Stock and coin portfolios tracked account funding and holdings but not the -remaining acquisition cost of each position, so they could not distinguish -realized sale results from unrealized open-position performance. - -## What Changed - -- Added persisted per-symbol total remaining `costBasis` to stock and coin - portfolios, with invariant checks on load, update, and save paths. -- Buys add actual spend; partial sells remove proportional weighted-average - basis and report realized P&L; full sells remove the position and its basis. -- Portfolio views now show average entry price and per-position unrealized P&L. - Account P&L remains total account value minus top-ups and is withheld when - missing quotes make valuation partial. -- Stock share dividends preserve total basis while increasing quantity; cash - dividends and top-ups remain outside position basis. -- Sorted, bounded portfolio replies retain summaries while omitting excess - position lines before Telegram's message limit. - -## Migration Safety - -- Enabled modules scan every portfolio before handlers are installed on every - boot, even when a completion marker already exists. -- Missing legacy basis is initialized from a complete current quote set, giving - each migrated position zero initial unrealized P&L without repricing rows that - already have basis. -- Versioned writes retry conflicts; the shared `system` marker is written only - after all rows succeed and remains an audit record rather than a scan bypass. -- A two-minute overall deadline bounds startup. Invalid data, noncanonical - symbols, missing quotes, exhausted conflicts, or storage failures abort - startup instead of allowing trades with unknown basis. - -## Decisions - -- Persist total remaining cost, deriving average entry price as basis divided by - held quantity; do not persist trade lots or cumulative realized P&L. -- Migrate only loaded modules, so disabled-module data waits until re-enabled. -- Preserve all command names, parameter contracts, and existing portfolio data. - -## Verification - -- Passed: focused stock and coin accounting, migration, output, and reply-budget - tests. -- Passed: MongoDB 8 Testcontainers migration and idempotency coverage. -- Passed: `go test ./...` -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Passed: `golangci-lint run` - -## Operational Impact - -- Deployments with legacy holdings may perform market-price lookups and writes - during startup; a required provider or database failure intentionally keeps - the bot offline until initialization can complete safely. -- Subsequent healthy boots still verify invariants but do not reprice completed - positions. diff --git a/docs/journals/260721-1704-dividend-api-and-migration-cleanup.md b/docs/journals/260721-1704-dividend-api-and-migration-cleanup.md deleted file mode 100644 index 7a8e8c2..0000000 --- a/docs/journals/260721-1704-dividend-api-and-migration-cleanup.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -type: technical-journal -topic: dividend-api-and-migration-cleanup -conducted_at: 2026-07-21T17:04:00+07:00 -status: complete ---- - -# Dividend API and Migration Cleanup Journal - -## Context - -Portfolio schema migrations were verified complete in production, so their -compatibility code had become permanent startup and maintenance overhead. Coin -positions also inherited a stock-only dividend cursor with no valid use. -Separately, research was needed before designing automatic discovery of -Vietnamese stock dividend events. - -## What Happened - -- Removed `dividendCheckedAt` from coin positions, validation, buy behavior, - and tests. Stock retains the cursor because dividend events apply there. -- Retired completed stock and coin portfolio migrations plus the completed - stats command-renaming migration and their migration-only tests. -- Preserved recurring startup maintenance: stats query indexes and the LoL - match-cache TTL index remain idempotently initialized and tested. -- Preserved historical migration records in MongoDB's `system` collection and - kept the reusable `internal/systemstate` helper for future migrations. -- Removed legacy numeric-position JSON/BSON decoders after production schema - completion was confirmed. Current models now describe only the supported - nested asset schema. -- Accepted lazy cleanup of stale coin cursor fields. BSON decoding ignores the - old unknown field, and the next portfolio mutation uses versioned - `ReplaceOne`, rewriting that portfolio in the current shape without a new - one-time migration. - -## Dividend API Research - -SSI iBoard's corporate-actions endpoint is the recommended initial source. It -currently returns anonymous JSON with ticker/date filters, pagination, stable -`CorId` values, cash amounts, ratios, and event dates. It covered verified cash -and share dividend examples. - -The endpoint is undocumented and has no published SLA, rate limit, or stability -contract. Any implementation should therefore isolate it behind a replaceable -provider, validate and locally classify events, deduplicate by `CorId`, and ask -the user to confirm before changing a portfolio. VSDC remains the authoritative -notice source; a licensed FiinGroup feed is the stronger future option if this -becomes production-critical. - -## Decisions - -- Keep dividend state stock-only; coin assets persist only `quantity` and - total remaining `base`. -- Remove completed one-time runtime code rather than continuing to scan already - migrated portfolios on every boot. -- Do not delete system history or general migration infrastructure. -- Do not introduce a cleanup migration solely for stale coin BSON fields; - normal writes remove them safely over time. -- Treat SSI iBoard as a replaceable prototype provider, not a guaranteed public - API contract. - -## Verification - -- Passed focused tests: - `go test -count=1 ./internal/modules/coin ./internal/modules/stock ./internal/modules/stats ./cmd/server` -- Passed full suite: `go test -count=1 ./...` -- MongoDB 8 tests executed and passed for stats indexes and the LoL TTL index. -- Passed: `go vet ./...` -- Passed: `go build ./...` -- Passed with zero issues: `golangci-lint run` -- Passed: `git diff --check` (expected LF/CRLF working-copy warnings only). -- Independent tester, debugger, and reviewer found no defects. - -## Reflection - -Migration code is operationally valuable only while incompatible data can -still exist. Removing it after verification narrows startup failure modes and -makes the active persistence contract explicit. Lazy removal is appropriate -for an ignored field because it does not affect reads or correctness; structural -schema changes still require guarded migrations. - -## Next - -Design the Telegram interaction for listing SSI dividend events, including date -windows, pagination, exact ratio conversion, ambiguity handling, and explicit -user confirmation. Preserve manual dividend commands and keep automatic event -application out of scope until that interaction is approved. diff --git a/docs/journals/260721-1815-stock-dividend-event-buttons.md b/docs/journals/260721-1815-stock-dividend-event-buttons.md deleted file mode 100644 index 2083f42..0000000 --- a/docs/journals/260721-1815-stock-dividend-event-buttons.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -type: technical-journal -topic: stock-dividend-event-buttons -conducted_at: 2026-07-21T18:15:00+07:00 -status: complete ---- - -# Stock Dividend Event Buttons Journal - -## Context - -`/stock_portfolio` needed to surface recent dividend events for held tickers -without obscuring the portfolio or silently changing financial state. SSI -iBoard was selected as a best-effort discovery source, while the existing -manual dividend commands remain available. - -## What Happened - -- The portfolio response is sent first. Each discovered cash or explicit share - dividend follows in its own message with an `Apply dividend` button; a ticker - with no relevant event produces no extra message. -- Added a replaceable SSI provider with pagination, local event classification, - stable event IDs, deduplication, and exact ratio handling. -- Added callback registration to the shared module framework and persisted - short-lived pending actions separately from portfolios. -- Successful checks advance `dividendCheckedAt`; failed checks leave the cursor - unchanged and never prevent the portfolio from being displayed. -- Added `openedAt` to distinguish the holding lifecycle and retained applied SSI - event identities for auditability and replay protection. - -SSI discovery accepts only day-granularity date windows. Queries therefore use -a cursor overlap at the lower boundary, then locally filter and deduplicate the -returned events. This avoids missing events around a date boundary while stable -SSI event IDs prevent overlap from producing repeated actionable suggestions. - -## Key Decisions - -- Callback data carries an opaque random token, not trusted dividend values. - Pending actions expire after 24 hours and are bound to the requesting - Telegram user, originating chat, and event message. -- A callback reloads the current portfolio and calculates from the holding at - click time. Another group member, an expired action, or a holding sold and - reopened after suggestion creation cannot apply it. -- Applied SSI event IDs are recorded atomically with the portfolio mutation, so - duplicate clicks and retries cannot apply the same provider event twice. -- Event fetches occur outside the per-user mutation lock. Cursor persistence - reloads and merges under that lock, preserving concurrent buys, sells, and - manual dividends without holding a lock across network calls. -- Notification or provider failures do not advance the affected cursor. A later - portfolio request can safely retry rather than lose an event. - -## Verification - -- Focused stock and module tests passed. -- Full suite passed: `go test -count=1 ./...`. -- Stock race tests and 20-iteration concurrency stress tests passed. -- `go vet ./...` and `go build ./...` passed. -- `golangci-lint run` completed with zero issues. -- Independent testing, debugging, and code review found no blockers. - -## Risks and Limitations - -- SSI iBoard is undocumented and has no published stability or availability - guarantee; the provider can require replacement if its contract changes. -- The portfolio has no dated transaction lots, so current quantity is not proof - of record-date entitlement. Users must verify the issuer notice. -- Manual commands do not record an SSI event ID. Applying manually and later - accepting the related button can count the same dividend twice. - -## Next - -Observe SSI response stability and callback behavior in real use. Commit these -changes only if the user requests it. diff --git a/plans/260720-1616-stock-dividend-commands/phase-01-implement-ratio-dividend-commands.md b/plans/260720-1616-stock-dividend-commands/phase-01-implement-ratio-dividend-commands.md deleted file mode 100644 index d9a43b3..0000000 --- a/plans/260720-1616-stock-dividend-commands/phase-01-implement-ratio-dividend-commands.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -phase: 1 -title: Implement Ratio Dividend Commands -status: completed -effort: '' -priority: P1 -dependencies: [] ---- - -# Phase 1: Implement Ratio Dividend Commands - - - -## Context Links - -- [Approved brainstorm](../reports/260720-1612-stock-dividend-command-brainstorm.md) -- [VSDC ratio research](../reports/260720-1608-dividend-notice-ratio-research.md) - -## Overview - -Replace the two ambiguous adjustment handlers with cash-only, share-only, and -combined dividend contracts. Centralize validated ratio math so all handlers -use the same overflow-safe, floor-rounded calculation. - -## Requirements - -- Functional: register `stock_cash_dividend`, `stock_share_dividend`, and - `stock_dividend` with the approved argument order and clear usage examples. -- Functional: accept cash only as positive whole VND per share; reject signs, - decimals, zero, parse overflow, and non-finite representations. -- Functional: accept only positive whole-number `owned:new` parts; reject - missing/extra colons, decimals, signs, zero, parse overflow, and invalid ticker. -- Functional: accept equivalent unreduced ratios and preserve the user's exact - valid ratio text in the success reply; do not require or display reduction. -- Functional: compute `floor(held * new / owned)` without overflowing `int64`; - compute the minimum holding for a non-zero share result safely. -- Functional: share-only rejects a zero result and reports that minimum; - combined credits cash and reports zero shares. -- Non-functional: cash and shares use the same pre-event holding; mutate only - after all validation; call `SavePortfolio` exactly once per successful event. -- Non-functional: allow intentional repeated calls; add no event ID, notice - lookup, history ledger, or duplicate-event guard. - -## Architecture - -Add a small dividend calculation helper beside the stock handlers. Parse cash -and ratio parts into positive integers while retaining the validated ratio -string for replies. Calculate quotient and remainder before -multiplication (or use checked operations) to preserve floor semantics without -`held * new` overflow. Each handler loads once, snapshots `held`, calculates -all outputs, mutates the in-memory portfolio, then saves once. The combined -handler must not let newly issued shares participate in its cash calculation. -Treat every successful invocation as an intentional manual adjustment. - -## Related Code Files - -- Create: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividends.go` — ratio parsing and checked entitlement math. -- Create: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividends_test.go` — parser, floor, minimum, and overflow boundaries. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers.go` — three handlers and user-facing replies. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers_test.go` — contracts, failures, pre-event basis, and one-save behavior. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock.go` — registry names, descriptions, handlers. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\portfolio_test.go` — portfolio outcomes where helper coverage belongs. - -## Implementation Steps - -1. Write table tests first for whole-VND parsing, `4:1`, `100:10`, equivalent - unreduced ratios, malformed inputs, `int64` boundaries, exact division, - floor division, preserved ratio text, and safe minimum holdings. -2. Add handler tests for usage text, invalid cash/ratio/ticker, no holdings, - zero-result divergence, and persistence failures. -3. Add combined tests proving cash and shares derive from the same snapshot and - one store write commits both; use an instrumented test store to count saves. -4. Implement parsing and overflow-safe integer entitlement helpers. Require - positive whole VND/share and reject fractional or overflowing totals. -5. Replace `handleBonus`/old cash `handleDividend` routing with explicit cash, - share, and combined handlers. Replies include ratio, old holding, cash, new - shares, and final holding as applicable. -6. Update the command registry to eight stock commands and remove public - registration of `stock_bonus`. -7. Run `gofmt` and focused stock tests. - -## Success Criteria - -- [x] All three approved commands and examples are registered exactly. -- [x] Cash rejects fractional VND; valid unreduced ratios are accepted and echoed unchanged. -- [x] Share math floors without overflow for every accepted `int64` input. -- [x] Share-only zero entitlement makes no change; combined zero entitlement credits cash and reports zero shares. -- [x] Successful combined events use pre-event holdings and one `SavePortfolio`. -- [x] Repeating a valid command applies the adjustment again; no deduplication state exists. -- [x] `go test ./internal/modules/stock` passes. - -## Risk Assessment - -- Overflow or invalid numeric acceptance could silently over-credit portfolios. - Mitigate with integer parsing, quotient/remainder math, checked cash totals, - and boundary tests. -- Partial mutation could diverge balances. Validate first, mutate in memory, - persist once; verify the unchanged state on every rejected path. -- Manual repeated calls can double-credit an event by design. State caller - responsibility clearly; do not silently infer or suppress duplicates. - -## Security Considerations - -No new authorization surface. Continue sender checks and strict ticker/input -validation; do not echo unbounded raw input. - -## Next Steps - -Phase 2 migrates persisted stats before the renamed commands are deployed. diff --git a/plans/260720-1616-stock-dividend-commands/phase-02-migrate-command-statistics.md b/plans/260720-1616-stock-dividend-commands/phase-02-migrate-command-statistics.md deleted file mode 100644 index b7405e8..0000000 --- a/plans/260720-1616-stock-dividend-commands/phase-02-migrate-command-statistics.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -phase: 2 -title: Migrate Command Statistics -status: completed -effort: '' -priority: P1 -dependencies: - - 1 ---- - -# Phase 2: Migrate Command Statistics - - - -## Context Links - -- [Approved compatibility decision](../reports/260720-1612-stock-dividend-command-brainstorm.md#compatibility-and-touchpoints) -- [Project stats compatibility rules](../../AGENTS.md#stats-compatibility) - -## Overview - -Move historical usage to the commands that retain the old meanings, then leave -`stock_dividend` empty for new combined-event usage. Guard the one-time move in -the shared `system` collection and run it during server startup. - -## Requirements - -- Functional: migrate `stock_dividend -> stock_cash_dividend` and - `stock_bonus -> stock_share_dividend`. -- Functional: include command-total rows (`uid=0`) and every per-user row; - merge source counts into existing target rows rather than overwrite them. -- Functional: preserve user ID/username metadata, remove migrated source rows, - and write the completion marker only after both mappings finish. -- Functional: a completed marker makes subsequent startups a no-op; tests cover - target merging and repeated invocation for memory and MongoDB stores. -- Functional: an incomplete migration resumes both remaining source rows and - `prepared` row checkpoints, including a checkpoint whose source was deleted. -- Functional: retain global and per-row markers permanently as migration history. -- Non-functional: migration errors fail startup before module registration, so - new command meanings never run against unmigrated history. - -## Architecture - -Extend stats startup maintenance to receive both `stats` and `system` -collections. Use the existing typed stats documents and `systemstate.Store`. -A stable marker such as `migration:stock-dividend-command-stats-v1` records -global completion. For each source row, first persist a stable per-row -`prepared` checkpoint in `system` containing the exact final target count. -Retries set the target to that checkpointed count instead of adding again, -then delete the source and mark the row complete. Preserve the best available -username deterministically. Mark global completion only after every row is -complete. On startup without a global completion marker, enumerate both source -rows and prepared checkpoints so deletion-before-row-complete can recover. -Retain all markers as migration history; add no cleanup path. - -## Related Code Files - -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\startup.go` — startup signature, marker guard, and migration orchestration. -- Create: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\startup_test.go` — memory migration, merges, anonymous/users, failures, idempotency. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\startup_mongo_test.go` — Mongo indexes plus migration parity and rerun checks. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\stats_test.go` — visible stats attribution after migration if needed. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\cmd\server\main.go` — pass `stats` and shared `system` collections before module build. -- Reference: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\systemstate\systemstate.go` — existing marker store; no schema change expected. - -## Implementation Steps - -1. Add memory tests seeding source-only, target-only, merged anonymous, merged - per-user, multiple users, and both command mappings. -2. Assert exact post-migration keys/counts/metadata, source removal, marker - content, and no change after a second startup call. -3. Add injected-failure tests at prepare, target-write, source-delete, and - row-complete boundaries. Retry each case—including source already deleted— - and prove counts never duplicate. -4. Implement deterministic global and per-row marker keys. A prepared marker - stores the exact merged target count; retries overwrite with that value, - never increment an already migrated target again. -5. Implement the mapping helper using deterministic `usageKey` values and the - existing typed store. Keep mapping data declarative and local to stats. -6. Extend `InitStore` to create indexes and run the guarded migration; wrap - errors with migration/command context. -7. Wire `provider.Collection(systemstate.CollectionName)` from server startup - and preserve fail-fast logging before command registration. -8. Mirror critical merge, partial-retry, and idempotency cases in Mongo tests. -9. Run `gofmt`, focused stats tests, and focused server tests. - -## Success Criteria - -- [x] Old cash usage appears only under `stock_cash_dividend`. -- [x] Old bonus usage appears only under `stock_share_dividend`. -- [x] Existing target counts merge for anonymous and per-user rows without loss. -- [x] Retries after every partial-write boundary cannot duplicate counts. -- [x] Prepared checkpoints resume even when their source rows no longer exist. -- [x] Completed migration is idempotent and marker-backed in memory and MongoDB. -- [x] Global and per-row migration markers remain after completion. -- [x] Startup aborts on migration failure and `go test ./internal/modules/stats ./cmd/server` passes. - -## Risk Assessment - -- Reusing `stock_dividend` before migration would mislabel history. Keep the - migration before module construction and treat errors as fatal. -- A mid-migration process crash may leave partial work. Prepare each row's exact - final count before touching the target; retry by setting that value, not by - adding again. Scan prepared checkpoints independently from source rows. Do - not mark row/global completion early or delete historical markers. - -## Security Considerations - -The migration touches counts and public usernames only. Never log full records; -log marker/mapping names and aggregate counts. - -## Next Steps - -Phase 3 aligns all user-facing surfaces and runs the repository-wide gate. diff --git a/plans/260720-1616-stock-dividend-commands/phase-03-verify-user-contracts.md b/plans/260720-1616-stock-dividend-commands/phase-03-verify-user-contracts.md deleted file mode 100644 index 25df217..0000000 --- a/plans/260720-1616-stock-dividend-commands/phase-03-verify-user-contracts.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -phase: 3 -title: Verify User Contracts -status: completed -effort: '' -priority: P1 -dependencies: - - 1 - - 2 ---- - -# Phase 3: Verify User Contracts - - - -## Context Links - -- [Approved command contracts](../reports/260720-1612-stock-dividend-command-brainstorm.md#approved-behavior) -- [Repository development rules](../../AGENTS.md#command-changes) - -## Overview - -Synchronize Telegram command-menu metadata and README documentation with the -implemented contracts, then run focused and repository-wide verification. - -## Requirements - -- Functional: command names, descriptions, argument order, examples, errors, - and menu behavior agree across registry, handlers, JSON, tests, and README. -- Functional: remove `stock_bonus` from active user surfaces; document that - cash is VND/share and share input is the `owned:new` notice ratio. -- Functional: document positive whole-VND input, acceptance of unreduced ratios, - preserved ratio text, and deliberate repeat-call behavior. -- Non-functional: preserve plan-approved migration behavior and run every gate - required for command, storage, migration, and shared startup changes. - -## Architecture - -Treat registered Go commands as runtime truth and `telegram-commands.json` as -the manual BotFather/menu source. Tests assert the final eight-command stock -registry and registered menu contents. README gives concise user-visible syntax -and the floor/pre-event behavior needed to use notices correctly. It states -that commands are manual adjustments: the caller verifies the notice and avoids -accidental duplicates; the bot enforces syntax and storage safety only. - -## Related Code Files - -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\telegram-commands.json` — replace old dividend/bonus entries with three approved commands. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\README.md` — document syntax, VND/share, ratio direction, floor rounding, and combined behavior. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers_test.go` — final registry and user-facing text assertions. -- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\cmd\server\command_menu_test.go` — command-menu presence and removal checks. -- Verify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\startup_test.go` — migration contracts remain green. - -## Implementation Steps - -1. Replace `stock_bonus` and old cash-only `stock_dividend` menu descriptions - with `stock_cash_dividend`, `stock_share_dividend`, and combined - `stock_dividend`; keep Telegram ordering coherent. -2. Add a compact README stock-command section with all three syntaxes and one - mixed example. State positive whole VND/share, `owned:new`, accepted - unreduced ratios, preserved ratio replies, integer floor rounding, - pre-event basis, and caller responsibility for repeat events. -3. Update registry/menu tests to assert new commands and reject old public - `stock_bonus`; ensure descriptions stay within Telegram limits. -4. Run `gofmt` over every changed Go file. -5. Run focused packages: `go test ./internal/modules/stock`, - `go test ./internal/modules/stats`, and `go test ./cmd/server`. -6. Run `go test ./...` and `go vet ./...`. -7. If `golangci-lint` is available, run `golangci-lint run`; record a skipped - gate explicitly when the binary is absent. - -## Success Criteria - -- [x] Registry, handlers, JSON, README, and menu tests expose the same three contracts. -- [x] No active user-facing surface advertises `stock_bonus` or old cash-only `/stock_dividend` syntax. -- [x] README distinguishes manual correctness responsibility from mandatory parser/overflow safety. -- [x] Focused tests, `go test ./...`, and `go vet ./...` pass. -- [x] `golangci-lint run` passes when available. -- [x] No unresolved questions remain. - -## Risk Assessment - -- Stale menu/docs can cause financially wrong manual entries. Search all active - surfaces for old names and syntax, while leaving migration fixtures intact. -- Mongo integration tests may require local infrastructure. Keep unit coverage - mandatory and report environment-based integration skips accurately. - -## Security Considerations - -Documentation must use synthetic holdings and tickers only; no tokens, -production data, or private portfolio records. - -## Next Steps - -Implementation can proceed phase-by-phase after plan approval. diff --git a/plans/260720-1616-stock-dividend-commands/plan.md b/plans/260720-1616-stock-dividend-commands/plan.md deleted file mode 100644 index be1126e..0000000 --- a/plans/260720-1616-stock-dividend-commands/plan.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Stock Dividend Commands -description: >- - Replace ambiguous stock adjustments with cash-only, share-only, and combined - dividend commands while preserving historical stats. -status: completed -priority: P1 -branch: main -tags: - - feature - - backend - - database -blockedBy: [] -blocks: [] -created: '2026-07-20T09:16:13.694Z' -createdBy: 'ck:plan' -source: skill ---- - -# Stock Dividend Commands - -## Overview - -Add three explicit public contracts: cash dividend in VND/share, share dividend -in `owned:new` ratio form, and a combined command. Use integer floor rounding, -pre-event holdings, and one portfolio save. Rename historical stats through an -idempotent startup migration before `/stock_dividend` gains its new meaning. - -## Phases - -| Phase | Name | Status | -|-------|------|--------| -| 1 | [Implement Ratio Dividend Commands](./phase-01-implement-ratio-dividend-commands.md) | Completed | -| 2 | [Migrate Command Statistics](./phase-02-migrate-command-statistics.md) | Completed | -| 3 | [Verify User Contracts](./phase-03-verify-user-contracts.md) | Completed | - -## Dependencies - -- Approved design: [Stock Dividend Command Brainstorm](../reports/260720-1612-stock-dividend-command-brainstorm.md) -- Evidence: [Dividend Notice Ratio Research](../reports/260720-1608-dividend-notice-ratio-research.md) -- Existing Go module, typed storage, `system` marker store, and stats startup path - -## Contracts - -- `/stock_cash_dividend ` credits a positive whole-VND amount per share. -- `/stock_share_dividend ` accepts any positive integer ratio, preserves its entered form in replies, and adds floor-rounded shares; zero result rejects. -- `/stock_dividend ` applies both from pre-event holdings; zero shares still credits cash. -- Stats move `stock_dividend -> stock_cash_dividend` and `stock_bonus -> stock_share_dividend`; combined usage starts fresh. -- Commands are deliberate manual adjustments: repeated calls are allowed and - no issuer-event deduplication or ratio reduction is imposed. - -## Validation Gate - -- Focused stock, stats migration, startup, and command-menu tests. -- `gofmt` changed Go files; run `go test ./...` and `go vet ./...`. -- Run `golangci-lint run` when installed. - -## Completion Criteria - -- Commands, replies, menu JSON, and README agree on names and argument order. -- Ratio math cannot overflow and always floors; combined state persists once. -- Stats migration merges anonymous/per-user targets and repeated startup is a no-op. -- No unresolved questions. - -## Validation Log - -### Session 1 — 2026-07-20 - -**Trigger:** User requested `/ck:plan validate` before implementation. -**Questions asked:** 4 - -### Verification Results - -- **Tier:** Standard (Fact Checker + Contract Verifier) -- **Claims checked:** 30 -- **Verified:** 30 | **Failed:** 0 | **Unverified:** 0 -- Verified current handlers/registry/portfolio storage, stats startup caller and - persisted row shape, shared system marker store, command-menu flow, tests, - README/JSON surfaces, and repository verification commands. -- Planned create-paths correctly do not exist yet: `dividends.go`, - `dividends_test.go`, and stats `startup_test.go`. -- **Failures:** None. - -#### Questions & Answers - -1. **[Assumption]** Should cash dividends require a positive whole VND amount per share and reject decimals? - - Options: Require positive whole VND (Recommended) | Preserve positive finite decimal input - - **Answer:** Require positive whole VND. - - **Rationale:** Matches approved manual-entry contract and avoids fractional VND credits. -2. **[Architecture]** Should share ratios accept any positive integer form and preserve the entered form without requiring reduction? - - Options: Accept and preserve any positive ratio (Recommended) | Require reduced ratios | Normalize replies - - **Answer:** Accept and preserve any positive ratio. - - **Rationale:** Mirrors issuer notices while avoiding unnecessary restrictions. -3. **[Scope]** Should repeated dividend commands be allowed without an issuer-event deduplication ledger? - - Options: Allow repeated manual adjustments (Recommended) | Add event IDs and deduplication - - **Answer:** Allow repeated manual adjustments; caller owns correctness. - - **Custom input:** "this is for user do manually, so not restrict anything, user call it will have reponsibility to make sure call it correct" - - **Rationale:** The feature is a manual portfolio adjustment, not an issuer-event ledger. -4. **[Risk]** Should migration retries scan prepared row checkpoints and retain all migration markers permanently? - - Options: Retry prepared rows and retain markers (Recommended) | Use global marker only | Clean row markers after completion - - **Answer:** Retry prepared rows and retain markers permanently. - - **Rationale:** Makes partial retries auditable and prevents double-counting. - -#### Confirmed Decisions - -- Manual responsibility does not bypass storage-safety validation: cash remains - positive whole VND; ratio parts remain positive integers; ticker and overflow - checks remain mandatory. -- No ratio canonicalization, notice lookup, event ID, or duplicate-call guard. -- Migration completion requires reconciling both source rows and prepared row - checkpoints; global and row markers remain as history. - -#### Action Items - -- [x] Propagate manual-input and repeat-call rules to Phase 1. -- [x] Propagate checkpoint retry/retention rules to Phase 2. -- [x] Propagate user-facing responsibility wording to Phase 3. - -#### Impact on Phases - -- Phase 1: tighten cash parsing; preserve ratio text; explicitly allow repeats. -- Phase 2: scan prepared checkpoints on retry; retain all markers. -- Phase 3: document manual responsibility and safety-validation boundary. - -### Whole-Plan Consistency Sweep - -- Files reread: `plan.md` and all three `phase-*.md` files. -- Decision deltas checked: 4. -- Reconciled stale references: 1 (`float acceptance` replaced by integer-input wording). -- Verified command names, input order, whole-VND rule, unreduced-ratio policy, - repeat-call policy, zero-share behavior, migration retry flow, and marker - retention agree across overview, requirements, steps, risks, and criteria. -- Unresolved contradictions: 0. diff --git a/plans/260720-1616-stock-dividend-commands/reports/pm-260720-1715-stock-dividend-completion.md b/plans/260720-1616-stock-dividend-commands/reports/pm-260720-1715-stock-dividend-completion.md deleted file mode 100644 index 631b9c6..0000000 --- a/plans/260720-1616-stock-dividend-commands/reports/pm-260720-1715-stock-dividend-completion.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -type: pm-completion-report -plan: stock-dividend-commands -status: completed -created_at: 2026-07-20T17:15:00+07:00 ---- - -# Plan Complete: Stock Dividend Commands - -## Summary - -| Metric | Result | -|---|---| -| Plan status | Completed | -| Phase progress | 3/3 (100%) | -| Success criteria | 21/21 checked | -| Unresolved mappings | 0 | -| Review | 9/10, approved | -| Adversarial review | Passed | - -## Delivered - -- Explicit cash-only, share-only, and combined dividend contracts. -- Overflow-safe floor entitlement math using pre-event holdings and one save. -- Idempotent stats renames with retained migration checkpoints. -- Registry, Telegram menu, tests, and README aligned with public contracts. - -## Verification - -- Focused stock, stats, server, and command-menu tests: passed. -- `go test ./...`: passed. -- `go vet ./...`: passed. -- Whole-plan consistency sweep: passed; no unresolved mappings. - -## Known Limitation - -- MongoDB partial-boundary execution unavailable in the verification - environment. Deterministic retry behavior remains covered by available - focused verification; no completion blocker recorded. - -## Unresolved Questions - -None. diff --git a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-01-design-persistence-and-migration.md b/plans/260721-1508-stock-coin-cost-basis-pnl/phase-01-design-persistence-and-migration.md deleted file mode 100644 index a80816d..0000000 --- a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-01-design-persistence-and-migration.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -phase: 1 -title: Design persistence and migration -status: completed -priority: P1 -effort: medium -dependencies: [] ---- - -# Phase 1: Design persistence and migration - -## Overview - -Extend both portfolio documents and add retry-safe startup migrations that -initialize legacy holdings from current quotes before command handlers run. - -## Requirements - -- Functional: persist `costBasis` as ticker/coin → total remaining cost. -- Functional: initialize missing legacy basis as `quantity × current price`. -- Functional: preserve any positive basis already written by a previous retry. -- Functional: scan every boot even after completion; the marker is an audit - record and never suppresses invariant checks. -- Non-functional: startup fails on a required quote/storage error; migration is - idempotent and bounded by a migration-wide deadline. - -## Architecture - -`cmd/server` checks which modules are loaded, then invokes each module's -`InitStore` before `modules.Install`. Every boot lists `user:` documents and -checks `positive holding => finite positive basis`. Migration fetches each -required symbol once, verifies the complete requested set, and writes only -missing basis using bounded CAS reload/retry. The shared `system` marker records -completion but does not skip future scans. Existing populated basis acts as -per-position retry progress. - -## Related Code Files - -- Modify: `internal/modules/stock/portfolio.go` -- Modify: `internal/modules/coin/portfolio.go` -- Create: `internal/modules/stock/startup.go` -- Create: `internal/modules/coin/startup.go` -- Modify: `cmd/server/main.go` -- Create: stock/coin startup memory and MongoDB tests - -## Implementation Steps - -1. Add and defensively initialize `CostBasis map[string]float64` with - `json:"costBasis" bson:"costBasis"` in both portfolio types. -2. Validate before normalization. Reject corrupt basis, noncanonical symbols, - canonical-key collisions, and non-finite holdings; do not delete or merge - ambiguous legacy assets. -3. Implement injectable stock and coin migrations with stable `system` keys, - `user:` listing, unique-symbol quote caching, complete quote-set validation, - bounded CAS reload/retry, progress logs, and a completion marker written last. -4. Wire migration only for loaded modules after registry construction and - before handler installation. Return startup-fatal errors with module/symbol - context but no credentials. -5. Apply an overall startup-migration timeout; timeout is startup-fatal and - leaves the marker incomplete. -6. Test empty, legacy, mixed, partial-retry, post-marker missing rows, partial - quote maps, noncanonical/corrupt rows, CAS conflicts, timeout, storage - failure, and idempotent second-boot cases in memory and MongoDB 8. - -## Success Criteria - -- [ ] New portfolios always contain initialized basis maps. -- [ ] Existing documents without `costBasis` still decode. -- [ ] Each legacy symbol receives migration-time current-price basis. -- [ ] Retry never overwrites basis initialized by an earlier attempt. -- [ ] Any required failure prevents the completion marker and aborts startup. -- [ ] Disabled modules do not call external quote providers. -- [ ] A completion marker never hides a later holding with missing basis. - -## Risk Assessment - -- External quote outages can block startup by explicit owner choice; provider - timeouts bound the delay and logs identify the module/symbol. -- Partial writes are recoverable because populated per-symbol basis is never - repriced and the global marker is written only after all rows succeed. -- Runtime validation fails closed if an old writer or restored row reintroduces - a positive holding without valid basis after startup. -- Storage listing remains unpaginated; the migration-wide deadline bounds the - current small, one-replica deployment without broad storage-API scope. diff --git a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-02-implement-weighted-average-accounting.md b/plans/260721-1508-stock-coin-cost-basis-pnl/phase-02-implement-weighted-average-accounting.md deleted file mode 100644 index 0087a09..0000000 --- a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-02-implement-weighted-average-accounting.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -phase: 2 -title: Implement weighted-average accounting -status: completed -priority: P1 -effort: medium -dependencies: - - 1 ---- - -# Phase 2: Implement weighted-average accounting - -## Overview - -Update stock and coin trade mutations to maintain remaining cost basis and show -realized P&L on every successful sale. - -## Requirements - -- Functional: buys add actual transaction spend to the symbol's total basis. -- Functional: sells remove proportional basis and report realized P&L. -- Functional: full exits remove both holding and basis keys. -- Functional: stock share dividends change quantity but not total basis. -- Non-functional: rejected trades and failed saves leave portfolio state intact. -- Non-functional: positive holdings with absent, nonpositive, or non-finite - basis fail closed instead of producing fabricated accounting. - -## Architecture - -For a sale, capture pre-sale holding and basis, then calculate: - -```text -sold_basis = total_basis × sold_quantity / held_quantity -realized_pnl = proceeds - sold_basis -realized_pct = realized_pnl / sold_basis × 100 -``` - -The remaining average price is unchanged after a partial sale. A share dividend -adds shares without cost, so `basis / new_quantity` automatically lowers the -average price. No lot ledger or cumulative realized-P&L field is introduced. - -## Related Code Files - -- Modify: stock/coin `portfolio.go`, trade handlers, and format helpers -- Modify: stock dividend handler tests to assert unchanged total basis -- Modify: stock/coin portfolio and handler tests - -## Implementation Steps - -1. Add small portfolio methods to add purchase basis and remove proportional - basis with finite/overflow guards appropriate to each module's precision. -2. Update buys so quantity, cash deduction, and basis addition persist in the - same mutation. -3. Update sells so quantity, cash credit, and basis reduction persist together; - append `Realized P&L` amount and percentage to the reply. -4. Determine coin full exit from normalized post-sale holdings. If quantity - disappears at the dust threshold, assign all remaining basis to that sale - and delete the basis key. Keep monetary validation separate from quantity - dust normalization. -5. Assert cash/share/combined dividend behavior: cash never changes basis and - share additions preserve total basis. -6. Cover weighted repeated buys, gain/loss/breakeven partial sells, full sells, - insufficient holdings, overflow/invalid data, conflicts, and save failure. - -## Success Criteria - -- [ ] `average price == remaining basis / remaining quantity` after every buy, - sell, and share dividend. -- [ ] Stock and coin sell replies report correct realized P&L. -- [ ] Full exits leave no holding or basis entry. -- [ ] Failed/rejected operations do not mutate persisted state. -- [ ] Missing or corrupt runtime basis blocks affected accounting operations - with a clear retry/operator message. - -## Risk Assessment - -- Stock uses float64 currency already; calculations must reject non-finite or - unsafe results. Coin retains its existing dust normalization. -- Mutation order must not delete quantity before sold basis is calculated. -- Existing account `Meta.Invested` continues to mean deposits and must not be - repurposed as position basis. diff --git a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-03-verify-p-l-behavior-and-compatibility.md b/plans/260721-1508-stock-coin-cost-basis-pnl/phase-03-verify-p-l-behavior-and-compatibility.md deleted file mode 100644 index 710a915..0000000 --- a/plans/260721-1508-stock-coin-cost-basis-pnl/phase-03-verify-p-l-behavior-and-compatibility.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -phase: 3 -title: Verify P&L behavior and compatibility -status: completed -priority: P1 -effort: medium -dependencies: - - 1 - - 2 ---- - -# Phase 3: Verify P&L behavior and compatibility - -## Overview - -Expose average entry price and unrealized position P&L in both portfolio views, -document the accounting model, and run full compatibility verification. - -## Requirements - -- Functional: each priced holding shows average price and unrealized P&L. -- Functional: retain cash, total value, invested deposits, and account P&L. -- Functional: unavailable quotes degrade gracefully without inventing P&L; - aggregate account P&L is suppressed when any holding is unpriced. -- Non-functional: output remains deterministic and within Telegram limits. - -## Architecture - -Position metrics derive from persisted remaining basis: - -```text -average_price = basis / quantity -unrealized_pnl = current_value - basis -unrealized_pct = unrealized_pnl / basis × 100 -``` - -Label position performance `Unrealized P&L` and the existing net-worth minus -top-ups metric `Account P&L` so dividends, cash, and realized proceeds are not -confused with open-position performance. If any holding lacks a quote, label -priced values as partial and do not render aggregate account P&L. - -## Related Code Files - -- Modify: `internal/modules/stock/handlers.go`, `stats_test.go` -- Modify: `internal/modules/coin/views.go` and view/handler tests -- Modify: `README.md`, `docs/deploy-coolify-selfhosted.md` -- Create: completion journal during finalization - -## Implementation Steps - -1. Render average basis and signed unrealized P&L for each priced stock/coin - position. Add explicit stock sorting; retain coin sorting. -2. Keep existing total-value and invested calculations; relabel the final line - as `Account P&L` and add aggregate `Unrealized P&L` for priced positions. -3. For unavailable current quotes, show stored average price when valid, mark - current value/P&L unavailable, label priced totals partial, and suppress - aggregate account P&L. -4. Add a conservative Telegram reply budget with deterministic truncation and - test worst-case multi-position output. -5. Update exact output, missing-price, reply-budget, migration compatibility, - and command behavior tests. -6. Document startup migration, weighted-average basis, realized vs unrealized - P&L, and dividend effects. -7. Run `gofmt`, focused stock/coin/server tests, MongoDB Testcontainers tests, - `go test -count=1 ./...`, `go vet ./...`, `go build ./...`, and - `golangci-lint run`; complete independent test/debug/review gates. - -## Success Criteria - -- [ ] Each open position shows average price and unrealized amount/percentage. -- [ ] Portfolio retains account-level P&L with a clearer label. -- [ ] Missing prices do not fabricate P&L or break the reply. -- [ ] Stock output is sorted and both portfolio replies remain within Telegram - limits under worst-case supported holdings. -- [ ] Legacy memory and MongoDB documents remain readable and migrate once. -- [ ] All focused and repository-wide verification passes. -- [ ] No command name, parameter, stats-history, or price-provider contract - changes. - -## Risk Assessment - -- Users may confuse account and position metrics; explicit labels and README - examples define both. -- Missing quotes make totals partial today; the new output must label them - partial and suppress account P&L rather than report a false loss. -- Longer replies require budget tests for multi-asset coin portfolios. diff --git a/plans/260721-1508-stock-coin-cost-basis-pnl/plan.md b/plans/260721-1508-stock-coin-cost-basis-pnl/plan.md deleted file mode 100644 index 4d419a6..0000000 --- a/plans/260721-1508-stock-coin-cost-basis-pnl/plan.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Stock and Coin Cost Basis P&L -description: >- - Persist weighted-average cost basis, migrate legacy holdings at startup - prices, and expose realized/unrealized P&L. -status: completed -priority: P1 -branch: main -tags: - - stock - - coin - - mongodb - - migration - - pnl -blockedBy: [] -blocks: [] -created: '2026-07-21T08:08:21.155Z' -createdBy: 'ck:plan' -source: skill ---- - -# Stock and Coin Cost Basis P&L - -## Overview - -Add a per-symbol remaining cost basis to stock and coin portfolios. New buys -increase basis, partial sells remove proportional weighted-average basis, and -full sells remove it. Portfolio views show average entry price and unrealized -P&L per position; sell replies show realized P&L for that sale while preserving -the existing account-level P&L. - -Legacy holdings migrate before handlers are installed. Each missing basis is -initialized from the current quote, so its unrealized P&L starts at zero. -Migration is idempotent, marker-backed, and fail-fast: the bot does not accept -trades if required quotes or storage writes fail. - -## Phases - -| Phase | Name | Status | -|-------|------|--------| -| 1 | [Design persistence and migration](./phase-01-design-persistence-and-migration.md) | Completed | -| 2 | [Implement weighted-average accounting](./phase-02-implement-weighted-average-accounting.md) | Completed | -| 3 | [Verify P&L behavior and compatibility](./phase-03-verify-p-l-behavior-and-compatibility.md) | Completed | - -## Dependencies - -- No active plan dependencies. The completed stock-dividend implementation - defines the share-dividend behavior this plan must preserve. -- MongoDB 8 Testcontainers are required for persisted migration verification; - existing Docker-unavailable warning/skip behavior remains unchanged. - -## Decisions - -- Persist total remaining cost per symbol, not a mutable average-price field. -- Derive average price as `basis / held quantity`. -- Use proportional weighted-average basis for partial sells. -- Keep total stock basis unchanged when share dividends add quantity. -- Keep cash dividends and top-ups outside position basis. -- Do not persist cumulative realized P&L or trade lots. -- Run a module migration only when that module is loaded; disabled-module data - migrates the next time the module is enabled. -- Abort startup on migration failure to avoid operating with unknown basis. - -## Acceptance Criteria - -- Stock and coin portfolios persist remaining cost basis without breaking - legacy BSON/JSON decoding. -- Repeated buys produce the correct weighted average; partial/full sells update - basis exactly and report realized gain or loss. -- Startup maintenance scans every boot, initializes only missing legacy basis - from one complete current quote per symbol, never reprices completed work, - and writes its system marker only after success. -- Share dividends add quantity without adding cost; cash dividends do not - change position basis. -- Portfolio output shows average price and per-position unrealized P&L plus the - existing account-level P&L. -- No command names or parameter contracts change. -- Focused tests, MongoDB migration tests, full tests, vet, build, and lint pass. - -## Red Team Review - -### Session — 2026-07-21 - -**Findings:** 12 deduplicated (9 accepted, 3 rejected) - -| Finding | Severity | Disposition | Applied To | -|---|---|---|---| -| Completion marker cannot enforce future row invariants | Critical | Accept | Phases 1–2 | -| Missing quotes fabricate aggregate account P&L | Critical | Accept | Phase 3 | -| Migration has no overall deadline | High | Accept | Phase 1 | -| Partial stock quote maps can look successful | High | Accept | Phase 1 | -| Corrupt/noncanonical legacy symbols are ambiguous | High | Accept | Phase 1 | -| Migration conflicts lack retry semantics | Medium | Accept | Phase 1 | -| Coin quantity dust can orphan monetary basis | Medium | Accept | Phase 2 | -| Invalid basis can be silently normalized | Medium | Accept | Phases 1–2 | -| Stock output order and reply size are unsafe | Medium | Accept | Phase 3 | -| Storage-wide pagination and migration lease | High | Reject | Deadline and fail-closed runtime validation fit the one-replica deployment | -| Persist quote manifests and correction tooling | High | Reject | Outside paper-trading scope; validate complete finite quotes and log context | -| Restrict portfolio commands to private chats | Medium | Reject | Pre-existing visibility contract outside this accounting change | - -### Whole-Plan Consistency Sweep - -- Files reread: `plan.md` and all three phase files. -- Decision deltas checked: every-boot invariant scan, fail-closed runtime checks, - bounded startup, quote completeness, corrupt-symbol rejection, CAS retry, - dust cleanup, missing-price suppression, sorting, and reply bounds. -- Reconciled stale references: marker short-circuiting and partial aggregate P&L. -- Unresolved contradictions: 0. diff --git a/plans/reports/260720-1612-stock-dividend-command-brainstorm.md b/plans/reports/260720-1612-stock-dividend-command-brainstorm.md deleted file mode 100644 index 9b3b16a..0000000 --- a/plans/reports/260720-1612-stock-dividend-command-brainstorm.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -type: brainstorm-report -topic: stock-dividend-command-naming -created_at: 2026-07-20T16:12:33+07:00 -status: approved -modes: - html: false - wiki: false ---- - -# Brainstorm Report: Stock Dividend Commands - -## Summary - -Approved three explicit commands: - -```text -/stock_cash_dividend -/stock_share_dividend -/stock_dividend -``` - -Cash uses VND per existing share. Share distributions use the official-notice -ratio form, such as `4:1` or `100:10`. Mixed dividends calculate both outcomes -from the pre-event holding and persist them atomically. - -## Problem-First Analysis - -### Solution-Jumping Diagnosis - -The original names encoded outcomes inconsistently: `/stock_bonus` added shares -while `/stock_dividend` added cash. Users could not distinguish a true bonus -share event from a stock dividend or represent a mixed dividend notice. - -### Underlying Problem - -Users need command names and inputs that map directly to Vietnamese dividend -notices, while preserving simple manual paper-portfolio accounting. - -### Assumptions and Validation - -| Assumption | Risk if wrong | Validation | -|---|---|---| -| Notices provide share ratios | Users must calculate quantities | Confirmed by VSDC `4:1` and `100:10` notices | -| Cash per share is sufficient | Percentage input may be expected | Notices explicitly state VND received per share | -| Fractions round down | Portfolio could over-credit shares | Confirmed by both VSDC examples | -| Mixed event uses one record-date holding | Cash may include newly issued shares | Calculate both before mutation | - -### Problem Statement - -Paper-trading users cannot accurately record cash-only, share-only, and mixed -dividends because current command semantics are incomplete and `stock_bonus` -does not necessarily mean a stock dividend. Success means each notice maps to -one obvious command and produces auditable integer-share and cash results. - -### Alternative Framings - -1. Keep current commands: smallest change, but mixed events need two commands - and terminology remains ambiguous. -2. Use explicit cash/share commands plus a combined command: clear, direct, - matches actual notices. Selected. -3. Add a generic corporate-action command with subtypes: extensible but too - complex for three small manual operations. - -### Evidence Status - -Strong for syntax and rounding: two recent official VSDC mixed-dividend notices -use cash-per-share explanations, `owned:new` ratios, and floor rounding. - -### Validation Plan - -- Unit-test ratio parsing, overflow boundaries, and floor division. -- Table-test cash-only, share-only, and combined handlers. -- Verify combined calculation uses the same pre-event holding. -- Test one-time stats migrations for anonymous and per-user rows. -- Reject the design if real notices require unsupported fractional settlement - rather than discarded fractions. - -### Stakeholder Message - -Use explicit cash and share commands for single-form notices, and the combined -command for mixed notices. Inputs mirror VSDC wording, reducing manual math and -making bot replies easy to compare against source notices. - -## Evaluated Approaches - -| Approach | Pros | Cons | Decision | -|---|---|---|---| -| Absolute new-share quantity | Reuses current handler behavior | Manual calculation; hides rounding | Reject | -| Percentage share input | Familiar shorthand | Ambiguous; less faithful to notices | Reject | -| `owned:new` share ratio | Mirrors notices; deterministic | Needs parser and integer safety | Approve | - -## Approved Behavior - -### Cash-only - -```text -/stock_cash_dividend 1500 IDC -cash = existing_shares * 1500 VND -``` - -### Share-only - -```text -/stock_share_dividend 100:10 IDC -new_shares = floor(existing_shares * 10 / 100) -``` - -Reject a share-only event when the valid ratio produces zero new shares and -report the minimum holding needed. - -### Mixed - -```text -/stock_dividend 1500 100:10 IDC -``` - -Calculate cash and new shares from the same pre-event holding. When the share -result is zero, still credit cash and report zero new shares. Save once so cash -and shares cannot diverge after a partial failure. - -## Compatibility and Touchpoints - -- Rename `/stock_bonus` to `/stock_share_dividend`. -- Move historical `/stock_bonus` stats to `/stock_share_dividend`. -- Move historical cash-only `/stock_dividend` stats to - `/stock_cash_dividend` before reusing `/stock_dividend` for mixed events. -- Update `internal/modules/stock/stock.go`, handlers, handler tests, and - `telegram-commands.json`. -- Add idempotent startup migration through the shared `system` collection; - cover anonymous rows, per-user rows, target-row merging, and repeated startup. - -## Risks - -- Integer multiplication can overflow before division; validate bounds or use a - safe quotient/remainder calculation. -- Reusing `/stock_dividend` without migrating old stats mislabels historical - cash-only usage as combined usage. -- Applying shares before cash calculation overpays the same event. -- Accepting decimal or zero ratio components creates undefined rounding. - -## Success Criteria - -- `4:1` and `100:10` parse; malformed or non-positive ratios fail clearly. -- Share calculations use integer floor semantics. -- Cash/share/mixed commands match their documented examples. -- Mixed updates are atomic and use pre-event holdings. -- Existing command statistics remain preserved under the correct new meanings. -- Focused tests, `go test ./...`, and `go vet ./...` pass. - -## Sources - -- [VSDC IDC mixed dividend notice](https://www.vsd.vn/vi/ad/197421) -- [VSDC PTB mixed dividend notice](https://vsd.vn/vi/ad/195203) -- [Supporting research report](./260720-1608-dividend-notice-ratio-research.md) - -## Next Steps - -Create a tests-first implementation plan because this change renames public -commands, changes persisted stats attribution, and modifies financial -calculation behavior. - -## Unresolved Questions - -None. diff --git a/plans/reports/pm-260721-1705-portfolio-cleanup.md b/plans/reports/pm-260721-1705-portfolio-cleanup.md deleted file mode 100644 index 7b4ae55..0000000 --- a/plans/reports/pm-260721-1705-portfolio-cleanup.md +++ /dev/null @@ -1,48 +0,0 @@ -# Portfolio Cleanup Completion Report - -## Summary - -| Item | Result | -|---|---| -| Coin dividend cursor | Removed from schema, API, validation, callers, tests | -| Stock dividend cursor | Retained unchanged | -| Portfolio migrations | Completed runtime paths and migration-only tests removed | -| Stats rename migration | Removed; recurring indexes retained | -| LoL startup maintenance | TTL index retained | -| Historical system data | Untouched; reusable helper retained | -| Dividend API research | SSI iBoard recommended behind provider interface | - -## Verification - -- Focused and full Go tests passed. -- MongoDB 8 stats-index and LoL TTL-index tests executed and passed. -- `go vet ./...`, `go build ./...`, and `golangci-lint run` passed. -- `git diff --check` passed. -- Independent tester, debugger, and reviewer reported no defects. -- Coin BSON regression coverage proves a stale cursor loads safely and is - omitted by the next whole-document encoding/write. - -## Documentation - -- README and deployment guide now distinguish stock and coin asset schemas. -- Standalone dividend API research report added under `plans/reports/`. -- Existing completed implementation plans remain historical records; no phase - statuses changed. - -## Known Limitations - -- Untouched MongoDB coin documents retain the ignored cursor until their next - portfolio write. -- SSI iBoard corporate actions are undocumented and have no published SLA. - -## Next Steps - -1. Commit the approved cleanup when requested. -2. Design the Telegram dividend-event selection/confirmation interaction. -3. Implement SSI behind a replaceable provider interface after command design - approval. - -## Unresolved Questions - -- Should dividend lookup inspect one ticker per command or all stock assets? -- Should an event only prefill guidance or execute after explicit confirmation? diff --git a/plans/reports/pm-260721-1815-stock-dividend-event-buttons.md b/plans/reports/pm-260721-1815-stock-dividend-event-buttons.md deleted file mode 100644 index 068548e..0000000 --- a/plans/reports/pm-260721-1815-stock-dividend-event-buttons.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -type: project-status-report -topic: stock-dividend-event-buttons -reported_at: 2026-07-21T18:15:00+07:00 -status: completed ---- - -# Stock Dividend Event Buttons Completion - -## Summary - -`/stock_portfolio` now checks SSI dividend events for held tickers after sending -the portfolio. Relevant events receive owner-bound, expiring Apply buttons; -successful checks with no events remain silent. - -## Completed - -- [x] Replaceable SSI provider with pagination, local classification, exact - share ratios, event-ID deduplication, and bounded date overlap. -- [x] Per-ticker cursor checks that preserve concurrent portfolio writes. -- [x] Opaque 24-hour pending actions bound to caller, chat, and message. -- [x] Current-holding cash/share application with replay-safe event ledger. -- [x] Position lifecycle binding that rejects buttons after full sale/reopen. -- [x] Shared callback registration, visibility validation, and authorization. -- [x] README and regression tests updated. - -## Verification - -| Gate | Result | -|---|---| -| Focused stock/modules tests | Passed | -| Full `go test -count=1 ./...` | Passed | -| Stock race and concurrency stress tests | Passed | -| `go vet ./...` | Passed | -| `golangci-lint run` | Passed, 0 issues | -| `go build ./...` | Passed | -| Independent final review | Passed, no blockers | - -## Plan Sync - -No active phase plan maps to this incremental request. Existing stock-dividend -and cost-basis plans were already completed before this work and remain -unchanged. Session task tracking is fully complete. - -## Remaining Risks - -- SSI iBoard is undocumented and best-effort. -- SSI publication timestamps are day-granularity; a bounded overlap prevents - same-day loss, while pending actions and applied IDs suppress duplicates. -- The portfolio has no dated-lot ledger, so acceptance uses holdings at click - time rather than legal record-date entitlement. -- Manual dividend commands cannot attach SSI event IDs; users must not apply - the same event both manually and through its button. - -## Next Step - -Commit only after explicit user approval.