mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-09-03 16:16:57 +00:00
docs: remove completed implementation records
This commit is contained in:
@@ -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.
|
||||
@@ -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 <vnd_per_share> <TICKER>` for cash dividends.
|
||||
- Approved `/stock_share_dividend <owned:new> <TICKER>` for share dividends.
|
||||
- Approved `/stock_dividend <vnd_per_share> <owned:new> <TICKER>` 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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 <vnd_per_share> <TICKER>` for positive whole-VND
|
||||
cash credits.
|
||||
- Added `/stock_share_dividend <owned:new> <TICKER>` with unreduced ratio
|
||||
preservation and floor-rounded whole shares.
|
||||
- Changed `/stock_dividend <vnd_per_share> <owned:new> <TICKER>` 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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -1,64 +0,0 @@
|
||||
# Telegram Command Discovery Journal
|
||||
|
||||
> Historical note: the later command-parameter convention simplifies
|
||||
> `<options(comma-separated)>` to `<option,...>`, 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 <username> | cmd <command_name>]`.
|
||||
- Normalized placeholders to lowercase descriptive names, including meaningful
|
||||
units or currencies; `[...]` marks optional input, `...` remaining free text,
|
||||
and parentheses structured input.
|
||||
- Normalized `/wheelofnames` to `<options(comma-separated)>` across metadata,
|
||||
usage text, and tests.
|
||||
- Finalized dividend placeholders as `<vnd_per_share> <ticker>`,
|
||||
`<ratio(owned:new)> <ticker>`, and
|
||||
`<vnd_per_share> <ratio(owned:new)> <ticker>` 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.
|
||||
@@ -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 `<option,...>` notation for required
|
||||
comma-separated values.
|
||||
- Updated `/random` and `/wheelofnames` metadata, usage text, and contract tests
|
||||
from `<options(comma-separated)>` to `<option,...>`.
|
||||
- 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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
---
|
||||
phase: 1
|
||||
title: Implement Ratio Dividend Commands
|
||||
status: completed
|
||||
effort: ''
|
||||
priority: P1
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
# Phase 1: Implement Ratio Dividend Commands
|
||||
|
||||
<!-- Updated: Validation Session 1 - manual-input policy and ratio/cash contracts -->
|
||||
|
||||
## 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.
|
||||
@@ -1,113 +0,0 @@
|
||||
---
|
||||
phase: 2
|
||||
title: Migrate Command Statistics
|
||||
status: completed
|
||||
effort: ''
|
||||
priority: P1
|
||||
dependencies:
|
||||
- 1
|
||||
---
|
||||
|
||||
# Phase 2: Migrate Command Statistics
|
||||
|
||||
<!-- Updated: Validation Session 1 - prepared-checkpoint retry and retention -->
|
||||
|
||||
## 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.
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
phase: 3
|
||||
title: Verify User Contracts
|
||||
status: completed
|
||||
effort: ''
|
||||
priority: P1
|
||||
dependencies:
|
||||
- 1
|
||||
- 2
|
||||
---
|
||||
|
||||
# Phase 3: Verify User Contracts
|
||||
|
||||
<!-- Updated: Validation Session 1 - document manual caller responsibility -->
|
||||
|
||||
## 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.
|
||||
@@ -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 <vnd_per_share> <TICKER>` credits a positive whole-VND amount per share.
|
||||
- `/stock_share_dividend <owned:new> <TICKER>` accepts any positive integer ratio, preserves its entered form in replies, and adds floor-rounded shares; zero result rejects.
|
||||
- `/stock_dividend <vnd_per_share> <owned:new> <TICKER>` 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.
|
||||
-43
@@ -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.
|
||||
-84
@@ -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.
|
||||
-81
@@ -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.
|
||||
-86
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 <vnd_per_share> <TICKER>
|
||||
/stock_share_dividend <owned:new> <TICKER>
|
||||
/stock_dividend <vnd_per_share> <owned:new> <TICKER>
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -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?
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user